commit a5883f0402ce0758ea9eb220370788a432084b33 Author: Suhaylzhao <838119509@qq.com> Date: Fri Aug 19 11:54:07 2016 +0800 First publish Apply to FNK0020 diff --git a/Code/C_Code/00.0.0_Hello/Hello.c b/Code/C_Code/00.0.0_Hello/Hello.c new file mode 100644 index 0000000..9a3f624 --- /dev/null +++ b/Code/C_Code/00.0.0_Hello/Hello.c @@ -0,0 +1,7 @@ +#include + +int main(){ + printf("hello, world!\n"); + + return 1; +} \ No newline at end of file diff --git a/Code/C_Code/01.1.1_Blink/Blink.c b/Code/C_Code/01.1.1_Blink/Blink.c new file mode 100644 index 0000000..b0b10d4 --- /dev/null +++ b/Code/C_Code/01.1.1_Blink/Blink.c @@ -0,0 +1,34 @@ +/********************************************************************** +* Filename : Blink.c +* Description : Make an led blinking. +* auther : www.freenove.com +* modification: 2016/06/07 +**********************************************************************/ +#include +#include + +#define ledPin 0 + +int main(void) +{ + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + //when initialize wiring successfully,print message to screen + printf("wiringPi initialize successfully, GPIO %d(wiringPi pin)\n",ledPin); + + pinMode(ledPin, OUTPUT);//Set the pin mode + + while(1){ + digitalWrite(ledPin, HIGH); //led on + printf("led on...\n"); + delay(1000); + digitalWrite(ledPin, LOW); //led off + printf("...led off\n"); + delay(1000); + } + + return 0; +} + diff --git a/Code/C_Code/02.1.1_ButtonLED/ButtonLED.c b/Code/C_Code/02.1.1_ButtonLED/ButtonLED.c new file mode 100644 index 0000000..595099a --- /dev/null +++ b/Code/C_Code/02.1.1_ButtonLED/ButtonLED.c @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : ButtonLED.c +* Description : Controlling an led by button. +* Author : freenove +* modification: 2016/06/12 +**********************************************************************/ +#include +#include + +#define ledPin 0 //define the ledPin +#define buttonPin 1 //define the buttonPin + +int main(void) +{ + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + + pinMode(ledPin, OUTPUT); //Set ledPin output + pinMode(buttonPin, INPUT);//Set buttonPin input + + pullUpDnControl(buttonPin, PUD_UP); //pull up to high level + while(1){ + + if(digitalRead(buttonPin) == LOW){ //button has pressed down + digitalWrite(ledPin, HIGH); //led on + printf("led on...\n"); + } + else { //button has released + digitalWrite(ledPin, LOW); //led off + printf("...led off\n"); + } + } + + return 0; +} + diff --git a/Code/C_Code/02.2.1_TableLamp/TableLamp.c b/Code/C_Code/02.2.1_TableLamp/TableLamp.c new file mode 100644 index 0000000..58020eb --- /dev/null +++ b/Code/C_Code/02.2.1_TableLamp/TableLamp.c @@ -0,0 +1,63 @@ +/********************************************************************** +* Filename : Tablelamp.c +* Description : a DIY MINI table lamp +* Author : freenove +* modification: 2016/06/13 +**********************************************************************/ +#include +#include + +#define ledPin 0 //define the ledPin +#define buttonPin 1 //define the buttonPin +int ledState=LOW; //store the State of led +int buttonState=HIGH; //store the State of button +int lastbuttonState=HIGH;//store the lastState of button +long lastChangeTime; //store the change time of button state +long captureTime=50; //set the button state stable time +int reading; +int main(void) +{ + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + printf("Program is starting...\n"); + pinMode(ledPin, OUTPUT); + pinMode(buttonPin, INPUT); + + pullUpDnControl(buttonPin, PUD_UP); //pull up to high level + while(1){ + reading = digitalRead(buttonPin); //read the current state of button + if( reading != lastbuttonState){ //if the button state has changed ,record the time point + lastChangeTime = millis(); + } + //if changing-state of the button last beyond the time we set,we considered that + //the current button state is an effective change rather than a buffeting + if(millis() - lastChangeTime > captureTime){ + //if button state is changed ,update the data. + if(reading != buttonState){ + buttonState = reading; + //if the state is low ,the action is pressing + if(buttonState == LOW){ + printf("Button is pressed!\n"); + ledState = !ledState; //Turn the LED state . + if(ledState){ + printf("turn on LED ...\n"); + } + else { + printf("turn off LED ...\n"); + } + } + //if the state is high ,the action is releasing + else { + printf("Button is released!\n"); + } + } + } + digitalWrite(ledPin,ledState); + lastbuttonState = reading; + } + + return 0; +} + diff --git a/Code/C_Code/03.1.1_LightWater/LightWater b/Code/C_Code/03.1.1_LightWater/LightWater new file mode 100644 index 0000000..6e60d95 Binary files /dev/null and b/Code/C_Code/03.1.1_LightWater/LightWater differ diff --git a/Code/C_Code/03.1.1_LightWater/LightWater.c b/Code/C_Code/03.1.1_LightWater/LightWater.c new file mode 100644 index 0000000..5f13f17 --- /dev/null +++ b/Code/C_Code/03.1.1_LightWater/LightWater.c @@ -0,0 +1,46 @@ +/********************************************************************** +* Filename : LightWater.c +* Description : Display 10 LEDBar Graph +* Author : freenove +* modification: 2016/06/13 +**********************************************************************/ +#include +#include +#define leds 10 +int pins[leds] = {0,1,2,3,4,5,6,8,9,10}; +void led_on(int n)//make led_n on +{ + digitalWrite(n, LOW); +} + +void led_off(int n)//make led_n off +{ + digitalWrite(n, HIGH); +} + +int main(void) +{ + int i; + printf("Program is starting ... \n"); + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + for(i=0;i-1;i--){ //make led on from right to left + led_on(pins[i]); + delay(100); + led_off(pins[i]); + } + } + return 0; +} + diff --git a/Code/C_Code/04.1.1_BreathingLED/BreathingLED b/Code/C_Code/04.1.1_BreathingLED/BreathingLED new file mode 100644 index 0000000..34e8de2 Binary files /dev/null and b/Code/C_Code/04.1.1_BreathingLED/BreathingLED differ diff --git a/Code/C_Code/04.1.1_BreathingLED/BreathingLED.c b/Code/C_Code/04.1.1_BreathingLED/BreathingLED.c new file mode 100644 index 0000000..36e1e3a --- /dev/null +++ b/Code/C_Code/04.1.1_BreathingLED/BreathingLED.c @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : BreathingLED.c +* Description : A breathing LED +* Author : freenove +* modification: 2016/06/14 +**********************************************************************/ + +#include +#include + +#define ledPin 1 //Only GPIO18 can output PWM + +int main(void) +{ + int i; + + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + + pinMode(ledPin, PWM_OUTPUT);//pwm output mode + + while(1){ + for(i=0;i<1024;i++){ + pwmWrite(ledPin, i); + delay(2); + } + delay(300); + for(i=1023;i>=0;i--){ + pwmWrite(ledPin, i); + delay(2); + } + delay(300); + } + return 0; +} + diff --git a/Code/C_Code/05.1.1_ColorfulLED/ColorfulLED.c b/Code/C_Code/05.1.1_ColorfulLED/ColorfulLED.c new file mode 100644 index 0000000..c393714 --- /dev/null +++ b/Code/C_Code/05.1.1_ColorfulLED/ColorfulLED.c @@ -0,0 +1,48 @@ +/********************************************************************** +* Filename : ColorfulLED.c +* Description : A auto flash ColorfulLED +* Author : freenove +* modification: 2016/06/14 +**********************************************************************/ +#include +#include +#include + +#define ledPinRed 0 +#define ledPinGreen 1 +#define ledPinBlue 2 + +void ledInit(void) +{ + softPwmCreate(ledPinRed, 0, 100);//Creat SoftPWM pin + softPwmCreate(ledPinGreen,0, 100); + softPwmCreate(ledPinBlue, 0, 100); +} + +void ledColorSet(int r_val, int g_val, int b_val) +{ + softPwmWrite(ledPinRed, r_val);//Set the duty cycle + softPwmWrite(ledPinGreen, g_val); + softPwmWrite(ledPinBlue, b_val); +} + +int main(void) +{ + int r,g,b; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + printf("Program is starting ...\n"); + ledInit(); + + while(1){ + r=random()%100;//get a random in (0,100) + g=random()%100; + b=random()%100; + ledColorSet(r,g,b);//set random as a duty cycle value + printf("r=%d, g=%d, b=%d \n",r,g,b); + delay(300); + } + return 0; +} diff --git a/Code/C_Code/06.1.1_Doorbell/Doorbell b/Code/C_Code/06.1.1_Doorbell/Doorbell new file mode 100644 index 0000000..8599b9c Binary files /dev/null and b/Code/C_Code/06.1.1_Doorbell/Doorbell differ diff --git a/Code/C_Code/06.1.1_Doorbell/Doorbell.c b/Code/C_Code/06.1.1_Doorbell/Doorbell.c new file mode 100644 index 0000000..73d4a96 --- /dev/null +++ b/Code/C_Code/06.1.1_Doorbell/Doorbell.c @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : Doorbell.c +* Description : Controlling an buzzer by button. +* Author : freenove +* modification: 2016/06/12 +**********************************************************************/ +#include +#include + +#define buzzerPin 0 //define the buzzerPin +#define buttonPin 1 //define the buttonPin + +int main(void) +{ + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + + pinMode(buzzerPin, OUTPUT); + pinMode(buttonPin, INPUT); + + pullUpDnControl(buttonPin, PUD_UP); //pull up to high level + while(1){ + + if(digitalRead(buttonPin) == LOW){ //button has pressed down + digitalWrite(buzzerPin, HIGH); //buzzer on + printf("buzzer on...\n"); + } + else { //button has released + digitalWrite(buzzerPin, LOW); //buzzer off + printf("...buzzer off\n"); + } + } + + return 0; +} + diff --git a/Code/C_Code/06.2.1_Alertor/Alertor.c b/Code/C_Code/06.2.1_Alertor/Alertor.c new file mode 100644 index 0000000..932c4d1 --- /dev/null +++ b/Code/C_Code/06.2.1_Alertor/Alertor.c @@ -0,0 +1,50 @@ +/********************************************************************** +* Filename : Alertor.c +* Description : Alarm by button. +* Author : freenove +* modification: 2016/06/14 +**********************************************************************/ +#include +#include +#include +#include + +#define buzzerPin 0 //define the buzzerPin +#define buttonPin 1 //define the buttonPin + +void alertor(int pin){ + int x; + double sinVal, toneVal; + for(x=0;x<360;x++){ //frequency of the alarm along the sine wave change + sinVal = sin(x * (M_PI / 180)); //calculate the sine value + toneVal = 2000 + sinVal * 500; //Add to the resonant frequency with a Weighted + softToneWrite(pin,toneVal); //output PWM + delay(1); + } +} +void stopAlertor(int pin){ + softToneWrite(pin,0); +} +int main(void) +{ + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pinMode(buzzerPin, OUTPUT); + pinMode(buttonPin, INPUT); + softToneCreate(buzzerPin); + pullUpDnControl(buttonPin, PUD_UP); //pull up to high level + while(1){ + if(digitalRead(buttonPin) == LOW){ //button has pressed down + alertor(buzzerPin); //buzzer on + printf("alertor on...\n"); + } + else { //button has released + stopAlertor(buzzerPin); //buzzer off + printf("...buzzer off\n"); + } + } + return 0; +} + diff --git a/Code/C_Code/06.2.1_Alertor/alertor b/Code/C_Code/06.2.1_Alertor/alertor new file mode 100644 index 0000000..10ef2c2 Binary files /dev/null and b/Code/C_Code/06.2.1_Alertor/alertor differ diff --git a/Code/C_Code/07.1.1_PCF8591/PCF8591 b/Code/C_Code/07.1.1_PCF8591/PCF8591 new file mode 100644 index 0000000..2b121f6 Binary files /dev/null and b/Code/C_Code/07.1.1_PCF8591/PCF8591 differ diff --git a/Code/C_Code/07.1.1_PCF8591/PCF8591.c b/Code/C_Code/07.1.1_PCF8591/PCF8591.c new file mode 100644 index 0000000..44816a0 --- /dev/null +++ b/Code/C_Code/07.1.1_PCF8591/PCF8591.c @@ -0,0 +1,32 @@ +/********************************************************************** +* Filename : PCF8591.c +* Description : ADC and DAC +* Author : freenove +* modification: 2016/06/14 +**********************************************************************/ + +#include +#include +#include + +#define address 0x48 //pcf8591 default address +#define pinbase 64 //any number above 64 +#define A0 pinbase + 0 +#define A1 pinbase + 1 +#define A2 pinbase + 2 +#define A3 pinbase + 3 + +int main(void){ + int value; + float voltage; + wiringPiSetup(); + pcf8591Setup(pinbase,address); + + while(1){ + value = analogRead(A0); //read A0 pin + analogWrite(pinbase+0,value); + voltage = (float)value / 255.0 * 3.3; // calculate voltage + printf("ADC value : %d ,\tVoltage : %.2fV\n",value,voltage); + delay(100); + } +} diff --git a/Code/C_Code/08.1.1_Softlight/Softlight b/Code/C_Code/08.1.1_Softlight/Softlight new file mode 100644 index 0000000..f178463 Binary files /dev/null and b/Code/C_Code/08.1.1_Softlight/Softlight differ diff --git a/Code/C_Code/08.1.1_Softlight/Softlight.c b/Code/C_Code/08.1.1_Softlight/Softlight.c new file mode 100644 index 0000000..1bf9451 --- /dev/null +++ b/Code/C_Code/08.1.1_Softlight/Softlight.c @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : Softlight.c +* Description : Potentiometer control LED +* Author : freenove +* modification: 2016/06/18 +**********************************************************************/ +#include +#include +#include +#include + +#define address 0x48 //pcf8591 default address +#define pinbase 64 //any number above 64 +#define A0 pinbase + 0 +#define A1 pinbase + 1 +#define A2 pinbase + 2 +#define A3 pinbase + 3 + +#define ledPin 0 +int main(void){ + int value; + float voltage; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + softPwmCreate(ledPin,0,100); + pcf8591Setup(pinbase,address); + + while(1){ + value = analogRead(A0); //read A0 pin + softPwmWrite(ledPin,value*100/255); + voltage = (float)value / 255.0 * 3.3; // calculate voltage + printf("ADC value : %d ,\tVoltage : %.2fV\n",value,voltage); + delay(100); + } + return 0; +} diff --git a/Code/C_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight b/Code/C_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight new file mode 100644 index 0000000..5f08e2a Binary files /dev/null and b/Code/C_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight differ diff --git a/Code/C_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight.c b/Code/C_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight.c new file mode 100644 index 0000000..5d57c63 --- /dev/null +++ b/Code/C_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight.c @@ -0,0 +1,45 @@ +/********************************************************************** +* Filename : ColorfulSoftlight.c +* Description : Potentiometer control RGBLED +* Author : freenove +* modification: 2016/07/03 +**********************************************************************/ +#include +#include +#include +#include + +#define address 0x48 //pcf8591 default address +#define pinbase 64 //any number above 64 +#define A0 pinbase + 0 +#define A1 pinbase + 1 +#define A2 pinbase + 2 +#define A3 pinbase + 3 + +#define ledRedPin 3 //define 3 pins of RGBLED +#define ledGreenPin 2 +#define ledBluePin 0 +int main(void){ + int val_Red,val_Green,val_Blue; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + softPwmCreate(ledRedPin,0,100); //creat 3 PMW output pins for RGBLED + softPwmCreate(ledGreenPin,0,100); + softPwmCreate(ledBluePin,0,100); + pcf8591Setup(pinbase,address); //initialize PCF8591 + + while(1){ + val_Red = analogRead(A0); //read 3 potentiometers + val_Green = analogRead(A1); + val_Blue = analogRead(A2); + softPwmWrite(ledRedPin,val_Red*100/255); //map the read value of potentiometers into PWM value and output it + softPwmWrite(ledGreenPin,val_Green*100/255); + softPwmWrite(ledBluePin,val_Blue*100/255); + //print out the read ADC value + printf("ADC value val_Red: %d ,\tval_Green: %d ,\tval_Blue: %d \n",val_Red,val_Green,val_Blue); + delay(100); + } + return 0; +} diff --git a/Code/C_Code/10.1.1_Nightlamp/Nightlamp b/Code/C_Code/10.1.1_Nightlamp/Nightlamp new file mode 100644 index 0000000..292f282 Binary files /dev/null and b/Code/C_Code/10.1.1_Nightlamp/Nightlamp differ diff --git a/Code/C_Code/10.1.1_Nightlamp/Nightlamp.c b/Code/C_Code/10.1.1_Nightlamp/Nightlamp.c new file mode 100644 index 0000000..9c1ece2 --- /dev/null +++ b/Code/C_Code/10.1.1_Nightlamp/Nightlamp.c @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : Nightlamp.c +* Description : Photoresistor control LED +* Author : freenove +* modification: 2016/06/18 +**********************************************************************/ +#include +#include +#include +#include + +#define address 0x48 //pcf8591 default address +#define pinbase 64 //any number above 64 +#define A0 pinbase + 0 +#define A1 pinbase + 1 +#define A2 pinbase + 2 +#define A3 pinbase + 3 + +#define ledPin 0 +int main(void){ + int value; + float voltage; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + softPwmCreate(ledPin,0,100); + pcf8591Setup(pinbase,address); + + while(1){ + value = analogRead(A0); //read A0 pin + softPwmWrite(ledPin,value*100/255); + voltage = (float)value / 255.0 * 3.3; // calculate voltage + printf("ADC value : %d ,\tVoltage : %.2fV\n",value,voltage); + delay(100); + } + return 0; +} diff --git a/Code/C_Code/11.1.1_Thermometer/Thermometer b/Code/C_Code/11.1.1_Thermometer/Thermometer new file mode 100644 index 0000000..324d080 Binary files /dev/null and b/Code/C_Code/11.1.1_Thermometer/Thermometer differ diff --git a/Code/C_Code/11.1.1_Thermometer/Thermometer.c b/Code/C_Code/11.1.1_Thermometer/Thermometer.c new file mode 100644 index 0000000..30f1792 --- /dev/null +++ b/Code/C_Code/11.1.1_Thermometer/Thermometer.c @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : Thermometer.c +* Description : A DIY Thermometer +* Author : freenove +* modification: 2016/06/20 +**********************************************************************/ +#include +#include +#include +#include + +#define address 0x48 //pcf8591 default address +#define pinbase 64 //any number above 64 +#define A0 pinbase + 0 +#define A1 pinbase + 1 +#define A2 pinbase + 2 +#define A3 pinbase + 3 + +int main(void){ + int adcValue; + float tempK,tempC; + float voltage,Rt; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pcf8591Setup(pinbase,address); + while(1){ + adcValue = analogRead(A0); //read A0 pin + voltage = (float)adcValue / 255.0 * 3.3; // calculate voltage + Rt = 10 * voltage / (3.3 - voltage); //calculate resistance value of thermistor + tempK = 1/(1/(273.15 + 25) + log(Rt/10)/3950.0); //calculate temperature (Kelvin) + tempC = tempK -273.15; //calculate temperature (Celsius) + printf("ADC value : %d ,\tVoltage : %.2fV, \tTemperature : %.2fC\n",adcValue,voltage,tempC); + delay(100); + } + return 0; +} diff --git a/Code/C_Code/12.1.1_Joystick/Joystick b/Code/C_Code/12.1.1_Joystick/Joystick new file mode 100644 index 0000000..933e079 Binary files /dev/null and b/Code/C_Code/12.1.1_Joystick/Joystick differ diff --git a/Code/C_Code/12.1.1_Joystick/Joystick.c b/Code/C_Code/12.1.1_Joystick/Joystick.c new file mode 100644 index 0000000..a2fc169 --- /dev/null +++ b/Code/C_Code/12.1.1_Joystick/Joystick.c @@ -0,0 +1,40 @@ +/********************************************************************** +* Filename : Joystick.c +* Description : Read Joystick +* Author : freenove +* modification: 2016/07/04 +**********************************************************************/ +#include +#include +#include +#include + +#define address 0x48 //pcf8591 default address +#define pinbase 64 //any number above 64 +#define A0 pinbase + 0 +#define A1 pinbase + 1 +#define A2 pinbase + 2 +#define A3 pinbase + 3 + +#define Z_Pin 1 //define pin for axis Z + +int main(void){ + int val_X,val_Y,val_Z; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pinMode(Z_Pin,INPUT); //set Z_Pin as input pin and pull-up mode + pullUpDnControl(Z_Pin,PUD_UP); + pcf8591Setup(pinbase,address); //initialize PCF8591 + + while(1){ + val_Z = digitalRead(Z_Pin); //read digital quality of axis Z + val_Y = analogRead(A1); //read analog quality of axis X and Y + val_X = analogRead(A2); + printf("val_X: %d ,\tval_Y: %d ,\tval_Z: %d \n",val_X,val_Y,val_Z); + delay(100); + } + return 0; +} + diff --git a/Code/C_Code/13.1.1_Motor/Motor b/Code/C_Code/13.1.1_Motor/Motor new file mode 100644 index 0000000..7df7d28 Binary files /dev/null and b/Code/C_Code/13.1.1_Motor/Motor differ diff --git a/Code/C_Code/13.1.1_Motor/Motor.c b/Code/C_Code/13.1.1_Motor/Motor.c new file mode 100644 index 0000000..5a63b9b --- /dev/null +++ b/Code/C_Code/13.1.1_Motor/Motor.c @@ -0,0 +1,69 @@ +/********************************************************************** +* Filename : Motor.c +* Description : Control Motor by L293D +* Author : freenove +* modification: 2016/06/18 +**********************************************************************/ +#include +#include +#include +#include +#include +#include + +#define address 0x48 //pcf8591 default address +#define pinbase 64 //any number above 64 +#define A0 pinbase + 0 +#define A1 pinbase + 1 +#define A2 pinbase + 2 +#define A3 pinbase + 3 + +#define motorPin1 2 //define the pin connected to L293D +#define motorPin2 0 +#define enablePin 3 +//Map function: map the value from a range of mapping to another range. +long map(long value,long fromLow,long fromHigh,long toLow,long toHigh){ + return (toHigh-toLow)*(value-fromLow) / (fromHigh-fromLow) + toLow; +} +//motor function: determine the direction and speed of the motor according to the ADC +void motor(int ADC){ + int value = ADC -128; + if(value>0){ + digitalWrite(motorPin1,HIGH); + digitalWrite(motorPin2,LOW); + printf("turn Forward...\n"); + } + else if (value<0){ + digitalWrite(motorPin1,LOW); + digitalWrite(motorPin2,HIGH); + printf("turn Back...\n"); + } + else { + digitalWrite(motorPin1,LOW); + digitalWrite(motorPin2,LOW); + printf("Motor Stop...\n"); + } + softPwmWrite(enablePin,map(abs(value),0,128,0,255)); + printf("The PWM duty cycle is %d%%\n",abs(value)*100/127);//print the PMW duty cycle +} +int main(void){ + int value; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pinMode(enablePin,OUTPUT);//set mode for the pin + pinMode(motorPin1,OUTPUT); + pinMode(motorPin2,OUTPUT); + softPwmCreate(enablePin,0,100);//define PMW pin + pcf8591Setup(pinbase,address);//initialize PCF8591 + + while(1){ + value = analogRead(A0); //read A0 pin + printf("ADC value : %d \n",value); + motor(value); //start the motor + delay(100); + } + return 0; +} + diff --git a/Code/C_Code/14.1.1_Relay/Relay b/Code/C_Code/14.1.1_Relay/Relay new file mode 100644 index 0000000..64edd97 Binary files /dev/null and b/Code/C_Code/14.1.1_Relay/Relay differ diff --git a/Code/C_Code/14.1.1_Relay/Relay.c b/Code/C_Code/14.1.1_Relay/Relay.c new file mode 100644 index 0000000..8ec464e --- /dev/null +++ b/Code/C_Code/14.1.1_Relay/Relay.c @@ -0,0 +1,62 @@ +/********************************************************************** +* Filename : Relay.c +* Description : Button control Relay and Motor +* Author : freenove +* modification: 2016/07/05 +**********************************************************************/ +#include +#include + +#define relayPin 0 //define the relayPin +#define buttonPin 1 //define the buttonPin +int relayState=LOW; //store the State of relay +int buttonState=HIGH; //store the State of button +int lastbuttonState=HIGH;//store the lastState of button +long lastChangeTime; //store the change time of button state +long captureTime=50; //set the button state stable time +int reading; +int main(void) +{ + if(wiringPiSetup() == -1){ //when initialize wiring fairelay,print messageto screen + printf("setup wiringPi fairelay !"); + return 1; + } + printf("Program is starting...\n"); + pinMode(relayPin, OUTPUT); + pinMode(buttonPin, INPUT); + pullUpDnControl(buttonPin, PUD_UP); //pull up to high level + while(1){ + reading = digitalRead(buttonPin); //read the current state of button + if( reading != lastbuttonState){ //if the button state has changed ,record the time point + lastChangeTime = millis(); + } + //if changing-state of the button last beyond the time we set,we considered that + //the current button state is an effective change rather than a buffeting + if(millis() - lastChangeTime > captureTime){ + //if button state is changed ,update the data. + if(reading != buttonState){ + buttonState = reading; + //if the state is low ,the action is pressing + if(buttonState == LOW){ + printf("Button is pressed!\n"); + relayState = !relayState; + if(relayState){ + printf("turn on relay ...\n"); + } + else { + printf("turn off relay ...\n"); + } + } + //if the state is high ,the action is releasing + else { + printf("Button is released!\n"); + } + } + } + digitalWrite(relayPin,relayState); + lastbuttonState = reading; + } + + return 0; +} + diff --git a/Code/C_Code/15.1.1_Sweep/Sweep b/Code/C_Code/15.1.1_Sweep/Sweep new file mode 100644 index 0000000..323192e Binary files /dev/null and b/Code/C_Code/15.1.1_Sweep/Sweep differ diff --git a/Code/C_Code/15.1.1_Sweep/Sweep.c b/Code/C_Code/15.1.1_Sweep/Sweep.c new file mode 100644 index 0000000..dcdf96d --- /dev/null +++ b/Code/C_Code/15.1.1_Sweep/Sweep.c @@ -0,0 +1,59 @@ +/********************************************************************** +* Filename : Sweep.c +* Description : Servo sweep +* Author : freenove +* modification: 2016/07/05 +**********************************************************************/ +#include +#include +#include +#define OFFSET_MS 3 //Define the unit of servo pulse offset: 0.1ms +#define SERVO_MIN_MS 5+OFFSET_MS //define the pulse duration for minimum angle of servo +#define SERVO_MAX_MS 25+OFFSET_MS //define the pulse duration for maximum angle of servo + +#define servoPin 1 //define the GPIO number connected to servo +long map(long value,long fromLow,long fromHigh,long toLow,long toHigh){ + return (toHigh-toLow)*(value-fromLow) / (fromHigh-fromLow) + toLow; +} +void servoInit(int pin){ //initialization function for servo PMW pin + softPwmCreate(pin, 0, 200); +} +void servoWrite(int pin, int angle){ //Specif a certain rotation angle (0-180) for the servo + if(angle > 180) + angle = 180; + if(angle < 0) + angle = 0; + softPwmWrite(pin,map(angle,0,180,SERVO_MIN_MS,SERVO_MAX_MS)); +} +void servoWriteMS(int pin, int ms){ //specific the unit for pulse(5-25ms) with specific duration output by servo pin: 0.1ms + if(ms > SERVO_MAX_MS) + ms = SERVO_MAX_MS; + if(ms < SERVO_MIN_MS) + ms = SERVO_MIN_MS; + softPwmWrite(pin,ms); +} + +int main(void) +{ + int i; + if(wiringPiSetup() == -1){ //when initialize wiring faiservo,print messageto screen + printf("setup wiringPi faiservo !"); + return 1; + } + printf("Program is starting ...\n"); + servoInit(servoPin); //initialize PMW pin of servo + while(1){ + for(i=SERVO_MIN_MS;iSERVO_MIN_MS;i--){ //make servo rotate from maximum angle to minimum angle + servoWriteMS(servoPin,i); + delay(10); + } + delay(500); + } + return 0; +} + diff --git a/Code/C_Code/16.1.1_SteppingMotor/SteppingMotor b/Code/C_Code/16.1.1_SteppingMotor/SteppingMotor new file mode 100644 index 0000000..079a06f Binary files /dev/null and b/Code/C_Code/16.1.1_SteppingMotor/SteppingMotor differ diff --git a/Code/C_Code/16.1.1_SteppingMotor/SteppingMotor.c b/Code/C_Code/16.1.1_SteppingMotor/SteppingMotor.c new file mode 100644 index 0000000..8348463 --- /dev/null +++ b/Code/C_Code/16.1.1_SteppingMotor/SteppingMotor.c @@ -0,0 +1,62 @@ +/********************************************************************** +* Filename : SteppingMotor.c +* Description : +* Author : freenove +* modification: 2016/07/07 +**********************************************************************/ +#include +#include + +const int motorPins[]={1,4,5,6}; //define pins connected to four phase ABCD of stepper motor +const int CCWStep[]={0x01,0x02,0x04,0x08}; //define power supply order for coil for rotating anticlockwise +const int CWStep[]={0x08,0x04,0x02,0x01}; //define power supply order for coil for rotating clockwise +//as for four phase stepping motor, four steps is a cycle. the function is used to drive the stepping motor clockwise or anticlockwise to take four steps +void moveOnePeriod(int dir,int ms){ + int i=0,j=0; + for (j=0;j<4;j++){ //cycle according to power supply order + for (i=0;i<4;i++){ //assign to each pin, a total of 4 pins + if(dir == 1) //power supply order clockwise + digitalWrite(motorPins[i],(CCWStep[j] == (1< +#include +#include + +#define dataPin 0 //DS Pin of 74HC595(Pin14) +#define latchPin 2 //ST_CP Pin of 74HC595(Pin12) +#define clockPin 3 //CH_CP Pin of 74HC595(Pin11) + +int main(void) +{ + int i; + unsigned char x; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pinMode(dataPin,OUTPUT); + pinMode(latchPin,OUTPUT); + pinMode(clockPin,OUTPUT); + while(1){ + x=0x01; + for(i=0;i<8;i++){ + digitalWrite(latchPin,LOW); // Output low level to latchPin + shiftOut(dataPin,clockPin,LSBFIRST,x);// Send serial data to 74HC595 + digitalWrite(latchPin,HIGH); // Output high level to latchPin, and 74HC595 will update the data to the parallel output port. + x<<=1; // make the variable move one bit to left once, then the bright LED move one step to the left once. + delay(100); + } + x=0x80; + for(i=0;i<8;i++){ + digitalWrite(latchPin,LOW); + shiftOut(dataPin,clockPin,LSBFIRST,x); + digitalWrite(latchPin,HIGH); + x>>=1; + delay(100); + } + } + return 0; +} + diff --git a/Code/C_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay b/Code/C_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay new file mode 100644 index 0000000..2c9fb65 Binary files /dev/null and b/Code/C_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay differ diff --git a/Code/C_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.c b/Code/C_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.c new file mode 100644 index 0000000..b276072 --- /dev/null +++ b/Code/C_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.c @@ -0,0 +1,43 @@ +/********************************************************************** +* Filename : SevenSegmentDisplay.c +* Description : Control SevenSegmentDisplay by 74HC595 +* Author : freenove +* modification: 2016/06/24 +**********************************************************************/ +#include +#include +#include + +#define dataPin 0 //DS Pin of 74HC595(Pin14) +#define latchPin 2 //ST_CP Pin of 74HC595(Pin12) +#define clockPin 3 //CH_CP Pin of 74HC595(Pin11) +//encoding for character 0-F of common anode SevenSegmentDisplay. +unsigned char num[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e}; + +int main(void) +{ + int i; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pinMode(dataPin,OUTPUT); + pinMode(latchPin,OUTPUT); + pinMode(clockPin,OUTPUT); + while(1){ + for(i=0;i +#include +#include +#include +#include +#define dataPin 5 //DS Pin of 74HC595(Pin14) +#define latchPin 4 //ST_CP Pin of 74HC595(Pin12) +#define clockPin 1 //CH_CP Pin of 74HC595(Pin11) +const int digitPin[]={0,2,3,12}; // Define 7-segment display common pin +// character 0-9 code of common anode 7-segment display +unsigned char num[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; +int counter = 0; //variable counter,the number will be displayed by 7-segment display +//Open one of the 7-segment display and close the remaining three, the parameter digit is optional for 1,2,4,8 +void selectDigit(int digit){ + digitalWrite(digitPin[0],((digit&0x08) == 0x08) ? LOW : HIGH); + digitalWrite(digitPin[1],((digit&0x04) == 0x04) ? LOW : HIGH); + digitalWrite(digitPin[2],((digit&0x02) == 0x02) ? LOW : HIGH); + digitalWrite(digitPin[3],((digit&0x01) == 0x01) ? LOW : HIGH); +} +void outData(int8_t data){ //function used to output data for 74HC595输出数据函数 + digitalWrite(latchPin,LOW); + shiftOut(dataPin,clockPin,MSBFIRST,data); + digitalWrite(latchPin,HIGH); +} +void display(int dec){ //display function for 7-segment display + selectDigit(0x01); //select the first, and display the single digit + outData(num[dec%10]); + delay(1); //display duration + selectDigit(0x02); //select the second, and display the tens digit + outData(num[dec%100/10]); + delay(1); + selectDigit(0x04); //select the third, and display the hundreds digit + outData(num[dec%1000/100]); + delay(1); + selectDigit(0x08); //select the fourth, and display the thousands digit + outData(num[dec%10000/1000]); + delay(1); +} +void timer(int sig){ //Timer function + if(sig == SIGALRM){ //If the signal is SIGALRM, the value of counter plus 1, and update the number displayed by 7-segment display + counter ++; + alarm(1); //set the next timer time + printf("counter : %d \n",counter); + } +} +int main(void) +{ + int i; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pinMode(dataPin,OUTPUT); //set the pin connected to74HC595 for output mode + pinMode(latchPin,OUTPUT); + pinMode(clockPin,OUTPUT); + //set the pin connected to 7-segment display common end to output mode + for(i=0;i<4;i++){ + pinMode(digitPin[i],OUTPUT); + digitalWrite(digitPin[i],LOW); + } + signal(SIGALRM,timer); //configure the timer + alarm(1); //set the time of timer to 1s + while(1){ + display(counter); //display the number counter + } + return 0; +} + + diff --git a/Code/C_Code/18.2.1_StopWatch/a.out b/Code/C_Code/18.2.1_StopWatch/a.out new file mode 100644 index 0000000..591eac7 Binary files /dev/null and b/Code/C_Code/18.2.1_StopWatch/a.out differ diff --git a/Code/C_Code/19.1.1_LEDMatrix/0_F_code.txt b/Code/C_Code/19.1.1_LEDMatrix/0_F_code.txt new file mode 100644 index 0000000..17b6df3 --- /dev/null +++ b/Code/C_Code/19.1.1_LEDMatrix/0_F_code.txt @@ -0,0 +1,17 @@ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " " + 0x00, 0x00, 0x21, 0x7F, 0x01, 0x00, 0x00, 0x00, // "1" + 0x00, 0x00, 0x23, 0x45, 0x49, 0x31, 0x00, 0x00, // "2" + 0x00, 0x00, 0x22, 0x49, 0x49, 0x36, 0x00, 0x00, // "3" + 0x00, 0x00, 0x0E, 0x32, 0x7F, 0x02, 0x00, 0x00, // "4" + 0x00, 0x00, 0x79, 0x49, 0x49, 0x46, 0x00, 0x00, // "5" + 0x00, 0x00, 0x3E, 0x49, 0x49, 0x26, 0x00, 0x00, // "6" + 0x00, 0x00, 0x60, 0x47, 0x48, 0x70, 0x00, 0x00, // "7" + 0x00, 0x00, 0x36, 0x49, 0x49, 0x36, 0x00, 0x00, // "8" + 0x00, 0x00, 0x32, 0x49, 0x49, 0x3E, 0x00, 0x00, // "9" + 0x00, 0x00, 0x3E, 0x41, 0x41, 0x3E, 0x00, 0x00, // "0" + 0x00, 0x00, 0x3F, 0x44, 0x44, 0x3F, 0x00, 0x00, // "A" + 0x00, 0x00, 0x7F, 0x49, 0x49, 0x36, 0x00, 0x00, // "B" + 0x00, 0x00, 0x3E, 0x41, 0x41, 0x22, 0x00, 0x00, // "C" + 0x00, 0x00, 0x7F, 0x41, 0x41, 0x3E, 0x00, 0x00, // "D" + 0x00, 0x00, 0x7F, 0x49, 0x49, 0x41, 0x00, 0x00, // "E" + 0x00, 0x00, 0x7F, 0x48, 0x48, 0x40, 0x00, 0x00 // "F" diff --git a/Code/C_Code/19.1.1_LEDMatrix/LEDMatrix b/Code/C_Code/19.1.1_LEDMatrix/LEDMatrix new file mode 100644 index 0000000..c64c5cf Binary files /dev/null and b/Code/C_Code/19.1.1_LEDMatrix/LEDMatrix differ diff --git a/Code/C_Code/19.1.1_LEDMatrix/LEDMatrix.c b/Code/C_Code/19.1.1_LEDMatrix/LEDMatrix.c new file mode 100644 index 0000000..c1883b3 --- /dev/null +++ b/Code/C_Code/19.1.1_LEDMatrix/LEDMatrix.c @@ -0,0 +1,77 @@ +/********************************************************************** +* Filename : LEDMatrix.c +* Description : Control LEDMatrix by 74HC595 +* Author : freenove +* modification: 2016/06/24 +**********************************************************************/ +#include +#include +#include + +#define dataPin 0 //DS Pin of 74HC595(Pin14) +#define latchPin 2 //ST_CP Pin of 74HC595(Pin12) +#define clockPin 3 //SH_CP Pin of 74HC595(Pin11) +// data of smiling face +unsigned char pic[]={0x1c,0x22,0x51,0x45,0x45,0x51,0x22,0x1c}; +unsigned char data[]={ // data of "0-F" + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " " + 0x00, 0x00, 0x3E, 0x41, 0x41, 0x3E, 0x00, 0x00, // "0" + 0x00, 0x00, 0x21, 0x7F, 0x01, 0x00, 0x00, 0x00, // "1" + 0x00, 0x00, 0x23, 0x45, 0x49, 0x31, 0x00, 0x00, // "2" + 0x00, 0x00, 0x22, 0x49, 0x49, 0x36, 0x00, 0x00, // "3" + 0x00, 0x00, 0x0E, 0x32, 0x7F, 0x02, 0x00, 0x00, // "4" + 0x00, 0x00, 0x79, 0x49, 0x49, 0x46, 0x00, 0x00, // "5" + 0x00, 0x00, 0x3E, 0x49, 0x49, 0x26, 0x00, 0x00, // "6" + 0x00, 0x00, 0x60, 0x47, 0x48, 0x70, 0x00, 0x00, // "7" + 0x00, 0x00, 0x36, 0x49, 0x49, 0x36, 0x00, 0x00, // "8" + 0x00, 0x00, 0x32, 0x49, 0x49, 0x3E, 0x00, 0x00, // "9" + 0x00, 0x00, 0x3F, 0x44, 0x44, 0x3F, 0x00, 0x00, // "A" + 0x00, 0x00, 0x7F, 0x49, 0x49, 0x36, 0x00, 0x00, // "B" + 0x00, 0x00, 0x3E, 0x41, 0x41, 0x22, 0x00, 0x00, // "C" + 0x00, 0x00, 0x7F, 0x41, 0x41, 0x3E, 0x00, 0x00, // "D" + 0x00, 0x00, 0x7F, 0x49, 0x49, 0x41, 0x00, 0x00, // "E" + 0x00, 0x00, 0x7F, 0x48, 0x48, 0x40, 0x00, 0x00, // "F" + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " " +}; +int main(void) +{ + int i,j,k; + unsigned char x; + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pinMode(dataPin,OUTPUT); + pinMode(latchPin,OUTPUT); + pinMode(clockPin,OUTPUT); + while(1){ + for(j=0;j<500;j++){// Repeat enough times to display the smiling face a period of time + x=0x80; + for(i=0;i<8;i++){ + digitalWrite(latchPin,LOW); + shiftOut(dataPin,clockPin,LSBFIRST,pic[i]);// first shift data of line information to the first stage 74HC959 + shiftOut(dataPin,clockPin,LSBFIRST,~x);//then shift data of column information to the second stage 74HC959 + + digitalWrite(latchPin,HIGH);//Output data of two stage 74HC595 at the same time + x>>=1;// display the next column + delay(1); + } + } + for(k=0;k>=1; + delay(1); + } + } + } + } + return 0; +} + + diff --git a/Code/C_Code/20.1.1_I2CLCD1602/I2CLCD1602.c b/Code/C_Code/20.1.1_I2CLCD1602/I2CLCD1602.c new file mode 100644 index 0000000..cbab64b --- /dev/null +++ b/Code/C_Code/20.1.1_I2CLCD1602/I2CLCD1602.c @@ -0,0 +1,75 @@ +/********************************************************************** +* Filename : I2CLCD1602.c +* Description : Use the LCD display data +* Author : freenove +* modification: 2016/06/25 +**********************************************************************/ +#include +#include +#include +#include +#include +#include + +#define pcf8574_address 0x27 // default I2C address of Pcf8574 +#define BASE 64 // BASE is not less than 64 +//////// Define the output pins of the PCF8574, which are directly connected to the LCD1602 pin. +#define RS BASE+0 +#define RW BASE+1 +#define EN BASE+2 +#define LED BASE+3 +#define D4 BASE+4 +#define D5 BASE+5 +#define D6 BASE+6 +#define D7 BASE+7 + +int lcdhd;// used to handle LCD +void printCPUTemperature(){// sub function used to print CPU temperature + FILE *fp; + char str_temp[15]; + float CPU_temp; + // CPU temperature data is stored in this directory. + fp=fopen("/sys/class/thermal/thermal_zone0/temp","r"); + fgets(str_temp,15,fp); // read file temp + CPU_temp = atof(str_temp)/1000.0; // convert to Celsius degrees + printf("CPU's temperature : %.2f \n",CPU_temp); + lcdPosition(lcdhd,0,0); // set the LCD cursor position to (0,0) + lcdPrintf(lcdhd,"CPU:%.2fC",CPU_temp);// Display CPU temperature on LCD + fclose(fp); +} +void printDataTime(){//used to print system time + time_t rawtime; + struct tm *timeinfo; + time(&rawtime);// get system time + timeinfo = localtime(&rawtime);// convert to local time + printf("%s \n",asctime(timeinfo)); + lcdPosition(lcdhd,0,1);// set the LCD cursor position to (0,1) + lcdPrintf(lcdhd,"Time:%d:%d:%d",timeinfo->tm_hour,timeinfo->tm_min,timeinfo->tm_sec); +//Display system time on LCD +} +int main(void){ + int i; + + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + pcf8574Setup(BASE,pcf8574_address);// initialize PCF8574 + for(i=0;i<8;i++){ + pinMode(BASE+i,OUTPUT); // set PCF8574 port to output mode + } + digitalWrite(LED,HIGH); // turn on LCD backlight + digitalWrite(RW,LOW); // allow writing to LCD +lcdhd = lcdInit(2,16,4,RS,EN,D4,D5,D6,D7,0,0,0,0);// initialize LCD and return “handle” used to handle LCD + if(lcdhd == -1){ + printf("lcdInit failed !"); + return 1; + } + while(1){ + printCPUTemperature();// print CPU temperature + printDataTime(); // print system time + delay(1000); + } + return 0; +} + diff --git a/Code/C_Code/20.1.1_I2CLCD1602/lcd b/Code/C_Code/20.1.1_I2CLCD1602/lcd new file mode 100644 index 0000000..d5ff9da Binary files /dev/null and b/Code/C_Code/20.1.1_I2CLCD1602/lcd differ diff --git a/Code/C_Code/21.1.1_DHT11/DHT.cpp b/Code/C_Code/21.1.1_DHT11/DHT.cpp new file mode 100644 index 0000000..ab5b380 --- /dev/null +++ b/Code/C_Code/21.1.1_DHT11/DHT.cpp @@ -0,0 +1,86 @@ +/********************************************************************** +* Filename : DHT.cpp +* Description : DHT Temperature & Humidity Sensor library for Raspberry +* Author : freenove +* modification: 2016/07/10 +**********************************************************************/ +#include "DHT.hpp" +//Function: Read DHT sensor, store the original data in bits[] +// return values:DHTLIB_OK DHTLIB_ERROR_CHECKSUM DHTLIB_ERROR_TIMEOUT +int DHT::readSensor(int pin,int wakeupDelay){ + int mask = 0x80; + int idx = 0; + int i ; + int32_t t; + for (i=0;i<5;i++){ + bits[i] = 0; + } + pinMode(pin,OUTPUT); + digitalWrite(pin,LOW); + delay(wakeupDelay); + digitalWrite(pin,HIGH); + delayMicroseconds(40); + pinMode(pin,INPUT); + + int loopCnt = DHTLIB_TIMEOUT; + t = micros(); + while(digitalRead(pin)==LOW){ + if((micros() - t) > loopCnt){ + return DHTLIB_ERROR_TIMEOUT; + } + } + loopCnt = DHTLIB_TIMEOUT; + t = micros(); + while(digitalRead(pin)==HIGH){ + if((micros() - t) > loopCnt){ + return DHTLIB_ERROR_TIMEOUT; + } + } + for (i = 0; i<40;i++){ + loopCnt = DHTLIB_TIMEOUT; + t = micros(); + while(digitalRead(pin)==LOW){ + if((micros() - t) > loopCnt) + return DHTLIB_ERROR_TIMEOUT; + } + t = micros(); + loopCnt = DHTLIB_TIMEOUT; + while(digitalRead(pin)==HIGH){ + if((micros() - t) > loopCnt){ + return DHTLIB_ERROR_TIMEOUT; + } + } + if((micros() - t ) > 60){ + bits[idx] |= mask; + } + mask >>= 1; + if(mask == 0){ + mask = 0x80; + idx++; + } + } + pinMode(pin,OUTPUT); + digitalWrite(pin,HIGH); + return DHTLIB_OK; +} +//Function:Read DHT sensor, analyze the data of temperature and humidity +//return:DHTLIB_OK DHTLIB_ERROR_CHECKSUM DHTLIB_ERROR_TIMEOUT +int DHT::readDHT11(int pin){ + int rv ; + int8_t sum; + rv = readSensor(pin,DHTLIB_DHT11_WAKEUP); + if(rv != DHTLIB_OK){ + humidity = DHTLIB_INVALID_VALUE; + temperature = DHTLIB_INVALID_VALUE; + return rv; + } + humidity = bits[0]; + temperature = bits[2]; + sum = bits[0] + bits[2]; + if(bits[4] != sum) + return DHTLIB_ERROR_CHECKSUM; + return DHTLIB_OK; +} + + + diff --git a/Code/C_Code/21.1.1_DHT11/DHT.hpp b/Code/C_Code/21.1.1_DHT11/DHT.hpp new file mode 100644 index 0000000..6d780c7 --- /dev/null +++ b/Code/C_Code/21.1.1_DHT11/DHT.hpp @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : DHT.hpp +* Description : DHT Temperature & Humidity Sensor library for Raspberry +* Author : freenove +* modification: 2016/07/10 +**********************************************************************/ +#ifndef _DHT_H_ +#define _DHT_H_ + +#include +#include +#include + +////read return flag of sensor +#define DHTLIB_OK 0 +#define DHTLIB_ERROR_CHECKSUM -1 +#define DHTLIB_ERROR_TIMEOUT -2 +#define DHTLIB_INVALID_VALUE -999 + +#define DHTLIB_DHT11_WAKEUP 18 +#define DHTLIB_DHT_WAKEUP 1 + +#define DHTLIB_TIMEOUT 100 + +class DHT{ + public: + double humidity,temperature; //use to store temperature and humidity data read + int readDHT11(int pin); //read DHT11 + private: + int bits[5]; //Buffer to receiver data + int readSensor(int pin,int wakeupDelay); // + +}; + + + + +#endif diff --git a/Code/C_Code/21.1.1_DHT11/DHT.o b/Code/C_Code/21.1.1_DHT11/DHT.o new file mode 100644 index 0000000..3ce1d43 Binary files /dev/null and b/Code/C_Code/21.1.1_DHT11/DHT.o differ diff --git a/Code/C_Code/21.1.1_DHT11/DHT11 b/Code/C_Code/21.1.1_DHT11/DHT11 new file mode 100644 index 0000000..a7852a0 Binary files /dev/null and b/Code/C_Code/21.1.1_DHT11/DHT11 differ diff --git a/Code/C_Code/21.1.1_DHT11/DHT11.cpp b/Code/C_Code/21.1.1_DHT11/DHT11.cpp new file mode 100644 index 0000000..2cefc21 --- /dev/null +++ b/Code/C_Code/21.1.1_DHT11/DHT11.cpp @@ -0,0 +1,44 @@ +/********************************************************************** +* Filename : DHT11.cpp +* Description : read the temperature and humidity data of DHT11 +* Author : freenove +* modification: 2016/07/10 +**********************************************************************/ +#include +#include +#include +#include "DHT.hpp" + +#define DHT11_Pin 0 //define the pin of sensor + +int main(){ + DHT dht; //create a DHT class object + int chk,sumCnt;//chk:read the return value of sensor; sumCnt:times of reading sensor + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + while(1){ + chk = dht.readDHT11(DHT11_Pin); //read DHT11 and get a return value. Then determine whether data read is normal according to the return value. + sumCnt++; //counting number of reading times + printf("The sumCnt is : %d \n",sumCnt); + switch(chk){ + case DHTLIB_OK: //if the return value is DHTLIB_OK, the data is normal. + printf("DHT11,OK! \n"); + break; + case DHTLIB_ERROR_CHECKSUM: //data check has errors + printf("DHTLIB_ERROR_CHECKSUM! \n"); + break; + case DHTLIB_ERROR_TIMEOUT: //reading DHT times out + printf("DHTLIB_ERROR_TIMEOUT! \n"); + break; + case DHTLIB_INVALID_VALUE: //other errors + printf("DHTLIB_INVALID_VALUE! \n"); + break; + } + printf("Humidity is %.2f %%, \t Temperature is %.2f *C\n\n",dht.humidity,dht.temperature); + delay(1000); + } + return 1; +} + diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Key.cpp b/Code/C_Code/22.1.1_MatrixKeypad/Key.cpp new file mode 100644 index 0000000..008853d --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Key.cpp @@ -0,0 +1,61 @@ +/* +|| @file Key.cpp +|| @version 1.0 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| @description +|| | Key class provides an abstract definition of a key or button +|| | and was initially designed to be used in conjunction with a +|| | state-machine. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ +#include "Key.hpp" + + +// default constructor +Key::Key() { + kchar = NO_KEY; + kstate = IDLE; + stateChanged = false; +} + +// constructor +Key::Key(char userKeyChar) { + kchar = userKeyChar; + kcode = -1; + kstate = IDLE; + stateChanged = false; +} + + +void Key::key_update (char userKeyChar, KeyState userState, boolean userStatus) { + kchar = userKeyChar; + kstate = userState; + stateChanged = userStatus; +} + + + +/* +|| @changelog +|| | 1.0 2012-06-04 - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Key.hpp b/Code/C_Code/22.1.1_MatrixKeypad/Key.hpp new file mode 100644 index 0000000..ede970a --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Key.hpp @@ -0,0 +1,70 @@ +/* +|| +|| @file Key.h +|| @version 1.0 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| @description +|| | Key class provides an abstract definition of a key or button +|| | and was initially designed to be used in conjunction with a +|| | state-machine. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ + +#ifndef KEY_H +#define KEY_H + +#include + +#define boolean bool +#define byte unsigned char +#define OPEN LOW +#define CLOSED HIGH + +typedef unsigned int uint; +typedef enum{ IDLE, PRESSED, HOLD, RELEASED } KeyState; + +const char NO_KEY = '\0'; + +class Key { +public: + // members + char kchar; + int kcode; + KeyState kstate; + boolean stateChanged; + + // methods + Key(); + Key(char userKeyChar); + void key_update(char userKeyChar, KeyState userState, boolean userStatus); + +private: + +}; + +#endif + +/* +|| @changelog +|| | 1.0 2012-06-04 - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad.cpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad.cpp new file mode 100644 index 0000000..cc6aaad --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad.cpp @@ -0,0 +1,279 @@ +/* +|| +|| @file Keypad.cpp +|| @version 3.1 +|| @author Mark Stanley, Alexander Brevig +|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com +|| +|| @description +|| | This library provides a simple interface for using matrix +|| | keypads. It supports multiple keypresses while maintaining +|| | backwards compatibility with the old single key library. +|| | It also supports user selectable pins and definable keymaps. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ +#include "Keypad.hpp" + +// <> Allows custom keymap, pin configuration, and keypad sizes. +Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) { + rowPins = row; + columnPins = col; + sizeKpd.rows = numRows; + sizeKpd.columns = numCols; + + begin(userKeymap); + + setDebounceTime(50); + setHoldTime(500); + keypadEventListener = 0; + + startTime = 0; + single_key = false; +} + +// Let the user define a keymap - assume the same row/column count as defined in constructor +void Keypad::begin(char *userKeymap) { + keymap = userKeymap; +} + +// Returns a single key only. Retained for backwards compatibility. +char Keypad::getKey() { + single_key = true; + if (getKeys() && key[0].stateChanged && (key[0].kstate==PRESSED)){ + return key[0].kchar; + } + + single_key = false; + return NO_KEY; +} + +// Populate the key list. +bool Keypad::getKeys() { + bool keyActivity = false; + + // Limit how often the keypad is scanned. This makes the loop() run 10 times as fast. + if ( (millis()-startTime)>debounceTime ) { + scanKeys(); + keyActivity = updateList(); + startTime = millis(); + } + return keyActivity; +} + +// Private : Hardware scan +void Keypad::scanKeys() { + // Re-intialize the row pins. Allows sharing these pins with other hardware. + for (byte r=0; r -1) { + nextKeyState(idx, button); + } + // Key is NOT on the list so add it. + if ((idx == -1) && button) { + for (byte i=0; iholdTime) // Waiting for a key HOLD... + transitionTo (idx, HOLD); + else if (button==OPEN) // or for a key to be RELEASED. + transitionTo (idx, RELEASED); + break; + case HOLD: + if (button==OPEN){ + + transitionTo (idx, RELEASED); + } + break; + case RELEASED: + transitionTo (idx, IDLE); + break; + } +} + +// New in 2.1 +bool Keypad::isPressed(char keyChar) { + for (byte i=0; i +#include + +//#define NULL '\0' +#define INPUT_PULLUP 0x02 +#define bitWrite(x,n,b) (b ? (x |= 1<>n)&1) == 1) ? 1 : 0) + +#define OPEN LOW +#define CLOSED HIGH + +typedef char KeypadEvent; +typedef unsigned int uint; +typedef unsigned long ulong; + +// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0 +// by Nick Gammon. Thanks for the input Nick. It actually saved 78 bytes for me. :) +typedef struct { + byte rows; + byte columns; +} KeypadSize; + +#define LIST_MAX 10 // Max number of keys on the active list. +#define MAPSIZE 10 // MAPSIZE is the number of rows (times 16 columns) +#define makeKeymap(x) ((char*)x) + + +//class Keypad : public Key, public HAL_obj { +class Keypad : public Key { +public: + + Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols); + + uint bitMap[MAPSIZE]; // 10 row x 16 column array of bits. Except Due which has 32 columns. + Key key[LIST_MAX]; + unsigned long holdTimer; + + char getKey(); + bool getKeys(); + KeyState getState(); + void begin(char *userKeymap); + bool isPressed(char keyChar); + void setDebounceTime(uint); + void setHoldTime(uint); + void addEventListener(void (*listener)(char)); + int findInList(char keyChar); + int findInList(int keyCode); + char waitForKey(); + bool keyStateChanged(); + byte numKeys(); + +private: + unsigned long startTime; + char *keymap; + byte *rowPins; + byte *columnPins; + KeypadSize sizeKpd; + uint debounceTime; + uint holdTime; + bool single_key; + + void scanKeys(); + bool updateList(); + void nextKeyState(byte n, boolean button); + void transitionTo(byte n, KeyState nextState); + void (*keypadEventListener)(char); +}; + +//#define __PIN_MODE__PINWRITE__PINREAD__ +void pin_mode(byte pinNum, byte mode); +void pin_write(byte pinNum, boolean level); +int pin_read(byte pinNum); +#endif + +/* +|| @changelog +|| | 3.1 2013-01-15 - Mark Stanley : Fixed missing RELEASED & IDLE status when using a single key. +|| | 3.0 2012-07-12 - Mark Stanley : Made library multi-keypress by default. (Backwards compatible) +|| | 3.0 2012-07-12 - Mark Stanley : Modified pin functions to support Keypad_I2C +|| | 3.0 2012-07-12 - Stanley & Young : Removed static variables. Fix for multiple keypad objects. +|| | 3.0 2012-07-12 - Mark Stanley : Fixed bug that caused shorted pins when pressing multiple keys. +|| | 2.0 2011-12-29 - Mark Stanley : Added waitForKey(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the public function keyStateChanged(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the private function scanKeys(). +|| | 2.0 2011-12-23 - Mark Stanley : Moved the Finite State Machine into the function getKeyState(). +|| | 2.0 2011-12-23 - Mark Stanley : Removed the member variable lastUdate. Not needed after rewrite. +|| | 1.8 2011-11-21 - Mark Stanley : Added test to determine which header file to compile, +|| | WProgram.h or Arduino.h. +|| | 1.8 2009-07-08 - Alexander Brevig : No longer uses arrays +|| | 1.7 2009-06-18 - Alexander Brevig : This library is a Finite State Machine every time a state changes +|| | the keypadEventListener will trigger, if set +|| | 1.7 2009-06-18 - Alexander Brevig : Added setDebounceTime setHoldTime specifies the amount of +|| | microseconds before a HOLD state triggers +|| | 1.7 2009-06-18 - Alexander Brevig : Added transitionTo +|| | 1.6 2009-06-15 - Alexander Brevig : Added getState() and state variable +|| | 1.5 2009-05-19 - Alexander Brevig : Added setHoldTime() +|| | 1.4 2009-05-15 - Alexander Brevig : Added addEventListener +|| | 1.3 2009-05-12 - Alexander Brevig : Added lastUdate, in order to do simple debouncing +|| | 1.2 2009-05-09 - Alexander Brevig : Changed getKey() +|| | 1.1 2009-04-28 - Alexander Brevig : Modified API, and made variables private +|| | 1.0 2007-XX-XX - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad/Keypad.cpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/Keypad.cpp new file mode 100644 index 0000000..9102c81 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/Keypad.cpp @@ -0,0 +1,293 @@ +/* +|| +|| @file Keypad.cpp +|| @version 3.1 +|| @author Mark Stanley, Alexander Brevig +|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com +|| +|| @description +|| | This library provides a simple interface for using matrix +|| | keypads. It supports multiple keypresses while maintaining +|| | backwards compatibility with the old single key library. +|| | It also supports user selectable pins and definable keymaps. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ +#include "Keypad.hpp" + +// <> Allows custom keymap, pin configuration, and keypad sizes. +Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) { + rowPins = row; + columnPins = col; + sizeKpd.rows = numRows; + sizeKpd.columns = numCols; + + begin(userKeymap); + + setDebounceTime(10); + setHoldTime(500); + keypadEventListener = 0; + + startTime = 0; + single_key = false; +} + +// Let the user define a keymap - assume the same row/column count as defined in constructor +void Keypad::begin(char *userKeymap) { + keymap = userKeymap; +} + +// Returns a single key only. Retained for backwards compatibility. +char Keypad::getKey() { + single_key = true; + + if (getKeys() && key[0].stateChanged && (key[0].kstate==PRESSED)) + return key[0].kchar; + + single_key = false; + + return NO_KEY; +} + +// Populate the key list. +bool Keypad::getKeys() { + bool keyActivity = false; + + // Limit how often the keypad is scanned. This makes the loop() run 10 times as fast. + if ( (millis()-startTime)>debounceTime ) { + scanKeys(); + keyActivity = updateList(); + startTime = millis(); + } + + return keyActivity; +} + +// Private : Hardware scan +void Keypad::scanKeys() { + // Re-intialize the row pins. Allows sharing these pins with other hardware. + for (byte r=0; r -1) { + nextKeyState(idx, button); + } + // Key is NOT on the list so add it. + if ((idx == -1) && button) { + for (byte i=0; iholdTime) // Waiting for a key HOLD... + transitionTo (idx, HOLD); + else if (button==OPEN) // or for a key to be RELEASED. + transitionTo (idx, RELEASED); + break; + case HOLD: + if (button==OPEN) + transitionTo (idx, RELEASED); + break; + case RELEASED: + transitionTo (idx, IDLE); + break; + } +} + +// New in 2.1 +bool Keypad::isPressed(char keyChar) { + for (byte i=0; i + +#define NULL 0 +#define INPUT_PULLUP 0x02 +#define bitWrite(x,n,b) (b ? (x |= b<>n)&1) == 1) ? 1 : 0) + + +#define OPEN LOW +#define CLOSED HIGH + +typedef char KeypadEvent; +typedef unsigned int uint; +typedef unsigned long ulong; + +// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0 +// by Nick Gammon. Thanks for the input Nick. It actually saved 78 bytes for me. :) +typedef struct { + byte rows; + byte columns; +} KeypadSize; + +#define LIST_MAX 10 // Max number of keys on the active list. +#define MAPSIZE 10 // MAPSIZE is the number of rows (times 16 columns) +#define makeKeymap(x) ((char*)x) + + +//class Keypad : public Key, public HAL_obj { +class Keypad : public Key { +public: + + Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols); + + virtual void pin_mode(byte pinNum, byte mode) { + if(mode == INPUT_PULLUP) { + pinMode(pinNum, INPUT); + pullUpDnControl(pinNum,PUD_UP); + } + else{ + pinMode(pinNum, mode); + } + } + virtual void pin_write(byte pinNum, boolean level) { digitalWrite(pinNum, level); } + virtual int pin_read(byte pinNum) { return digitalRead(pinNum); } + + uint bitMap[MAPSIZE]; // 10 row x 16 column array of bits. Except Due which has 32 columns. + Key key[LIST_MAX]; + unsigned long holdTimer; + + char getKey(); + bool getKeys(); + KeyState getState(); + void begin(char *userKeymap); + bool isPressed(char keyChar); + void setDebounceTime(uint); + void setHoldTime(uint); + void addEventListener(void (*listener)(char)); + int findInList(char keyChar); + int findInList(int keyCode); + char waitForKey(); + bool keyStateChanged(); + byte numKeys(); + +private: + unsigned long startTime; + char *keymap; + byte *rowPins; + byte *columnPins; + KeypadSize sizeKpd; + uint debounceTime; + uint holdTime; + bool single_key; + + void scanKeys(); + bool updateList(); + void nextKeyState(byte n, boolean button); + void transitionTo(byte n, KeyState nextState); + void (*keypadEventListener)(char); +}; + + + +#endif + +/* +|| @changelog +|| | 3.1 2013-01-15 - Mark Stanley : Fixed missing RELEASED & IDLE status when using a single key. +|| | 3.0 2012-07-12 - Mark Stanley : Made library multi-keypress by default. (Backwards compatible) +|| | 3.0 2012-07-12 - Mark Stanley : Modified pin functions to support Keypad_I2C +|| | 3.0 2012-07-12 - Stanley & Young : Removed static variables. Fix for multiple keypad objects. +|| | 3.0 2012-07-12 - Mark Stanley : Fixed bug that caused shorted pins when pressing multiple keys. +|| | 2.0 2011-12-29 - Mark Stanley : Added waitForKey(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the public function keyStateChanged(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the private function scanKeys(). +|| | 2.0 2011-12-23 - Mark Stanley : Moved the Finite State Machine into the function getKeyState(). +|| | 2.0 2011-12-23 - Mark Stanley : Removed the member variable lastUdate. Not needed after rewrite. +|| | 1.8 2011-11-21 - Mark Stanley : Added test to determine which header file to compile, +|| | WProgram.h or Arduino.h. +|| | 1.8 2009-07-08 - Alexander Brevig : No longer uses arrays +|| | 1.7 2009-06-18 - Alexander Brevig : This library is a Finite State Machine every time a state changes +|| | the keypadEventListener will trigger, if set +|| | 1.7 2009-06-18 - Alexander Brevig : Added setDebounceTime setHoldTime specifies the amount of +|| | microseconds before a HOLD state triggers +|| | 1.7 2009-06-18 - Alexander Brevig : Added transitionTo +|| | 1.6 2009-06-15 - Alexander Brevig : Added getState() and state variable +|| | 1.5 2009-05-19 - Alexander Brevig : Added setHoldTime() +|| | 1.4 2009-05-15 - Alexander Brevig : Added addEventListener +|| | 1.3 2009-05-12 - Alexander Brevig : Added lastUdate, in order to do simple debouncing +|| | 1.2 2009-05-09 - Alexander Brevig : Changed getKey() +|| | 1.1 2009-04-28 - Alexander Brevig : Modified API, and made variables private +|| | 1.0 2007-XX-XX - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad/Keypad.hpp.gch b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/Keypad.hpp.gch new file mode 100644 index 0000000..614e6e5 Binary files /dev/null and b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/Keypad.hpp.gch differ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad/keywords.txt b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/keywords.txt new file mode 100644 index 0000000..e400940 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/keywords.txt @@ -0,0 +1,38 @@ +# Keypad Library data types +KeyState KEYWORD1 +Keypad KEYWORD1 +KeypadEvent KEYWORD1 + +# Keypad Library constants +NO_KEY LITERAL1 +IDLE LITERAL1 +PRESSED LITERAL1 +HOLD LITERAL1 +RELEASED LITERAL1 + +# Keypad Library methods & functions +addEventListener KEYWORD2 +bitMap KEYWORD2 +findKeyInList KEYWORD2 +getKey KEYWORD2 +getKeys KEYWORD2 +getState KEYWORD2 +holdTimer KEYWORD2 +isPressed KEYWORD2 +keyStateChanged KEYWORD2 +numKeys KEYWORD2 +pin_mode KEYWORD2 +pin_write KEYWORD2 +pin_read KEYWORD2 +setDebounceTime KEYWORD2 +setHoldTime KEYWORD2 +waitForKey KEYWORD2 + +# this is a macro that converts 2d arrays to pointers +makeKeymap KEYWORD2 + +# List of objects created in the example sketches. +kpd KEYWORD3 +keypad KEYWORD3 +kbrd KEYWORD3 +keyboard KEYWORD3 diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.cpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.cpp new file mode 100644 index 0000000..008853d --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.cpp @@ -0,0 +1,61 @@ +/* +|| @file Key.cpp +|| @version 1.0 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| @description +|| | Key class provides an abstract definition of a key or button +|| | and was initially designed to be used in conjunction with a +|| | state-machine. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ +#include "Key.hpp" + + +// default constructor +Key::Key() { + kchar = NO_KEY; + kstate = IDLE; + stateChanged = false; +} + +// constructor +Key::Key(char userKeyChar) { + kchar = userKeyChar; + kcode = -1; + kstate = IDLE; + stateChanged = false; +} + + +void Key::key_update (char userKeyChar, KeyState userState, boolean userStatus) { + kchar = userKeyChar; + kstate = userState; + stateChanged = userStatus; +} + + + +/* +|| @changelog +|| | 1.0 2012-06-04 - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.hpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.hpp new file mode 100644 index 0000000..ede970a --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.hpp @@ -0,0 +1,70 @@ +/* +|| +|| @file Key.h +|| @version 1.0 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| @description +|| | Key class provides an abstract definition of a key or button +|| | and was initially designed to be used in conjunction with a +|| | state-machine. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ + +#ifndef KEY_H +#define KEY_H + +#include + +#define boolean bool +#define byte unsigned char +#define OPEN LOW +#define CLOSED HIGH + +typedef unsigned int uint; +typedef enum{ IDLE, PRESSED, HOLD, RELEASED } KeyState; + +const char NO_KEY = '\0'; + +class Key { +public: + // members + char kchar; + int kcode; + KeyState kstate; + boolean stateChanged; + + // methods + Key(); + Key(char userKeyChar); + void key_update(char userKeyChar, KeyState userState, boolean userStatus); + +private: + +}; + +#endif + +/* +|| @changelog +|| | 1.0 2012-06-04 - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.hpp.gch b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.hpp.gch new file mode 100644 index 0000000..6a2d040 Binary files /dev/null and b/Code/C_Code/22.1.1_MatrixKeypad/Keypad/utility/Key.hpp.gch differ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/HelloKeypad.cpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/HelloKeypad.cpp new file mode 100644 index 0000000..08b3a0d --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/HelloKeypad.cpp @@ -0,0 +1,37 @@ +/* @file HelloKeypad.pde +|| @version 1.0 +|| @author Alexander Brevig +|| @contact alexanderbrevig@gmail.com +|| +|| @description +|| | Demonstrates the simplest use of the matrix Keypad library. +|| # +*/ +#include "Keypad.hpp" +#include +const byte ROWS = 4; //four rows +const byte COLS = 3; //three columns +char keys[ROWS][COLS] = { + {'1','2','3'}, + {'4','5','6'}, + {'7','8','9'}, + {'*','0','#'} +}; +byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad + +Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); + +void setup(){ + //Serial.begin(9600); +} + +int main(){ + while(1){ + char key = keypad.getKey(); + + if (key){ + printf("%s \n",key); + } + } +} diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/Keypad.cpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/Keypad.cpp new file mode 100644 index 0000000..26cebc5 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/Keypad.cpp @@ -0,0 +1,308 @@ +/* +|| +|| @file Keypad.cpp +|| @version 3.1 +|| @author Mark Stanley, Alexander Brevig +|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com +|| +|| @description +|| | This library provides a simple interface for using matrix +|| | keypads. It supports multiple keypresses while maintaining +|| | backwards compatibility with the old single key library. +|| | It also supports user selectable pins and definable keymaps. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ +#include "Keypad.hpp" + +// <> Allows custom keymap, pin configuration, and keypad sizes. +Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) { + rowPins = row; + columnPins = col; + sizeKpd.rows = numRows; + sizeKpd.columns = numCols; + + begin(userKeymap); + + setDebounceTime(10); + setHoldTime(500); + keypadEventListener = 0; + + startTime = 0; + single_key = false; +} + +// Let the user define a keymap - assume the same row/column count as defined in constructor +void Keypad::begin(char *userKeymap) { + keymap = userKeymap; +} + +// Returns a single key only. Retained for backwards compatibility. +char Keypad::getKey() { + single_key = true; + + if (getKeys() && key[0].stateChanged && (key[0].kstate==PRESSED)) + return key[0].kchar; + + single_key = false; + + return NO_KEY; +} + +// Populate the key list. +bool Keypad::getKeys() { + bool keyActivity = false; + + // Limit how often the keypad is scanned. This makes the loop() run 10 times as fast. + if ( (millis()-startTime)>debounceTime ) { + scanKeys(); + keyActivity = updateList(); + startTime = millis(); + } + + return keyActivity; +} + +// Private : Hardware scan +void Keypad::scanKeys() { + // Re-intialize the row pins. Allows sharing these pins with other hardware. + for (byte r=0; r -1) { + nextKeyState(idx, button); + } + // Key is NOT on the list so add it. + if ((idx == -1) && button) { + for (byte i=0; iholdTime) // Waiting for a key HOLD... + transitionTo (idx, HOLD); + else if (button==OPEN) // or for a key to be RELEASED. + transitionTo (idx, RELEASED); + break; + case HOLD: + if (button==OPEN) + transitionTo (idx, RELEASED); + break; + case RELEASED: + transitionTo (idx, IDLE); + break; + } +} + +// New in 2.1 +bool Keypad::isPressed(char keyChar) { + for (byte i=0; i + +#define NULL 0 +#define INPUT_PULLUP 0x02 +#define bitWrite(x,n,b) (b ? (x |= b<>n)&1) == 1) ? 1 : 0) + + +#define OPEN LOW +#define CLOSED HIGH + +typedef char KeypadEvent; +typedef unsigned int uint; +typedef unsigned long ulong; + +// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0 +// by Nick Gammon. Thanks for the input Nick. It actually saved 78 bytes for me. :) +typedef struct { + byte rows; + byte columns; +} KeypadSize; + +#define LIST_MAX 10 // Max number of keys on the active list. +#define MAPSIZE 10 // MAPSIZE is the number of rows (times 16 columns) +#define makeKeymap(x) ((char*)x) + + +//class Keypad : public Key, public HAL_obj { +class Keypad : public Key { +public: + + Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols); + + uint bitMap[MAPSIZE]; // 10 row x 16 column array of bits. Except Due which has 32 columns. + Key key[LIST_MAX]; + unsigned long holdTimer; + + char getKey(); + bool getKeys(); + KeyState getState(); + void begin(char *userKeymap); + bool isPressed(char keyChar); + void setDebounceTime(uint); + void setHoldTime(uint); + void addEventListener(void (*listener)(char)); + int findInList(char keyChar); + int findInList(int keyCode); + char waitForKey(); + bool keyStateChanged(); + byte numKeys(); + +private: + unsigned long startTime; + char *keymap; + byte *rowPins; + byte *columnPins; + KeypadSize sizeKpd; + uint debounceTime; + uint holdTime; + bool single_key; + + void scanKeys(); + bool updateList(); + void nextKeyState(byte n, boolean button); + void transitionTo(byte n, KeyState nextState); + void (*keypadEventListener)(char); +}; + +void pin_mode(byte pinNum, byte mode) ; + +void pin_write(byte pinNum, boolean level) ; +int pin_read(byte pinNum) ; + +#endif + +/* +|| @changelog +|| | 3.1 2013-01-15 - Mark Stanley : Fixed missing RELEASED & IDLE status when using a single key. +|| | 3.0 2012-07-12 - Mark Stanley : Made library multi-keypress by default. (Backwards compatible) +|| | 3.0 2012-07-12 - Mark Stanley : Modified pin functions to support Keypad_I2C +|| | 3.0 2012-07-12 - Stanley & Young : Removed static variables. Fix for multiple keypad objects. +|| | 3.0 2012-07-12 - Mark Stanley : Fixed bug that caused shorted pins when pressing multiple keys. +|| | 2.0 2011-12-29 - Mark Stanley : Added waitForKey(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the public function keyStateChanged(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the private function scanKeys(). +|| | 2.0 2011-12-23 - Mark Stanley : Moved the Finite State Machine into the function getKeyState(). +|| | 2.0 2011-12-23 - Mark Stanley : Removed the member variable lastUdate. Not needed after rewrite. +|| | 1.8 2011-11-21 - Mark Stanley : Added test to determine which header file to compile, +|| | WProgram.h or Arduino.h. +|| | 1.8 2009-07-08 - Alexander Brevig : No longer uses arrays +|| | 1.7 2009-06-18 - Alexander Brevig : This library is a Finite State Machine every time a state changes +|| | the keypadEventListener will trigger, if set +|| | 1.7 2009-06-18 - Alexander Brevig : Added setDebounceTime setHoldTime specifies the amount of +|| | microseconds before a HOLD state triggers +|| | 1.7 2009-06-18 - Alexander Brevig : Added transitionTo +|| | 1.6 2009-06-15 - Alexander Brevig : Added getState() and state variable +|| | 1.5 2009-05-19 - Alexander Brevig : Added setHoldTime() +|| | 1.4 2009-05-15 - Alexander Brevig : Added addEventListener +|| | 1.3 2009-05-12 - Alexander Brevig : Added lastUdate, in order to do simple debouncing +|| | 1.2 2009-05-09 - Alexander Brevig : Changed getKey() +|| | 1.1 2009-04-28 - Alexander Brevig : Modified API, and made variables private +|| | 1.0 2007-XX-XX - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/Keypad.hpp.gch b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/Keypad.hpp.gch new file mode 100644 index 0000000..614e6e5 Binary files /dev/null and b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/Keypad.hpp.gch differ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/CustomKeypad/CustomKeypad.ino b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/CustomKeypad/CustomKeypad.ino new file mode 100644 index 0000000..659c186 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/CustomKeypad/CustomKeypad.ino @@ -0,0 +1,37 @@ +/* @file CustomKeypad.pde +|| @version 1.0 +|| @author Alexander Brevig +|| @contact alexanderbrevig@gmail.com +|| +|| @description +|| | Demonstrates changing the keypad size and key values. +|| # +*/ +#include + +const byte ROWS = 4; //four rows +const byte COLS = 4; //four columns +//define the cymbols on the buttons of the keypads +char hexaKeys[ROWS][COLS] = { + {'0','1','2','3'}, + {'4','5','6','7'}, + {'8','9','A','B'}, + {'C','D','E','F'} +}; +byte rowPins[ROWS] = {3, 2, 1, 0}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {7, 6, 5, 4}; //connect to the column pinouts of the keypad + +//initialize an instance of class NewKeypad +Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); + +void setup(){ + Serial.begin(9600); +} + +void loop(){ + char customKey = customKeypad.getKey(); + + if (customKey){ + Serial.println(customKey); + } +} diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/DynamicKeypad/DynamicKeypad.ino b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/DynamicKeypad/DynamicKeypad.ino new file mode 100644 index 0000000..530b523 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/DynamicKeypad/DynamicKeypad.ino @@ -0,0 +1,213 @@ +/* @file DynamicKeypad.pde +|| @version 1.2 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| 07/11/12 - Re-modified (from DynamicKeypadJoe2) to use direct-connect kpds +|| 02/28/12 - Modified to use I2C i/o G. D. (Joe) Young +|| +|| +|| @dificulty: Intermediate +|| +|| @description +|| | This is a demonstration of keypadEvents. It's used to switch between keymaps +|| | while using only one keypad. The main concepts being demonstrated are: +|| | +|| | Using the keypad events, PRESSED, HOLD and RELEASED to simplify coding. +|| | How to use setHoldTime() and why. +|| | Making more than one thing happen with the same key. +|| | Assigning and changing keymaps on the fly. +|| | +|| | Another useful feature is also included with this demonstration although +|| | it's not really one of the concepts that I wanted to show you. If you look +|| | at the code in the PRESSED event you will see that the first section of that +|| | code is used to scroll through three different letters on each key. For +|| | example, pressing the '2' key will step through the letters 'd', 'e' and 'f'. +|| | +|| | +|| | Using the keypad events, PRESSED, HOLD and RELEASED to simplify coding +|| | Very simply, the PRESSED event occurs imediately upon detecting a pressed +|| | key and will not happen again until after a RELEASED event. When the HOLD +|| | event fires it always falls between PRESSED and RELEASED. However, it will +|| | only occur if a key has been pressed for longer than the setHoldTime() interval. +|| | +|| | How to use setHoldTime() and why +|| | Take a look at keypad.setHoldTime(500) in the code. It is used to set the +|| | time delay between a PRESSED event and the start of a HOLD event. The value +|| | 500 is in milliseconds (mS) and is equivalent to half a second. After pressing +|| | a key for 500mS the HOLD event will fire and any code contained therein will be +|| | executed. This event will stay active for as long as you hold the key except +|| | in the case of bug #1 listed above. +|| | +|| | Making more than one thing happen with the same key. +|| | If you look under the PRESSED event (case PRESSED:) you will see that the '#' +|| | is used to print a new line, Serial.println(). But take a look at the first +|| | half of the HOLD event and you will see the same key being used to switch back +|| | and forth between the letter and number keymaps that were created with alphaKeys[4][5] +|| | and numberKeys[4][5] respectively. +|| | +|| | Assigning and changing keymaps on the fly +|| | You will see that the '#' key has been designated to perform two different functions +|| | depending on how long you hold it down. If you press the '#' key for less than the +|| | setHoldTime() then it will print a new line. However, if you hold if for longer +|| | than that it will switch back and forth between numbers and letters. You can see the +|| | keymap changes in the HOLD event. +|| | +|| | +|| | In addition... +|| | You might notice a couple of things that you won't find in the Arduino language +|| | reference. The first would be #include . This is a standard library from +|| | the C programming language and though I don't normally demonstrate these types of +|| | things from outside the Arduino language reference I felt that its use here was +|| | justified by the simplicity that it brings to this sketch. +|| | That simplicity is provided by the two calls to isalpha(key) and isdigit(key). +|| | The first one is used to decide if the key that was pressed is any letter from a-z +|| | or A-Z and the second one decides if the key is any number from 0-9. The return +|| | value from these two functions is either a zero or some positive number greater +|| | than zero. This makes it very simple to test a key and see if it is a number or +|| | a letter. So when you see the following: +|| | +|| | if (isalpha(key)) // this tests to see if your key was a letter +|| | +|| | And the following may be more familiar to some but it is equivalent: +|| | +|| | if (isalpha(key) != 0) // this tests to see if your key was a letter +|| | +|| | And Finally... +|| | To better understand how the event handler affects your code you will need to remember +|| | that it gets called only when you press, hold or release a key. However, once a key +|| | is pressed or held then the event handler gets called at the full speed of the loop(). +|| | +|| # +*/ +#include +#include + +const byte ROWS = 4; //four rows +const byte COLS = 3; //three columns +// Define the keymaps. The blank spot (lower left) is the space character. +char alphaKeys[ROWS][COLS] = { + { 'a','d','g' }, + { 'j','m','p' }, + { 's','v','y' }, + { ' ','.','#' } +}; + +char numberKeys[ROWS][COLS] = { + { '1','2','3' }, + { '4','5','6' }, + { '7','8','9' }, + { ' ','0','#' } +}; + +boolean alpha = false; // Start with the numeric keypad. + +byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad + +// Create two new keypads, one is a number pad and the other is a letter pad. +Keypad numpad( makeKeymap(numberKeys), rowPins, colPins, sizeof(rowPins), sizeof(colPins) ); +Keypad ltrpad( makeKeymap(alphaKeys), rowPins, colPins, sizeof(rowPins), sizeof(colPins) ); + + +unsigned long startTime; +const byte ledPin = 13; // Use the LED on pin 13. + +void setup() { + Serial.begin(9600); + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, LOW); // Turns the LED on. + ltrpad.begin( makeKeymap(alphaKeys) ); + numpad.begin( makeKeymap(numberKeys) ); + ltrpad.addEventListener(keypadEvent_ltr); // Add an event listener. + ltrpad.setHoldTime(500); // Default is 1000mS + numpad.addEventListener(keypadEvent_num); // Add an event listener. + numpad.setHoldTime(500); // Default is 1000mS +} + +char key; + +void loop() { + + if( alpha ) + key = ltrpad.getKey( ); + else + key = numpad.getKey( ); + + if (alpha && millis()-startTime>100) { // Flash the LED if we are using the letter keymap. + digitalWrite(ledPin,!digitalRead(ledPin)); + startTime = millis(); + } +} + +static char virtKey = NO_KEY; // Stores the last virtual key press. (Alpha keys only) +static char physKey = NO_KEY; // Stores the last physical key press. (Alpha keys only) +static char buildStr[12]; +static byte buildCount; +static byte pressCount; + +static byte kpadState; + +// Take care of some special events. + +void keypadEvent_ltr(KeypadEvent key) { + // in here when in alpha mode. + kpadState = ltrpad.getState( ); + swOnState( key ); +} // end ltrs keypad events + +void keypadEvent_num( KeypadEvent key ) { + // in here when using number keypad + kpadState = numpad.getState( ); + swOnState( key ); +} // end numbers keypad events + +void swOnState( char key ) { + switch( kpadState ) { + case PRESSED: + if (isalpha(key)) { // This is a letter key so we're using the letter keymap. + if (physKey != key) { // New key so start with the first of 3 characters. + pressCount = 0; + virtKey = key; + physKey = key; + } + else { // Pressed the same key again... + virtKey++; // so select the next character on that key. + pressCount++; // Tracks how many times we press the same key. + } + if (pressCount > 2) { // Last character reached so cycle back to start. + pressCount = 0; + virtKey = key; + } + Serial.print(virtKey); // Used for testing. + } + if (isdigit(key) || key == ' ' || key == '.') + Serial.print(key); + if (key == '#') + Serial.println(); + break; + + case HOLD: + if (key == '#') { // Toggle between keymaps. + if (alpha == true) { // We are currently using a keymap with letters + alpha = false; // Now we want a keymap with numbers. + digitalWrite(ledPin, LOW); + } + else { // We are currently using a keymap with numbers + alpha = true; // Now we want a keymap with letters. + } + } + else { // Some key other than '#' was pressed. + buildStr[buildCount++] = (isalpha(key)) ? virtKey : key; + buildStr[buildCount] = '\0'; + Serial.println(); + Serial.println(buildStr); + } + break; + + case RELEASED: + if (buildCount >= sizeof(buildStr)) buildCount = 0; // Our string is full. Start fresh. + break; + } // end switch-case +}// end switch on state function + diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/EventKeypad/EventKeypad.ino b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/EventKeypad/EventKeypad.ino new file mode 100644 index 0000000..4c8d27e --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/EventKeypad/EventKeypad.ino @@ -0,0 +1,73 @@ +/* @file EventSerialKeypad.pde + || @version 1.0 + || @author Alexander Brevig + || @contact alexanderbrevig@gmail.com + || + || @description + || | Demonstrates using the KeypadEvent. + || # + */ +#include + +const byte ROWS = 4; //four rows +const byte COLS = 3; //three columns +char keys[ROWS][COLS] = { + {'1','2','3'}, + {'4','5','6'}, + {'7','8','9'}, + {'*','0','#'} +}; + +byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad + +Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); +byte ledPin = 13; + +boolean blink = false; +boolean ledPin_state; + +void setup(){ + Serial.begin(9600); + pinMode(ledPin, OUTPUT); // Sets the digital pin as output. + digitalWrite(ledPin, HIGH); // Turn the LED on. + ledPin_state = digitalRead(ledPin); // Store initial LED state. HIGH when LED is on. + keypad.addEventListener(keypadEvent); // Add an event listener for this keypad +} + +void loop(){ + char key = keypad.getKey(); + + if (key) { + Serial.println(key); + } + if (blink){ + digitalWrite(ledPin,!digitalRead(ledPin)); // Change the ledPin from Hi2Lo or Lo2Hi. + delay(100); + } +} + +// Taking care of some special events. +void keypadEvent(KeypadEvent key){ + switch (keypad.getState()){ + case PRESSED: + if (key == '#') { + digitalWrite(ledPin,!digitalRead(ledPin)); + ledPin_state = digitalRead(ledPin); // Remember LED state, lit or unlit. + } + break; + + case RELEASED: + if (key == '*') { + digitalWrite(ledPin,ledPin_state); // Restore LED state from before it started blinking. + blink = false; + } + break; + + case HOLD: + if (key == '*') { + blink = true; // Blink the LED when holding the * key. + } + break; + } +} diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/HelloKeypad/HelloKeypad.ino b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/HelloKeypad/HelloKeypad.ino new file mode 100644 index 0000000..261f044 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/HelloKeypad/HelloKeypad.ino @@ -0,0 +1,35 @@ +/* @file HelloKeypad.pde +|| @version 1.0 +|| @author Alexander Brevig +|| @contact alexanderbrevig@gmail.com +|| +|| @description +|| | Demonstrates the simplest use of the matrix Keypad library. +|| # +*/ +#include + +const byte ROWS = 4; //four rows +const byte COLS = 3; //three columns +char keys[ROWS][COLS] = { + {'1','2','3'}, + {'4','5','6'}, + {'7','8','9'}, + {'*','0','#'} +}; +byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad + +Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); + +void setup(){ + Serial.begin(9600); +} + +void loop(){ + char key = keypad.getKey(); + + if (key){ + Serial.println(key); + } +} diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/HelloKeypad3/HelloKeypad3.ino b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/HelloKeypad3/HelloKeypad3.ino new file mode 100644 index 0000000..5605b72 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/HelloKeypad3/HelloKeypad3.ino @@ -0,0 +1,68 @@ +#include + + +const byte ROWS = 2; // use 4X4 keypad for both instances +const byte COLS = 2; +char keys[ROWS][COLS] = { + {'1','2'}, + {'3','4'} +}; +byte rowPins[ROWS] = {5, 4}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {7, 6}; //connect to the column pinouts of the keypad +Keypad kpd( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); + + +const byte ROWSR = 2; +const byte COLSR = 2; +char keysR[ROWSR][COLSR] = { + {'a','b'}, + {'c','d'} +}; +byte rowPinsR[ROWSR] = {3, 2}; //connect to the row pinouts of the keypad +byte colPinsR[COLSR] = {7, 6}; //connect to the column pinouts of the keypad +Keypad kpdR( makeKeymap(keysR), rowPinsR, colPinsR, ROWSR, COLSR ); + + +const byte ROWSUR = 4; +const byte COLSUR = 1; +char keysUR[ROWSUR][COLSUR] = { + {'M'}, + {'A'}, + {'R'}, + {'K'} +}; +// Digitran keypad, bit numbers of PCF8574 i/o port +byte rowPinsUR[ROWSUR] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad +byte colPinsUR[COLSUR] = {8}; //connect to the column pinouts of the keypad + +Keypad kpdUR( makeKeymap(keysUR), rowPinsUR, colPinsUR, ROWSUR, COLSUR ); + + +void setup(){ +// Wire.begin( ); + kpdUR.begin( makeKeymap(keysUR) ); + kpdR.begin( makeKeymap(keysR) ); + kpd.begin( makeKeymap(keys) ); + Serial.begin(9600); + Serial.println( "start" ); +} + +//byte alternate = false; +char key, keyR, keyUR; +void loop(){ + +// alternate = !alternate; + key = kpd.getKey( ); + keyUR = kpdUR.getKey( ); + keyR = kpdR.getKey( ); + + if (key){ + Serial.println(key); + } + if( keyR ) { + Serial.println( keyR ); + } + if( keyUR ) { + Serial.println( keyUR ); + } +} diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/MultiKey/MultiKey.ino b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/MultiKey/MultiKey.ino new file mode 100644 index 0000000..850dc1a --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/examples/MultiKey/MultiKey.ino @@ -0,0 +1,78 @@ +/* @file MultiKey.ino +|| @version 1.0 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| @description +|| | The latest version, 3.0, of the keypad library supports up to 10 +|| | active keys all being pressed at the same time. This sketch is an +|| | example of how you can get multiple key presses from a keypad or +|| | keyboard. +|| # +*/ + +#include + +const byte ROWS = 4; //four rows +const byte COLS = 3; //three columns +char keys[ROWS][COLS] = { +{'1','2','3'}, +{'4','5','6'}, +{'7','8','9'}, +{'*','0','#'} +}; +byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the kpd +byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the kpd + +Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); + +unsigned long loopCount; +unsigned long startTime; +String msg; + + +void setup() { + Serial.begin(9600); + loopCount = 0; + startTime = millis(); + msg = ""; +} + + +void loop() { + loopCount++; + if ( (millis()-startTime)>5000 ) { + Serial.print("Average loops per second = "); + Serial.println(loopCount/5); + startTime = millis(); + loopCount = 0; + } + + // Fills kpd.key[ ] array with up-to 10 active keys. + // Returns true if there are ANY active keys. + if (kpd.getKeys()) + { + for (int i=0; i + + +const byte ROWS = 4; //four rows +const byte COLS = 3; //three columns +char keys[ROWS][COLS] = { + {'1','2','3'}, + {'4','5','6'}, + {'7','8','9'}, + {'*','0','#'} +}; +byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad + +Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); + +unsigned long loopCount = 0; +unsigned long timer_t = 0; + +void setup(){ + Serial.begin(9600); + + // Try playing with different debounceTime settings to see how it affects + // the number of times per second your loop will run. The library prevents + // setting it to anything below 1 millisecond. + kpd.setDebounceTime(10); // setDebounceTime(mS) +} + +void loop(){ + char key = kpd.getKey(); + + // Report the number of times through the loop in 1 second. This will give + // you a relative idea of just how much the debounceTime has changed the + // speed of your code. If you set a high debounceTime your loopCount will + // look good but your keypresses will start to feel sluggish. + if ((millis() - timer_t) > 1000) { + Serial.print("Your loop code ran "); + Serial.print(loopCount); + Serial.println(" times over the last second"); + loopCount = 0; + timer_t = millis(); + } + loopCount++; + if(key) + Serial.println(key); +} diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/keywords.txt b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/keywords.txt new file mode 100644 index 0000000..e400940 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/keywords.txt @@ -0,0 +1,38 @@ +# Keypad Library data types +KeyState KEYWORD1 +Keypad KEYWORD1 +KeypadEvent KEYWORD1 + +# Keypad Library constants +NO_KEY LITERAL1 +IDLE LITERAL1 +PRESSED LITERAL1 +HOLD LITERAL1 +RELEASED LITERAL1 + +# Keypad Library methods & functions +addEventListener KEYWORD2 +bitMap KEYWORD2 +findKeyInList KEYWORD2 +getKey KEYWORD2 +getKeys KEYWORD2 +getState KEYWORD2 +holdTimer KEYWORD2 +isPressed KEYWORD2 +keyStateChanged KEYWORD2 +numKeys KEYWORD2 +pin_mode KEYWORD2 +pin_write KEYWORD2 +pin_read KEYWORD2 +setDebounceTime KEYWORD2 +setHoldTime KEYWORD2 +waitForKey KEYWORD2 + +# this is a macro that converts 2d arrays to pointers +makeKeymap KEYWORD2 + +# List of objects created in the example sketches. +kpd KEYWORD3 +keypad KEYWORD3 +kbrd KEYWORD3 +keyboard KEYWORD3 diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.cpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.cpp new file mode 100644 index 0000000..008853d --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.cpp @@ -0,0 +1,61 @@ +/* +|| @file Key.cpp +|| @version 1.0 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| @description +|| | Key class provides an abstract definition of a key or button +|| | and was initially designed to be used in conjunction with a +|| | state-machine. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ +#include "Key.hpp" + + +// default constructor +Key::Key() { + kchar = NO_KEY; + kstate = IDLE; + stateChanged = false; +} + +// constructor +Key::Key(char userKeyChar) { + kchar = userKeyChar; + kcode = -1; + kstate = IDLE; + stateChanged = false; +} + + +void Key::key_update (char userKeyChar, KeyState userState, boolean userStatus) { + kchar = userKeyChar; + kstate = userState; + stateChanged = userStatus; +} + + + +/* +|| @changelog +|| | 1.0 2012-06-04 - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.hpp b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.hpp new file mode 100644 index 0000000..ede970a --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.hpp @@ -0,0 +1,70 @@ +/* +|| +|| @file Key.h +|| @version 1.0 +|| @author Mark Stanley +|| @contact mstanley@technologist.com +|| +|| @description +|| | Key class provides an abstract definition of a key or button +|| | and was initially designed to be used in conjunction with a +|| | state-machine. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ + +#ifndef KEY_H +#define KEY_H + +#include + +#define boolean bool +#define byte unsigned char +#define OPEN LOW +#define CLOSED HIGH + +typedef unsigned int uint; +typedef enum{ IDLE, PRESSED, HOLD, RELEASED } KeyState; + +const char NO_KEY = '\0'; + +class Key { +public: + // members + char kchar; + int kcode; + KeyState kstate; + boolean stateChanged; + + // methods + Key(); + Key(char userKeyChar); + void key_update(char userKeyChar, KeyState userState, boolean userStatus); + +private: + +}; + +#endif + +/* +|| @changelog +|| | 1.0 2012-06-04 - Mark Stanley : Initial Release +|| # +*/ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.hpp.gch b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.hpp.gch new file mode 100644 index 0000000..6a2d040 Binary files /dev/null and b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/Keypad/utility/Key.hpp.gch differ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/MatrixKeyBoard b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/MatrixKeyBoard new file mode 100644 index 0000000..97f6981 Binary files /dev/null and b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/MatrixKeyBoard differ diff --git a/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/MatrixKeyBoard.c b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/MatrixKeyBoard.c new file mode 100644 index 0000000..75e7404 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/Keypad_Bak/MatrixKeyBoard.c @@ -0,0 +1,21 @@ +#include +#include +#include +//#include "Keypad.hpp" +#define bitWrite(x,n,b) (b ? (x |= 1<>n)&1) == 1) ? 1 : 0) + +int main(){ + unsigned char a=0x85,b=4,c=1; + char ch = 'A'; + printf("a : %x\n",a); + printf("%d,%d \n",bitRead(a,7),bitRead(a,4)); + + bitWrite(a,b,c); + bitWrite(a,2,0); + printf("a : %x\n",a); + printf("%d,%d \n",bitRead(a,7),bitRead(a,4)); + + printf("char is %c ... \n",ch); + return 1; +} diff --git a/Code/C_Code/22.1.1_MatrixKeypad/MatrixKeypad.cpp b/Code/C_Code/22.1.1_MatrixKeypad/MatrixKeypad.cpp new file mode 100644 index 0000000..ba9df96 --- /dev/null +++ b/Code/C_Code/22.1.1_MatrixKeypad/MatrixKeypad.cpp @@ -0,0 +1,38 @@ +/********************************************************************** +* Filename : MatrixKeypad.cpp +* Description : obtain the key code of 4x4 Matrix Keypad +* Author : freenove +* modification: 2016/07/10 +**********************************************************************/ +#include "Keypad.hpp" +#include +const byte ROWS = 4; //four rows +const byte COLS = 4; //four columns +char keys[ROWS][COLS] = { //key code + {'1','2','3','A'}, + {'4','5','6','B'}, + {'7','8','9','C'}, + {'*','0','#','D'} +}; +byte rowPins[ROWS] = {1, 4, 5, 6 }; //connect to the row pinouts of the keypad +byte colPins[COLS] = {12,3, 2, 0 }; //connect to the column pinouts of the keypad +//create Keypad object +Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); + +int main(){ + printf("Program is starting ... \n"); + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + char key = 0; + keypad.setDebounceTime(50); + while(1){ + key = keypad.getKey(); //get the state of keys + if (key){ //if a key is pressed, print out its key code + printf("You Pressed key : %c \n",key); + } + } + return 1; +} + diff --git a/Code/C_Code/23.1.1_SenseLED/SenseLED b/Code/C_Code/23.1.1_SenseLED/SenseLED new file mode 100644 index 0000000..aabd0a4 Binary files /dev/null and b/Code/C_Code/23.1.1_SenseLED/SenseLED differ diff --git a/Code/C_Code/23.1.1_SenseLED/SenseLED.c b/Code/C_Code/23.1.1_SenseLED/SenseLED.c new file mode 100644 index 0000000..395e0c1 --- /dev/null +++ b/Code/C_Code/23.1.1_SenseLED/SenseLED.c @@ -0,0 +1,37 @@ +/********************************************************************** +* Filename : SenseLED.c +* Description : Controlling an led by infrared Motion sensor. +* Author : freenove +* modification: 2016/06/12 +**********************************************************************/ +#include +#include + +#define ledPin 1 //define the ledPin +#define sensorPin 0 //define the sensorPin + +int main(void) +{ + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + + pinMode(ledPin, OUTPUT); + pinMode(sensorPin, INPUT); + + while(1){ + + if(digitalRead(sensorPin) == HIGH){ //sensor has pressed down + digitalWrite(ledPin, HIGH); //led on + printf("led on...\n"); + } + else { //sensor has released + digitalWrite(ledPin, LOW); //led off + printf("...led off\n"); + } + } + + return 0; +} + diff --git a/Code/C_Code/24.1.1_UltrasonicRanging/UltrasonicRanging b/Code/C_Code/24.1.1_UltrasonicRanging/UltrasonicRanging new file mode 100644 index 0000000..e0f3ec9 Binary files /dev/null and b/Code/C_Code/24.1.1_UltrasonicRanging/UltrasonicRanging differ diff --git a/Code/C_Code/24.1.1_UltrasonicRanging/UltrasonicRanging.c b/Code/C_Code/24.1.1_UltrasonicRanging/UltrasonicRanging.c new file mode 100644 index 0000000..4cd1576 --- /dev/null +++ b/Code/C_Code/24.1.1_UltrasonicRanging/UltrasonicRanging.c @@ -0,0 +1,69 @@ +/********************************************************************** +* Filename : UltrasonicRanging.c +* Description : Get distance from UltrasonicRanging +* Author : freenove +* modification: 2016/07/14 +**********************************************************************/ +#include +#include +#include + +#define trigPin 4 +#define echoPin 5 +#define MAX_DISTANCE 220 // define the maximum measured distance +#define timeOut MAX_DISTANCE*60 // calculate timeout according to the maximum measured distance +//function pulseIn: obtain pulse time of a pin +int pulseIn(int pin, int level, int timeout); +float getSonar(){ // get the measurement results of ultrasonic module,with unit: cm + long pingTime; + float distance; + digitalWrite(trigPin,HIGH); //trigPin send 10us high level + delayMicroseconds(10); + digitalWrite(trigPin,LOW); + pingTime = pulseIn(echoPin,HIGH,timeOut); //read plus time of echoPin + distance = (float)pingTime * 340.0 / 2.0 / 10000.0; // the sound speed is 340m/s,and calculate distance + return distance; +} + +int main(){ + printf("Program is starting ... \n"); + if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen + printf("setup wiringPi failed !"); + return 1; + } + float distance = 0; + pinMode(trigPin,OUTPUT); + pinMode(echoPin,INPUT); + while(1){ + distance = getSonar(); + printf("The distance is : %.2f cm\n",distance); + delay(1000); + } + return 1; +} + +int pulseIn(int pin, int level, int timeout) +{ + struct timeval tn, t0, t1; + long micros; + gettimeofday(&t0, NULL); + micros = 0; + while (digitalRead(pin) != level) + { + gettimeofday(&tn, NULL); + if (tn.tv_sec > t0.tv_sec) micros = 1000000L; else micros = 0; + micros += (tn.tv_usec - t0.tv_usec); + if (micros > timeout) return 0; + } + gettimeofday(&t1, NULL); + while (digitalRead(pin) == level) + { + gettimeofday(&tn, NULL); + if (tn.tv_sec > t0.tv_sec) micros = 1000000L; else micros = 0; + micros = micros + (tn.tv_usec - t0.tv_usec); + if (micros > timeout) return 0; + } + if (tn.tv_sec > t1.tv_sec) micros = 1000000L; else micros = 0; + micros = micros + (tn.tv_usec - t1.tv_usec); + return micros; +} diff --git a/Code/C_Code/25.1.1_MPU6050/I2Cdev.cpp b/Code/C_Code/25.1.1_MPU6050/I2Cdev.cpp new file mode 100644 index 0000000..49765ad --- /dev/null +++ b/Code/C_Code/25.1.1_MPU6050/I2Cdev.cpp @@ -0,0 +1,427 @@ +// I2Cdev library collection - Main I2C device class +// Abstracts bit and byte I2C R/W functions into a convenient class +// 6/9/2012 by Jeff Rowberg +// +// Changelog: +// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire +// - add compiler warnings when using outdated or IDE or limited I2Cdev implementation +// 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums) +// 2011-10-03 - added automatic Arduino version detection for ease of use +// 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications +// 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x) +// 2011-08-03 - added optional timeout parameter to read* methods to easily change from default +// 2011-08-02 - added support for 16-bit registers +// - fixed incorrect Doxygen comments on some methods +// - added timeout value for read operations (thanks mem @ Arduino forums) +// 2011-07-30 - changed read/write function structures to return success or byte counts +// - made all methods static for multi-device memory savings +// 2011-07-28 - initial release + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "I2Cdev.h" + +/** Default constructor. + */ +I2Cdev::I2Cdev() { +} + +/** Read a single bit from an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to read from + * @param bitNum Bit position to read (0-7) + * @param data Container for single bit value + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Status of read operation (true = success) + */ +int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout) { + uint8_t b; + uint8_t count = readByte(devAddr, regAddr, &b, timeout); + *data = b & (1 << bitNum); + return count; +} + +/** Read a single bit from a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to read from + * @param bitNum Bit position to read (0-15) + * @param data Container for single bit value + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Status of read operation (true = success) + */ +int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout) { + uint16_t b; + uint8_t count = readWord(devAddr, regAddr, &b, timeout); + *data = b & (1 << bitNum); + return count; +} + +/** Read multiple bits from an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to read from + * @param bitStart First bit position to read (0-7) + * @param length Number of bits to read (not more than 8) + * @param data Container for right-aligned value (i.e. '101' read from any bitStart position will equal 0x05) + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Status of read operation (true = success) + */ +int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout) { + // 01101001 read byte + // 76543210 bit numbers + // xxx args: bitStart=4, length=3 + // 010 masked + // -> 010 shifted + uint8_t count, b; + if ((count = readByte(devAddr, regAddr, &b, timeout)) != 0) { + uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); + b &= mask; + b >>= (bitStart - length + 1); + *data = b; + } + return count; +} + +/** Read multiple bits from a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to read from + * @param bitStart First bit position to read (0-15) + * @param length Number of bits to read (not more than 16) + * @param data Container for right-aligned value (i.e. '101' read from any bitStart position will equal 0x05) + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Status of read operation (1 = success, 0 = failure, -1 = timeout) + */ +int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout) { + // 1101011001101001 read byte + // fedcba9876543210 bit numbers + // xxx args: bitStart=12, length=3 + // 010 masked + // -> 010 shifted + uint8_t count; + uint16_t w; + if ((count = readWord(devAddr, regAddr, &w, timeout)) != 0) { + uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1); + w &= mask; + w >>= (bitStart - length + 1); + *data = w; + } + return count; +} + +/** Read single byte from an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to read from + * @param data Container for byte value read from device + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Status of read operation (true = success) + */ +int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout) { + return readBytes(devAddr, regAddr, 1, data, timeout); +} + +/** Read single word from a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to read from + * @param data Container for word value read from device + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Status of read operation (true = success) + */ +int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout) { + return readWords(devAddr, regAddr, 1, data, timeout); +} + +/** Read multiple bytes from an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr First register regAddr to read from + * @param length Number of bytes to read + * @param data Buffer to store read data in + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Number of bytes read (-1 indicates failure) + */ +int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout) { + int8_t count = 0; + int fd = open("/dev/i2c-1", O_RDWR); + + if (fd < 0) { + fprintf(stderr, "Failed to open device: %s\n", strerror(errno)); + return(-1); + } + if (ioctl(fd, I2C_SLAVE, devAddr) < 0) { + fprintf(stderr, "Failed to select device: %s\n", strerror(errno)); + close(fd); + return(-1); + } + if (write(fd, ®Addr, 1) != 1) { + fprintf(stderr, "Failed to write reg: %s\n", strerror(errno)); + close(fd); + return(-1); + } + count = read(fd, data, length); + if (count < 0) { + fprintf(stderr, "Failed to read device(%d): %s\n", count, ::strerror(errno)); + close(fd); + return(-1); + } else if (count != length) { + fprintf(stderr, "Short read from device, expected %d, got %d\n", length, count); + close(fd); + return(-1); + } + close(fd); + + return count; +} + +/** Read multiple words from a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr First register regAddr to read from + * @param length Number of words to read + * @param data Buffer to store read data in + * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + * @return Number of words read (0 indicates failure) + */ +int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout) { + int8_t count = 0; + + printf("ReadWords() not implemented\n"); + // Use readBytes() and potential byteswap + *data = 0; // keep the compiler quiet + + return count; +} + +/** write a single bit in an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to write to + * @param bitNum Bit position to write (0-7) + * @param value New bit value to write + * @return Status of operation (true = success) + */ +bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data) { + uint8_t b; + readByte(devAddr, regAddr, &b); + b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum)); + return writeByte(devAddr, regAddr, b); +} + +/** write a single bit in a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to write to + * @param bitNum Bit position to write (0-15) + * @param value New bit value to write + * @return Status of operation (true = success) + */ +bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data) { + uint16_t w; + readWord(devAddr, regAddr, &w); + w = (data != 0) ? (w | (1 << bitNum)) : (w & ~(1 << bitNum)); + return writeWord(devAddr, regAddr, w); +} + +/** Write multiple bits in an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to write to + * @param bitStart First bit position to write (0-7) + * @param length Number of bits to write (not more than 8) + * @param data Right-aligned value to write + * @return Status of operation (true = success) + */ +bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) { + // 010 value to write + // 76543210 bit numbers + // xxx args: bitStart=4, length=3 + // 00011100 mask byte + // 10101111 original value (sample) + // 10100011 original & ~mask + // 10101011 masked | value + uint8_t b; + if (readByte(devAddr, regAddr, &b) != 0) { + uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); + data <<= (bitStart - length + 1); // shift data into correct position + data &= mask; // zero all non-important bits in data + b &= ~(mask); // zero all important bits in existing byte + b |= data; // combine data with existing byte + return writeByte(devAddr, regAddr, b); + } else { + return false; + } +} + +/** Write multiple bits in a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register regAddr to write to + * @param bitStart First bit position to write (0-15) + * @param length Number of bits to write (not more than 16) + * @param data Right-aligned value to write + * @return Status of operation (true = success) + */ +bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data) { + // 010 value to write + // fedcba9876543210 bit numbers + // xxx args: bitStart=12, length=3 + // 0001110000000000 mask byte + // 1010111110010110 original value (sample) + // 1010001110010110 original & ~mask + // 1010101110010110 masked | value + uint16_t w; + if (readWord(devAddr, regAddr, &w) != 0) { + uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); + data <<= (bitStart - length + 1); // shift data into correct position + data &= mask; // zero all non-important bits in data + w &= ~(mask); // zero all important bits in existing word + w |= data; // combine data with existing word + return writeWord(devAddr, regAddr, w); + } else { + return false; + } +} + +/** Write single byte to an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register address to write to + * @param data New byte value to write + * @return Status of operation (true = success) + */ +bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) { + return writeBytes(devAddr, regAddr, 1, &data); +} + +/** Write single word to a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr Register address to write to + * @param data New word value to write + * @return Status of operation (true = success) + */ +bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data) { + return writeWords(devAddr, regAddr, 1, &data); +} + +/** Write multiple bytes to an 8-bit device register. + * @param devAddr I2C slave device address + * @param regAddr First register address to write to + * @param length Number of bytes to write + * @param data Buffer to copy new data from + * @return Status of operation (true = success) + */ +bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data) { + int8_t count = 0; + uint8_t buf[128]; + int fd; + + if (length > 127) { + fprintf(stderr, "Byte write count (%d) > 127\n", length); + return(FALSE); + } + + fd = open("/dev/i2c-1", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Failed to open device: %s\n", strerror(errno)); + return(FALSE); + } + if (ioctl(fd, I2C_SLAVE, devAddr) < 0) { + fprintf(stderr, "Failed to select device: %s\n", strerror(errno)); + close(fd); + return(FALSE); + } + buf[0] = regAddr; + memcpy(buf+1,data,length); + count = write(fd, buf, length+1); + if (count < 0) { + fprintf(stderr, "Failed to write device(%d): %s\n", count, ::strerror(errno)); + close(fd); + return(FALSE); + } else if (count != length+1) { + fprintf(stderr, "Short write to device, expected %d, got %d\n", length+1, count); + close(fd); + return(FALSE); + } + close(fd); + + return TRUE; +} + +/** Write multiple words to a 16-bit device register. + * @param devAddr I2C slave device address + * @param regAddr First register address to write to + * @param length Number of words to write + * @param data Buffer to copy new data from + * @return Status of operation (true = success) + */ +bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data) { + int8_t count = 0; + uint8_t buf[128]; + int i, fd; + + // Should do potential byteswap and call writeBytes() really, but that + // messes with the callers buffer + + if (length > 63) { + fprintf(stderr, "Word write count (%d) > 63\n", length); + return(FALSE); + } + + fd = open("/dev/i2c-1", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Failed to open device: %s\n", strerror(errno)); + return(FALSE); + } + if (ioctl(fd, I2C_SLAVE, devAddr) < 0) { + fprintf(stderr, "Failed to select device: %s\n", strerror(errno)); + close(fd); + return(FALSE); + } + buf[0] = regAddr; + for (i = 0; i < length; i++) { + buf[i*2+1] = data[i] >> 8; + buf[i*2+2] = data[i]; + } + count = write(fd, buf, length*2+1); + if (count < 0) { + fprintf(stderr, "Failed to write device(%d): %s\n", count, ::strerror(errno)); + close(fd); + return(FALSE); + } else if (count != length*2+1) { + fprintf(stderr, "Short write to device, expected %d, got %d\n", length+1, count); + close(fd); + return(FALSE); + } + close(fd); + return TRUE; +} + +/** Default timeout value for read operations. + * Set this to 0 to disable timeout detection. + */ +uint16_t I2Cdev::readTimeout = 0; + diff --git a/Code/C_Code/25.1.1_MPU6050/I2Cdev.h b/Code/C_Code/25.1.1_MPU6050/I2Cdev.h new file mode 100644 index 0000000..47581df --- /dev/null +++ b/Code/C_Code/25.1.1_MPU6050/I2Cdev.h @@ -0,0 +1,77 @@ +// I2Cdev library collection - Main I2C device class header file +// Abstracts bit and byte I2C R/W functions into a convenient class +// 6/9/2012 by Jeff Rowberg +// +// Changelog: +// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire +// - add compiler warnings when using outdated or IDE or limited I2Cdev implementation +// 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums) +// 2011-10-03 - added automatic Arduino version detection for ease of use +// 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications +// 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x) +// 2011-08-03 - added optional timeout parameter to read* methods to easily change from default +// 2011-08-02 - added support for 16-bit registers +// - fixed incorrect Doxygen comments on some methods +// - added timeout value for read operations (thanks mem @ Arduino forums) +// 2011-07-30 - changed read/write function structures to return success or byte counts +// - made all methods static for multi-device memory savings +// 2011-07-28 - initial release + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#ifndef _I2CDEV_H_ +#define _I2CDEV_H_ + +#ifndef TRUE +#define TRUE (1==1) +#define FALSE (0==1) +#endif + +class I2Cdev { + public: + I2Cdev(); + + static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + + static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data); + static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data); + static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); + static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data); + static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); + static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data); + static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data); + static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data); + + static uint16_t readTimeout; +}; + +#endif /* _I2CDEV_H_ */ diff --git a/Code/C_Code/25.1.1_MPU6050/MPU6050.cpp b/Code/C_Code/25.1.1_MPU6050/MPU6050.cpp new file mode 100644 index 0000000..3bbad24 --- /dev/null +++ b/Code/C_Code/25.1.1_MPU6050/MPU6050.cpp @@ -0,0 +1,3147 @@ +// I2Cdev library collection - MPU6050 I2C device class +// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) +// 8/24/2011 by Jeff Rowberg +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// ... - ongoing debug release + +// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE +// DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF +// YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING. + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +#include +#include +#include +#include +#include +#include "MPU6050.h" + +/** Default constructor, uses default I2C address. + * @see MPU6050_DEFAULT_ADDRESS + */ +MPU6050::MPU6050() { + devAddr = MPU6050_DEFAULT_ADDRESS; +} + +/** Specific address constructor. + * @param address I2C address + * @see MPU6050_DEFAULT_ADDRESS + * @see MPU6050_ADDRESS_AD0_LOW + * @see MPU6050_ADDRESS_AD0_HIGH + */ +MPU6050::MPU6050(uint8_t address) { + devAddr = address; +} + +/** Power on and prepare for general usage. + * This will activate the device and take it out of sleep mode (which must be done + * after start-up). This function also sets both the accelerometer and the gyroscope + * to their most sensitive settings, namely +/- 2g and +/- 250 degrees/sec, and sets + * the clock source to use the X Gyro for reference, which is slightly better than + * the default internal clock source. + */ +void MPU6050::initialize() { + setClockSource(MPU6050_CLOCK_PLL_XGYRO); + setFullScaleGyroRange(MPU6050_GYRO_FS_250); + setFullScaleAccelRange(MPU6050_ACCEL_FS_2); + setSleepEnabled(false); // thanks to Jack Elston for pointing this one out! +} + +/** Verify the I2C connection. + * Make sure the device is connected and responds as expected. + * @return True if connection is valid, false otherwise + */ +bool MPU6050::testConnection() { + return getDeviceID() == 0x34; +} + +// AUX_VDDIO register (InvenSense demo code calls this RA_*G_OFFS_TC) + +/** Get the auxiliary I2C supply voltage level. + * When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to + * 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to + * the MPU-6000, which does not have a VLOGIC pin. + * @return I2C supply voltage level (0=VLOGIC, 1=VDD) + */ +uint8_t MPU6050::getAuxVDDIOLevel() { + I2Cdev::readBit(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_PWR_MODE_BIT, buffer); + return buffer[0]; +} +/** Set the auxiliary I2C supply voltage level. + * When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to + * 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to + * the MPU-6000, which does not have a VLOGIC pin. + * @param level I2C supply voltage level (0=VLOGIC, 1=VDD) + */ +void MPU6050::setAuxVDDIOLevel(uint8_t level) { + I2Cdev::writeBit(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_PWR_MODE_BIT, level); +} + +// SMPLRT_DIV register + +/** Get gyroscope output rate divider. + * The sensor register output, FIFO output, DMP sampling, Motion detection, Zero + * Motion detection, and Free Fall detection are all based on the Sample Rate. + * The Sample Rate is generated by dividing the gyroscope output rate by + * SMPLRT_DIV: + * + * Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV) + * + * where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or + * 7), and 1kHz when the DLPF is enabled (see Register 26). + * + * Note: The accelerometer output rate is 1kHz. This means that for a Sample + * Rate greater than 1kHz, the same accelerometer sample may be output to the + * FIFO, DMP, and sensor registers more than once. + * + * For a diagram of the gyroscope and accelerometer signal paths, see Section 8 + * of the MPU-6000/MPU-6050 Product Specification document. + * + * @return Current sample rate + * @see MPU6050_RA_SMPLRT_DIV + */ +uint8_t MPU6050::getRate() { + I2Cdev::readByte(devAddr, MPU6050_RA_SMPLRT_DIV, buffer); + return buffer[0]; +} +/** Set gyroscope sample rate divider. + * @param rate New sample rate divider + * @see getRate() + * @see MPU6050_RA_SMPLRT_DIV + */ +void MPU6050::setRate(uint8_t rate) { + I2Cdev::writeByte(devAddr, MPU6050_RA_SMPLRT_DIV, rate); +} + +// CONFIG register + +/** Get external FSYNC configuration. + * Configures the external Frame Synchronization (FSYNC) pin sampling. An + * external signal connected to the FSYNC pin can be sampled by configuring + * EXT_SYNC_SET. Signal changes to the FSYNC pin are latched so that short + * strobes may be captured. The latched FSYNC signal will be sampled at the + * Sampling Rate, as defined in register 25. After sampling, the latch will + * reset to the current FSYNC signal state. + * + * The sampled value will be reported in place of the least significant bit in + * a sensor data register determined by the value of EXT_SYNC_SET according to + * the following table. + * + *
+ * EXT_SYNC_SET | FSYNC Bit Location
+ * -------------+-------------------
+ * 0            | Input disabled
+ * 1            | TEMP_OUT_L[0]
+ * 2            | GYRO_XOUT_L[0]
+ * 3            | GYRO_YOUT_L[0]
+ * 4            | GYRO_ZOUT_L[0]
+ * 5            | ACCEL_XOUT_L[0]
+ * 6            | ACCEL_YOUT_L[0]
+ * 7            | ACCEL_ZOUT_L[0]
+ * 
+ * + * @return FSYNC configuration value + */ +uint8_t MPU6050::getExternalFrameSync() { + I2Cdev::readBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_EXT_SYNC_SET_BIT, MPU6050_CFG_EXT_SYNC_SET_LENGTH, buffer); + return buffer[0]; +} +/** Set external FSYNC configuration. + * @see getExternalFrameSync() + * @see MPU6050_RA_CONFIG + * @param sync New FSYNC configuration value + */ +void MPU6050::setExternalFrameSync(uint8_t sync) { + I2Cdev::writeBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_EXT_SYNC_SET_BIT, MPU6050_CFG_EXT_SYNC_SET_LENGTH, sync); +} +/** Get digital low-pass filter configuration. + * The DLPF_CFG parameter sets the digital low pass filter configuration. It + * also determines the internal sampling rate used by the device as shown in + * the table below. + * + * Note: The accelerometer output rate is 1kHz. This means that for a Sample + * Rate greater than 1kHz, the same accelerometer sample may be output to the + * FIFO, DMP, and sensor registers more than once. + * + *
+ *          |   ACCELEROMETER    |           GYROSCOPE
+ * DLPF_CFG | Bandwidth | Delay  | Bandwidth | Delay  | Sample Rate
+ * ---------+-----------+--------+-----------+--------+-------------
+ * 0        | 260Hz     | 0ms    | 256Hz     | 0.98ms | 8kHz
+ * 1        | 184Hz     | 2.0ms  | 188Hz     | 1.9ms  | 1kHz
+ * 2        | 94Hz      | 3.0ms  | 98Hz      | 2.8ms  | 1kHz
+ * 3        | 44Hz      | 4.9ms  | 42Hz      | 4.8ms  | 1kHz
+ * 4        | 21Hz      | 8.5ms  | 20Hz      | 8.3ms  | 1kHz
+ * 5        | 10Hz      | 13.8ms | 10Hz      | 13.4ms | 1kHz
+ * 6        | 5Hz       | 19.0ms | 5Hz       | 18.6ms | 1kHz
+ * 7        |   -- Reserved --   |   -- Reserved --   | Reserved
+ * 
+ * + * @return DLFP configuration + * @see MPU6050_RA_CONFIG + * @see MPU6050_CFG_DLPF_CFG_BIT + * @see MPU6050_CFG_DLPF_CFG_LENGTH + */ +uint8_t MPU6050::getDLPFMode() { + I2Cdev::readBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_DLPF_CFG_BIT, MPU6050_CFG_DLPF_CFG_LENGTH, buffer); + return buffer[0]; +} +/** Set digital low-pass filter configuration. + * @param mode New DLFP configuration setting + * @see getDLPFBandwidth() + * @see MPU6050_DLPF_BW_256 + * @see MPU6050_RA_CONFIG + * @see MPU6050_CFG_DLPF_CFG_BIT + * @see MPU6050_CFG_DLPF_CFG_LENGTH + */ +void MPU6050::setDLPFMode(uint8_t mode) { + I2Cdev::writeBits(devAddr, MPU6050_RA_CONFIG, MPU6050_CFG_DLPF_CFG_BIT, MPU6050_CFG_DLPF_CFG_LENGTH, mode); +} + +// GYRO_CONFIG register + +/** Get full-scale gyroscope range. + * The FS_SEL parameter allows setting the full-scale range of the gyro sensors, + * as described in the table below. + * + *
+ * 0 = +/- 250 degrees/sec
+ * 1 = +/- 500 degrees/sec
+ * 2 = +/- 1000 degrees/sec
+ * 3 = +/- 2000 degrees/sec
+ * 
+ * + * @return Current full-scale gyroscope range setting + * @see MPU6050_GYRO_FS_250 + * @see MPU6050_RA_GYRO_CONFIG + * @see MPU6050_GCONFIG_FS_SEL_BIT + * @see MPU6050_GCONFIG_FS_SEL_LENGTH + */ +uint8_t MPU6050::getFullScaleGyroRange() { + I2Cdev::readBits(devAddr, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, buffer); + return buffer[0]; +} +/** Set full-scale gyroscope range. + * @param range New full-scale gyroscope range value + * @see getFullScaleRange() + * @see MPU6050_GYRO_FS_250 + * @see MPU6050_RA_GYRO_CONFIG + * @see MPU6050_GCONFIG_FS_SEL_BIT + * @see MPU6050_GCONFIG_FS_SEL_LENGTH + */ +void MPU6050::setFullScaleGyroRange(uint8_t range) { + I2Cdev::writeBits(devAddr, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, range); +} + +// ACCEL_CONFIG register + +/** Get self-test enabled setting for accelerometer X axis. + * @return Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +bool MPU6050::getAccelXSelfTest() { + I2Cdev::readBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_XA_ST_BIT, buffer); + return buffer[0]; +} +/** Get self-test enabled setting for accelerometer X axis. + * @param enabled Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050::setAccelXSelfTest(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_XA_ST_BIT, enabled); +} +/** Get self-test enabled value for accelerometer Y axis. + * @return Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +bool MPU6050::getAccelYSelfTest() { + I2Cdev::readBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_YA_ST_BIT, buffer); + return buffer[0]; +} +/** Get self-test enabled value for accelerometer Y axis. + * @param enabled Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050::setAccelYSelfTest(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_YA_ST_BIT, enabled); +} +/** Get self-test enabled value for accelerometer Z axis. + * @return Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +bool MPU6050::getAccelZSelfTest() { + I2Cdev::readBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ZA_ST_BIT, buffer); + return buffer[0]; +} +/** Set self-test enabled value for accelerometer Z axis. + * @param enabled Self-test enabled value + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050::setAccelZSelfTest(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ZA_ST_BIT, enabled); +} +/** Get full-scale accelerometer range. + * The FS_SEL parameter allows setting the full-scale range of the accelerometer + * sensors, as described in the table below. + * + *
+ * 0 = +/- 2g
+ * 1 = +/- 4g
+ * 2 = +/- 8g
+ * 3 = +/- 16g
+ * 
+ * + * @return Current full-scale accelerometer range setting + * @see MPU6050_ACCEL_FS_2 + * @see MPU6050_RA_ACCEL_CONFIG + * @see MPU6050_ACONFIG_AFS_SEL_BIT + * @see MPU6050_ACONFIG_AFS_SEL_LENGTH + */ +uint8_t MPU6050::getFullScaleAccelRange() { + I2Cdev::readBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, buffer); + return buffer[0]; +} +/** Set full-scale accelerometer range. + * @param range New full-scale accelerometer range setting + * @see getFullScaleAccelRange() + */ +void MPU6050::setFullScaleAccelRange(uint8_t range) { + I2Cdev::writeBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, range); +} +/** Get the high-pass filter configuration. + * The DHPF is a filter module in the path leading to motion detectors (Free + * Fall, Motion threshold, and Zero Motion). The high pass filter output is not + * available to the data registers (see Figure in Section 8 of the MPU-6000/ + * MPU-6050 Product Specification document). + * + * The high pass filter has three modes: + * + *
+ *    Reset: The filter output settles to zero within one sample. This
+ *           effectively disables the high pass filter. This mode may be toggled
+ *           to quickly settle the filter.
+ *
+ *    On:    The high pass filter will pass signals above the cut off frequency.
+ *
+ *    Hold:  When triggered, the filter holds the present sample. The filter
+ *           output will be the difference between the input sample and the held
+ *           sample.
+ * 
+ * + *
+ * ACCEL_HPF | Filter Mode | Cut-off Frequency
+ * ----------+-------------+------------------
+ * 0         | Reset       | None
+ * 1         | On          | 5Hz
+ * 2         | On          | 2.5Hz
+ * 3         | On          | 1.25Hz
+ * 4         | On          | 0.63Hz
+ * 7         | Hold        | None
+ * 
+ * + * @return Current high-pass filter configuration + * @see MPU6050_DHPF_RESET + * @see MPU6050_RA_ACCEL_CONFIG + */ +uint8_t MPU6050::getDHPFMode() { + I2Cdev::readBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_LENGTH, buffer); + return buffer[0]; +} +/** Set the high-pass filter configuration. + * @param bandwidth New high-pass filter configuration + * @see setDHPFMode() + * @see MPU6050_DHPF_RESET + * @see MPU6050_RA_ACCEL_CONFIG + */ +void MPU6050::setDHPFMode(uint8_t bandwidth) { + I2Cdev::writeBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_ACCEL_HPF_BIT, MPU6050_ACONFIG_ACCEL_HPF_LENGTH, bandwidth); +} + +// FF_THR register + +/** Get free-fall event acceleration threshold. + * This register configures the detection threshold for Free Fall event + * detection. The unit of FF_THR is 1LSB = 2mg. Free Fall is detected when the + * absolute value of the accelerometer measurements for the three axes are each + * less than the detection threshold. This condition increments the Free Fall + * duration counter (Register 30). The Free Fall interrupt is triggered when the + * Free Fall duration counter reaches the time specified in FF_DUR. + * + * For more details on the Free Fall detection interrupt, see Section 8.2 of the + * MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and + * 58 of this document. + * + * @return Current free-fall acceleration threshold value (LSB = 2mg) + * @see MPU6050_RA_FF_THR + */ +uint8_t MPU6050::getFreefallDetectionThreshold() { + I2Cdev::readByte(devAddr, MPU6050_RA_FF_THR, buffer); + return buffer[0]; +} +/** Get free-fall event acceleration threshold. + * @param threshold New free-fall acceleration threshold value (LSB = 2mg) + * @see getFreefallDetectionThreshold() + * @see MPU6050_RA_FF_THR + */ +void MPU6050::setFreefallDetectionThreshold(uint8_t threshold) { + I2Cdev::writeByte(devAddr, MPU6050_RA_FF_THR, threshold); +} + +// FF_DUR register + +/** Get free-fall event duration threshold. + * This register configures the duration counter threshold for Free Fall event + * detection. The duration counter ticks at 1kHz, therefore FF_DUR has a unit + * of 1 LSB = 1 ms. + * + * The Free Fall duration counter increments while the absolute value of the + * accelerometer measurements are each less than the detection threshold + * (Register 29). The Free Fall interrupt is triggered when the Free Fall + * duration counter reaches the time specified in this register. + * + * For more details on the Free Fall detection interrupt, see Section 8.2 of + * the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 + * and 58 of this document. + * + * @return Current free-fall duration threshold value (LSB = 1ms) + * @see MPU6050_RA_FF_DUR + */ +uint8_t MPU6050::getFreefallDetectionDuration() { + I2Cdev::readByte(devAddr, MPU6050_RA_FF_DUR, buffer); + return buffer[0]; +} +/** Get free-fall event duration threshold. + * @param duration New free-fall duration threshold value (LSB = 1ms) + * @see getFreefallDetectionDuration() + * @see MPU6050_RA_FF_DUR + */ +void MPU6050::setFreefallDetectionDuration(uint8_t duration) { + I2Cdev::writeByte(devAddr, MPU6050_RA_FF_DUR, duration); +} + +// MOT_THR register + +/** Get motion detection event acceleration threshold. + * This register configures the detection threshold for Motion interrupt + * generation. The unit of MOT_THR is 1LSB = 2mg. Motion is detected when the + * absolute value of any of the accelerometer measurements exceeds this Motion + * detection threshold. This condition increments the Motion detection duration + * counter (Register 32). The Motion detection interrupt is triggered when the + * Motion Detection counter reaches the time count specified in MOT_DUR + * (Register 32). + * + * The Motion interrupt will indicate the axis and polarity of detected motion + * in MOT_DETECT_STATUS (Register 97). + * + * For more details on the Motion detection interrupt, see Section 8.3 of the + * MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and + * 58 of this document. + * + * @return Current motion detection acceleration threshold value (LSB = 2mg) + * @see MPU6050_RA_MOT_THR + */ +uint8_t MPU6050::getMotionDetectionThreshold() { + I2Cdev::readByte(devAddr, MPU6050_RA_MOT_THR, buffer); + return buffer[0]; +} +/** Set free-fall event acceleration threshold. + * @param threshold New motion detection acceleration threshold value (LSB = 2mg) + * @see getMotionDetectionThreshold() + * @see MPU6050_RA_MOT_THR + */ +void MPU6050::setMotionDetectionThreshold(uint8_t threshold) { + I2Cdev::writeByte(devAddr, MPU6050_RA_MOT_THR, threshold); +} + +// MOT_DUR register + +/** Get motion detection event duration threshold. + * This register configures the duration counter threshold for Motion interrupt + * generation. The duration counter ticks at 1 kHz, therefore MOT_DUR has a unit + * of 1LSB = 1ms. The Motion detection duration counter increments when the + * absolute value of any of the accelerometer measurements exceeds the Motion + * detection threshold (Register 31). The Motion detection interrupt is + * triggered when the Motion detection counter reaches the time count specified + * in this register. + * + * For more details on the Motion detection interrupt, see Section 8.3 of the + * MPU-6000/MPU-6050 Product Specification document. + * + * @return Current motion detection duration threshold value (LSB = 1ms) + * @see MPU6050_RA_MOT_DUR + */ +uint8_t MPU6050::getMotionDetectionDuration() { + I2Cdev::readByte(devAddr, MPU6050_RA_MOT_DUR, buffer); + return buffer[0]; +} +/** Set motion detection event duration threshold. + * @param duration New motion detection duration threshold value (LSB = 1ms) + * @see getMotionDetectionDuration() + * @see MPU6050_RA_MOT_DUR + */ +void MPU6050::setMotionDetectionDuration(uint8_t duration) { + I2Cdev::writeByte(devAddr, MPU6050_RA_MOT_DUR, duration); +} + +// ZRMOT_THR register + +/** Get zero motion detection event acceleration threshold. + * This register configures the detection threshold for Zero Motion interrupt + * generation. The unit of ZRMOT_THR is 1LSB = 2mg. Zero Motion is detected when + * the absolute value of the accelerometer measurements for the 3 axes are each + * less than the detection threshold. This condition increments the Zero Motion + * duration counter (Register 34). The Zero Motion interrupt is triggered when + * the Zero Motion duration counter reaches the time count specified in + * ZRMOT_DUR (Register 34). + * + * Unlike Free Fall or Motion detection, Zero Motion detection triggers an + * interrupt both when Zero Motion is first detected and when Zero Motion is no + * longer detected. + * + * When a zero motion event is detected, a Zero Motion Status will be indicated + * in the MOT_DETECT_STATUS register (Register 97). When a motion-to-zero-motion + * condition is detected, the status bit is set to 1. When a zero-motion-to- + * motion condition is detected, the status bit is set to 0. + * + * For more details on the Zero Motion detection interrupt, see Section 8.4 of + * the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 + * and 58 of this document. + * + * @return Current zero motion detection acceleration threshold value (LSB = 2mg) + * @see MPU6050_RA_ZRMOT_THR + */ +uint8_t MPU6050::getZeroMotionDetectionThreshold() { + I2Cdev::readByte(devAddr, MPU6050_RA_ZRMOT_THR, buffer); + return buffer[0]; +} +/** Set zero motion detection event acceleration threshold. + * @param threshold New zero motion detection acceleration threshold value (LSB = 2mg) + * @see getZeroMotionDetectionThreshold() + * @see MPU6050_RA_ZRMOT_THR + */ +void MPU6050::setZeroMotionDetectionThreshold(uint8_t threshold) { + I2Cdev::writeByte(devAddr, MPU6050_RA_ZRMOT_THR, threshold); +} + +// ZRMOT_DUR register + +/** Get zero motion detection event duration threshold. + * This register configures the duration counter threshold for Zero Motion + * interrupt generation. The duration counter ticks at 16 Hz, therefore + * ZRMOT_DUR has a unit of 1 LSB = 64 ms. The Zero Motion duration counter + * increments while the absolute value of the accelerometer measurements are + * each less than the detection threshold (Register 33). The Zero Motion + * interrupt is triggered when the Zero Motion duration counter reaches the time + * count specified in this register. + * + * For more details on the Zero Motion detection interrupt, see Section 8.4 of + * the MPU-6000/MPU-6050 Product Specification document, as well as Registers 56 + * and 58 of this document. + * + * @return Current zero motion detection duration threshold value (LSB = 64ms) + * @see MPU6050_RA_ZRMOT_DUR + */ +uint8_t MPU6050::getZeroMotionDetectionDuration() { + I2Cdev::readByte(devAddr, MPU6050_RA_ZRMOT_DUR, buffer); + return buffer[0]; +} +/** Set zero motion detection event duration threshold. + * @param duration New zero motion detection duration threshold value (LSB = 1ms) + * @see getZeroMotionDetectionDuration() + * @see MPU6050_RA_ZRMOT_DUR + */ +void MPU6050::setZeroMotionDetectionDuration(uint8_t duration) { + I2Cdev::writeByte(devAddr, MPU6050_RA_ZRMOT_DUR, duration); +} + +// FIFO_EN register + +/** Get temperature FIFO enabled value. + * When set to 1, this bit enables TEMP_OUT_H and TEMP_OUT_L (Registers 65 and + * 66) to be written into the FIFO buffer. + * @return Current temperature FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getTempFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_TEMP_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set temperature FIFO enabled value. + * @param enabled New temperature FIFO enabled value + * @see getTempFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setTempFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_TEMP_FIFO_EN_BIT, enabled); +} +/** Get gyroscope X-axis FIFO enabled value. + * When set to 1, this bit enables GYRO_XOUT_H and GYRO_XOUT_L (Registers 67 and + * 68) to be written into the FIFO buffer. + * @return Current gyroscope X-axis FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getXGyroFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_XG_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set gyroscope X-axis FIFO enabled value. + * @param enabled New gyroscope X-axis FIFO enabled value + * @see getXGyroFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setXGyroFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_XG_FIFO_EN_BIT, enabled); +} +/** Get gyroscope Y-axis FIFO enabled value. + * When set to 1, this bit enables GYRO_YOUT_H and GYRO_YOUT_L (Registers 69 and + * 70) to be written into the FIFO buffer. + * @return Current gyroscope Y-axis FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getYGyroFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_YG_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set gyroscope Y-axis FIFO enabled value. + * @param enabled New gyroscope Y-axis FIFO enabled value + * @see getYGyroFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setYGyroFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_YG_FIFO_EN_BIT, enabled); +} +/** Get gyroscope Z-axis FIFO enabled value. + * When set to 1, this bit enables GYRO_ZOUT_H and GYRO_ZOUT_L (Registers 71 and + * 72) to be written into the FIFO buffer. + * @return Current gyroscope Z-axis FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getZGyroFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ZG_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set gyroscope Z-axis FIFO enabled value. + * @param enabled New gyroscope Z-axis FIFO enabled value + * @see getZGyroFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setZGyroFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ZG_FIFO_EN_BIT, enabled); +} +/** Get accelerometer FIFO enabled value. + * When set to 1, this bit enables ACCEL_XOUT_H, ACCEL_XOUT_L, ACCEL_YOUT_H, + * ACCEL_YOUT_L, ACCEL_ZOUT_H, and ACCEL_ZOUT_L (Registers 59 to 64) to be + * written into the FIFO buffer. + * @return Current accelerometer FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getAccelFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ACCEL_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set accelerometer FIFO enabled value. + * @param enabled New accelerometer FIFO enabled value + * @see getAccelFIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setAccelFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_ACCEL_FIFO_EN_BIT, enabled); +} +/** Get Slave 2 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 2 to be written into the FIFO buffer. + * @return Current Slave 2 FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getSlave2FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV2_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set Slave 2 FIFO enabled value. + * @param enabled New Slave 2 FIFO enabled value + * @see getSlave2FIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setSlave2FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV2_FIFO_EN_BIT, enabled); +} +/** Get Slave 1 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 1 to be written into the FIFO buffer. + * @return Current Slave 1 FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getSlave1FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV1_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set Slave 1 FIFO enabled value. + * @param enabled New Slave 1 FIFO enabled value + * @see getSlave1FIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setSlave1FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV1_FIFO_EN_BIT, enabled); +} +/** Get Slave 0 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 0 to be written into the FIFO buffer. + * @return Current Slave 0 FIFO enabled value + * @see MPU6050_RA_FIFO_EN + */ +bool MPU6050::getSlave0FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV0_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set Slave 0 FIFO enabled value. + * @param enabled New Slave 0 FIFO enabled value + * @see getSlave0FIFOEnabled() + * @see MPU6050_RA_FIFO_EN + */ +void MPU6050::setSlave0FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_FIFO_EN, MPU6050_SLV0_FIFO_EN_BIT, enabled); +} + +// I2C_MST_CTRL register + +/** Get multi-master enabled value. + * Multi-master capability allows multiple I2C masters to operate on the same + * bus. In circuits where multi-master capability is required, set MULT_MST_EN + * to 1. This will increase current drawn by approximately 30uA. + * + * In circuits where multi-master capability is required, the state of the I2C + * bus must always be monitored by each separate I2C Master. Before an I2C + * Master can assume arbitration of the bus, it must first confirm that no other + * I2C Master has arbitration of the bus. When MULT_MST_EN is set to 1, the + * MPU-60X0's bus arbitration detection logic is turned on, enabling it to + * detect when the bus is available. + * + * @return Current multi-master enabled value + * @see MPU6050_RA_I2C_MST_CTRL + */ +bool MPU6050::getMultiMasterEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_MULT_MST_EN_BIT, buffer); + return buffer[0]; +} +/** Set multi-master enabled value. + * @param enabled New multi-master enabled value + * @see getMultiMasterEnabled() + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050::setMultiMasterEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_MULT_MST_EN_BIT, enabled); +} +/** Get wait-for-external-sensor-data enabled value. + * When the WAIT_FOR_ES bit is set to 1, the Data Ready interrupt will be + * delayed until External Sensor data from the Slave Devices are loaded into the + * EXT_SENS_DATA registers. This is used to ensure that both the internal sensor + * data (i.e. from gyro and accel) and external sensor data have been loaded to + * their respective data registers (i.e. the data is synced) when the Data Ready + * interrupt is triggered. + * + * @return Current wait-for-external-sensor-data enabled value + * @see MPU6050_RA_I2C_MST_CTRL + */ +bool MPU6050::getWaitForExternalSensorEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_WAIT_FOR_ES_BIT, buffer); + return buffer[0]; +} +/** Set wait-for-external-sensor-data enabled value. + * @param enabled New wait-for-external-sensor-data enabled value + * @see getWaitForExternalSensorEnabled() + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050::setWaitForExternalSensorEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_WAIT_FOR_ES_BIT, enabled); +} +/** Get Slave 3 FIFO enabled value. + * When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) + * associated with Slave 3 to be written into the FIFO buffer. + * @return Current Slave 3 FIFO enabled value + * @see MPU6050_RA_MST_CTRL + */ +bool MPU6050::getSlave3FIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_SLV_3_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set Slave 3 FIFO enabled value. + * @param enabled New Slave 3 FIFO enabled value + * @see getSlave3FIFOEnabled() + * @see MPU6050_RA_MST_CTRL + */ +void MPU6050::setSlave3FIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_SLV_3_FIFO_EN_BIT, enabled); +} +/** Get slave read/write transition enabled value. + * The I2C_MST_P_NSR bit configures the I2C Master's transition from one slave + * read to the next slave read. If the bit equals 0, there will be a restart + * between reads. If the bit equals 1, there will be a stop followed by a start + * of the following read. When a write transaction follows a read transaction, + * the stop followed by a start of the successive write will be always used. + * + * @return Current slave read/write transition enabled value + * @see MPU6050_RA_I2C_MST_CTRL + */ +bool MPU6050::getSlaveReadWriteTransitionEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_P_NSR_BIT, buffer); + return buffer[0]; +} +/** Set slave read/write transition enabled value. + * @param enabled New slave read/write transition enabled value + * @see getSlaveReadWriteTransitionEnabled() + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050::setSlaveReadWriteTransitionEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_P_NSR_BIT, enabled); +} +/** Get I2C master clock speed. + * I2C_MST_CLK is a 4 bit unsigned value which configures a divider on the + * MPU-60X0 internal 8MHz clock. It sets the I2C master clock speed according to + * the following table: + * + *
+ * I2C_MST_CLK | I2C Master Clock Speed | 8MHz Clock Divider
+ * ------------+------------------------+-------------------
+ * 0           | 348kHz                 | 23
+ * 1           | 333kHz                 | 24
+ * 2           | 320kHz                 | 25
+ * 3           | 308kHz                 | 26
+ * 4           | 296kHz                 | 27
+ * 5           | 286kHz                 | 28
+ * 6           | 276kHz                 | 29
+ * 7           | 267kHz                 | 30
+ * 8           | 258kHz                 | 31
+ * 9           | 500kHz                 | 16
+ * 10          | 471kHz                 | 17
+ * 11          | 444kHz                 | 18
+ * 12          | 421kHz                 | 19
+ * 13          | 400kHz                 | 20
+ * 14          | 381kHz                 | 21
+ * 15          | 364kHz                 | 22
+ * 
+ * + * @return Current I2C master clock speed + * @see MPU6050_RA_I2C_MST_CTRL + */ +uint8_t MPU6050::getMasterClockSpeed() { + I2Cdev::readBits(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_LENGTH, buffer); + return buffer[0]; +} +/** Set I2C master clock speed. + * @reparam speed Current I2C master clock speed + * @see MPU6050_RA_I2C_MST_CTRL + */ +void MPU6050::setMasterClockSpeed(uint8_t speed) { + I2Cdev::writeBits(devAddr, MPU6050_RA_I2C_MST_CTRL, MPU6050_I2C_MST_CLK_BIT, MPU6050_I2C_MST_CLK_LENGTH, speed); +} + +// I2C_SLV* registers (Slave 0-3) + +/** Get the I2C address of the specified slave (0-3). + * Note that Bit 7 (MSB) controls read/write mode. If Bit 7 is set, it's a read + * operation, and if it is cleared, then it's a write operation. The remaining + * bits (6-0) are the 7-bit device address of the slave device. + * + * In read mode, the result of the read is placed in the lowest available + * EXT_SENS_DATA register. For further information regarding the allocation of + * read results, please refer to the EXT_SENS_DATA register description + * (Registers 73 - 96). + * + * The MPU-6050 supports a total of five slaves, but Slave 4 has unique + * characteristics, and so it has its own functions (getSlave4* and setSlave4*). + * + * I2C data transactions are performed at the Sample Rate, as defined in + * Register 25. The user is responsible for ensuring that I2C data transactions + * to and from each enabled Slave can be completed within a single period of the + * Sample Rate. + * + * The I2C slave access rate can be reduced relative to the Sample Rate. This + * reduced access rate is determined by I2C_MST_DLY (Register 52). Whether a + * slave's access rate is reduced relative to the Sample Rate is determined by + * I2C_MST_DELAY_CTRL (Register 103). + * + * The processing order for the slaves is fixed. The sequence followed for + * processing the slaves is Slave 0, Slave 1, Slave 2, Slave 3 and Slave 4. If a + * particular Slave is disabled it will be skipped. + * + * Each slave can either be accessed at the sample rate or at a reduced sample + * rate. In a case where some slaves are accessed at the Sample Rate and some + * slaves are accessed at the reduced rate, the sequence of accessing the slaves + * (Slave 0 to Slave 4) is still followed. However, the reduced rate slaves will + * be skipped if their access rate dictates that they should not be accessed + * during that particular cycle. For further information regarding the reduced + * access rate, please refer to Register 52. Whether a slave is accessed at the + * Sample Rate or at the reduced rate is determined by the Delay Enable bits in + * Register 103. + * + * @param num Slave number (0-3) + * @return Current address for specified slave + * @see MPU6050_RA_I2C_SLV0_ADDR + */ +uint8_t MPU6050::getSlaveAddress(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV0_ADDR + num*3, buffer); + return buffer[0]; +} +/** Set the I2C address of the specified slave (0-3). + * @param num Slave number (0-3) + * @param address New address for specified slave + * @see getSlaveAddress() + * @see MPU6050_RA_I2C_SLV0_ADDR + */ +void MPU6050::setSlaveAddress(uint8_t num, uint8_t address) { + if (num > 3) return; + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV0_ADDR + num*3, address); +} +/** Get the active internal register for the specified slave (0-3). + * Read/write operations for this slave will be done to whatever internal + * register address is stored in this MPU register. + * + * The MPU-6050 supports a total of five slaves, but Slave 4 has unique + * characteristics, and so it has its own functions. + * + * @param num Slave number (0-3) + * @return Current active register for specified slave + * @see MPU6050_RA_I2C_SLV0_REG + */ +uint8_t MPU6050::getSlaveRegister(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV0_REG + num*3, buffer); + return buffer[0]; +} +/** Set the active internal register for the specified slave (0-3). + * @param num Slave number (0-3) + * @param reg New active register for specified slave + * @see getSlaveRegister() + * @see MPU6050_RA_I2C_SLV0_REG + */ +void MPU6050::setSlaveRegister(uint8_t num, uint8_t reg) { + if (num > 3) return; + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV0_REG + num*3, reg); +} +/** Get the enabled value for the specified slave (0-3). + * When set to 1, this bit enables Slave 0 for data transfer operations. When + * cleared to 0, this bit disables Slave 0 from data transfer operations. + * @param num Slave number (0-3) + * @return Current enabled value for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050::getSlaveEnabled(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_EN_BIT, buffer); + return buffer[0]; +} +/** Set the enabled value for the specified slave (0-3). + * @param num Slave number (0-3) + * @param enabled New enabled value for specified slave + * @see getSlaveEnabled() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050::setSlaveEnabled(uint8_t num, bool enabled) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_EN_BIT, enabled); +} +/** Get word pair byte-swapping enabled for the specified slave (0-3). + * When set to 1, this bit enables byte swapping. When byte swapping is enabled, + * the high and low bytes of a word pair are swapped. Please refer to + * I2C_SLV0_GRP for the pairing convention of the word pairs. When cleared to 0, + * bytes transferred to and from Slave 0 will be written to EXT_SENS_DATA + * registers in the order they were transferred. + * + * @param num Slave number (0-3) + * @return Current word pair byte-swapping enabled value for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050::getSlaveWordByteSwap(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_BYTE_SW_BIT, buffer); + return buffer[0]; +} +/** Set word pair byte-swapping enabled for the specified slave (0-3). + * @param num Slave number (0-3) + * @param enabled New word pair byte-swapping enabled value for specified slave + * @see getSlaveWordByteSwap() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050::setSlaveWordByteSwap(uint8_t num, bool enabled) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_BYTE_SW_BIT, enabled); +} +/** Get write mode for the specified slave (0-3). + * When set to 1, the transaction will read or write data only. When cleared to + * 0, the transaction will write a register address prior to reading or writing + * data. This should equal 0 when specifying the register address within the + * Slave device to/from which the ensuing data transaction will take place. + * + * @param num Slave number (0-3) + * @return Current write mode for specified slave (0 = register address + data, 1 = data only) + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050::getSlaveWriteMode(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_REG_DIS_BIT, buffer); + return buffer[0]; +} +/** Set write mode for the specified slave (0-3). + * @param num Slave number (0-3) + * @param mode New write mode for specified slave (0 = register address + data, 1 = data only) + * @see getSlaveWriteMode() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050::setSlaveWriteMode(uint8_t num, bool mode) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_REG_DIS_BIT, mode); +} +/** Get word pair grouping order offset for the specified slave (0-3). + * This sets specifies the grouping order of word pairs received from registers. + * When cleared to 0, bytes from register addresses 0 and 1, 2 and 3, etc (even, + * then odd register addresses) are paired to form a word. When set to 1, bytes + * from register addresses are paired 1 and 2, 3 and 4, etc. (odd, then even + * register addresses) are paired to form a word. + * + * @param num Slave number (0-3) + * @return Current word pair grouping order offset for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +bool MPU6050::getSlaveWordGroupOffset(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_GRP_BIT, buffer); + return buffer[0]; +} +/** Set word pair grouping order offset for the specified slave (0-3). + * @param num Slave number (0-3) + * @param enabled New word pair grouping order offset for specified slave + * @see getSlaveWordGroupOffset() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050::setSlaveWordGroupOffset(uint8_t num, bool enabled) { + if (num > 3) return; + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_GRP_BIT, enabled); +} +/** Get number of bytes to read for the specified slave (0-3). + * Specifies the number of bytes transferred to and from Slave 0. Clearing this + * bit to 0 is equivalent to disabling the register by writing 0 to I2C_SLV0_EN. + * @param num Slave number (0-3) + * @return Number of bytes to read for specified slave + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +uint8_t MPU6050::getSlaveDataLength(uint8_t num) { + if (num > 3) return 0; + I2Cdev::readBits(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_LEN_BIT, MPU6050_I2C_SLV_LEN_LENGTH, buffer); + return buffer[0]; +} +/** Set number of bytes to read for the specified slave (0-3). + * @param num Slave number (0-3) + * @param length Number of bytes to read for specified slave + * @see getSlaveDataLength() + * @see MPU6050_RA_I2C_SLV0_CTRL + */ +void MPU6050::setSlaveDataLength(uint8_t num, uint8_t length) { + if (num > 3) return; + I2Cdev::writeBits(devAddr, MPU6050_RA_I2C_SLV0_CTRL + num*3, MPU6050_I2C_SLV_LEN_BIT, MPU6050_I2C_SLV_LEN_LENGTH, length); +} + +// I2C_SLV* registers (Slave 4) + +/** Get the I2C address of Slave 4. + * Note that Bit 7 (MSB) controls read/write mode. If Bit 7 is set, it's a read + * operation, and if it is cleared, then it's a write operation. The remaining + * bits (6-0) are the 7-bit device address of the slave device. + * + * @return Current address for Slave 4 + * @see getSlaveAddress() + * @see MPU6050_RA_I2C_SLV4_ADDR + */ +uint8_t MPU6050::getSlave4Address() { + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV4_ADDR, buffer); + return buffer[0]; +} +/** Set the I2C address of Slave 4. + * @param address New address for Slave 4 + * @see getSlave4Address() + * @see MPU6050_RA_I2C_SLV4_ADDR + */ +void MPU6050::setSlave4Address(uint8_t address) { + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV4_ADDR, address); +} +/** Get the active internal register for the Slave 4. + * Read/write operations for this slave will be done to whatever internal + * register address is stored in this MPU register. + * + * @return Current active register for Slave 4 + * @see MPU6050_RA_I2C_SLV4_REG + */ +uint8_t MPU6050::getSlave4Register() { + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV4_REG, buffer); + return buffer[0]; +} +/** Set the active internal register for Slave 4. + * @param reg New active register for Slave 4 + * @see getSlave4Register() + * @see MPU6050_RA_I2C_SLV4_REG + */ +void MPU6050::setSlave4Register(uint8_t reg) { + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV4_REG, reg); +} +/** Set new byte to write to Slave 4. + * This register stores the data to be written into the Slave 4. If I2C_SLV4_RW + * is set 1 (set to read), this register has no effect. + * @param data New byte to write to Slave 4 + * @see MPU6050_RA_I2C_SLV4_DO + */ +void MPU6050::setSlave4OutputByte(uint8_t data) { + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV4_DO, data); +} +/** Get the enabled value for the Slave 4. + * When set to 1, this bit enables Slave 4 for data transfer operations. When + * cleared to 0, this bit disables Slave 4 from data transfer operations. + * @return Current enabled value for Slave 4 + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +bool MPU6050::getSlave4Enabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_EN_BIT, buffer); + return buffer[0]; +} +/** Set the enabled value for Slave 4. + * @param enabled New enabled value for Slave 4 + * @see getSlave4Enabled() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050::setSlave4Enabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_EN_BIT, enabled); +} +/** Get the enabled value for Slave 4 transaction interrupts. + * When set to 1, this bit enables the generation of an interrupt signal upon + * completion of a Slave 4 transaction. When cleared to 0, this bit disables the + * generation of an interrupt signal upon completion of a Slave 4 transaction. + * The interrupt status can be observed in Register 54. + * + * @return Current enabled value for Slave 4 transaction interrupts. + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +bool MPU6050::getSlave4InterruptEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_INT_EN_BIT, buffer); + return buffer[0]; +} +/** Set the enabled value for Slave 4 transaction interrupts. + * @param enabled New enabled value for Slave 4 transaction interrupts. + * @see getSlave4InterruptEnabled() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050::setSlave4InterruptEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_INT_EN_BIT, enabled); +} +/** Get write mode for Slave 4. + * When set to 1, the transaction will read or write data only. When cleared to + * 0, the transaction will write a register address prior to reading or writing + * data. This should equal 0 when specifying the register address within the + * Slave device to/from which the ensuing data transaction will take place. + * + * @return Current write mode for Slave 4 (0 = register address + data, 1 = data only) + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +bool MPU6050::getSlave4WriteMode() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_REG_DIS_BIT, buffer); + return buffer[0]; +} +/** Set write mode for the Slave 4. + * @param mode New write mode for Slave 4 (0 = register address + data, 1 = data only) + * @see getSlave4WriteMode() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050::setSlave4WriteMode(bool mode) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_REG_DIS_BIT, mode); +} +/** Get Slave 4 master delay value. + * This configures the reduced access rate of I2C slaves relative to the Sample + * Rate. When a slave's access rate is decreased relative to the Sample Rate, + * the slave is accessed every: + * + * 1 / (1 + I2C_MST_DLY) samples + * + * This base Sample Rate in turn is determined by SMPLRT_DIV (register 25) and + * DLPF_CFG (register 26). Whether a slave's access rate is reduced relative to + * the Sample Rate is determined by I2C_MST_DELAY_CTRL (register 103). For + * further information regarding the Sample Rate, please refer to register 25. + * + * @return Current Slave 4 master delay value + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +uint8_t MPU6050::getSlave4MasterDelay() { + I2Cdev::readBits(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_MST_DLY_BIT, MPU6050_I2C_SLV4_MST_DLY_LENGTH, buffer); + return buffer[0]; +} +/** Set Slave 4 master delay value. + * @param delay New Slave 4 master delay value + * @see getSlave4MasterDelay() + * @see MPU6050_RA_I2C_SLV4_CTRL + */ +void MPU6050::setSlave4MasterDelay(uint8_t delay) { + I2Cdev::writeBits(devAddr, MPU6050_RA_I2C_SLV4_CTRL, MPU6050_I2C_SLV4_MST_DLY_BIT, MPU6050_I2C_SLV4_MST_DLY_LENGTH, delay); +} +/** Get last available byte read from Slave 4. + * This register stores the data read from Slave 4. This field is populated + * after a read transaction. + * @return Last available byte read from to Slave 4 + * @see MPU6050_RA_I2C_SLV4_DI + */ +uint8_t MPU6050::getSlate4InputByte() { + I2Cdev::readByte(devAddr, MPU6050_RA_I2C_SLV4_DI, buffer); + return buffer[0]; +} + +// I2C_MST_STATUS register + +/** Get FSYNC interrupt status. + * This bit reflects the status of the FSYNC interrupt from an external device + * into the MPU-60X0. This is used as a way to pass an external interrupt + * through the MPU-60X0 to the host application processor. When set to 1, this + * bit will cause an interrupt if FSYNC_INT_EN is asserted in INT_PIN_CFG + * (Register 55). + * @return FSYNC interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getPassthroughStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_PASS_THROUGH_BIT, buffer); + return buffer[0]; +} +/** Get Slave 4 transaction done status. + * Automatically sets to 1 when a Slave 4 transaction has completed. This + * triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register + * (Register 56) is asserted and if the SLV_4_DONE_INT bit is asserted in the + * I2C_SLV4_CTRL register (Register 52). + * @return Slave 4 transaction done status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getSlave4IsDone() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV4_DONE_BIT, buffer); + return buffer[0]; +} +/** Get master arbitration lost status. + * This bit automatically sets to 1 when the I2C Master has lost arbitration of + * the auxiliary I2C bus (an error condition). This triggers an interrupt if the + * I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted. + * @return Master arbitration lost status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getLostArbitration() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_LOST_ARB_BIT, buffer); + return buffer[0]; +} +/** Get Slave 4 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 4. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 4 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getSlave4Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV4_NACK_BIT, buffer); + return buffer[0]; +} +/** Get Slave 3 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 3. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 3 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getSlave3Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV3_NACK_BIT, buffer); + return buffer[0]; +} +/** Get Slave 2 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 2. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 2 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getSlave2Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV2_NACK_BIT, buffer); + return buffer[0]; +} +/** Get Slave 1 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 1. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 1 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getSlave1Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV1_NACK_BIT, buffer); + return buffer[0]; +} +/** Get Slave 0 NACK status. + * This bit automatically sets to 1 when the I2C Master receives a NACK in a + * transaction with Slave 0. This triggers an interrupt if the I2C_MST_INT_EN + * bit in the INT_ENABLE register (Register 56) is asserted. + * @return Slave 0 NACK interrupt status + * @see MPU6050_RA_I2C_MST_STATUS + */ +bool MPU6050::getSlave0Nack() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_STATUS, MPU6050_MST_I2C_SLV0_NACK_BIT, buffer); + return buffer[0]; +} + +// INT_PIN_CFG register + +/** Get interrupt logic level mode. + * Will be set 0 for active-high, 1 for active-low. + * @return Current interrupt mode (0=active-high, 1=active-low) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_LEVEL_BIT + */ +bool MPU6050::getInterruptMode() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_LEVEL_BIT, buffer); + return buffer[0]; +} +/** Set interrupt logic level mode. + * @param mode New interrupt mode (0=active-high, 1=active-low) + * @see getInterruptMode() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_LEVEL_BIT + */ +void MPU6050::setInterruptMode(bool mode) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_LEVEL_BIT, mode); +} +/** Get interrupt drive mode. + * Will be set 0 for push-pull, 1 for open-drain. + * @return Current interrupt drive mode (0=push-pull, 1=open-drain) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_OPEN_BIT + */ +bool MPU6050::getInterruptDrive() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_OPEN_BIT, buffer); + return buffer[0]; +} +/** Set interrupt drive mode. + * @param drive New interrupt drive mode (0=push-pull, 1=open-drain) + * @see getInterruptDrive() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_OPEN_BIT + */ +void MPU6050::setInterruptDrive(bool drive) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_OPEN_BIT, drive); +} +/** Get interrupt latch mode. + * Will be set 0 for 50us-pulse, 1 for latch-until-int-cleared. + * @return Current latch mode (0=50us-pulse, 1=latch-until-int-cleared) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_LATCH_INT_EN_BIT + */ +bool MPU6050::getInterruptLatch() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_LATCH_INT_EN_BIT, buffer); + return buffer[0]; +} +/** Set interrupt latch mode. + * @param latch New latch mode (0=50us-pulse, 1=latch-until-int-cleared) + * @see getInterruptLatch() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_LATCH_INT_EN_BIT + */ +void MPU6050::setInterruptLatch(bool latch) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_LATCH_INT_EN_BIT, latch); +} +/** Get interrupt latch clear mode. + * Will be set 0 for status-read-only, 1 for any-register-read. + * @return Current latch clear mode (0=status-read-only, 1=any-register-read) + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_RD_CLEAR_BIT + */ +bool MPU6050::getInterruptLatchClear() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_RD_CLEAR_BIT, buffer); + return buffer[0]; +} +/** Set interrupt latch clear mode. + * @param clear New latch clear mode (0=status-read-only, 1=any-register-read) + * @see getInterruptLatchClear() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_INT_RD_CLEAR_BIT + */ +void MPU6050::setInterruptLatchClear(bool clear) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_INT_RD_CLEAR_BIT, clear); +} +/** Get FSYNC interrupt logic level mode. + * @return Current FSYNC interrupt mode (0=active-high, 1=active-low) + * @see getFSyncInterruptMode() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT + */ +bool MPU6050::getFSyncInterruptLevel() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT, buffer); + return buffer[0]; +} +/** Set FSYNC interrupt logic level mode. + * @param mode New FSYNC interrupt mode (0=active-high, 1=active-low) + * @see getFSyncInterruptMode() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT + */ +void MPU6050::setFSyncInterruptLevel(bool level) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT, level); +} +/** Get FSYNC pin interrupt enabled setting. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled setting + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_EN_BIT + */ +bool MPU6050::getFSyncInterruptEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_EN_BIT, buffer); + return buffer[0]; +} +/** Set FSYNC pin interrupt enabled setting. + * @param enabled New FSYNC pin interrupt enabled setting + * @see getFSyncInterruptEnabled() + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_FSYNC_INT_EN_BIT + */ +void MPU6050::setFSyncInterruptEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_FSYNC_INT_EN_BIT, enabled); +} +/** Get I2C bypass enabled status. + * When this bit is equal to 1 and I2C_MST_EN (Register 106 bit[5]) is equal to + * 0, the host application processor will be able to directly access the + * auxiliary I2C bus of the MPU-60X0. When this bit is equal to 0, the host + * application processor will not be able to directly access the auxiliary I2C + * bus of the MPU-60X0 regardless of the state of I2C_MST_EN (Register 106 + * bit[5]). + * @return Current I2C bypass enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_I2C_BYPASS_EN_BIT + */ +bool MPU6050::getI2CBypassEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT, buffer); + return buffer[0]; +} +/** Set I2C bypass enabled status. + * When this bit is equal to 1 and I2C_MST_EN (Register 106 bit[5]) is equal to + * 0, the host application processor will be able to directly access the + * auxiliary I2C bus of the MPU-60X0. When this bit is equal to 0, the host + * application processor will not be able to directly access the auxiliary I2C + * bus of the MPU-60X0 regardless of the state of I2C_MST_EN (Register 106 + * bit[5]). + * @param enabled New I2C bypass enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_I2C_BYPASS_EN_BIT + */ +void MPU6050::setI2CBypassEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT, enabled); +} +/** Get reference clock output enabled status. + * When this bit is equal to 1, a reference clock output is provided at the + * CLKOUT pin. When this bit is equal to 0, the clock output is disabled. For + * further information regarding CLKOUT, please refer to the MPU-60X0 Product + * Specification document. + * @return Current reference clock output enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_CLKOUT_EN_BIT + */ +bool MPU6050::getClockOutputEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_CLKOUT_EN_BIT, buffer); + return buffer[0]; +} +/** Set reference clock output enabled status. + * When this bit is equal to 1, a reference clock output is provided at the + * CLKOUT pin. When this bit is equal to 0, the clock output is disabled. For + * further information regarding CLKOUT, please refer to the MPU-60X0 Product + * Specification document. + * @param enabled New reference clock output enabled status + * @see MPU6050_RA_INT_PIN_CFG + * @see MPU6050_INTCFG_CLKOUT_EN_BIT + */ +void MPU6050::setClockOutputEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_CLKOUT_EN_BIT, enabled); +} + +// INT_ENABLE register + +/** Get full interrupt enabled status. + * Full register byte for all interrupts, for quick reading. Each bit will be + * set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +uint8_t MPU6050::getIntEnabled() { + I2Cdev::readByte(devAddr, MPU6050_RA_INT_ENABLE, buffer); + return buffer[0]; +} +/** Set full interrupt enabled status. + * Full register byte for all interrupts, for quick reading. Each bit should be + * set 0 for disabled, 1 for enabled. + * @param enabled New interrupt enabled status + * @see getIntFreefallEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +void MPU6050::setIntEnabled(uint8_t enabled) { + I2Cdev::writeByte(devAddr, MPU6050_RA_INT_ENABLE, enabled); +} +/** Get Free Fall interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +bool MPU6050::getIntFreefallEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FF_BIT, buffer); + return buffer[0]; +} +/** Set Free Fall interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntFreefallEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FF_BIT + **/ +void MPU6050::setIntFreefallEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FF_BIT, enabled); +} +/** Get Motion Detection interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_MOT_BIT + **/ +bool MPU6050::getIntMotionEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_MOT_BIT, buffer); + return buffer[0]; +} +/** Set Motion Detection interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntMotionEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_MOT_BIT + **/ +void MPU6050::setIntMotionEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_MOT_BIT, enabled); +} +/** Get Zero Motion Detection interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_ZMOT_BIT + **/ +bool MPU6050::getIntZeroMotionEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_ZMOT_BIT, buffer); + return buffer[0]; +} +/** Set Zero Motion Detection interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntZeroMotionEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_ZMOT_BIT + **/ +void MPU6050::setIntZeroMotionEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_ZMOT_BIT, enabled); +} +/** Get FIFO Buffer Overflow interrupt enabled status. + * Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FIFO_OFLOW_BIT + **/ +bool MPU6050::getIntFIFOBufferOverflowEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FIFO_OFLOW_BIT, buffer); + return buffer[0]; +} +/** Set FIFO Buffer Overflow interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntFIFOBufferOverflowEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_FIFO_OFLOW_BIT + **/ +void MPU6050::setIntFIFOBufferOverflowEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_FIFO_OFLOW_BIT, enabled); +} +/** Get I2C Master interrupt enabled status. + * This enables any of the I2C Master interrupt sources to generate an + * interrupt. Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_I2C_MST_INT_BIT + **/ +bool MPU6050::getIntI2CMasterEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_I2C_MST_INT_BIT, buffer); + return buffer[0]; +} +/** Set I2C Master interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntI2CMasterEnabled() + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_I2C_MST_INT_BIT + **/ +void MPU6050::setIntI2CMasterEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_I2C_MST_INT_BIT, enabled); +} +/** Get Data Ready interrupt enabled setting. + * This event occurs each time a write operation to all of the sensor registers + * has been completed. Will be set 0 for disabled, 1 for enabled. + * @return Current interrupt enabled status + * @see MPU6050_RA_INT_ENABLE + * @see MPU6050_INTERRUPT_DATA_RDY_BIT + */ +bool MPU6050::getIntDataReadyEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DATA_RDY_BIT, buffer); + return buffer[0]; +} +/** Set Data Ready interrupt enabled status. + * @param enabled New interrupt enabled status + * @see getIntDataReadyEnabled() + * @see MPU6050_RA_INT_CFG + * @see MPU6050_INTERRUPT_DATA_RDY_BIT + */ +void MPU6050::setIntDataReadyEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DATA_RDY_BIT, enabled); +} + +// INT_STATUS register + +/** Get full set of interrupt status bits. + * These bits clear to 0 after the register has been read. Very useful + * for getting multiple INT statuses, since each single bit read clears + * all of them because it has to read the whole byte. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + */ +uint8_t MPU6050::getIntStatus() { + I2Cdev::readByte(devAddr, MPU6050_RA_INT_STATUS, buffer); + return buffer[0]; +} +/** Get Free Fall interrupt status. + * This bit automatically sets to 1 when a Free Fall interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_FF_BIT + */ +bool MPU6050::getIntFreefallStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_FF_BIT, buffer); + return buffer[0]; +} +/** Get Motion Detection interrupt status. + * This bit automatically sets to 1 when a Motion Detection interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_MOT_BIT + */ +bool MPU6050::getIntMotionStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_MOT_BIT, buffer); + return buffer[0]; +} +/** Get Zero Motion Detection interrupt status. + * This bit automatically sets to 1 when a Zero Motion Detection interrupt has + * been generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_ZMOT_BIT + */ +bool MPU6050::getIntZeroMotionStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_ZMOT_BIT, buffer); + return buffer[0]; +} +/** Get FIFO Buffer Overflow interrupt status. + * This bit automatically sets to 1 when a Free Fall interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_FIFO_OFLOW_BIT + */ +bool MPU6050::getIntFIFOBufferOverflowStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_FIFO_OFLOW_BIT, buffer); + return buffer[0]; +} +/** Get I2C Master interrupt status. + * This bit automatically sets to 1 when an I2C Master interrupt has been + * generated. For a list of I2C Master interrupts, please refer to Register 54. + * The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_I2C_MST_INT_BIT + */ +bool MPU6050::getIntI2CMasterStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_I2C_MST_INT_BIT, buffer); + return buffer[0]; +} +/** Get Data Ready interrupt status. + * This bit automatically sets to 1 when a Data Ready interrupt has been + * generated. The bit clears to 0 after the register has been read. + * @return Current interrupt status + * @see MPU6050_RA_INT_STATUS + * @see MPU6050_INTERRUPT_DATA_RDY_BIT + */ +bool MPU6050::getIntDataReadyStatus() { + I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_DATA_RDY_BIT, buffer); + return buffer[0]; +} + +// ACCEL_*OUT_* registers + +/** Get raw 9-axis motion sensor readings (accel/gyro/compass). + * FUNCTION NOT FULLY IMPLEMENTED YET. + * @param ax 16-bit signed integer container for accelerometer X-axis value + * @param ay 16-bit signed integer container for accelerometer Y-axis value + * @param az 16-bit signed integer container for accelerometer Z-axis value + * @param gx 16-bit signed integer container for gyroscope X-axis value + * @param gy 16-bit signed integer container for gyroscope Y-axis value + * @param gz 16-bit signed integer container for gyroscope Z-axis value + * @param mx 16-bit signed integer container for magnetometer X-axis value + * @param my 16-bit signed integer container for magnetometer Y-axis value + * @param mz 16-bit signed integer container for magnetometer Z-axis value + * @see getMotion6() + * @see getAcceleration() + * @see getRotation() + * @see MPU6050_RA_ACCEL_XOUT_H + */ +void MPU6050::getMotion9(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz, int16_t* mx, int16_t* my, int16_t* mz) { + getMotion6(ax, ay, az, gx, gy, gz); + // TODO: magnetometer integration +} +/** Get raw 6-axis motion sensor readings (accel/gyro). + * Retrieves all currently available motion sensor values. + * @param ax 16-bit signed integer container for accelerometer X-axis value + * @param ay 16-bit signed integer container for accelerometer Y-axis value + * @param az 16-bit signed integer container for accelerometer Z-axis value + * @param gx 16-bit signed integer container for gyroscope X-axis value + * @param gy 16-bit signed integer container for gyroscope Y-axis value + * @param gz 16-bit signed integer container for gyroscope Z-axis value + * @see getAcceleration() + * @see getRotation() + * @see MPU6050_RA_ACCEL_XOUT_H + */ +void MPU6050::getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz) { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_XOUT_H, 14, buffer); + *ax = (((int16_t)buffer[0]) << 8) | buffer[1]; + *ay = (((int16_t)buffer[2]) << 8) | buffer[3]; + *az = (((int16_t)buffer[4]) << 8) | buffer[5]; + *gx = (((int16_t)buffer[8]) << 8) | buffer[9]; + *gy = (((int16_t)buffer[10]) << 8) | buffer[11]; + *gz = (((int16_t)buffer[12]) << 8) | buffer[13]; +} +/** Get 3-axis accelerometer readings. + * These registers store the most recent accelerometer measurements. + * Accelerometer measurements are written to these registers at the Sample Rate + * as defined in Register 25. + * + * The accelerometer measurement registers, along with the temperature + * measurement registers, gyroscope measurement registers, and external sensor + * data registers, are composed of two sets of registers: an internal register + * set and a user-facing read register set. + * + * The data within the accelerometer sensors' internal register set is always + * updated at the Sample Rate. Meanwhile, the user-facing read register set + * duplicates the internal register set's data values whenever the serial + * interface is idle. This guarantees that a burst read of sensor registers will + * read measurements from the same sampling instant. Note that if burst reads + * are not used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * Each 16-bit accelerometer measurement has a full scale defined in ACCEL_FS + * (Register 28). For each full scale setting, the accelerometers' sensitivity + * per LSB in ACCEL_xOUT is shown in the table below: + * + *
+ * AFS_SEL | Full Scale Range | LSB Sensitivity
+ * --------+------------------+----------------
+ * 0       | +/- 2g           | 8192 LSB/mg
+ * 1       | +/- 4g           | 4096 LSB/mg
+ * 2       | +/- 8g           | 2048 LSB/mg
+ * 3       | +/- 16g          | 1024 LSB/mg
+ * 
+ * + * @param x 16-bit signed integer container for X-axis acceleration + * @param y 16-bit signed integer container for Y-axis acceleration + * @param z 16-bit signed integer container for Z-axis acceleration + * @see MPU6050_RA_GYRO_XOUT_H + */ +void MPU6050::getAcceleration(int16_t* x, int16_t* y, int16_t* z) { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_XOUT_H, 6, buffer); + *x = (((int16_t)buffer[0]) << 8) | buffer[1]; + *y = (((int16_t)buffer[2]) << 8) | buffer[3]; + *z = (((int16_t)buffer[4]) << 8) | buffer[5]; +} +/** Get X-axis accelerometer reading. + * @return X-axis acceleration measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_ACCEL_XOUT_H + */ +int16_t MPU6050::getAccelerationX() { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_XOUT_H, 2, buffer); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Y-axis accelerometer reading. + * @return Y-axis acceleration measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_ACCEL_YOUT_H + */ +int16_t MPU6050::getAccelerationY() { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_YOUT_H, 2, buffer); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Z-axis accelerometer reading. + * @return Z-axis acceleration measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_ACCEL_ZOUT_H + */ +int16_t MPU6050::getAccelerationZ() { + I2Cdev::readBytes(devAddr, MPU6050_RA_ACCEL_ZOUT_H, 2, buffer); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} + +// TEMP_OUT_* registers + +/** Get current internal temperature. + * @return Temperature reading in 16-bit 2's complement format + * @see MPU6050_RA_TEMP_OUT_H + */ +int16_t MPU6050::getTemperature() { + I2Cdev::readBytes(devAddr, MPU6050_RA_TEMP_OUT_H, 2, buffer); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} + +// GYRO_*OUT_* registers + +/** Get 3-axis gyroscope readings. + * These gyroscope measurement registers, along with the accelerometer + * measurement registers, temperature measurement registers, and external sensor + * data registers, are composed of two sets of registers: an internal register + * set and a user-facing read register set. + * The data within the gyroscope sensors' internal register set is always + * updated at the Sample Rate. Meanwhile, the user-facing read register set + * duplicates the internal register set's data values whenever the serial + * interface is idle. This guarantees that a burst read of sensor registers will + * read measurements from the same sampling instant. Note that if burst reads + * are not used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * Each 16-bit gyroscope measurement has a full scale defined in FS_SEL + * (Register 27). For each full scale setting, the gyroscopes' sensitivity per + * LSB in GYRO_xOUT is shown in the table below: + * + *
+ * FS_SEL | Full Scale Range   | LSB Sensitivity
+ * -------+--------------------+----------------
+ * 0      | +/- 250 degrees/s  | 131 LSB/deg/s
+ * 1      | +/- 500 degrees/s  | 65.5 LSB/deg/s
+ * 2      | +/- 1000 degrees/s | 32.8 LSB/deg/s
+ * 3      | +/- 2000 degrees/s | 16.4 LSB/deg/s
+ * 
+ * + * @param x 16-bit signed integer container for X-axis rotation + * @param y 16-bit signed integer container for Y-axis rotation + * @param z 16-bit signed integer container for Z-axis rotation + * @see getMotion6() + * @see MPU6050_RA_GYRO_XOUT_H + */ +void MPU6050::getRotation(int16_t* x, int16_t* y, int16_t* z) { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_XOUT_H, 6, buffer); + *x = (((int16_t)buffer[0]) << 8) | buffer[1]; + *y = (((int16_t)buffer[2]) << 8) | buffer[3]; + *z = (((int16_t)buffer[4]) << 8) | buffer[5]; +} +/** Get X-axis gyroscope reading. + * @return X-axis rotation measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_GYRO_XOUT_H + */ +int16_t MPU6050::getRotationX() { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_XOUT_H, 2, buffer); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Y-axis gyroscope reading. + * @return Y-axis rotation measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_GYRO_YOUT_H + */ +int16_t MPU6050::getRotationY() { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_YOUT_H, 2, buffer); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} +/** Get Z-axis gyroscope reading. + * @return Z-axis rotation measurement in 16-bit 2's complement format + * @see getMotion6() + * @see MPU6050_RA_GYRO_ZOUT_H + */ +int16_t MPU6050::getRotationZ() { + I2Cdev::readBytes(devAddr, MPU6050_RA_GYRO_ZOUT_H, 2, buffer); + return (((int16_t)buffer[0]) << 8) | buffer[1]; +} + +// EXT_SENS_DATA_* registers + +/** Read single byte from external sensor data register. + * These registers store data read from external sensors by the Slave 0, 1, 2, + * and 3 on the auxiliary I2C interface. Data read by Slave 4 is stored in + * I2C_SLV4_DI (Register 53). + * + * External sensor data is written to these registers at the Sample Rate as + * defined in Register 25. This access rate can be reduced by using the Slave + * Delay Enable registers (Register 103). + * + * External sensor data registers, along with the gyroscope measurement + * registers, accelerometer measurement registers, and temperature measurement + * registers, are composed of two sets of registers: an internal register set + * and a user-facing read register set. + * + * The data within the external sensors' internal register set is always updated + * at the Sample Rate (or the reduced access rate) whenever the serial interface + * is idle. This guarantees that a burst read of sensor registers will read + * measurements from the same sampling instant. Note that if burst reads are not + * used, the user is responsible for ensuring a set of single byte reads + * correspond to a single sampling instant by checking the Data Ready interrupt. + * + * Data is placed in these external sensor data registers according to + * I2C_SLV0_CTRL, I2C_SLV1_CTRL, I2C_SLV2_CTRL, and I2C_SLV3_CTRL (Registers 39, + * 42, 45, and 48). When more than zero bytes are read (I2C_SLVx_LEN > 0) from + * an enabled slave (I2C_SLVx_EN = 1), the slave is read at the Sample Rate (as + * defined in Register 25) or delayed rate (if specified in Register 52 and + * 103). During each Sample cycle, slave reads are performed in order of Slave + * number. If all slaves are enabled with more than zero bytes to be read, the + * order will be Slave 0, followed by Slave 1, Slave 2, and Slave 3. + * + * Each enabled slave will have EXT_SENS_DATA registers associated with it by + * number of bytes read (I2C_SLVx_LEN) in order of slave number, starting from + * EXT_SENS_DATA_00. Note that this means enabling or disabling a slave may + * change the higher numbered slaves' associated registers. Furthermore, if + * fewer total bytes are being read from the external sensors as a result of + * such a change, then the data remaining in the registers which no longer have + * an associated slave device (i.e. high numbered registers) will remain in + * these previously allocated registers unless reset. + * + * If the sum of the read lengths of all SLVx transactions exceed the number of + * available EXT_SENS_DATA registers, the excess bytes will be dropped. There + * are 24 EXT_SENS_DATA registers and hence the total read lengths between all + * the slaves cannot be greater than 24 or some bytes will be lost. + * + * Note: Slave 4's behavior is distinct from that of Slaves 0-3. For further + * information regarding the characteristics of Slave 4, please refer to + * Registers 49 to 53. + * + * EXAMPLE: + * Suppose that Slave 0 is enabled with 4 bytes to be read (I2C_SLV0_EN = 1 and + * I2C_SLV0_LEN = 4) while Slave 1 is enabled with 2 bytes to be read so that + * I2C_SLV1_EN = 1 and I2C_SLV1_LEN = 2. In such a situation, EXT_SENS_DATA _00 + * through _03 will be associated with Slave 0, while EXT_SENS_DATA _04 and 05 + * will be associated with Slave 1. If Slave 2 is enabled as well, registers + * starting from EXT_SENS_DATA_06 will be allocated to Slave 2. + * + * If Slave 2 is disabled while Slave 3 is enabled in this same situation, then + * registers starting from EXT_SENS_DATA_06 will be allocated to Slave 3 + * instead. + * + * REGISTER ALLOCATION FOR DYNAMIC DISABLE VS. NORMAL DISABLE: + * If a slave is disabled at any time, the space initially allocated to the + * slave in the EXT_SENS_DATA register, will remain associated with that slave. + * This is to avoid dynamic adjustment of the register allocation. + * + * The allocation of the EXT_SENS_DATA registers is recomputed only when (1) all + * slaves are disabled, or (2) the I2C_MST_RST bit is set (Register 106). + * + * This above is also true if one of the slaves gets NACKed and stops + * functioning. + * + * @param position Starting position (0-23) + * @return Byte read from register + */ +uint8_t MPU6050::getExternalSensorByte(int position) { + I2Cdev::readByte(devAddr, MPU6050_RA_EXT_SENS_DATA_00 + position, buffer); + return buffer[0]; +} +/** Read word (2 bytes) from external sensor data registers. + * @param position Starting position (0-21) + * @return Word read from register + * @see getExternalSensorByte() + */ +uint16_t MPU6050::getExternalSensorWord(int position) { + I2Cdev::readBytes(devAddr, MPU6050_RA_EXT_SENS_DATA_00 + position, 2, buffer); + return (((uint16_t)buffer[0]) << 8) | buffer[1]; +} +/** Read double word (4 bytes) from external sensor data registers. + * @param position Starting position (0-20) + * @return Double word read from registers + * @see getExternalSensorByte() + */ +uint32_t MPU6050::getExternalSensorDWord(int position) { + I2Cdev::readBytes(devAddr, MPU6050_RA_EXT_SENS_DATA_00 + position, 4, buffer); + return (((uint32_t)buffer[0]) << 24) | (((uint32_t)buffer[1]) << 16) | (((uint16_t)buffer[2]) << 8) | buffer[3]; +} + +// MOT_DETECT_STATUS register + +/** Get X-axis negative motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_XNEG_BIT + */ +bool MPU6050::getXNegMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_XNEG_BIT, buffer); + return buffer[0]; +} +/** Get X-axis positive motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_XPOS_BIT + */ +bool MPU6050::getXPosMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_XPOS_BIT, buffer); + return buffer[0]; +} +/** Get Y-axis negative motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_YNEG_BIT + */ +bool MPU6050::getYNegMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_YNEG_BIT, buffer); + return buffer[0]; +} +/** Get Y-axis positive motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_YPOS_BIT + */ +bool MPU6050::getYPosMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_YPOS_BIT, buffer); + return buffer[0]; +} +/** Get Z-axis negative motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_ZNEG_BIT + */ +bool MPU6050::getZNegMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_ZNEG_BIT, buffer); + return buffer[0]; +} +/** Get Z-axis positive motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_ZPOS_BIT + */ +bool MPU6050::getZPosMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_ZPOS_BIT, buffer); + return buffer[0]; +} +/** Get zero motion detection interrupt status. + * @return Motion detection status + * @see MPU6050_RA_MOT_DETECT_STATUS + * @see MPU6050_MOTION_MOT_ZRMOT_BIT + */ +bool MPU6050::getZeroMotionDetected() { + I2Cdev::readBit(devAddr, MPU6050_RA_MOT_DETECT_STATUS, MPU6050_MOTION_MOT_ZRMOT_BIT, buffer); + return buffer[0]; +} + +// I2C_SLV*_DO register + +/** Write byte to Data Output container for specified slave. + * This register holds the output data written into Slave when Slave is set to + * write mode. For further information regarding Slave control, please + * refer to Registers 37 to 39 and immediately following. + * @param num Slave number (0-3) + * @param data Byte to write + * @see MPU6050_RA_I2C_SLV0_DO + */ +void MPU6050::setSlaveOutputByte(uint8_t num, uint8_t data) { + if (num > 3) return; + I2Cdev::writeByte(devAddr, MPU6050_RA_I2C_SLV0_DO + num, data); +} + +// I2C_MST_DELAY_CTRL register + +/** Get external data shadow delay enabled status. + * This register is used to specify the timing of external sensor data + * shadowing. When DELAY_ES_SHADOW is set to 1, shadowing of external + * sensor data is delayed until all data has been received. + * @return Current external data shadow delay enabled status. + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT + */ +bool MPU6050::getExternalShadowDelayEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT, buffer); + return buffer[0]; +} +/** Set external data shadow delay enabled status. + * @param enabled New external data shadow delay enabled status. + * @see getExternalShadowDelayEnabled() + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT + */ +void MPU6050::setExternalShadowDelayEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT, enabled); +} +/** Get slave delay enabled status. + * When a particular slave delay is enabled, the rate of access for the that + * slave device is reduced. When a slave's access rate is decreased relative to + * the Sample Rate, the slave is accessed every: + * + * 1 / (1 + I2C_MST_DLY) Samples + * + * This base Sample Rate in turn is determined by SMPLRT_DIV (register * 25) + * and DLPF_CFG (register 26). + * + * For further information regarding I2C_MST_DLY, please refer to register 52. + * For further information regarding the Sample Rate, please refer to register 25. + * + * @param num Slave number (0-4) + * @return Current slave delay enabled status. + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT + */ +bool MPU6050::getSlaveDelayEnabled(uint8_t num) { + // MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT is 4, SLV3 is 3, etc. + if (num > 4) return 0; + I2Cdev::readBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, num, buffer); + return buffer[0]; +} +/** Set slave delay enabled status. + * @param num Slave number (0-4) + * @param enabled New slave delay enabled status. + * @see MPU6050_RA_I2C_MST_DELAY_CTRL + * @see MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT + */ +void MPU6050::setSlaveDelayEnabled(uint8_t num, bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_I2C_MST_DELAY_CTRL, num, enabled); +} + +// SIGNAL_PATH_RESET register + +/** Reset gyroscope signal path. + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * @see MPU6050_RA_SIGNAL_PATH_RESET + * @see MPU6050_PATHRESET_GYRO_RESET_BIT + */ +void MPU6050::resetGyroscopePath() { + I2Cdev::writeBit(devAddr, MPU6050_RA_SIGNAL_PATH_RESET, MPU6050_PATHRESET_GYRO_RESET_BIT, true); +} +/** Reset accelerometer signal path. + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * @see MPU6050_RA_SIGNAL_PATH_RESET + * @see MPU6050_PATHRESET_ACCEL_RESET_BIT + */ +void MPU6050::resetAccelerometerPath() { + I2Cdev::writeBit(devAddr, MPU6050_RA_SIGNAL_PATH_RESET, MPU6050_PATHRESET_ACCEL_RESET_BIT, true); +} +/** Reset temperature sensor signal path. + * The reset will revert the signal path analog to digital converters and + * filters to their power up configurations. + * @see MPU6050_RA_SIGNAL_PATH_RESET + * @see MPU6050_PATHRESET_TEMP_RESET_BIT + */ +void MPU6050::resetTemperaturePath() { + I2Cdev::writeBit(devAddr, MPU6050_RA_SIGNAL_PATH_RESET, MPU6050_PATHRESET_TEMP_RESET_BIT, true); +} + +// MOT_DETECT_CTRL register + +/** Get accelerometer power-on delay. + * The accelerometer data path provides samples to the sensor registers, Motion + * detection, Zero Motion detection, and Free Fall detection modules. The + * signal path contains filters which must be flushed on wake-up with new + * samples before the detection modules begin operations. The default wake-up + * delay, of 4ms can be lengthened by up to 3ms. This additional delay is + * specified in ACCEL_ON_DELAY in units of 1 LSB = 1 ms. The user may select + * any value above zero unless instructed otherwise by InvenSense. Please refer + * to Section 8 of the MPU-6000/MPU-6050 Product Specification document for + * further information regarding the detection modules. + * @return Current accelerometer power-on delay + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_ACCEL_ON_DELAY_BIT + */ +uint8_t MPU6050::getAccelerometerPowerOnDelay() { + I2Cdev::readBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_ACCEL_ON_DELAY_BIT, MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH, buffer); + return buffer[0]; +} +/** Set accelerometer power-on delay. + * @param delay New accelerometer power-on delay (0-3) + * @see getAccelerometerPowerOnDelay() + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_ACCEL_ON_DELAY_BIT + */ +void MPU6050::setAccelerometerPowerOnDelay(uint8_t delay) { + I2Cdev::writeBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_ACCEL_ON_DELAY_BIT, MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH, delay); +} +/** Get Free Fall detection counter decrement configuration. + * Detection is registered by the Free Fall detection module after accelerometer + * measurements meet their respective threshold conditions over a specified + * number of samples. When the threshold conditions are met, the corresponding + * detection counter increments by 1. The user may control the rate at which the + * detection counter decrements when the threshold condition is not met by + * configuring FF_COUNT. The decrement rate can be set according to the + * following table: + * + *
+ * FF_COUNT | Counter Decrement
+ * ---------+------------------
+ * 0        | Reset
+ * 1        | 1
+ * 2        | 2
+ * 3        | 4
+ * 
+ * + * When FF_COUNT is configured to 0 (reset), any non-qualifying sample will + * reset the counter to 0. For further information on Free Fall detection, + * please refer to Registers 29 to 32. + * + * @return Current decrement configuration + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_FF_COUNT_BIT + */ +uint8_t MPU6050::getFreefallDetectionCounterDecrement() { + I2Cdev::readBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_FF_COUNT_BIT, MPU6050_DETECT_FF_COUNT_LENGTH, buffer); + return buffer[0]; +} +/** Set Free Fall detection counter decrement configuration. + * @param decrement New decrement configuration value + * @see getFreefallDetectionCounterDecrement() + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_FF_COUNT_BIT + */ +void MPU6050::setFreefallDetectionCounterDecrement(uint8_t decrement) { + I2Cdev::writeBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_FF_COUNT_BIT, MPU6050_DETECT_FF_COUNT_LENGTH, decrement); +} +/** Get Motion detection counter decrement configuration. + * Detection is registered by the Motion detection module after accelerometer + * measurements meet their respective threshold conditions over a specified + * number of samples. When the threshold conditions are met, the corresponding + * detection counter increments by 1. The user may control the rate at which the + * detection counter decrements when the threshold condition is not met by + * configuring MOT_COUNT. The decrement rate can be set according to the + * following table: + * + *
+ * MOT_COUNT | Counter Decrement
+ * ----------+------------------
+ * 0         | Reset
+ * 1         | 1
+ * 2         | 2
+ * 3         | 4
+ * 
+ * + * When MOT_COUNT is configured to 0 (reset), any non-qualifying sample will + * reset the counter to 0. For further information on Motion detection, + * please refer to Registers 29 to 32. + * + */ +uint8_t MPU6050::getMotionDetectionCounterDecrement() { + I2Cdev::readBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_MOT_COUNT_BIT, MPU6050_DETECT_MOT_COUNT_LENGTH, buffer); + return buffer[0]; +} +/** Set Motion detection counter decrement configuration. + * @param decrement New decrement configuration value + * @see getMotionDetectionCounterDecrement() + * @see MPU6050_RA_MOT_DETECT_CTRL + * @see MPU6050_DETECT_MOT_COUNT_BIT + */ +void MPU6050::setMotionDetectionCounterDecrement(uint8_t decrement) { + I2Cdev::writeBits(devAddr, MPU6050_RA_MOT_DETECT_CTRL, MPU6050_DETECT_MOT_COUNT_BIT, MPU6050_DETECT_MOT_COUNT_LENGTH, decrement); +} + +// USER_CTRL register + +/** Get FIFO enabled status. + * When this bit is set to 0, the FIFO buffer is disabled. The FIFO buffer + * cannot be written to or read from while disabled. The FIFO buffer's state + * does not change unless the MPU-60X0 is power cycled. + * @return Current FIFO enabled status + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_FIFO_EN_BIT + */ +bool MPU6050::getFIFOEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_FIFO_EN_BIT, buffer); + return buffer[0]; +} +/** Set FIFO enabled status. + * @param enabled New FIFO enabled status + * @see getFIFOEnabled() + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_FIFO_EN_BIT + */ +void MPU6050::setFIFOEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_FIFO_EN_BIT, enabled); +} +/** Get I2C Master Mode enabled status. + * When this mode is enabled, the MPU-60X0 acts as the I2C Master to the + * external sensor slave devices on the auxiliary I2C bus. When this bit is + * cleared to 0, the auxiliary I2C bus lines (AUX_DA and AUX_CL) are logically + * driven by the primary I2C bus (SDA and SCL). This is a precondition to + * enabling Bypass Mode. For further information regarding Bypass Mode, please + * refer to Register 55. + * @return Current I2C Master Mode enabled status + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_I2C_MST_EN_BIT + */ +bool MPU6050::getI2CMasterModeEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_MST_EN_BIT, buffer); + return buffer[0]; +} +/** Set I2C Master Mode enabled status. + * @param enabled New I2C Master Mode enabled status + * @see getI2CMasterModeEnabled() + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_I2C_MST_EN_BIT + */ +void MPU6050::setI2CMasterModeEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_MST_EN_BIT, enabled); +} +/** Switch from I2C to SPI mode (MPU-6000 only) + * If this is set, the primary SPI interface will be enabled in place of the + * disabled primary I2C interface. + */ +void MPU6050::switchSPIEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_IF_DIS_BIT, enabled); +} +/** Reset the FIFO. + * This bit resets the FIFO buffer when set to 1 while FIFO_EN equals 0. This + * bit automatically clears to 0 after the reset has been triggered. + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_FIFO_RESET_BIT + */ +void MPU6050::resetFIFO() { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_FIFO_RESET_BIT, true); +} +/** Reset the I2C Master. + * This bit resets the I2C Master when set to 1 while I2C_MST_EN equals 0. + * This bit automatically clears to 0 after the reset has been triggered. + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_I2C_MST_RESET_BIT + */ +void MPU6050::resetI2CMaster() { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_MST_RESET_BIT, true); +} +/** Reset all sensor registers and signal paths. + * When set to 1, this bit resets the signal paths for all sensors (gyroscopes, + * accelerometers, and temperature sensor). This operation will also clear the + * sensor registers. This bit automatically clears to 0 after the reset has been + * triggered. + * + * When resetting only the signal path (and not the sensor registers), please + * use Register 104, SIGNAL_PATH_RESET. + * + * @see MPU6050_RA_USER_CTRL + * @see MPU6050_USERCTRL_SIG_COND_RESET_BIT + */ +void MPU6050::resetSensors() { + I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_SIG_COND_RESET_BIT, true); +} + +// PWR_MGMT_1 register + +/** Trigger a full device reset. + * A small delay of ~50ms may be desirable after triggering a reset. + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_DEVICE_RESET_BIT + */ +void MPU6050::reset() { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_DEVICE_RESET_BIT, true); +} +/** Get sleep mode status. + * Setting the SLEEP bit in the register puts the device into very low power + * sleep mode. In this mode, only the serial interface and internal registers + * remain active, allowing for a very low standby current. Clearing this bit + * puts the device back into normal mode. To save power, the individual standby + * selections for each of the gyros should be used if any gyro axis is not used + * by the application. + * @return Current sleep mode enabled status + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_SLEEP_BIT + */ +bool MPU6050::getSleepEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, buffer); + return buffer[0]; +} +/** Set sleep mode status. + * @param enabled New sleep mode enabled status + * @see getSleepEnabled() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_SLEEP_BIT + */ +void MPU6050::setSleepEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, enabled); +} +/** Get wake cycle enabled status. + * When this bit is set to 1 and SLEEP is disabled, the MPU-60X0 will cycle + * between sleep mode and waking up to take a single sample of data from active + * sensors at a rate determined by LP_WAKE_CTRL (register 108). + * @return Current sleep mode enabled status + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CYCLE_BIT + */ +bool MPU6050::getWakeCycleEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CYCLE_BIT, buffer); + return buffer[0]; +} +/** Set wake cycle enabled status. + * @param enabled New sleep mode enabled status + * @see getWakeCycleEnabled() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CYCLE_BIT + */ +void MPU6050::setWakeCycleEnabled(bool enabled) { + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CYCLE_BIT, enabled); +} +/** Get temperature sensor enabled status. + * Control the usage of the internal temperature sensor. + * + * Note: this register stores the *disabled* value, but for consistency with the + * rest of the code, the function is named and used with standard true/false + * values to indicate whether the sensor is enabled or disabled, respectively. + * + * @return Current temperature sensor enabled status + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_TEMP_DIS_BIT + */ +bool MPU6050::getTempSensorEnabled() { + I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, buffer); + return buffer[0] == 0; // 1 is actually disabled here +} +/** Set temperature sensor enabled status. + * Note: this register stores the *disabled* value, but for consistency with the + * rest of the code, the function is named and used with standard true/false + * values to indicate whether the sensor is enabled or disabled, respectively. + * + * @param enabled New temperature sensor enabled status + * @see getTempSensorEnabled() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_TEMP_DIS_BIT + */ +void MPU6050::setTempSensorEnabled(bool enabled) { + // 1 is actually disabled here + I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_TEMP_DIS_BIT, !enabled); +} +/** Get clock source setting. + * @return Current clock source setting + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CLKSEL_BIT + * @see MPU6050_PWR1_CLKSEL_LENGTH + */ +uint8_t MPU6050::getClockSource() { + I2Cdev::readBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, buffer); + return buffer[0]; +} +/** Set clock source setting. + * An internal 8MHz oscillator, gyroscope based clock, or external sources can + * be selected as the MPU-60X0 clock source. When the internal 8 MHz oscillator + * or an external source is chosen as the clock source, the MPU-60X0 can operate + * in low power modes with the gyroscopes disabled. + * + * Upon power up, the MPU-60X0 clock source defaults to the internal oscillator. + * However, it is highly recommended that the device be configured to use one of + * the gyroscopes (or an external clock source) as the clock reference for + * improved stability. The clock source can be selected according to the following table: + * + *
+ * CLK_SEL | Clock Source
+ * --------+--------------------------------------
+ * 0       | Internal oscillator
+ * 1       | PLL with X Gyro reference
+ * 2       | PLL with Y Gyro reference
+ * 3       | PLL with Z Gyro reference
+ * 4       | PLL with external 32.768kHz reference
+ * 5       | PLL with external 19.2MHz reference
+ * 6       | Reserved
+ * 7       | Stops the clock and keeps the timing generator in reset
+ * 
+ * + * @param source New clock source setting + * @see getClockSource() + * @see MPU6050_RA_PWR_MGMT_1 + * @see MPU6050_PWR1_CLKSEL_BIT + * @see MPU6050_PWR1_CLKSEL_LENGTH + */ +void MPU6050::setClockSource(uint8_t source) { + I2Cdev::writeBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, source); +} + +// PWR_MGMT_2 register + +/** Get wake frequency in Accel-Only Low Power Mode. + * The MPU-60X0 can be put into Accerlerometer Only Low Power Mode by setting + * PWRSEL to 1 in the Power Management 1 register (Register 107). In this mode, + * the device will power off all devices except for the primary I2C interface, + * waking only the accelerometer at fixed intervals to take a single + * measurement. The frequency of wake-ups can be configured with LP_WAKE_CTRL + * as shown below: + * + *
+ * LP_WAKE_CTRL | Wake-up Frequency
+ * -------------+------------------
+ * 0            | 1.25 Hz
+ * 1            | 2.5 Hz
+ * 2            | 5 Hz
+ * 3            | 10 Hz
+ * 
+ *
+ * For further information regarding the MPU-60X0's power modes, please refer to
+ * Register 107.
+ *
+ * @return Current wake frequency
+ * @see MPU6050_RA_PWR_MGMT_2
+ */
+uint8_t MPU6050::getWakeFrequency() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_LP_WAKE_CTRL_BIT, MPU6050_PWR2_LP_WAKE_CTRL_LENGTH, buffer);
+    return buffer[0];
+}
+/** Set wake frequency in Accel-Only Low Power Mode.
+ * @param frequency New wake frequency
+ * @see MPU6050_RA_PWR_MGMT_2
+ */
+void MPU6050::setWakeFrequency(uint8_t frequency) {
+    I2Cdev::writeBits(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_LP_WAKE_CTRL_BIT, MPU6050_PWR2_LP_WAKE_CTRL_LENGTH, frequency);
+}
+
+/** Get X-axis accelerometer standby enabled status.
+ * If enabled, the X-axis will not gather or report data (or use power).
+ * @return Current X-axis standby enabled status
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_XA_BIT
+ */
+bool MPU6050::getStandbyXAccelEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XA_BIT, buffer);
+    return buffer[0];
+}
+/** Set X-axis accelerometer standby enabled status.
+ * @param New X-axis standby enabled status
+ * @see getStandbyXAccelEnabled()
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_XA_BIT
+ */
+void MPU6050::setStandbyXAccelEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XA_BIT, enabled);
+}
+/** Get Y-axis accelerometer standby enabled status.
+ * If enabled, the Y-axis will not gather or report data (or use power).
+ * @return Current Y-axis standby enabled status
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_YA_BIT
+ */
+bool MPU6050::getStandbyYAccelEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YA_BIT, buffer);
+    return buffer[0];
+}
+/** Set Y-axis accelerometer standby enabled status.
+ * @param New Y-axis standby enabled status
+ * @see getStandbyYAccelEnabled()
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_YA_BIT
+ */
+void MPU6050::setStandbyYAccelEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YA_BIT, enabled);
+}
+/** Get Z-axis accelerometer standby enabled status.
+ * If enabled, the Z-axis will not gather or report data (or use power).
+ * @return Current Z-axis standby enabled status
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_ZA_BIT
+ */
+bool MPU6050::getStandbyZAccelEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZA_BIT, buffer);
+    return buffer[0];
+}
+/** Set Z-axis accelerometer standby enabled status.
+ * @param New Z-axis standby enabled status
+ * @see getStandbyZAccelEnabled()
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_ZA_BIT
+ */
+void MPU6050::setStandbyZAccelEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZA_BIT, enabled);
+}
+/** Get X-axis gyroscope standby enabled status.
+ * If enabled, the X-axis will not gather or report data (or use power).
+ * @return Current X-axis standby enabled status
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_XG_BIT
+ */
+bool MPU6050::getStandbyXGyroEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XG_BIT, buffer);
+    return buffer[0];
+}
+/** Set X-axis gyroscope standby enabled status.
+ * @param New X-axis standby enabled status
+ * @see getStandbyXGyroEnabled()
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_XG_BIT
+ */
+void MPU6050::setStandbyXGyroEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_XG_BIT, enabled);
+}
+/** Get Y-axis gyroscope standby enabled status.
+ * If enabled, the Y-axis will not gather or report data (or use power).
+ * @return Current Y-axis standby enabled status
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_YG_BIT
+ */
+bool MPU6050::getStandbyYGyroEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YG_BIT, buffer);
+    return buffer[0];
+}
+/** Set Y-axis gyroscope standby enabled status.
+ * @param New Y-axis standby enabled status
+ * @see getStandbyYGyroEnabled()
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_YG_BIT
+ */
+void MPU6050::setStandbyYGyroEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_YG_BIT, enabled);
+}
+/** Get Z-axis gyroscope standby enabled status.
+ * If enabled, the Z-axis will not gather or report data (or use power).
+ * @return Current Z-axis standby enabled status
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_ZG_BIT
+ */
+bool MPU6050::getStandbyZGyroEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZG_BIT, buffer);
+    return buffer[0];
+}
+/** Set Z-axis gyroscope standby enabled status.
+ * @param New Z-axis standby enabled status
+ * @see getStandbyZGyroEnabled()
+ * @see MPU6050_RA_PWR_MGMT_2
+ * @see MPU6050_PWR2_STBY_ZG_BIT
+ */
+void MPU6050::setStandbyZGyroEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_2, MPU6050_PWR2_STBY_ZG_BIT, enabled);
+}
+
+// FIFO_COUNT* registers
+
+/** Get current FIFO buffer size.
+ * This value indicates the number of bytes stored in the FIFO buffer. This
+ * number is in turn the number of bytes that can be read from the FIFO buffer
+ * and it is directly proportional to the number of samples available given the
+ * set of sensor data bound to be stored in the FIFO (register 35 and 36).
+ * @return Current FIFO buffer size
+ */
+uint16_t MPU6050::getFIFOCount() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_FIFO_COUNTH, 2, buffer);
+    return (((uint16_t)buffer[0]) << 8) | buffer[1];
+}
+
+// FIFO_R_W register
+
+/** Get byte from FIFO buffer.
+ * This register is used to read and write data from the FIFO buffer. Data is
+ * written to the FIFO in order of register number (from lowest to highest). If
+ * all the FIFO enable flags (see below) are enabled and all External Sensor
+ * Data registers (Registers 73 to 96) are associated with a Slave device, the
+ * contents of registers 59 through 96 will be written in order at the Sample
+ * Rate.
+ *
+ * The contents of the sensor data registers (Registers 59 to 96) are written
+ * into the FIFO buffer when their corresponding FIFO enable flags are set to 1
+ * in FIFO_EN (Register 35). An additional flag for the sensor data registers
+ * associated with I2C Slave 3 can be found in I2C_MST_CTRL (Register 36).
+ *
+ * If the FIFO buffer has overflowed, the status bit FIFO_OFLOW_INT is
+ * automatically set to 1. This bit is located in INT_STATUS (Register 58).
+ * When the FIFO buffer has overflowed, the oldest data will be lost and new
+ * data will be written to the FIFO.
+ *
+ * If the FIFO buffer is empty, reading this register will return the last byte
+ * that was previously read from the FIFO until new data is available. The user
+ * should check FIFO_COUNT to ensure that the FIFO buffer is not read when
+ * empty.
+ *
+ * @return Byte from FIFO buffer
+ */
+uint8_t MPU6050::getFIFOByte() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_FIFO_R_W, buffer);
+    return buffer[0];
+}
+void MPU6050::getFIFOBytes(uint8_t *data, uint8_t length) {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_FIFO_R_W, length, data);
+}
+/** Write byte to FIFO buffer.
+ * @see getFIFOByte()
+ * @see MPU6050_RA_FIFO_R_W
+ */
+void MPU6050::setFIFOByte(uint8_t data) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_FIFO_R_W, data);
+}
+
+// WHO_AM_I register
+
+/** Get Device ID.
+ * This register is used to verify the identity of the device (0b110100, 0x34).
+ * @return Device ID (6 bits only! should be 0x34)
+ * @see MPU6050_RA_WHO_AM_I
+ * @see MPU6050_WHO_AM_I_BIT
+ * @see MPU6050_WHO_AM_I_LENGTH
+ */
+uint8_t MPU6050::getDeviceID() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_WHO_AM_I, MPU6050_WHO_AM_I_BIT, MPU6050_WHO_AM_I_LENGTH, buffer);
+    return buffer[0];
+}
+/** Set Device ID.
+ * Write a new ID into the WHO_AM_I register (no idea why this should ever be
+ * necessary though).
+ * @param id New device ID to set.
+ * @see getDeviceID()
+ * @see MPU6050_RA_WHO_AM_I
+ * @see MPU6050_WHO_AM_I_BIT
+ * @see MPU6050_WHO_AM_I_LENGTH
+ */
+void MPU6050::setDeviceID(uint8_t id) {
+    I2Cdev::writeBits(devAddr, MPU6050_RA_WHO_AM_I, MPU6050_WHO_AM_I_BIT, MPU6050_WHO_AM_I_LENGTH, id);
+}
+
+// ======== UNDOCUMENTED/DMP REGISTERS/METHODS ========
+
+// XG_OFFS_TC register
+
+uint8_t MPU6050::getOTPBankValid() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OTP_BNK_VLD_BIT, buffer);
+    return buffer[0];
+}
+void MPU6050::setOTPBankValid(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OTP_BNK_VLD_BIT, enabled);
+}
+int8_t MPU6050::getXGyroOffset() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
+    return buffer[0];
+}
+void MPU6050::setXGyroOffset(int8_t offset) {
+    I2Cdev::writeBits(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset);
+}
+
+// YG_OFFS_TC register
+
+int8_t MPU6050::getYGyroOffset() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
+    return buffer[0];
+}
+void MPU6050::setYGyroOffset(int8_t offset) {
+    I2Cdev::writeBits(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset);
+}
+
+// ZG_OFFS_TC register
+
+int8_t MPU6050::getZGyroOffset() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
+    return buffer[0];
+}
+void MPU6050::setZGyroOffset(int8_t offset) {
+    I2Cdev::writeBits(devAddr, MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset);
+}
+
+// X_FINE_GAIN register
+
+int8_t MPU6050::getXFineGain() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_X_FINE_GAIN, buffer);
+    return buffer[0];
+}
+void MPU6050::setXFineGain(int8_t gain) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_X_FINE_GAIN, gain);
+}
+
+// Y_FINE_GAIN register
+
+int8_t MPU6050::getYFineGain() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_Y_FINE_GAIN, buffer);
+    return buffer[0];
+}
+void MPU6050::setYFineGain(int8_t gain) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_Y_FINE_GAIN, gain);
+}
+
+// Z_FINE_GAIN register
+
+int8_t MPU6050::getZFineGain() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_Z_FINE_GAIN, buffer);
+    return buffer[0];
+}
+void MPU6050::setZFineGain(int8_t gain) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_Z_FINE_GAIN, gain);
+}
+
+// XA_OFFS_* registers
+
+int16_t MPU6050::getXAccelOffset() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_XA_OFFS_H, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setXAccelOffset(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_XA_OFFS_H, offset);
+}
+
+// YA_OFFS_* register
+
+int16_t MPU6050::getYAccelOffset() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_YA_OFFS_H, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setYAccelOffset(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_YA_OFFS_H, offset);
+}
+
+// ZA_OFFS_* register
+
+int16_t MPU6050::getZAccelOffset() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_ZA_OFFS_H, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setZAccelOffset(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_ZA_OFFS_H, offset);
+}
+
+// XG_OFFS_USR* registers
+
+int16_t MPU6050::getXGyroOffsetUser() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_XG_OFFS_USRH, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setXGyroOffsetUser(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_XG_OFFS_USRH, offset);
+}
+
+// YG_OFFS_USR* register
+
+int16_t MPU6050::getYGyroOffsetUser() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_YG_OFFS_USRH, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setYGyroOffsetUser(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_YG_OFFS_USRH, offset);
+}
+
+// ZG_OFFS_USR* register
+
+int16_t MPU6050::getZGyroOffsetUser() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_ZG_OFFS_USRH, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setZGyroOffsetUser(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_ZG_OFFS_USRH, offset);
+}
+
+// INT_ENABLE register (DMP functions)
+
+bool MPU6050::getIntPLLReadyEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_PLL_RDY_INT_BIT, buffer);
+    return buffer[0];
+}
+void MPU6050::setIntPLLReadyEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_PLL_RDY_INT_BIT, enabled);
+}
+bool MPU6050::getIntDMPEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DMP_INT_BIT, buffer);
+    return buffer[0];
+}
+void MPU6050::setIntDMPEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_INT_ENABLE, MPU6050_INTERRUPT_DMP_INT_BIT, enabled);
+}
+
+// DMP_INT_STATUS
+
+bool MPU6050::getDMPInt5Status() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_5_BIT, buffer);
+    return buffer[0];
+}
+bool MPU6050::getDMPInt4Status() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_4_BIT, buffer);
+    return buffer[0];
+}
+bool MPU6050::getDMPInt3Status() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_3_BIT, buffer);
+    return buffer[0];
+}
+bool MPU6050::getDMPInt2Status() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_2_BIT, buffer);
+    return buffer[0];
+}
+bool MPU6050::getDMPInt1Status() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_1_BIT, buffer);
+    return buffer[0];
+}
+bool MPU6050::getDMPInt0Status() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_DMP_INT_STATUS, MPU6050_DMPINT_0_BIT, buffer);
+    return buffer[0];
+}
+
+// INT_STATUS register (DMP functions)
+
+bool MPU6050::getIntPLLReadyStatus() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_PLL_RDY_INT_BIT, buffer);
+    return buffer[0];
+}
+bool MPU6050::getIntDMPStatus() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_INT_STATUS, MPU6050_INTERRUPT_DMP_INT_BIT, buffer);
+    return buffer[0];
+}
+
+// USER_CTRL register (DMP functions)
+
+bool MPU6050::getDMPEnabled() {
+    I2Cdev::readBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_DMP_EN_BIT, buffer);
+    return buffer[0];
+}
+void MPU6050::setDMPEnabled(bool enabled) {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_DMP_EN_BIT, enabled);
+}
+void MPU6050::resetDMP() {
+    I2Cdev::writeBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_DMP_RESET_BIT, true);
+}
+
+// BANK_SEL register
+
+void MPU6050::setMemoryBank(uint8_t bank, bool prefetchEnabled, bool userBank) {
+    bank &= 0x1F;
+    if (userBank) bank |= 0x20;
+    if (prefetchEnabled) bank |= 0x40;
+    I2Cdev::writeByte(devAddr, MPU6050_RA_BANK_SEL, bank);
+}
+
+// MEM_START_ADDR register
+
+void MPU6050::setMemoryStartAddress(uint8_t address) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_MEM_START_ADDR, address);
+}
+
+// MEM_R_W register
+
+uint8_t MPU6050::readMemoryByte() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_MEM_R_W, buffer);
+    return buffer[0];
+}
+void MPU6050::writeMemoryByte(uint8_t data) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_MEM_R_W, data);
+}
+void MPU6050::readMemoryBlock(uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address) {
+    setMemoryBank(bank);
+    setMemoryStartAddress(address);
+    uint8_t chunkSize;
+    for (uint16_t i = 0; i < dataSize;) {
+        // determine correct chunk size according to bank position and data size
+        chunkSize = MPU6050_DMP_MEMORY_CHUNK_SIZE;
+
+        // make sure we don't go past the data size
+        if (i + chunkSize > dataSize) chunkSize = dataSize - i;
+
+        // make sure this chunk doesn't go past the bank boundary (256 bytes)
+        if (chunkSize > 256 - address) chunkSize = 256 - address;
+
+        // read the chunk of data as specified
+        I2Cdev::readBytes(devAddr, MPU6050_RA_MEM_R_W, chunkSize, data + i);
+        
+        // increase byte index by [chunkSize]
+        i += chunkSize;
+
+        // uint8_t automatically wraps to 0 at 256
+        address += chunkSize;
+
+        // if we aren't done, update bank (if necessary) and address
+        if (i < dataSize) {
+            if (address == 0) bank++;
+            setMemoryBank(bank);
+            setMemoryStartAddress(address);
+        }
+    }
+}
+bool MPU6050::writeMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address, bool verify, bool useProgMem) {
+    setMemoryBank(bank);
+    setMemoryStartAddress(address);
+    uint8_t chunkSize;
+    uint8_t *verifyBuffer;
+    uint8_t *progBuffer = NULL; // Keep compiler quiet
+    uint16_t i;
+    uint8_t j;
+    if (verify) verifyBuffer = (uint8_t *)malloc(MPU6050_DMP_MEMORY_CHUNK_SIZE);
+    if (useProgMem) progBuffer = (uint8_t *)malloc(MPU6050_DMP_MEMORY_CHUNK_SIZE);
+    for (i = 0; i < dataSize;) {
+        // determine correct chunk size according to bank position and data size
+        chunkSize = MPU6050_DMP_MEMORY_CHUNK_SIZE;
+
+        // make sure we don't go past the data size
+        if (i + chunkSize > dataSize) chunkSize = dataSize - i;
+
+        // make sure this chunk doesn't go past the bank boundary (256 bytes)
+        if (chunkSize > 256 - address) chunkSize = 256 - address;
+        
+        if (useProgMem) {
+            // write the chunk of data as specified
+            for (j = 0; j < chunkSize; j++) progBuffer[j] = pgm_read_byte(data + i + j);
+        } else {
+            // write the chunk of data as specified
+            progBuffer = (uint8_t *)data + i;
+        }
+
+        I2Cdev::writeBytes(devAddr, MPU6050_RA_MEM_R_W, chunkSize, progBuffer);
+
+        // verify data if needed
+        if (verify && verifyBuffer) {
+            setMemoryBank(bank);
+            setMemoryStartAddress(address);
+            I2Cdev::readBytes(devAddr, MPU6050_RA_MEM_R_W, chunkSize, verifyBuffer);
+            if (memcmp(progBuffer, verifyBuffer, chunkSize) != 0) {
+                /*Serial.print("Block write verification error, bank ");
+                Serial.print(bank, DEC);
+                Serial.print(", address ");
+                Serial.print(address, DEC);
+                Serial.print("!\nExpected:");
+                for (j = 0; j < chunkSize; j++) {
+                    Serial.print(" 0x");
+                    if (progBuffer[j] < 16) Serial.print("0");
+                    Serial.print(progBuffer[j], HEX);
+                }
+                Serial.print("\nReceived:");
+                for (uint8_t j = 0; j < chunkSize; j++) {
+                    Serial.print(" 0x");
+                    if (verifyBuffer[i + j] < 16) Serial.print("0");
+                    Serial.print(verifyBuffer[i + j], HEX);
+                }
+                Serial.print("\n");*/
+                free(verifyBuffer);
+                if (useProgMem) free(progBuffer);
+                return false; // uh oh.
+            }
+        }
+
+        // increase byte index by [chunkSize]
+        i += chunkSize;
+
+        // uint8_t automatically wraps to 0 at 256
+        address += chunkSize;
+
+        // if we aren't done, update bank (if necessary) and address
+        if (i < dataSize) {
+            if (address == 0) bank++;
+            setMemoryBank(bank);
+            setMemoryStartAddress(address);
+        }
+    }
+    if (verify) free(verifyBuffer);
+    if (useProgMem) free(progBuffer);
+    return true;
+}
+bool MPU6050::writeProgMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank, uint8_t address, bool verify) {
+    return writeMemoryBlock(data, dataSize, bank, address, verify, true);
+}
+bool MPU6050::writeDMPConfigurationSet(const uint8_t *data, uint16_t dataSize, bool useProgMem) {
+    uint8_t *progBuffer = NULL, success, special;
+    uint16_t i, j;
+    if (useProgMem) {
+        progBuffer = (uint8_t *)malloc(8); // assume 8-byte blocks, realloc later if necessary
+    }
+
+    // config set data is a long string of blocks with the following structure:
+    // [bank] [offset] [length] [byte[0], byte[1], ..., byte[length]]
+    uint8_t bank, offset, length;
+    for (i = 0; i < dataSize;) {
+        if (useProgMem) {
+            bank = pgm_read_byte(data + i++);
+            offset = pgm_read_byte(data + i++);
+            length = pgm_read_byte(data + i++);
+        } else {
+            bank = data[i++];
+            offset = data[i++];
+            length = data[i++];
+        }
+
+        // write data or perform special action
+        if (length > 0) {
+            // regular block of data to write
+            /*Serial.print("Writing config block to bank ");
+            Serial.print(bank);
+            Serial.print(", offset ");
+            Serial.print(offset);
+            Serial.print(", length=");
+            Serial.println(length);*/
+            if (useProgMem) {
+                if (sizeof(progBuffer) < length) progBuffer = (uint8_t *)realloc(progBuffer, length);
+                for (j = 0; j < length; j++) progBuffer[j] = pgm_read_byte(data + i + j);
+            } else {
+                progBuffer = (uint8_t *)data + i;
+            }
+            success = writeMemoryBlock(progBuffer, length, bank, offset, true);
+            i += length;
+        } else {
+            // special instruction
+            // NOTE: this kind of behavior (what and when to do certain things)
+            // is totally undocumented. This code is in here based on observed
+            // behavior only, and exactly why (or even whether) it has to be here
+            // is anybody's guess for now.
+            if (useProgMem) {
+                special = pgm_read_byte(data + i++);
+            } else {
+                special = data[i++];
+            }
+            /*Serial.print("Special command code ");
+            Serial.print(special, HEX);
+            Serial.println(" found...");*/
+            if (special == 0x01) {
+                // enable DMP-related interrupts
+                
+                //setIntZeroMotionEnabled(true);
+                //setIntFIFOBufferOverflowEnabled(true);
+                //setIntDMPEnabled(true);
+                I2Cdev::writeByte(devAddr, MPU6050_RA_INT_ENABLE, 0x32);  // single operation
+
+                success = true;
+            } else {
+                // unknown special command
+                success = false;
+            }
+        }
+        
+        if (!success) {
+            if (useProgMem) free(progBuffer);
+            return false; // uh oh
+        }
+    }
+    if (useProgMem) free(progBuffer);
+    return true;
+}
+bool MPU6050::writeProgDMPConfigurationSet(const uint8_t *data, uint16_t dataSize) {
+    return writeDMPConfigurationSet(data, dataSize, true);
+}
+
+// DMP_CFG_1 register
+
+uint8_t MPU6050::getDMPConfig1() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_DMP_CFG_1, buffer);
+    return buffer[0];
+}
+void MPU6050::setDMPConfig1(uint8_t config) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_DMP_CFG_1, config);
+}
+
+// DMP_CFG_2 register
+
+uint8_t MPU6050::getDMPConfig2() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_DMP_CFG_2, buffer);
+    return buffer[0];
+}
+void MPU6050::setDMPConfig2(uint8_t config) {
+    I2Cdev::writeByte(devAddr, MPU6050_RA_DMP_CFG_2, config);
+}
diff --git a/Code/C_Code/25.1.1_MPU6050/MPU6050.h b/Code/C_Code/25.1.1_MPU6050/MPU6050.h
new file mode 100644
index 0000000..00d8dd8
--- /dev/null
+++ b/Code/C_Code/25.1.1_MPU6050/MPU6050.h
@@ -0,0 +1,988 @@
+// I2Cdev library collection - MPU6050 I2C device class
+// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00)
+// 10/3/2011 by Jeff Rowberg 
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//     ... - ongoing debug release
+
+// NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE
+// DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF
+// YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING.
+
+/* ============================================
+I2Cdev device library code is placed under the MIT license
+Copyright (c) 2012 Jeff Rowberg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+===============================================
+*/
+
+#ifndef _MPU6050_H_
+#define _MPU6050_H_
+
+#include "I2Cdev.h"
+//#include 
+
+#define pgm_read_byte(p) (*(uint8_t *)(p))
+
+
+#define MPU6050_ADDRESS_AD0_LOW     0x68 // address pin low (GND), default for InvenSense evaluation board
+#define MPU6050_ADDRESS_AD0_HIGH    0x69 // address pin high (VCC)
+#define MPU6050_DEFAULT_ADDRESS     MPU6050_ADDRESS_AD0_LOW
+
+#define MPU6050_RA_XG_OFFS_TC       0x00 //[7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD
+#define MPU6050_RA_YG_OFFS_TC       0x01 //[7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD
+#define MPU6050_RA_ZG_OFFS_TC       0x02 //[7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD
+#define MPU6050_RA_X_FINE_GAIN      0x03 //[7:0] X_FINE_GAIN
+#define MPU6050_RA_Y_FINE_GAIN      0x04 //[7:0] Y_FINE_GAIN
+#define MPU6050_RA_Z_FINE_GAIN      0x05 //[7:0] Z_FINE_GAIN
+#define MPU6050_RA_XA_OFFS_H        0x06 //[15:0] XA_OFFS
+#define MPU6050_RA_XA_OFFS_L_TC     0x07
+#define MPU6050_RA_YA_OFFS_H        0x08 //[15:0] YA_OFFS
+#define MPU6050_RA_YA_OFFS_L_TC     0x09
+#define MPU6050_RA_ZA_OFFS_H        0x0A //[15:0] ZA_OFFS
+#define MPU6050_RA_ZA_OFFS_L_TC     0x0B
+#define MPU6050_RA_XG_OFFS_USRH     0x13 //[15:0] XG_OFFS_USR
+#define MPU6050_RA_XG_OFFS_USRL     0x14
+#define MPU6050_RA_YG_OFFS_USRH     0x15 //[15:0] YG_OFFS_USR
+#define MPU6050_RA_YG_OFFS_USRL     0x16
+#define MPU6050_RA_ZG_OFFS_USRH     0x17 //[15:0] ZG_OFFS_USR
+#define MPU6050_RA_ZG_OFFS_USRL     0x18
+#define MPU6050_RA_SMPLRT_DIV       0x19
+#define MPU6050_RA_CONFIG           0x1A
+#define MPU6050_RA_GYRO_CONFIG      0x1B
+#define MPU6050_RA_ACCEL_CONFIG     0x1C
+#define MPU6050_RA_FF_THR           0x1D
+#define MPU6050_RA_FF_DUR           0x1E
+#define MPU6050_RA_MOT_THR          0x1F
+#define MPU6050_RA_MOT_DUR          0x20
+#define MPU6050_RA_ZRMOT_THR        0x21
+#define MPU6050_RA_ZRMOT_DUR        0x22
+#define MPU6050_RA_FIFO_EN          0x23
+#define MPU6050_RA_I2C_MST_CTRL     0x24
+#define MPU6050_RA_I2C_SLV0_ADDR    0x25
+#define MPU6050_RA_I2C_SLV0_REG     0x26
+#define MPU6050_RA_I2C_SLV0_CTRL    0x27
+#define MPU6050_RA_I2C_SLV1_ADDR    0x28
+#define MPU6050_RA_I2C_SLV1_REG     0x29
+#define MPU6050_RA_I2C_SLV1_CTRL    0x2A
+#define MPU6050_RA_I2C_SLV2_ADDR    0x2B
+#define MPU6050_RA_I2C_SLV2_REG     0x2C
+#define MPU6050_RA_I2C_SLV2_CTRL    0x2D
+#define MPU6050_RA_I2C_SLV3_ADDR    0x2E
+#define MPU6050_RA_I2C_SLV3_REG     0x2F
+#define MPU6050_RA_I2C_SLV3_CTRL    0x30
+#define MPU6050_RA_I2C_SLV4_ADDR    0x31
+#define MPU6050_RA_I2C_SLV4_REG     0x32
+#define MPU6050_RA_I2C_SLV4_DO      0x33
+#define MPU6050_RA_I2C_SLV4_CTRL    0x34
+#define MPU6050_RA_I2C_SLV4_DI      0x35
+#define MPU6050_RA_I2C_MST_STATUS   0x36
+#define MPU6050_RA_INT_PIN_CFG      0x37
+#define MPU6050_RA_INT_ENABLE       0x38
+#define MPU6050_RA_DMP_INT_STATUS   0x39
+#define MPU6050_RA_INT_STATUS       0x3A
+#define MPU6050_RA_ACCEL_XOUT_H     0x3B
+#define MPU6050_RA_ACCEL_XOUT_L     0x3C
+#define MPU6050_RA_ACCEL_YOUT_H     0x3D
+#define MPU6050_RA_ACCEL_YOUT_L     0x3E
+#define MPU6050_RA_ACCEL_ZOUT_H     0x3F
+#define MPU6050_RA_ACCEL_ZOUT_L     0x40
+#define MPU6050_RA_TEMP_OUT_H       0x41
+#define MPU6050_RA_TEMP_OUT_L       0x42
+#define MPU6050_RA_GYRO_XOUT_H      0x43
+#define MPU6050_RA_GYRO_XOUT_L      0x44
+#define MPU6050_RA_GYRO_YOUT_H      0x45
+#define MPU6050_RA_GYRO_YOUT_L      0x46
+#define MPU6050_RA_GYRO_ZOUT_H      0x47
+#define MPU6050_RA_GYRO_ZOUT_L      0x48
+#define MPU6050_RA_EXT_SENS_DATA_00 0x49
+#define MPU6050_RA_EXT_SENS_DATA_01 0x4A
+#define MPU6050_RA_EXT_SENS_DATA_02 0x4B
+#define MPU6050_RA_EXT_SENS_DATA_03 0x4C
+#define MPU6050_RA_EXT_SENS_DATA_04 0x4D
+#define MPU6050_RA_EXT_SENS_DATA_05 0x4E
+#define MPU6050_RA_EXT_SENS_DATA_06 0x4F
+#define MPU6050_RA_EXT_SENS_DATA_07 0x50
+#define MPU6050_RA_EXT_SENS_DATA_08 0x51
+#define MPU6050_RA_EXT_SENS_DATA_09 0x52
+#define MPU6050_RA_EXT_SENS_DATA_10 0x53
+#define MPU6050_RA_EXT_SENS_DATA_11 0x54
+#define MPU6050_RA_EXT_SENS_DATA_12 0x55
+#define MPU6050_RA_EXT_SENS_DATA_13 0x56
+#define MPU6050_RA_EXT_SENS_DATA_14 0x57
+#define MPU6050_RA_EXT_SENS_DATA_15 0x58
+#define MPU6050_RA_EXT_SENS_DATA_16 0x59
+#define MPU6050_RA_EXT_SENS_DATA_17 0x5A
+#define MPU6050_RA_EXT_SENS_DATA_18 0x5B
+#define MPU6050_RA_EXT_SENS_DATA_19 0x5C
+#define MPU6050_RA_EXT_SENS_DATA_20 0x5D
+#define MPU6050_RA_EXT_SENS_DATA_21 0x5E
+#define MPU6050_RA_EXT_SENS_DATA_22 0x5F
+#define MPU6050_RA_EXT_SENS_DATA_23 0x60
+#define MPU6050_RA_MOT_DETECT_STATUS    0x61
+#define MPU6050_RA_I2C_SLV0_DO      0x63
+#define MPU6050_RA_I2C_SLV1_DO      0x64
+#define MPU6050_RA_I2C_SLV2_DO      0x65
+#define MPU6050_RA_I2C_SLV3_DO      0x66
+#define MPU6050_RA_I2C_MST_DELAY_CTRL   0x67
+#define MPU6050_RA_SIGNAL_PATH_RESET    0x68
+#define MPU6050_RA_MOT_DETECT_CTRL      0x69
+#define MPU6050_RA_USER_CTRL        0x6A
+#define MPU6050_RA_PWR_MGMT_1       0x6B
+#define MPU6050_RA_PWR_MGMT_2       0x6C
+#define MPU6050_RA_BANK_SEL         0x6D
+#define MPU6050_RA_MEM_START_ADDR   0x6E
+#define MPU6050_RA_MEM_R_W          0x6F
+#define MPU6050_RA_DMP_CFG_1        0x70
+#define MPU6050_RA_DMP_CFG_2        0x71
+#define MPU6050_RA_FIFO_COUNTH      0x72
+#define MPU6050_RA_FIFO_COUNTL      0x73
+#define MPU6050_RA_FIFO_R_W         0x74
+#define MPU6050_RA_WHO_AM_I         0x75
+
+#define MPU6050_TC_PWR_MODE_BIT     7
+#define MPU6050_TC_OFFSET_BIT       6
+#define MPU6050_TC_OFFSET_LENGTH    6
+#define MPU6050_TC_OTP_BNK_VLD_BIT  0
+
+#define MPU6050_VDDIO_LEVEL_VLOGIC  0
+#define MPU6050_VDDIO_LEVEL_VDD     1
+
+#define MPU6050_CFG_EXT_SYNC_SET_BIT    5
+#define MPU6050_CFG_EXT_SYNC_SET_LENGTH 3
+#define MPU6050_CFG_DLPF_CFG_BIT    2
+#define MPU6050_CFG_DLPF_CFG_LENGTH 3
+
+#define MPU6050_EXT_SYNC_DISABLED       0x0
+#define MPU6050_EXT_SYNC_TEMP_OUT_L     0x1
+#define MPU6050_EXT_SYNC_GYRO_XOUT_L    0x2
+#define MPU6050_EXT_SYNC_GYRO_YOUT_L    0x3
+#define MPU6050_EXT_SYNC_GYRO_ZOUT_L    0x4
+#define MPU6050_EXT_SYNC_ACCEL_XOUT_L   0x5
+#define MPU6050_EXT_SYNC_ACCEL_YOUT_L   0x6
+#define MPU6050_EXT_SYNC_ACCEL_ZOUT_L   0x7
+
+#define MPU6050_DLPF_BW_256         0x00
+#define MPU6050_DLPF_BW_188         0x01
+#define MPU6050_DLPF_BW_98          0x02
+#define MPU6050_DLPF_BW_42          0x03
+#define MPU6050_DLPF_BW_20          0x04
+#define MPU6050_DLPF_BW_10          0x05
+#define MPU6050_DLPF_BW_5           0x06
+
+#define MPU6050_GCONFIG_FS_SEL_BIT      4
+#define MPU6050_GCONFIG_FS_SEL_LENGTH   2
+
+#define MPU6050_GYRO_FS_250         0x00
+#define MPU6050_GYRO_FS_500         0x01
+#define MPU6050_GYRO_FS_1000        0x02
+#define MPU6050_GYRO_FS_2000        0x03
+
+#define MPU6050_ACONFIG_XA_ST_BIT           7
+#define MPU6050_ACONFIG_YA_ST_BIT           6
+#define MPU6050_ACONFIG_ZA_ST_BIT           5
+#define MPU6050_ACONFIG_AFS_SEL_BIT         4
+#define MPU6050_ACONFIG_AFS_SEL_LENGTH      2
+#define MPU6050_ACONFIG_ACCEL_HPF_BIT       2
+#define MPU6050_ACONFIG_ACCEL_HPF_LENGTH    3
+
+#define MPU6050_ACCEL_FS_2          0x00
+#define MPU6050_ACCEL_FS_4          0x01
+#define MPU6050_ACCEL_FS_8          0x02
+#define MPU6050_ACCEL_FS_16         0x03
+
+#define MPU6050_DHPF_RESET          0x00
+#define MPU6050_DHPF_5              0x01
+#define MPU6050_DHPF_2P5            0x02
+#define MPU6050_DHPF_1P25           0x03
+#define MPU6050_DHPF_0P63           0x04
+#define MPU6050_DHPF_HOLD           0x07
+
+#define MPU6050_TEMP_FIFO_EN_BIT    7
+#define MPU6050_XG_FIFO_EN_BIT      6
+#define MPU6050_YG_FIFO_EN_BIT      5
+#define MPU6050_ZG_FIFO_EN_BIT      4
+#define MPU6050_ACCEL_FIFO_EN_BIT   3
+#define MPU6050_SLV2_FIFO_EN_BIT    2
+#define MPU6050_SLV1_FIFO_EN_BIT    1
+#define MPU6050_SLV0_FIFO_EN_BIT    0
+
+#define MPU6050_MULT_MST_EN_BIT     7
+#define MPU6050_WAIT_FOR_ES_BIT     6
+#define MPU6050_SLV_3_FIFO_EN_BIT   5
+#define MPU6050_I2C_MST_P_NSR_BIT   4
+#define MPU6050_I2C_MST_CLK_BIT     3
+#define MPU6050_I2C_MST_CLK_LENGTH  4
+
+#define MPU6050_CLOCK_DIV_348       0x0
+#define MPU6050_CLOCK_DIV_333       0x1
+#define MPU6050_CLOCK_DIV_320       0x2
+#define MPU6050_CLOCK_DIV_308       0x3
+#define MPU6050_CLOCK_DIV_296       0x4
+#define MPU6050_CLOCK_DIV_286       0x5
+#define MPU6050_CLOCK_DIV_276       0x6
+#define MPU6050_CLOCK_DIV_267       0x7
+#define MPU6050_CLOCK_DIV_258       0x8
+#define MPU6050_CLOCK_DIV_500       0x9
+#define MPU6050_CLOCK_DIV_471       0xA
+#define MPU6050_CLOCK_DIV_444       0xB
+#define MPU6050_CLOCK_DIV_421       0xC
+#define MPU6050_CLOCK_DIV_400       0xD
+#define MPU6050_CLOCK_DIV_381       0xE
+#define MPU6050_CLOCK_DIV_364       0xF
+
+#define MPU6050_I2C_SLV_RW_BIT      7
+#define MPU6050_I2C_SLV_ADDR_BIT    6
+#define MPU6050_I2C_SLV_ADDR_LENGTH 7
+#define MPU6050_I2C_SLV_EN_BIT      7
+#define MPU6050_I2C_SLV_BYTE_SW_BIT 6
+#define MPU6050_I2C_SLV_REG_DIS_BIT 5
+#define MPU6050_I2C_SLV_GRP_BIT     4
+#define MPU6050_I2C_SLV_LEN_BIT     3
+#define MPU6050_I2C_SLV_LEN_LENGTH  4
+
+#define MPU6050_I2C_SLV4_RW_BIT         7
+#define MPU6050_I2C_SLV4_ADDR_BIT       6
+#define MPU6050_I2C_SLV4_ADDR_LENGTH    7
+#define MPU6050_I2C_SLV4_EN_BIT         7
+#define MPU6050_I2C_SLV4_INT_EN_BIT     6
+#define MPU6050_I2C_SLV4_REG_DIS_BIT    5
+#define MPU6050_I2C_SLV4_MST_DLY_BIT    4
+#define MPU6050_I2C_SLV4_MST_DLY_LENGTH 5
+
+#define MPU6050_MST_PASS_THROUGH_BIT    7
+#define MPU6050_MST_I2C_SLV4_DONE_BIT   6
+#define MPU6050_MST_I2C_LOST_ARB_BIT    5
+#define MPU6050_MST_I2C_SLV4_NACK_BIT   4
+#define MPU6050_MST_I2C_SLV3_NACK_BIT   3
+#define MPU6050_MST_I2C_SLV2_NACK_BIT   2
+#define MPU6050_MST_I2C_SLV1_NACK_BIT   1
+#define MPU6050_MST_I2C_SLV0_NACK_BIT   0
+
+#define MPU6050_INTCFG_INT_LEVEL_BIT        7
+#define MPU6050_INTCFG_INT_OPEN_BIT         6
+#define MPU6050_INTCFG_LATCH_INT_EN_BIT     5
+#define MPU6050_INTCFG_INT_RD_CLEAR_BIT     4
+#define MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT  3
+#define MPU6050_INTCFG_FSYNC_INT_EN_BIT     2
+#define MPU6050_INTCFG_I2C_BYPASS_EN_BIT    1
+#define MPU6050_INTCFG_CLKOUT_EN_BIT        0
+
+#define MPU6050_INTMODE_ACTIVEHIGH  0x00
+#define MPU6050_INTMODE_ACTIVELOW   0x01
+
+#define MPU6050_INTDRV_PUSHPULL     0x00
+#define MPU6050_INTDRV_OPENDRAIN    0x01
+
+#define MPU6050_INTLATCH_50USPULSE  0x00
+#define MPU6050_INTLATCH_WAITCLEAR  0x01
+
+#define MPU6050_INTCLEAR_STATUSREAD 0x00
+#define MPU6050_INTCLEAR_ANYREAD    0x01
+
+#define MPU6050_INTERRUPT_FF_BIT            7
+#define MPU6050_INTERRUPT_MOT_BIT           6
+#define MPU6050_INTERRUPT_ZMOT_BIT          5
+#define MPU6050_INTERRUPT_FIFO_OFLOW_BIT    4
+#define MPU6050_INTERRUPT_I2C_MST_INT_BIT   3
+#define MPU6050_INTERRUPT_PLL_RDY_INT_BIT   2
+#define MPU6050_INTERRUPT_DMP_INT_BIT       1
+#define MPU6050_INTERRUPT_DATA_RDY_BIT      0
+
+// TODO: figure out what these actually do
+// UMPL source code is not very obivous
+#define MPU6050_DMPINT_5_BIT            5
+#define MPU6050_DMPINT_4_BIT            4
+#define MPU6050_DMPINT_3_BIT            3
+#define MPU6050_DMPINT_2_BIT            2
+#define MPU6050_DMPINT_1_BIT            1
+#define MPU6050_DMPINT_0_BIT            0
+
+#define MPU6050_MOTION_MOT_XNEG_BIT     7
+#define MPU6050_MOTION_MOT_XPOS_BIT     6
+#define MPU6050_MOTION_MOT_YNEG_BIT     5
+#define MPU6050_MOTION_MOT_YPOS_BIT     4
+#define MPU6050_MOTION_MOT_ZNEG_BIT     3
+#define MPU6050_MOTION_MOT_ZPOS_BIT     2
+#define MPU6050_MOTION_MOT_ZRMOT_BIT    0
+
+#define MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT   7
+#define MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT   4
+#define MPU6050_DELAYCTRL_I2C_SLV3_DLY_EN_BIT   3
+#define MPU6050_DELAYCTRL_I2C_SLV2_DLY_EN_BIT   2
+#define MPU6050_DELAYCTRL_I2C_SLV1_DLY_EN_BIT   1
+#define MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT   0
+
+#define MPU6050_PATHRESET_GYRO_RESET_BIT    2
+#define MPU6050_PATHRESET_ACCEL_RESET_BIT   1
+#define MPU6050_PATHRESET_TEMP_RESET_BIT    0
+
+#define MPU6050_DETECT_ACCEL_ON_DELAY_BIT       5
+#define MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH    2
+#define MPU6050_DETECT_FF_COUNT_BIT             3
+#define MPU6050_DETECT_FF_COUNT_LENGTH          2
+#define MPU6050_DETECT_MOT_COUNT_BIT            1
+#define MPU6050_DETECT_MOT_COUNT_LENGTH         2
+
+#define MPU6050_DETECT_DECREMENT_RESET  0x0
+#define MPU6050_DETECT_DECREMENT_1      0x1
+#define MPU6050_DETECT_DECREMENT_2      0x2
+#define MPU6050_DETECT_DECREMENT_4      0x3
+
+#define MPU6050_USERCTRL_DMP_EN_BIT             7
+#define MPU6050_USERCTRL_FIFO_EN_BIT            6
+#define MPU6050_USERCTRL_I2C_MST_EN_BIT         5
+#define MPU6050_USERCTRL_I2C_IF_DIS_BIT         4
+#define MPU6050_USERCTRL_DMP_RESET_BIT          3
+#define MPU6050_USERCTRL_FIFO_RESET_BIT         2
+#define MPU6050_USERCTRL_I2C_MST_RESET_BIT      1
+#define MPU6050_USERCTRL_SIG_COND_RESET_BIT     0
+
+#define MPU6050_PWR1_DEVICE_RESET_BIT   7
+#define MPU6050_PWR1_SLEEP_BIT          6
+#define MPU6050_PWR1_CYCLE_BIT          5
+#define MPU6050_PWR1_TEMP_DIS_BIT       3
+#define MPU6050_PWR1_CLKSEL_BIT         2
+#define MPU6050_PWR1_CLKSEL_LENGTH      3
+
+#define MPU6050_CLOCK_INTERNAL          0x00
+#define MPU6050_CLOCK_PLL_XGYRO         0x01
+#define MPU6050_CLOCK_PLL_YGYRO         0x02
+#define MPU6050_CLOCK_PLL_ZGYRO         0x03
+#define MPU6050_CLOCK_PLL_EXT32K        0x04
+#define MPU6050_CLOCK_PLL_EXT19M        0x05
+#define MPU6050_CLOCK_KEEP_RESET        0x07
+
+#define MPU6050_PWR2_LP_WAKE_CTRL_BIT       7
+#define MPU6050_PWR2_LP_WAKE_CTRL_LENGTH    2
+#define MPU6050_PWR2_STBY_XA_BIT            5
+#define MPU6050_PWR2_STBY_YA_BIT            4
+#define MPU6050_PWR2_STBY_ZA_BIT            3
+#define MPU6050_PWR2_STBY_XG_BIT            2
+#define MPU6050_PWR2_STBY_YG_BIT            1
+#define MPU6050_PWR2_STBY_ZG_BIT            0
+
+#define MPU6050_WAKE_FREQ_1P25      0x0
+#define MPU6050_WAKE_FREQ_2P5       0x1
+#define MPU6050_WAKE_FREQ_5         0x2
+#define MPU6050_WAKE_FREQ_10        0x3
+
+#define MPU6050_BANKSEL_PRFTCH_EN_BIT       6
+#define MPU6050_BANKSEL_CFG_USER_BANK_BIT   5
+#define MPU6050_BANKSEL_MEM_SEL_BIT         4
+#define MPU6050_BANKSEL_MEM_SEL_LENGTH      5
+
+#define MPU6050_WHO_AM_I_BIT        6
+#define MPU6050_WHO_AM_I_LENGTH     6
+
+#define MPU6050_DMP_MEMORY_BANKS        8
+#define MPU6050_DMP_MEMORY_BANK_SIZE    256
+#define MPU6050_DMP_MEMORY_CHUNK_SIZE   16
+
+// note: DMP code memory blocks defined at end of header file
+
+class MPU6050 {
+    public:
+        MPU6050();
+        MPU6050(uint8_t address);
+
+        void initialize();
+        bool testConnection();
+
+        // AUX_VDDIO register
+        uint8_t getAuxVDDIOLevel();
+        void setAuxVDDIOLevel(uint8_t level);
+
+        // SMPLRT_DIV register
+        uint8_t getRate();
+        void setRate(uint8_t rate);
+
+        // CONFIG register
+        uint8_t getExternalFrameSync();
+        void setExternalFrameSync(uint8_t sync);
+        uint8_t getDLPFMode();
+        void setDLPFMode(uint8_t bandwidth);
+
+        // GYRO_CONFIG register
+        uint8_t getFullScaleGyroRange();
+        void setFullScaleGyroRange(uint8_t range);
+
+        // ACCEL_CONFIG register
+        bool getAccelXSelfTest();
+        void setAccelXSelfTest(bool enabled);
+        bool getAccelYSelfTest();
+        void setAccelYSelfTest(bool enabled);
+        bool getAccelZSelfTest();
+        void setAccelZSelfTest(bool enabled);
+        uint8_t getFullScaleAccelRange();
+        void setFullScaleAccelRange(uint8_t range);
+        uint8_t getDHPFMode();
+        void setDHPFMode(uint8_t mode);
+
+        // FF_THR register
+        uint8_t getFreefallDetectionThreshold();
+        void setFreefallDetectionThreshold(uint8_t threshold);
+
+        // FF_DUR register
+        uint8_t getFreefallDetectionDuration();
+        void setFreefallDetectionDuration(uint8_t duration);
+
+        // MOT_THR register
+        uint8_t getMotionDetectionThreshold();
+        void setMotionDetectionThreshold(uint8_t threshold);
+
+        // MOT_DUR register
+        uint8_t getMotionDetectionDuration();
+        void setMotionDetectionDuration(uint8_t duration);
+
+        // ZRMOT_THR register
+        uint8_t getZeroMotionDetectionThreshold();
+        void setZeroMotionDetectionThreshold(uint8_t threshold);
+
+        // ZRMOT_DUR register
+        uint8_t getZeroMotionDetectionDuration();
+        void setZeroMotionDetectionDuration(uint8_t duration);
+
+        // FIFO_EN register
+        bool getTempFIFOEnabled();
+        void setTempFIFOEnabled(bool enabled);
+        bool getXGyroFIFOEnabled();
+        void setXGyroFIFOEnabled(bool enabled);
+        bool getYGyroFIFOEnabled();
+        void setYGyroFIFOEnabled(bool enabled);
+        bool getZGyroFIFOEnabled();
+        void setZGyroFIFOEnabled(bool enabled);
+        bool getAccelFIFOEnabled();
+        void setAccelFIFOEnabled(bool enabled);
+        bool getSlave2FIFOEnabled();
+        void setSlave2FIFOEnabled(bool enabled);
+        bool getSlave1FIFOEnabled();
+        void setSlave1FIFOEnabled(bool enabled);
+        bool getSlave0FIFOEnabled();
+        void setSlave0FIFOEnabled(bool enabled);
+
+        // I2C_MST_CTRL register
+        bool getMultiMasterEnabled();
+        void setMultiMasterEnabled(bool enabled);
+        bool getWaitForExternalSensorEnabled();
+        void setWaitForExternalSensorEnabled(bool enabled);
+        bool getSlave3FIFOEnabled();
+        void setSlave3FIFOEnabled(bool enabled);
+        bool getSlaveReadWriteTransitionEnabled();
+        void setSlaveReadWriteTransitionEnabled(bool enabled);
+        uint8_t getMasterClockSpeed();
+        void setMasterClockSpeed(uint8_t speed);
+
+        // I2C_SLV* registers (Slave 0-3)
+        uint8_t getSlaveAddress(uint8_t num);
+        void setSlaveAddress(uint8_t num, uint8_t address);
+        uint8_t getSlaveRegister(uint8_t num);
+        void setSlaveRegister(uint8_t num, uint8_t reg);
+        bool getSlaveEnabled(uint8_t num);
+        void setSlaveEnabled(uint8_t num, bool enabled);
+        bool getSlaveWordByteSwap(uint8_t num);
+        void setSlaveWordByteSwap(uint8_t num, bool enabled);
+        bool getSlaveWriteMode(uint8_t num);
+        void setSlaveWriteMode(uint8_t num, bool mode);
+        bool getSlaveWordGroupOffset(uint8_t num);
+        void setSlaveWordGroupOffset(uint8_t num, bool enabled);
+        uint8_t getSlaveDataLength(uint8_t num);
+        void setSlaveDataLength(uint8_t num, uint8_t length);
+
+        // I2C_SLV* registers (Slave 4)
+        uint8_t getSlave4Address();
+        void setSlave4Address(uint8_t address);
+        uint8_t getSlave4Register();
+        void setSlave4Register(uint8_t reg);
+        void setSlave4OutputByte(uint8_t data);
+        bool getSlave4Enabled();
+        void setSlave4Enabled(bool enabled);
+        bool getSlave4InterruptEnabled();
+        void setSlave4InterruptEnabled(bool enabled);
+        bool getSlave4WriteMode();
+        void setSlave4WriteMode(bool mode);
+        uint8_t getSlave4MasterDelay();
+        void setSlave4MasterDelay(uint8_t delay);
+        uint8_t getSlate4InputByte();
+
+        // I2C_MST_STATUS register
+        bool getPassthroughStatus();
+        bool getSlave4IsDone();
+        bool getLostArbitration();
+        bool getSlave4Nack();
+        bool getSlave3Nack();
+        bool getSlave2Nack();
+        bool getSlave1Nack();
+        bool getSlave0Nack();
+
+        // INT_PIN_CFG register
+        bool getInterruptMode();
+        void setInterruptMode(bool mode);
+        bool getInterruptDrive();
+        void setInterruptDrive(bool drive);
+        bool getInterruptLatch();
+        void setInterruptLatch(bool latch);
+        bool getInterruptLatchClear();
+        void setInterruptLatchClear(bool clear);
+        bool getFSyncInterruptLevel();
+        void setFSyncInterruptLevel(bool level);
+        bool getFSyncInterruptEnabled();
+        void setFSyncInterruptEnabled(bool enabled);
+        bool getI2CBypassEnabled();
+        void setI2CBypassEnabled(bool enabled);
+        bool getClockOutputEnabled();
+        void setClockOutputEnabled(bool enabled);
+
+        // INT_ENABLE register
+        uint8_t getIntEnabled();
+        void setIntEnabled(uint8_t enabled);
+        bool getIntFreefallEnabled();
+        void setIntFreefallEnabled(bool enabled);
+        bool getIntMotionEnabled();
+        void setIntMotionEnabled(bool enabled);
+        bool getIntZeroMotionEnabled();
+        void setIntZeroMotionEnabled(bool enabled);
+        bool getIntFIFOBufferOverflowEnabled();
+        void setIntFIFOBufferOverflowEnabled(bool enabled);
+        bool getIntI2CMasterEnabled();
+        void setIntI2CMasterEnabled(bool enabled);
+        bool getIntDataReadyEnabled();
+        void setIntDataReadyEnabled(bool enabled);
+
+        // INT_STATUS register
+        uint8_t getIntStatus();
+        bool getIntFreefallStatus();
+        bool getIntMotionStatus();
+        bool getIntZeroMotionStatus();
+        bool getIntFIFOBufferOverflowStatus();
+        bool getIntI2CMasterStatus();
+        bool getIntDataReadyStatus();
+
+        // ACCEL_*OUT_* registers
+        void getMotion9(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz, int16_t* mx, int16_t* my, int16_t* mz);
+        void getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz);
+        void getAcceleration(int16_t* x, int16_t* y, int16_t* z);
+        int16_t getAccelerationX();
+        int16_t getAccelerationY();
+        int16_t getAccelerationZ();
+
+        // TEMP_OUT_* registers
+        int16_t getTemperature();
+
+        // GYRO_*OUT_* registers
+        void getRotation(int16_t* x, int16_t* y, int16_t* z);
+        int16_t getRotationX();
+        int16_t getRotationY();
+        int16_t getRotationZ();
+
+        // EXT_SENS_DATA_* registers
+        uint8_t getExternalSensorByte(int position);
+        uint16_t getExternalSensorWord(int position);
+        uint32_t getExternalSensorDWord(int position);
+
+        // MOT_DETECT_STATUS register
+        bool getXNegMotionDetected();
+        bool getXPosMotionDetected();
+        bool getYNegMotionDetected();
+        bool getYPosMotionDetected();
+        bool getZNegMotionDetected();
+        bool getZPosMotionDetected();
+        bool getZeroMotionDetected();
+
+        // I2C_SLV*_DO register
+        void setSlaveOutputByte(uint8_t num, uint8_t data);
+
+        // I2C_MST_DELAY_CTRL register
+        bool getExternalShadowDelayEnabled();
+        void setExternalShadowDelayEnabled(bool enabled);
+        bool getSlaveDelayEnabled(uint8_t num);
+        void setSlaveDelayEnabled(uint8_t num, bool enabled);
+
+        // SIGNAL_PATH_RESET register
+        void resetGyroscopePath();
+        void resetAccelerometerPath();
+        void resetTemperaturePath();
+
+        // MOT_DETECT_CTRL register
+        uint8_t getAccelerometerPowerOnDelay();
+        void setAccelerometerPowerOnDelay(uint8_t delay);
+        uint8_t getFreefallDetectionCounterDecrement();
+        void setFreefallDetectionCounterDecrement(uint8_t decrement);
+        uint8_t getMotionDetectionCounterDecrement();
+        void setMotionDetectionCounterDecrement(uint8_t decrement);
+
+        // USER_CTRL register
+        bool getFIFOEnabled();
+        void setFIFOEnabled(bool enabled);
+        bool getI2CMasterModeEnabled();
+        void setI2CMasterModeEnabled(bool enabled);
+        void switchSPIEnabled(bool enabled);
+        void resetFIFO();
+        void resetI2CMaster();
+        void resetSensors();
+
+        // PWR_MGMT_1 register
+        void reset();
+        bool getSleepEnabled();
+        void setSleepEnabled(bool enabled);
+        bool getWakeCycleEnabled();
+        void setWakeCycleEnabled(bool enabled);
+        bool getTempSensorEnabled();
+        void setTempSensorEnabled(bool enabled);
+        uint8_t getClockSource();
+        void setClockSource(uint8_t source);
+
+        // PWR_MGMT_2 register
+        uint8_t getWakeFrequency();
+        void setWakeFrequency(uint8_t frequency);
+        bool getStandbyXAccelEnabled();
+        void setStandbyXAccelEnabled(bool enabled);
+        bool getStandbyYAccelEnabled();
+        void setStandbyYAccelEnabled(bool enabled);
+        bool getStandbyZAccelEnabled();
+        void setStandbyZAccelEnabled(bool enabled);
+        bool getStandbyXGyroEnabled();
+        void setStandbyXGyroEnabled(bool enabled);
+        bool getStandbyYGyroEnabled();
+        void setStandbyYGyroEnabled(bool enabled);
+        bool getStandbyZGyroEnabled();
+        void setStandbyZGyroEnabled(bool enabled);
+
+        // FIFO_COUNT_* registers
+        uint16_t getFIFOCount();
+
+        // FIFO_R_W register
+        uint8_t getFIFOByte();
+        void setFIFOByte(uint8_t data);
+        void getFIFOBytes(uint8_t *data, uint8_t length);
+
+        // WHO_AM_I register
+        uint8_t getDeviceID();
+        void setDeviceID(uint8_t id);
+        
+        // ======== UNDOCUMENTED/DMP REGISTERS/METHODS ========
+        
+        // XG_OFFS_TC register
+        uint8_t getOTPBankValid();
+        void setOTPBankValid(bool enabled);
+        int8_t getXGyroOffset();
+        void setXGyroOffset(int8_t offset);
+
+        // YG_OFFS_TC register
+        int8_t getYGyroOffset();
+        void setYGyroOffset(int8_t offset);
+
+        // ZG_OFFS_TC register
+        int8_t getZGyroOffset();
+        void setZGyroOffset(int8_t offset);
+
+        // X_FINE_GAIN register
+        int8_t getXFineGain();
+        void setXFineGain(int8_t gain);
+
+        // Y_FINE_GAIN register
+        int8_t getYFineGain();
+        void setYFineGain(int8_t gain);
+
+        // Z_FINE_GAIN register
+        int8_t getZFineGain();
+        void setZFineGain(int8_t gain);
+
+        // XA_OFFS_* registers
+        int16_t getXAccelOffset();
+        void setXAccelOffset(int16_t offset);
+
+        // YA_OFFS_* register
+        int16_t getYAccelOffset();
+        void setYAccelOffset(int16_t offset);
+
+        // ZA_OFFS_* register
+        int16_t getZAccelOffset();
+        void setZAccelOffset(int16_t offset);
+
+        // XG_OFFS_USR* registers
+        int16_t getXGyroOffsetUser();
+        void setXGyroOffsetUser(int16_t offset);
+
+        // YG_OFFS_USR* register
+        int16_t getYGyroOffsetUser();
+        void setYGyroOffsetUser(int16_t offset);
+
+        // ZG_OFFS_USR* register
+        int16_t getZGyroOffsetUser();
+        void setZGyroOffsetUser(int16_t offset);
+        
+        // INT_ENABLE register (DMP functions)
+        bool getIntPLLReadyEnabled();
+        void setIntPLLReadyEnabled(bool enabled);
+        bool getIntDMPEnabled();
+        void setIntDMPEnabled(bool enabled);
+        
+        // DMP_INT_STATUS
+        bool getDMPInt5Status();
+        bool getDMPInt4Status();
+        bool getDMPInt3Status();
+        bool getDMPInt2Status();
+        bool getDMPInt1Status();
+        bool getDMPInt0Status();
+
+        // INT_STATUS register (DMP functions)
+        bool getIntPLLReadyStatus();
+        bool getIntDMPStatus();
+        
+        // USER_CTRL register (DMP functions)
+        bool getDMPEnabled();
+        void setDMPEnabled(bool enabled);
+        void resetDMP();
+        
+        // BANK_SEL register
+        void setMemoryBank(uint8_t bank, bool prefetchEnabled=false, bool userBank=false);
+        
+        // MEM_START_ADDR register
+        void setMemoryStartAddress(uint8_t address);
+        
+        // MEM_R_W register
+        uint8_t readMemoryByte();
+        void writeMemoryByte(uint8_t data);
+        void readMemoryBlock(uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0);
+        bool writeMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0, bool verify=true, bool useProgMem=false);
+        bool writeProgMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0, bool verify=true);
+
+        bool writeDMPConfigurationSet(const uint8_t *data, uint16_t dataSize, bool useProgMem=false);
+        bool writeProgDMPConfigurationSet(const uint8_t *data, uint16_t dataSize);
+
+        // DMP_CFG_1 register
+        uint8_t getDMPConfig1();
+        void setDMPConfig1(uint8_t config);
+
+        // DMP_CFG_2 register
+        uint8_t getDMPConfig2();
+        void setDMPConfig2(uint8_t config);
+
+        // special methods for MotionApps 2.0 implementation
+        #ifdef MPU6050_INCLUDE_DMP_MOTIONAPPS20
+            uint8_t *dmpPacketBuffer;
+            uint16_t dmpPacketSize;
+
+            uint8_t dmpInitialize();
+            bool dmpPacketAvailable();
+
+            uint8_t dmpSetFIFORate(uint8_t fifoRate);
+            uint8_t dmpGetFIFORate();
+            uint8_t dmpGetSampleStepSizeMS();
+            uint8_t dmpGetSampleFrequency();
+            int32_t dmpDecodeTemperature(int8_t tempReg);
+            
+            // Register callbacks after a packet of FIFO data is processed
+            //uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority);
+            //uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func);
+            uint8_t dmpRunFIFORateProcesses();
+            
+            // Setup FIFO for various output
+            uint8_t dmpSendQuaternion(uint_fast16_t accuracy);
+            uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendPacketNumber(uint_fast16_t accuracy);
+            uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy);
+
+            // Get Fixed Point data from FIFO
+            uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0);
+            uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0);
+            uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpSetLinearAccelFilterCoefficient(float coef);
+            uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity);
+            uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q);
+            uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0);
+            uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q);
+            uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0);
+            uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0);
+            
+            uint8_t dmpGetEuler(float *data, Quaternion *q);
+            uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity);
+
+            // Get Floating Point data from FIFO
+            uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0);
+
+            uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData);
+            uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL);
+
+            uint8_t dmpSetFIFOProcessedCallback(void (*func) (void));
+
+            uint8_t dmpInitFIFOParam();
+            uint8_t dmpCloseFIFO();
+            uint8_t dmpSetGyroDataSource(uint8_t source);
+            uint8_t dmpDecodeQuantizedAccel();
+            uint32_t dmpGetGyroSumOfSquare();
+            uint32_t dmpGetAccelSumOfSquare();
+            void dmpOverrideQuaternion(long *q);
+            uint16_t dmpGetFIFOPacketSize();
+        #endif
+
+        // special methods for MotionApps 4.1 implementation
+        #ifdef MPU6050_INCLUDE_DMP_MOTIONAPPS41
+            uint8_t *dmpPacketBuffer;
+            uint16_t dmpPacketSize;
+
+            uint8_t dmpInitialize();
+            bool dmpPacketAvailable();
+
+            uint8_t dmpSetFIFORate(uint8_t fifoRate);
+            uint8_t dmpGetFIFORate();
+            uint8_t dmpGetSampleStepSizeMS();
+            uint8_t dmpGetSampleFrequency();
+            int32_t dmpDecodeTemperature(int8_t tempReg);
+            
+            // Register callbacks after a packet of FIFO data is processed
+            //uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority);
+            //uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func);
+            uint8_t dmpRunFIFORateProcesses();
+            
+            // Setup FIFO for various output
+            uint8_t dmpSendQuaternion(uint_fast16_t accuracy);
+            uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendPacketNumber(uint_fast16_t accuracy);
+            uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+            uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy);
+
+            // Get Fixed Point data from FIFO
+            uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0);
+            uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0);
+            uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetMag(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpSetLinearAccelFilterCoefficient(float coef);
+            uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity);
+            uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q);
+            uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0);
+            uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q);
+            uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0);
+            uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0);
+            uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0);
+            
+            uint8_t dmpGetEuler(float *data, Quaternion *q);
+            uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity);
+
+            // Get Floating Point data from FIFO
+            uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0);
+            uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0);
+
+            uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData);
+            uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL);
+
+            uint8_t dmpSetFIFOProcessedCallback(void (*func) (void));
+
+            uint8_t dmpInitFIFOParam();
+            uint8_t dmpCloseFIFO();
+            uint8_t dmpSetGyroDataSource(uint8_t source);
+            uint8_t dmpDecodeQuantizedAccel();
+            uint32_t dmpGetGyroSumOfSquare();
+            uint32_t dmpGetAccelSumOfSquare();
+            void dmpOverrideQuaternion(long *q);
+            uint16_t dmpGetFIFOPacketSize();
+        #endif
+
+    private:
+        uint8_t devAddr;
+        uint8_t buffer[14];
+};
+
+#endif /* _MPU6050_H_ */
diff --git a/Code/C_Code/25.1.1_MPU6050/MPU6050RAW.cpp b/Code/C_Code/25.1.1_MPU6050/MPU6050RAW.cpp
new file mode 100644
index 0000000..7961cf1
--- /dev/null
+++ b/Code/C_Code/25.1.1_MPU6050/MPU6050RAW.cpp
@@ -0,0 +1,45 @@
+/**********************************************************************
+* Filename    : MPU6050RAW.c
+* Description : Read the Raw data of MPU6050
+* Author      : freenove
+* modification: 2016/07/18
+**********************************************************************/
+#include 
+#include 
+#include 
+#include "I2Cdev.h"
+#include "MPU6050.h"
+
+MPU6050 accelgyro;      //instantiate a MPU6050 class object
+
+int16_t ax, ay, az;     //store acceleration data
+int16_t gx, gy, gz;     //store gyroscope data
+
+void setup() {
+    // initialize device
+    printf("Initializing I2C devices...\n");
+    accelgyro.initialize();     //initialize MPU6050
+
+    // verify connection
+    printf("Testing device connections...\n");
+    printf(accelgyro.testConnection() ? "MPU6050 connection successful\n" : "MPU6050 connection failed\n");
+}
+
+void loop() {
+    // read raw accel/gyro measurements from device
+    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
+    // display accel/gyro x/y/z values
+    printf("a/g: %6hd %6hd %6hd   %6hd %6hd %6hd\n",ax,ay,az,gx,gy,gz);
+    printf("a/g: %.2f g %.2f g %.2f g   %.2f d/s %.2f d/s %.2f d/s \n",(float)ax/16384,(float)ay/16384,(float)az/16384,
+        (float)gx/131,(float)gy/131,(float)gz/131);
+}
+
+int main()
+{
+    setup();
+    while(1){
+        loop();
+    }
+    return 0;
+}
+
diff --git a/Code/C_Code/25.1.1_MPU6050/mpu b/Code/C_Code/25.1.1_MPU6050/mpu
new file mode 100644
index 0000000..20f7fc0
Binary files /dev/null and b/Code/C_Code/25.1.1_MPU6050/mpu differ
diff --git a/Code/C_Code/27.2.1_LightWater03/LightWater03 b/Code/C_Code/27.2.1_LightWater03/LightWater03
new file mode 100644
index 0000000..b30bbbf
Binary files /dev/null and b/Code/C_Code/27.2.1_LightWater03/LightWater03 differ
diff --git a/Code/C_Code/27.2.1_LightWater03/LightWater03.c b/Code/C_Code/27.2.1_LightWater03/LightWater03.c
new file mode 100644
index 0000000..e375214
--- /dev/null
+++ b/Code/C_Code/27.2.1_LightWater03/LightWater03.c
@@ -0,0 +1,54 @@
+/**********************************************************************
+* Filename    : LightWater03.c
+* Description : Control LED by 74HC595 on the DIY circuit board
+* Author      : freenove
+* modification: 2016/08/16
+**********************************************************************/
+#include 
+#include 
+#include 
+#include 
+
+#define   dataPin   0   //DS Pin of 74HC595(Pin14)
+#define   latchPin  2   //ST_CP Pin of 74HC595(Pin12)
+#define   clockPin 3    //SH_CP Pin of 74HC595(Pin11)
+//Define an array to save the pulse width of LED. Output the signal to the 8 adjacent LEDs in order.
+const int pluseWidth[]={0,0,0,0,0,0,0,0,64,32,16,8,4,2,1,0,0,0,0,0,0,0,0};
+void outData(int8_t data){
+	digitalWrite(latchPin,LOW);
+	shiftOut(dataPin,clockPin,LSBFIRST,data);
+	digitalWrite(latchPin,HIGH);
+}
+int main(void)
+{
+	int i,j,index;	//index:current position in array pluseWidth
+	int moveSpeed = 100;	//move speed delay, the larger, the slower
+	long lastMove;			//Record the last time point of the move
+	if(wiringPiSetup() == -1){ //when initialize wiring failed,print messageto screen
+		printf("setup wiringPi failed !");
+		return 1; 
+	}
+	pinMode(dataPin,OUTPUT);
+	pinMode(latchPin,OUTPUT);
+	pinMode(clockPin,OUTPUT);
+	index = 0;		//Starting from the array index 0
+	lastMove = millis();	//the start time
+	while(1){
+		if(millis() - lastMove > moveSpeed) { //speed control
+			lastMove = millis();	//Record the time point of the move
+			index++;		//move to next 
+			if(index > 15) index = 0; 	//index to 0
+		}
+		for(i=0;i<64;i++){		//The cycle of PWM is 64 cycles
+			int8_t data = 0;	//This loop of output data
+			for(j=0;j<8;j++){	//Calculate the output state of this loop
+				if(i < pluseWidth[index+j]){	//Calculate the LED state according to the pulse width 
+					data |= 0x01< 0):
+        GPIO.output(motoRPin1,GPIO.HIGH)
+        GPIO.output(motoRPin2,GPIO.LOW)
+        print 'Turn Forward...'
+    elif (value < 0):
+        GPIO.output(motoRPin1,GPIO.LOW)
+        GPIO.output(motoRPin2,GPIO.HIGH)
+        print 'Turn Backward...'
+    else :
+        GPIO.output(motoRPin1,GPIO.LOW)
+        GPIO.output(motoRPin2,GPIO.LOW)
+        print 'Motor Stop...'
+    p.start(mapNUM(abs(value),0,128,0,100))
+    print 'The PWM duty cycle is %d%%\n'%(abs(value)*100/127)   #print PMW duty cycle.
+
+def loop():
+    while True:
+        value = analogRead(0)
+        print 'ADC Value : %d'%(value)
+        motor(value)
+        time.sleep(0.01)
+
+def destroy():
+    bus.close()
+    GPIO.cleanup()
+    
+if __name__ == '__main__':
+    print 'Program is starting ... '
+    setup()
+    try:
+        loop()
+    except KeyboardInterrupt:
+        destroy()
+
diff --git a/Code/Python_Code/14.1.1_Relay/Relay.py b/Code/Python_Code/14.1.1_Relay/Relay.py
new file mode 100644
index 0000000..6dec590
--- /dev/null
+++ b/Code/Python_Code/14.1.1_Relay/Relay.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+########################################################################
+# Filename    : Relay.py
+# Description : Button control Relay and Motor
+# Author      : freenove
+# modification: 2016/07/04
+########################################################################
+import RPi.GPIO as GPIO
+
+relayPin = 11    # define the relayPin
+buttonPin = 12    # define the buttonPin
+relayState = False
+
+def setup():
+	print 'Program is starting...'
+	GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical location
+	GPIO.setup(relayPin, GPIO.OUT)   # Set relayPin's mode is output
+	GPIO.setup(buttonPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)    # Set buttonPin's mode is input, and pull up to high
+
+def buttonEvent(channel):
+	global relayState 
+	print 'buttonEvent GPIO%d'%channel
+	relayState = not relayState
+	if relayState :
+		print 'Turn on relay ... '
+	else :
+		print 'Turn off relay ... '
+	GPIO.output(relayPin,relayState)
+	
+def loop():
+	#Button detect 
+	GPIO.add_event_detect(buttonPin,GPIO.FALLING,callback = buttonEvent,bouncetime=300)
+	while True:
+		pass
+	
+def destroy():
+	GPIO.output(relayPin, GPIO.LOW)     # relay off
+	GPIO.cleanup()                     # Release resource
+
+if __name__ == '__main__':     # Program start from here
+	setup()
+	try:
+		loop()
+	except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child program destroy() will be  executed.
+		destroy()
+
diff --git a/Code/Python_Code/15.1.1_Sweep/Sweep.py b/Code/Python_Code/15.1.1_Sweep/Sweep.py
new file mode 100644
index 0000000..28c6e08
--- /dev/null
+++ b/Code/Python_Code/15.1.1_Sweep/Sweep.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+########################################################################
+# Filename    : Sweep.py
+# Description : Servo sweep
+# Author      : freenove
+# modification: 2016/07/06
+########################################################################
+import RPi.GPIO as GPIO
+import time
+OFFSE_DUTY = 0.5        #define pulse offset of servo
+SERVO_MIN_DUTY = 2.5+OFFSE_DUTY     #define pulse duty cycle for minimum angle of servo
+SERVO_MAX_DUTY = 12.5+OFFSE_DUTY    #define pulse duty cycle for maximum angle of servo
+servoPin = 12
+
+def map( value, fromLow, fromHigh, toLow, toHigh):
+    return (toHigh-toLow)*(value-fromLow) / (fromHigh-fromLow) + toLow
+
+def setup():
+    global p
+    GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical location
+    GPIO.setup(servoPin, GPIO.OUT)   # Set servoPin's mode is output
+    GPIO.output(servoPin, GPIO.LOW)  # Set servoPin to low
+
+    p = GPIO.PWM(servoPin, 50)     # set Frequece to 50Hz
+    p.start(0)                     # Duty Cycle = 0
+    
+def servoWrite(angle):      # make the servo rotate to specific angle (0-180 degrees) 
+    if(angle<0):
+        angle = 0
+    elif(angle > 180):
+        angle = 180
+    p.ChangeDutyCycle(map(angle,0,180,SERVO_MIN_DUTY,SERVO_MAX_DUTY))#map the angle to duty cycle and output it
+    
+def loop():
+    while True:
+        for dc in range(0, 181, 1):   #make servo rotate from 0° to 180°
+            servoWrite(dc)     # Write to servo
+            time.sleep(0.001)
+        time.sleep(0.5)
+        for dc in range(180, -1, -1): #make servo rotate from 180°to 0°
+            servoWrite(dc)
+            time.sleep(0.001)
+        time.sleep(0.5)
+
+def destroy():
+    p.stop()
+    GPIO.cleanup()
+
+if __name__ == '__main__':     #Program start from here
+    print 'Program is starting...'
+    setup()
+    try:
+        loop()
+    except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child program destroy() will be  executed.
+        destroy()
diff --git a/Code/Python_Code/16.1.1_SteppingMotor/SteppingMotor.py b/Code/Python_Code/16.1.1_SteppingMotor/SteppingMotor.py
new file mode 100644
index 0000000..dcbf669
--- /dev/null
+++ b/Code/Python_Code/16.1.1_SteppingMotor/SteppingMotor.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+########################################################################
+# Filename    : SteppingMotor.py
+# Description : 
+# Author      : freenove
+# modification: 2016/07/07
+########################################################################
+import RPi.GPIO as GPIO
+import time 
+
+motorPins = (12, 16, 18, 22)    #define pins connected to four phase ABCD of stepper motor
+CCWStep = (0x01,0x02,0x04,0x08) #define power supply order for coil for rotating anticlockwise 
+CWStep = (0x08,0x04,0x02,0x01)  #define power supply order for coil for rotating clockwise
+
+def setup():
+    print 'Program is starting...'
+    GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical location
+    for pin in motorPins:
+        GPIO.setup(pin,GPIO.OUT)
+#as for four phase stepping motor, four steps is a cycle. the function is used to drive the stepping motor clockwise or anticlockwise to take four steps    
+def moveOnePeriod(direction,ms):    
+    for j in range(0,4,1):      #cycle for power supply order
+        for i in range(0,4,1):  #assign to each pin, a total of 4 pins
+            if (direction == 1):#power supply order clockwise
+                GPIO.output(motorPins[i],((CCWStep[j] == 1<>i)==0x01) and GPIO.HIGH or GPIO.LOW)
+		elif(order == MSBFIRST):
+			GPIO.output(dPin,(0x80&(val<>=1
+			time.sleep(0.1)
+
+def destroy():   # When 'Ctrl+C' is pressed, the function is executed. 
+	GPIO.cleanup()
+
+if __name__ == '__main__': # Program starting from here 
+	print 'Program is starting...' 
+	setup() 
+	try:
+		loop()  
+	except KeyboardInterrupt:  
+		destroy()  
diff --git a/Code/Python_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.py b/Code/Python_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.py
new file mode 100644
index 0000000..d7200a2
--- /dev/null
+++ b/Code/Python_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#############################################################################
+# Filename    : SevenSegmentDisplay.py
+# Description : Control SevenSegmentDisplay by 74HC595
+# Author      : freenove
+# modification: 2016/06/24
+########################################################################
+import RPi.GPIO as GPIO
+import time
+
+LSBFIRST = 1
+MSBFIRST = 2
+#define the pins connect to 74HC595
+dataPin   = 11		#DS Pin of 74HC595(Pin14)
+latchPin  = 13		#ST_CP Pin of 74HC595(Pin12)
+clockPin = 15		#CH_CP Pin of 74HC595(Pin11)
+#SevenSegmentDisplay display the character "0"- "F"successively
+num = [0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e]
+def setup():
+	GPIO.setmode(GPIO.BOARD)    # Number GPIOs by its physical location
+	GPIO.setup(dataPin, GPIO.OUT)
+	GPIO.setup(latchPin, GPIO.OUT)
+	GPIO.setup(clockPin, GPIO.OUT)
+	
+def shiftOut(dPin,cPin,order,val):
+	for i in range(0,8):
+		GPIO.output(cPin,GPIO.LOW);
+		if(order == LSBFIRST):
+			GPIO.output(dPin,(0x01&(val>>i)==0x01) and GPIO.HIGH or GPIO.LOW)
+		elif(order == MSBFIRST):
+			GPIO.output(dPin,(0x80&(val<>i)==0x01) and GPIO.HIGH or GPIO.LOW)
+        elif(order == MSBFIRST):
+            GPIO.output(dPin,(0x80&(val<>i)==0x01) and GPIO.HIGH or GPIO.LOW)
+        elif(order == MSBFIRST):
+            GPIO.output(dPin,(0x80&(val<>=1
+        for k in range(0,len(data)-8):#len(data) total number of "0-F" columns 
+            for j in range(0,20):# times of repeated displaying LEDMatrix in every frame, the bigger the "j", the longer the display time.
+                x=0x80      # Set the column information to start from the first column
+                for i in range(k,k+8):
+                    GPIO.output(latchPin,GPIO.LOW)
+                    shiftOut(dataPin,clockPin,LSBFIRST,data[i])
+                    shiftOut(dataPin,clockPin,LSBFIRST,~x)
+                    GPIO.output(latchPin,GPIO.HIGH)
+                    time.sleep(0.001)
+                    x>>=1
+def destroy():   # When 'Ctrl+C' is pressed, the function is executed. 
+    GPIO.cleanup()
+if __name__ == '__main__': # Program starting from here 
+    print 'Program is starting...' 
+    setup() 
+    try:
+        loop()  
+    except KeyboardInterrupt:  
+        destroy()  
+
diff --git a/Code/Python_Code/20.1.1_I2CLCD1602/Adafruit_LCD1602.py b/Code/Python_Code/20.1.1_I2CLCD1602/Adafruit_LCD1602.py
new file mode 100644
index 0000000..e783d43
--- /dev/null
+++ b/Code/Python_Code/20.1.1_I2CLCD1602/Adafruit_LCD1602.py
@@ -0,0 +1,202 @@
+from time import sleep
+
+
+class Adafruit_CharLCD(object):
+
+    # commands
+    LCD_CLEARDISPLAY        = 0x01
+    LCD_RETURNHOME          = 0x02
+    LCD_ENTRYMODESET        = 0x04
+    LCD_DISPLAYCONTROL      = 0x08
+    LCD_CURSORSHIFT         = 0x10
+    LCD_FUNCTIONSET         = 0x20
+    LCD_SETCGRAMADDR        = 0x40
+    LCD_SETDDRAMADDR        = 0x80
+
+    # flags for display entry mode
+    LCD_ENTRYRIGHT          = 0x00
+    LCD_ENTRYLEFT           = 0x02
+    LCD_ENTRYSHIFTINCREMENT = 0x01
+    LCD_ENTRYSHIFTDECREMENT = 0x00
+
+    # flags for display on/off control
+    LCD_DISPLAYON           = 0x04
+    LCD_DISPLAYOFF          = 0x00
+    LCD_CURSORON            = 0x02
+    LCD_CURSOROFF           = 0x00
+    LCD_BLINKON             = 0x01
+    LCD_BLINKOFF            = 0x00
+
+    # flags for display/cursor shift
+    LCD_DISPLAYMOVE         = 0x08
+    LCD_CURSORMOVE          = 0x00
+
+    # flags for display/cursor shift
+    LCD_DISPLAYMOVE         = 0x08
+    LCD_CURSORMOVE          = 0x00
+    LCD_MOVERIGHT           = 0x04
+    LCD_MOVELEFT            = 0x00
+
+    # flags for function set
+    LCD_8BITMODE            = 0x10
+    LCD_4BITMODE            = 0x00
+    LCD_2LINE               = 0x08
+    LCD_1LINE               = 0x00
+    LCD_5x10DOTS            = 0x04
+    LCD_5x8DOTS             = 0x00
+
+    def __init__(self, pin_rs=25, pin_e=24, pins_db=[23, 17, 21, 22], GPIO=None):
+        # Emulate the old behavior of using RPi.GPIO if we haven't been given
+        # an explicit GPIO interface to use
+        if not GPIO:
+            import RPi.GPIO as GPIO
+            GPIO.setwarnings(False)
+        self.GPIO = GPIO
+        self.pin_rs = pin_rs
+        self.pin_e = pin_e
+        self.pins_db = pins_db
+
+        self.GPIO.setmode(GPIO.BCM) #GPIO=None use Raspi PIN in BCM mode
+        self.GPIO.setup(self.pin_e, GPIO.OUT)
+        self.GPIO.setup(self.pin_rs, GPIO.OUT)
+
+        for pin in self.pins_db:
+            self.GPIO.setup(pin, GPIO.OUT)
+
+        self.write4bits(0x33)  # initialization
+        self.write4bits(0x32)  # initialization
+        self.write4bits(0x28)  # 2 line 5x7 matrix
+        self.write4bits(0x0C)  # turn cursor off 0x0E to enable cursor
+        self.write4bits(0x06)  # shift cursor right
+
+        self.displaycontrol = self.LCD_DISPLAYON | self.LCD_CURSOROFF | self.LCD_BLINKOFF
+
+        self.displayfunction = self.LCD_4BITMODE | self.LCD_1LINE | self.LCD_5x8DOTS
+        self.displayfunction |= self.LCD_2LINE
+
+        # Initialize to default text direction (for romance languages)
+        self.displaymode = self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT
+        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)  # set the entry mode
+
+        self.clear()
+
+    def begin(self, cols, lines):
+        if (lines > 1):
+            self.numlines = lines
+            self.displayfunction |= self.LCD_2LINE
+
+    def home(self):
+        self.write4bits(self.LCD_RETURNHOME)  # set cursor position to zero
+        self.delayMicroseconds(3000)  # this command takes a long time!
+
+    def clear(self):
+        self.write4bits(self.LCD_CLEARDISPLAY)  # command to clear display
+        self.delayMicroseconds(3000)  # 3000 microsecond sleep, clearing the display takes a long time
+
+    def setCursor(self, col, row):
+        self.row_offsets = [0x00, 0x40, 0x14, 0x54]
+        if row > self.numlines:
+            row = self.numlines - 1  # we count rows starting w/0
+        self.write4bits(self.LCD_SETDDRAMADDR | (col + self.row_offsets[row]))
+
+    def noDisplay(self):
+        """ Turn the display off (quickly) """
+        self.displaycontrol &= ~self.LCD_DISPLAYON
+        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
+
+    def display(self):
+        """ Turn the display on (quickly) """
+        self.displaycontrol |= self.LCD_DISPLAYON
+        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
+
+    def noCursor(self):
+        """ Turns the underline cursor off """
+        self.displaycontrol &= ~self.LCD_CURSORON
+        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
+
+    def cursor(self):
+        """ Turns the underline cursor on """
+        self.displaycontrol |= self.LCD_CURSORON
+        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
+
+    def noBlink(self):
+        """ Turn the blinking cursor off """
+        self.displaycontrol &= ~self.LCD_BLINKON
+        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
+
+    def blink(self):
+        """ Turn the blinking cursor on """
+        self.displaycontrol |= self.LCD_BLINKON
+        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)
+
+    def DisplayLeft(self):
+        """ These commands scroll the display without changing the RAM """
+        self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT)
+
+    def scrollDisplayRight(self):
+        """ These commands scroll the display without changing the RAM """
+        self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT)
+
+    def leftToRight(self):
+        """ This is for text that flows Left to Right """
+        self.displaymode |= self.LCD_ENTRYLEFT
+        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)
+
+    def rightToLeft(self):
+        """ This is for text that flows Right to Left """
+        self.displaymode &= ~self.LCD_ENTRYLEFT
+        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)
+
+    def autoscroll(self):
+        """ This will 'right justify' text from the cursor """
+        self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT
+        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)
+
+    def noAutoscroll(self):
+        """ This will 'left justify' text from the cursor """
+        self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT
+        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)
+
+    def write4bits(self, bits, char_mode=False):
+        """ Send command to LCD """
+        self.delayMicroseconds(1000)  # 1000 microsecond sleep
+        bits = bin(bits)[2:].zfill(8)
+        self.GPIO.output(self.pin_rs, char_mode)
+        for pin in self.pins_db:
+            self.GPIO.output(pin, False)
+        for i in range(4):
+            if bits[i] == "1":
+                self.GPIO.output(self.pins_db[::-1][i], True)
+        self.pulseEnable()
+        for pin in self.pins_db:
+            self.GPIO.output(pin, False)
+        for i in range(4, 8):
+            if bits[i] == "1":
+                self.GPIO.output(self.pins_db[::-1][i-4], True)
+        self.pulseEnable()
+
+    def delayMicroseconds(self, microseconds):
+        seconds = microseconds / float(1000000)  # divide microseconds by 1 million for seconds
+        sleep(seconds)
+
+    def pulseEnable(self):
+        self.GPIO.output(self.pin_e, False)
+        self.delayMicroseconds(1)       # 1 microsecond pause - enable pulse must be > 450ns
+        self.GPIO.output(self.pin_e, True)
+        self.delayMicroseconds(1)       # 1 microsecond pause - enable pulse must be > 450ns
+        self.GPIO.output(self.pin_e, False)
+        self.delayMicroseconds(1)       # commands need > 37us to settle
+
+    def message(self, text):
+        """ Send string to LCD. Newline wraps to second line"""
+        for char in text:
+            if char == '\n':
+                self.write4bits(0xC0)  # next line
+            else:
+                self.write4bits(ord(char), True)
+
+
+if __name__ == '__main__':
+    lcd = Adafruit_CharLCD()
+    lcd.clear()
+    lcd.message("  Adafruit 16x2\n  Standard LCD")
\ No newline at end of file
diff --git a/Code/Python_Code/20.1.1_I2CLCD1602/Adafruit_LCD1602.pyc b/Code/Python_Code/20.1.1_I2CLCD1602/Adafruit_LCD1602.pyc
new file mode 100644
index 0000000..2e53d78
Binary files /dev/null and b/Code/Python_Code/20.1.1_I2CLCD1602/Adafruit_LCD1602.pyc differ
diff --git a/Code/Python_Code/20.1.1_I2CLCD1602/I2CLCD1602.py b/Code/Python_Code/20.1.1_I2CLCD1602/I2CLCD1602.py
new file mode 100644
index 0000000..e9e78e0
--- /dev/null
+++ b/Code/Python_Code/20.1.1_I2CLCD1602/I2CLCD1602.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+########################################################################
+# Filename    : I2CLCD1602.py
+# Description : Use the LCD display data
+# Author      : freenove
+# modification: 2016/06/26
+########################################################################
+from PCF8574 import PCF8574_GPIO
+from Adafruit_LCD1602 import Adafruit_CharLCD
+
+from time import sleep, strftime
+from datetime import datetime
+ 
+def get_cpu_temp():     # get CPU temperature and store it into file "/sys/class/thermal/thermal_zone0/temp"
+    tmp = open('/sys/class/thermal/thermal_zone0/temp')
+    cpu = tmp.read()
+    tmp.close()
+    return '{:.2f}'.format( float(cpu)/1000 ) + ' C'
+ 
+def get_time_now():     # get system time
+    return datetime.now().strftime('    %H:%M:%S')
+    
+def loop():
+    mcp.output(3,1)     # turn on LCD backlight
+    lcd.begin(16,2)     # set number of LCD lines and columns
+    while(True):         
+        #lcd.clear()
+        lcd.setCursor(0,0)  # set cursor position
+        lcd.message( 'CPU: ' + get_cpu_temp()+'\n' )# display CPU temperature
+        lcd.message( get_time_now() )   # display the time
+        sleep(1)
+        
+def destroy():
+    lcd.clear()
+    
+address = 0x27  # I2C address of the PCF8574 chip.
+# Create PCF8574 GPIO adapter.
+mcp = PCF8574_GPIO(address)
+# Create LCD, passing in MCP GPIO adapter.
+lcd = Adafruit_CharLCD(pin_rs=0, pin_e=2, pins_db=[4,5,6,7], GPIO=mcp)
+
+if __name__ == '__main__':
+    print 'Program is starting ... '
+    try:
+        loop()
+    except KeyboardInterrupt:
+        destroy()
+
diff --git a/Code/Python_Code/20.1.1_I2CLCD1602/PCF8574.py b/Code/Python_Code/20.1.1_I2CLCD1602/PCF8574.py
new file mode 100644
index 0000000..9206f4a
--- /dev/null
+++ b/Code/Python_Code/20.1.1_I2CLCD1602/PCF8574.py
@@ -0,0 +1,78 @@
+########################################################################
+# Filename    : PCF8574.py
+# Description : PCF8574 as Raspberry GPIO
+# Author      : freenove
+# modification: 2016/06/26
+########################################################################
+import smbus
+import time
+class PCF8574_I2C(object):
+	OUPUT = 0
+	INPUT = 1
+	
+	def __init__(self,address):
+		# Note you need to change the bus number to 0 if running on a revision 1 Raspberry Pi.
+		self.bus = smbus.SMBus(1)
+		self.address = address
+		self.currentValue = 0
+		
+	def readByte(self):#Read PCF8574 all port of the data
+		#value = self.bus.read_byte(self.address)
+		return self.currentValue#value
+		
+	def writeByte(self,value):#Write data to PCF8574 port
+		self.currentValue = value
+		self.bus.write_byte(self.address,value)
+
+	def digitalRead(self,pin):#Read PCF8574 one port of the data
+		value = readByte()	
+		return (value&(1< loopCnt):
+				return self.DHTLIB_ERROR_TIMEOUT
+		t = time.time()
+		while(GPIO.input(pin) == GPIO.HIGH):
+			if((time.time() - t) > loopCnt):
+				return self.DHTLIB_ERROR_TIMEOUT
+		for i in range(0,40,1):
+			t = time.time()
+			while(GPIO.input(pin) == GPIO.LOW):
+				if((time.time() - t) > loopCnt):
+					return self.DHTLIB_ERROR_TIMEOUT
+			t = time.time()
+			while(GPIO.input(pin) == GPIO.HIGH):
+				if((time.time() - t) > loopCnt):
+					return self.DHTLIB_ERROR_TIMEOUT		
+			if((time.time() - t) > 0.00005):	
+				self.bits[idx] |= mask
+			#print"t : %f"%(time.time()-t)
+			mask >>= 1
+			if(mask == 0):
+				mask = 0x80
+				idx += 1	
+		GPIO.setup(pin,GPIO.OUT)
+		GPIO.output(pin,GPIO.HIGH)
+		return self.DHTLIB_OK
+	#Read DHT sensor, analyze the data of temperature and humidity
+	def readDHT11(self,pin):
+		
+		rv = self.readSensor(pin,self.DHTLIB_DHT11_WAKEUP)
+		if (rv is not self.DHTLIB_OK):
+			self.humidity = self.DHTLIB_INVALID_VALUE
+			self.temperature = self.DHTLIB_INVALID_VALUE
+			return rv
+		self.humidity = self.bits[0]
+		self.temperature = self.bits[2]
+		sumChk = ((self.bits[0] + self.bits[2]) & 0xff)
+		if(self.bits[4] is not sumChk):
+			return self.DHTLIB_ERROR_CHECKSUM
+		return self.DHTLIB_OK
+		
+def loop():
+	dht = DHT(11)
+	sumCnt = 0
+	okCnt = 0
+	while(True):
+		sumCnt += 1
+		chk = dht.readDHT11(11)	
+		if (chk is 0):
+			okCnt += 1		
+		okRate = 100.0*okCnt/sumCnt;
+		print"sumCnt : %d, \t okRate : %.2f%% "%(sumCnt,okRate)		
+		print"chk : %d, \t Humidity : %.2f, \t Temperature : %.2f "%(chk,dht.humidity,dht.temperature)		
+		time.sleep(1)		
+		
+if __name__ == '__main__':
+	print 'Program is starting ... '
+	try:
+		loop()
+	except KeyboardInterrupt:
+		pass
+		exit()		
+		
+		
diff --git a/Code/Python_Code/21.1.1_DHT11/setup.py b/Code/Python_Code/21.1.1_DHT11/setup.py
new file mode 100644
index 0000000..e426364
--- /dev/null
+++ b/Code/Python_Code/21.1.1_DHT11/setup.py
@@ -0,0 +1,13 @@
+
+from setuptools import setup,find_packages
+
+setup(
+	name = "Freenove_DHT",
+	version = "V1.0.0",
+	description = "Read DHT Sensor",
+	author = "Freenove",
+	url = "http://www.freenove.com",
+	license = " ",
+	packages = find_packages(),
+	scripts = ["Freenove_DHT.py"],
+	)
diff --git a/Code/Python_Code/22.1.1_MatrixKeypad/Keypad.py b/Code/Python_Code/22.1.1_MatrixKeypad/Keypad.py
new file mode 100644
index 0000000..0f6bd5d
--- /dev/null
+++ b/Code/Python_Code/22.1.1_MatrixKeypad/Keypad.py
@@ -0,0 +1,207 @@
+########################################################################
+# Filename    : Keypad.py
+# Description : The module of matrix keypad 
+# Author      : freenove
+# modification: 2016/07/13
+########################################################################
+import RPi.GPIO as GPIO 
+import time
+#class Key:Define some of the properties of Key
+class Key(object):
+	NO_KEY = '\0'
+	#Defines the four states of Key
+	IDLE = 0
+	PRESSED = 1
+	HOLD = 2
+	RELEASED = 3
+	#define OPEN and CLOSED
+	OPEN = 0
+	CLOSED =1
+	#constructor
+	def __init__(self):
+		self.kchar = self.NO_KEY
+		self.kstate = self.IDLE
+		self.kcode = -1
+		self.stateChanged = False
+
+class Keypad(object):
+	NULL = '\0'
+	LIST_MAX = 10	#Max number of keys on the active list.
+	MAPSIZE = 10	#MAPSIZE is the number of rows (times 16 columns)
+	bitMap = [0]*MAPSIZE
+	key = [Key()]*LIST_MAX
+	holdTime = 500		#key hold time
+	holdTimer = 0
+	startTime = 0
+	#Allows custom keymap, pin configuration, and keypad sizes.
+	def __init__(self,usrKeyMap,row_Pins,col_Pins,num_Rows,num_Cols):
+		GPIO.setmode(GPIO.BOARD)
+		self.rowPins = row_Pins
+		self.colPins = col_Pins
+		self.numRows = num_Rows
+		self.numCols = num_Cols
+		
+		self.keymap = usrKeyMap
+		self.setDebounceTime(10)
+	#Returns a single key only. Retained for backwards compatibility.	
+	def getKey(self):
+		single_key = True
+		if(self.getKeys() and self.key[0].stateChanged and (self.key[0].kstate == self.key[0].PRESSED)):
+			return self.key[0].kchar
+		single_key = False
+		return self.key[0].NO_KEY
+	#Populate the key list.	
+	def getKeys(self):
+		keyActivity = False
+		#Limit how often the keypad is scanned.
+		if((time.time() - self.startTime) > self.debounceTime*0.001):
+			self.scanKeys()
+			keyActivity = self.updateList()
+			self.startTime = time.time()
+		return keyActivity
+	#Hardware scan ,the result store in bitMap	
+	def scanKeys(self):
+		#Re-intialize the row pins. Allows sharing these pins with other hardware.
+		for pin_r in self.rowPins:
+			GPIO.setup(pin_r,GPIO.IN,pull_up_down = GPIO.PUD_UP)
+		#bitMap stores ALL the keys that are being pressed.
+		for pin_c in self.colPins:
+			GPIO.setup(pin_c,GPIO.OUT)
+			GPIO.output(pin_c,GPIO.LOW)
+			for r in self.rowPins: #keypress is active low so invert to high.
+				self.bitMap[self.rowPins.index(r)] = self.bitWrite(self.bitMap[self.rowPins.index(r)],self.colPins.index(pin_c),not GPIO.input(r))
+			#Set pin to high impedance input. Effectively ends column pulse.
+			GPIO.output(pin_c,GPIO.HIGH)
+			GPIO.setup(pin_c,GPIO.IN)
+	#Manage the list without rearranging the keys. Returns true if any keys on the list changed state.		
+	def updateList(self):
+		anyActivity = False
+		kk = Key()
+		#Delete any IDLE keys
+		for i in range(self.LIST_MAX):
+			if(self.key[i].kstate == kk.IDLE):
+				self.key[i].kchar = kk.NO_KEY
+				self.key[i].kcode = -1
+				self.key[i].stateChanged = False
+		# Add new keys to empty slots in the key list.
+		for r in range(self.numRows):
+			for c in range(self.numCols):
+				button = self.bitRead(self.bitMap[r],c)
+				keyChar = self.keymap[r * self.numCols +c]
+				keyCode = r * self.numCols +c
+				idx = self.findInList(keyCode)
+				#Key is already on the list so set its next state.
+				if(idx > -1):
+					self.nextKeyState(idx,button)
+				#Key is NOT on the list so add it.
+				if((idx == -1) and button):
+					for i in range(self.LIST_MAX):
+						if(self.key[i].kchar == kk.NO_KEY):	#Find an empty slot or don't add key to list.
+							self.key[i].kchar = keyChar
+							self.key[i].kcode = keyCode
+							self.key[i].kstate = kk.IDLE	#Keys NOT on the list have an initial state of IDLE.
+							self.nextKeyState(i,button)
+							break	#Don't fill all the empty slots with the same key.
+		#Report if the user changed the state of any key.
+		for i in range(self.LIST_MAX):
+			if(self.key[i].stateChanged):
+				anyActivity = True
+		return anyActivity		
+	#This function is a state machine but is also used for debouncing the keys.	
+	def nextKeyState(self,idx, button):
+		self.key[idx].stateChanged = False
+		kk = Key()
+		if(self.key[idx].kstate == kk.IDLE):
+			if(button == kk.CLOSED):
+				self.transitionTo(idx,kk.PRESSED)
+				self.holdTimer = time.time()	#Get ready for next HOLD state.
+		elif(self.key[idx].kstate == kk.PRESSED):
+			if((time.time() - self.holdTimer) > self.holdTime*0.001):	#Waiting for a key HOLD...	
+				self.transitionTo(idx,kk.HOLD)
+			elif(button == kk.OPEN):		# or for a key to be RELEASED.
+				self.transitionTo(idx,kk.RELEASED)
+		elif(self.key[idx].kstate == kk.HOLD):
+			if(button == kk.OPEN):
+				self.transitionTo(idx,kk.RELEASED)
+		elif(self.key[idx].kstate == kk.RELEASED):
+			self.transitionTo(idx,kk.IDLE)
+			
+	def transitionTo(self,idx,nextState):
+		self.key[idx].kstate = nextState
+		self.key[idx].stateChanged = True
+	#Search by code for a key in the list of active keys.
+	#Returns -1 if not found or the index into the list of active keys.
+	def findInList(self,keyCode):
+		for i in range(self.LIST_MAX):
+			if(self.key[i].kcode == keyCode):
+				return i
+		return -1
+	#set Debounce Time,	The default is 50ms					
+	def setDebounceTime(self,ms):
+		self.debounceTime = ms
+	#set HoldTime,The default is 500ms
+	def setHoldTime(self,ms):
+		self.holdTime = ms
+	#	
+	def isPressed(keyChar):
+		for i in range(self.LIST_MAX):
+			if(self.key[i].kchar == keyChar):
+				if(self.key[i].kstate == self.self.key[i].PRESSED and self.key[i].stateChanged):
+					return True
+		return False
+	#			
+	def waitForKey():
+		kk = Key()
+		waitKey = kk.NO_KEY
+		while(waitKey == kk.NO_KEY):
+			waitKey = getKey()
+		return waitKey
+	
+	def getState():
+		return self.key[0].kstate
+	#	
+	def keyStateChanged():
+		return self.key[0].stateChanged
+	
+	def bitWrite(self,x,n,b):
+		if(b):
+			x |= (1<>n)&1 == 1):
+			return True
+		else:
+			return False
+
+#######################EXAMPLE##################################		
+ROWS = 4
+COLS = 4
+keys = 	[	'1','2','3','A',
+			'4','5','6','B',
+			'7','8','9','C',
+			'*','0','#','D'		]
+rowsPins = [12,16,18,22]
+colsPins = [19,15,13,11]	
+
+def loop():
+	keypad = Keypad.Keypad(keys,rowsPins,colsPins,ROWS,COLS)
+	keypad.setDebounceTime(50)
+	while(True):
+		key = keypad.getKey()
+		if(key != keypad.NULL):
+			print "You Pressed Key : %c "%(key) 
+			
+if __name__ == '__main__':     # Program start from here
+	print "Program is starting ... "
+	try:
+		loop()
+	except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child program destroy() will be  executed.
+		pass
+		GPIO.cleanup()	
+			
+			
+			
+			
+
diff --git a/Code/Python_Code/22.1.1_MatrixKeypad/MatrixKeypad.py b/Code/Python_Code/22.1.1_MatrixKeypad/MatrixKeypad.py
new file mode 100644
index 0000000..1f3cb48
--- /dev/null
+++ b/Code/Python_Code/22.1.1_MatrixKeypad/MatrixKeypad.py
@@ -0,0 +1,29 @@
+########################################################################
+# Filename    : MatrixKeypad.py
+# Description : obtain the key code of 4x4 Matrix Keypad
+# Author      : freenove
+# modification: 2016/07/13
+########################################################################
+import Keypad       #import module Keypad
+ROWS = 4        # number of rows of the Keypad
+COLS = 4        #number of columns of the Keypad
+keys =  [   '1','2','3','A',    #key code
+            '4','5','6','B',
+            '7','8','9','C',
+            '*','0','#','D'     ]
+rowsPins = [12,16,18,22]        #connect to the row pinouts of the keypad
+colsPins = [19,15,13,11]        #connect to the column pinouts of the keypad
+
+def loop():
+    keypad = Keypad.Keypad(keys,rowsPins,colsPins,ROWS,COLS)    #creat Keypad object
+    keypad.setDebounceTime(50)      #set the debounce time
+    while(True):
+        key = keypad.getKey()       #obtain the state of keys
+        if(key != keypad.NULL):     #if there is key pressed, print its key code.
+            print "You Pressed Key : %c "%(key) 
+            
+if __name__ == '__main__':     #Program start from here
+    print "Program is starting ... "
+    try:
+        loop()
+    except KeyboardInterrupt:  #When 'Ctrl+C' is pressed, exit the program.      GPIO.cleanup()  
diff --git a/Code/Python_Code/23.1.1_SenseLED/SenseLED.py b/Code/Python_Code/23.1.1_SenseLED/SenseLED.py
new file mode 100644
index 0000000..625f395
--- /dev/null
+++ b/Code/Python_Code/23.1.1_SenseLED/SenseLED.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+########################################################################
+# Filename    : SenseLED.py
+# Description : Controlling an led by infrared Motion sensor.
+# Author      : freenove
+# modification: 2016/06/12
+########################################################################
+import RPi.GPIO as GPIO
+
+ledPin = 12    # define the ledPin
+sensorPin = 11    # define the sensorPin
+
+def setup():
+	print 'Program is starting...'
+	GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical location
+	GPIO.setup(ledPin, GPIO.OUT)   # Set ledPin's mode is output
+	GPIO.setup(sensorPin, GPIO.IN)    # Set sensorPin's mode is input
+
+def loop():
+	while True:
+		if GPIO.input(sensorPin)==GPIO.HIGH:
+			GPIO.output(ledPin,GPIO.HIGH)
+			print 'led on ...'
+		else :
+			GPIO.output(ledPin,GPIO.LOW)
+			print 'led off ...'		
+
+def destroy():
+	GPIO.cleanup()                     # Release resource
+
+if __name__ == '__main__':     # Program start from here
+	setup()
+	try:
+		loop()
+	except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child program destroy() will be  executed.
+		destroy()
+
diff --git a/Code/Python_Code/24.1.1_UtrasonicRanging/UtrasonicRanging.py b/Code/Python_Code/24.1.1_UtrasonicRanging/UtrasonicRanging.py
new file mode 100644
index 0000000..b7858fb
--- /dev/null
+++ b/Code/Python_Code/24.1.1_UtrasonicRanging/UtrasonicRanging.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+########################################################################
+# Filename    : UltrasonicRanging.py
+# Description : Get distance from UltrasonicRanging.
+# Author      : freenove
+# modification: 2016/07/15
+########################################################################
+import RPi.GPIO as GPIO
+import time
+
+trigPin = 16
+echoPin = 18
+MAX_DISTANCE = 220          #define the maximum measured distance
+timeOut = MAX_DISTANCE*60   #calculate timeout according to the maximum measured distance
+
+def pulseIn(pin,level,timeOut): # function pulseIn: obtain pulse time of a pin
+    t0 = time.time()
+    while(GPIO.input(pin) != level):
+        if((time.time() - t0) > timeOut*0.000001):
+            return 0;
+    t0 = time.time()
+    while(GPIO.input(pin) == level):
+        if((time.time() - t0) > timeOut*0.000001):
+            return 0;
+    pulseTime = (time.time() - t0)*1000000
+    return pulseTime
+    
+def getSonar():     #get the measurement results of ultrasonic module,with unit: cm
+    GPIO.output(trigPin,GPIO.HIGH)      #make trigPin send 10us high level 
+    time.sleep(0.00001)     #10us
+    GPIO.output(trigPin,GPIO.LOW)
+    pingTime = pulseIn(echoPin,GPIO.HIGH,timeOut)   #read plus time of echoPin
+    distance = pingTime * 340.0 / 2.0 / 10000.0     # the sound speed is 340m/s, and calculate distance
+    return distance
+    
+def setup():
+    print 'Program is starting...'
+    GPIO.setmode(GPIO.BOARD)       #numbers GPIOs by physical location
+    GPIO.setup(trigPin, GPIO.OUT)   #
+    GPIO.setup(echoPin, GPIO.IN)    #
+
+def loop():
+    GPIO.setup(11,GPIO.IN)
+    while(True):
+        distance = getSonar()
+        print "The distance is : %.2f cm"%(distance)
+        time.sleep(1)
+        
+if __name__ == '__main__':     #program start from here
+    setup()
+    try:
+        loop()
+    except KeyboardInterrupt:  #when 'Ctrl+C' is pressed, the program will exit
+        GPIO.cleanup()         #release resource
+
+
+	
diff --git a/Code/Python_Code/25.1.1_MPU6050/LICENSE b/Code/Python_Code/25.1.1_MPU6050/LICENSE
new file mode 100644
index 0000000..2f78a9a
--- /dev/null
+++ b/Code/Python_Code/25.1.1_MPU6050/LICENSE
@@ -0,0 +1,24 @@
+The MIT License (MIT)
+
+MPU6050 Python I2C Class
+
+Copyright (c) 2015 Geir Istad
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/Code/Python_Code/25.1.1_MPU6050/MPU6050.py b/Code/Python_Code/25.1.1_MPU6050/MPU6050.py
new file mode 100644
index 0000000..4accc0e
--- /dev/null
+++ b/Code/Python_Code/25.1.1_MPU6050/MPU6050.py
@@ -0,0 +1,946 @@
+__author__ = 'Geir Istad'
+"""
+MPU6050 Python I2C Class
+Copyright (c) 2015 Geir Istad
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+Code based on
+I2Cdev library collection - MPU6050 I2C device class
+by Jeff Rowberg 
+============================================
+I2Cdev device library code is placed under the MIT license
+Copyright (c) 2012 Jeff Rowberg
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+===============================================
+"""
+
+import math
+import ctypes
+import time
+import smbus
+import csv
+from MPUConstants import MPUConstants as C
+from Quaternion import Quaternion as Q
+from Quaternion import XYZVector as V
+
+
+class MPU6050:
+    __buffer = [0] * 14
+    __debug = False
+    __DMP_packet_size = 0
+    __dev_id = 0
+    __bus = None
+
+    def __init__(self, a_bus=1, a_address=C.MPU6050_DEFAULT_ADDRESS,
+                 a_xAOff=None, a_yAOff=None, a_zAOff=None, a_xGOff=None,
+                 a_yGOff=None, a_zGOff=None, a_debug=False):
+        self.__dev_id = a_address
+        # Connect to num 1 SMBus
+        self.__bus = smbus.SMBus(a_bus)
+        # Set clock source to gyro
+        self.set_clock_source(C.MPU6050_CLOCK_PLL_XGYRO)
+        # Set accelerometer range
+        self.set_full_scale_accel_range(C.MPU6050_ACCEL_FS_2)
+        # Set gyro range
+        self.set_full_scale_gyro_range(C.MPU6050_GYRO_FS_250)
+        # Take the MPU out of time.sleep mode
+        self.wake_up()
+        # Set offsets
+        if a_xAOff:
+            self.set_x_accel_offset(a_xAOff)
+        if a_yAOff:
+            self.set_y_accel_offset(a_yAOff)
+        if a_zAOff:
+            self.set_z_accel_offset(a_zAOff)
+        if a_xGOff:
+            self.set_x_gyro_offset(a_xGOff)
+        if a_yGOff:
+            self.set_y_gyro_offset(a_yGOff)
+        if a_zGOff:
+            self.set_z_gyro_offset(a_zGOff)
+        self.__debug = a_debug
+
+    # Core bit and byte operations
+    def read_bit(self, a_reg_add, a_bit_position):
+        return self.read_bits(a_reg_add, a_bit_position, 1)
+
+    def write_bit(self, a_reg_add, a_bit_num, a_bit):
+        byte = self.__bus.read_byte_data(self.__dev_id, a_reg_add)
+        if a_bit:
+            byte |= 1 << a_bit_num
+        else:
+            byte &= ~(1 << a_bit_num)
+        self.__bus.write_byte_data(
+            self.__dev_id, a_reg_add, ctypes.c_int8(byte).value)
+
+    def read_bits(self, a_reg_add, a_bit_start, a_length):
+        byte = self.__bus.read_byte_data(self.__dev_id, a_reg_add)
+        mask = ((1 << a_length) - 1) << (a_bit_start - a_length + 1)
+        byte &= mask
+        byte >>= a_bit_start - a_length + 1
+        return byte
+
+    def write_bits(self, a_reg_add, a_bit_start, a_length, a_data):
+        byte = self.__bus.read_byte_data(self.__dev_id, a_reg_add)
+        mask = ((1 << a_length) - 1) << (a_bit_start - a_length + 1)
+        # Get data in position and zero all non-important bits in data
+        a_data <<= a_bit_start - a_length + 1
+        a_data &= mask
+        # Clear all important bits in read byte and combine with data
+        byte &= ~mask
+        byte = byte | a_data
+        # Write the data to the I2C device
+        self.__bus.write_byte_data(
+            self.__dev_id, a_reg_add, ctypes.c_int8(byte).value)
+
+    def read_memory_byte(self):
+        return self.__bus.read_byte_data(self.__dev_id, C.MPU6050_RA_MEM_R_W)
+
+    def read_bytes(self, a_data_list, a_address, a_length):
+        if a_length > len(a_data_list):
+            print('read_bytes, length of passed list too short')
+            return a_data_list
+        # Attempt to use the built in read bytes function in the adafruit lib
+        # a_data_list = self.__bus.read_i2c_block_data(self.__dev_id, a_address,
+        #                                             a_length)
+        # Attempt to bypass adafruit lib
+        #a_data_list = self.__mpu.bus.read_i2c_block_data(0x68, a_address, a_length)
+        #print('data' + str(a_data_list))
+        for x in xrange(0, a_length):
+            a_data_list[x] = self.__bus.read_byte_data(self.__dev_id,
+                                                       a_address + x)
+        return a_data_list
+
+    def write_memory_block(self, a_data_list, a_data_size, a_bank, a_address,
+                           a_verify):
+        success = True
+        self.set_memory_bank(a_bank)
+        self.set_memory_start_address(a_address)
+
+        # For each a_data_item we want to write it to the board to a certain
+        # memory bank and address
+        for i in range(0, a_data_size):
+            # Write each data to memory
+            self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_MEM_R_W,
+                                       a_data_list[i])
+
+            if a_verify:
+                self.set_memory_bank(a_bank)
+                self.set_memory_start_address(a_address)
+                verify_data = self.__bus.read_byte_data(self.__dev_id,
+                                                        C.MPU6050_RA_MEM_R_W)
+                if verify_data != a_data_list[i]:
+                    success = False
+
+            # If we've filled the bank, change the memory bank
+            if a_address == 255:
+                a_address = 0
+                a_bank += 1
+                self.set_memory_bank(a_bank)
+            else:
+                a_address += 1
+
+            # Either way update the memory address
+            self.set_memory_start_address(a_address)
+
+        return success
+
+    def wake_up(self):
+        self.write_bit(
+            C.MPU6050_RA_PWR_MGMT_1, C.MPU6050_PWR1_SLEEP_BIT, 0)
+
+    def set_clock_source(self, a_source):
+        self.write_bits(C.MPU6050_RA_PWR_MGMT_1, C.MPU6050_PWR1_CLKSEL_BIT,
+                        C.MPU6050_PWR1_CLKSEL_LENGTH, a_source)
+
+    def set_full_scale_gyro_range(self, a_data):
+        self.write_bits(C.MPU6050_RA_GYRO_CONFIG,
+                        C.MPU6050_GCONFIG_FS_SEL_BIT,
+                        C.MPU6050_GCONFIG_FS_SEL_LENGTH, a_data)
+
+    def set_full_scale_accel_range(self, a_data):
+        self.write_bits(C.MPU6050_RA_ACCEL_CONFIG,
+                        C.MPU6050_ACONFIG_AFS_SEL_BIT,
+                        C.MPU6050_ACONFIG_AFS_SEL_LENGTH, a_data)
+
+    def reset(self):
+        self.write_bit(C.MPU6050_RA_PWR_MGMT_1,
+                       C.MPU6050_PWR1_DEVICE_RESET_BIT, 1)
+
+    def set_sleep_enabled(self, a_enabled):
+        set_bit = 0
+        if a_enabled:
+            set_bit = 1
+        self.write_bit(C.MPU6050_RA_PWR_MGMT_1,
+                       C.MPU6050_PWR1_SLEEP_BIT, set_bit)
+
+    def set_memory_bank(self, a_bank, a_prefetch_enabled=False,
+                        a_user_bank=False):
+        a_bank &= 0x1F
+        if a_user_bank:
+            a_bank |= 0x20
+        if a_prefetch_enabled:
+            a_bank |= 0x20
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_BANK_SEL, a_bank)
+
+    def set_memory_start_address(self, a_address):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_MEM_START_ADDR, a_address)
+
+    def get_x_gyro_offset_TC(self):
+        return self.read_bits(C.MPU6050_RA_XG_OFFS_TC,
+                              C.MPU6050_TC_OFFSET_BIT,
+                              C.MPU6050_TC_OFFSET_LENGTH)
+
+    def set_x_gyro_offset_TC(self, a_offset):
+        self.write_bits(C.MPU6050_RA_XG_OFFS_TC,
+                        C.MPU6050_TC_OFFSET_BIT,
+                        C.MPU6050_TC_OFFSET_LENGTH, a_offset)
+
+    def get_y_gyro_offset_TC(self):
+        return self.read_bits(C.MPU6050_RA_YG_OFFS_TC,
+                              C.MPU6050_TC_OFFSET_BIT,
+                              C.MPU6050_TC_OFFSET_LENGTH)
+
+    def set_y_gyro_offset_TC(self, a_offset):
+        self.write_bits(C.MPU6050_RA_YG_OFFS_TC,
+                        C.MPU6050_TC_OFFSET_BIT,
+                        C.MPU6050_TC_OFFSET_LENGTH, a_offset)
+
+    def get_z_gyro_offset_TC(self):
+        return self.read_bits(C.MPU6050_RA_ZG_OFFS_TC,
+                              C.MPU6050_TC_OFFSET_BIT,
+                              C.MPU6050_TC_OFFSET_LENGTH)
+
+    def set_z_gyro_offset_TC(self, a_offset):
+        self.write_bits(C.MPU6050_RA_ZG_OFFS_TC,
+                        C.MPU6050_TC_OFFSET_BIT,
+                        C.MPU6050_TC_OFFSET_LENGTH, a_offset)
+
+    def set_slave_address(self, a_num, a_address):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_I2C_SLV0_ADDR + a_num * 3, a_address)
+
+    def set_I2C_master_mode_enabled(self, a_enabled):
+        bit = 0
+        if a_enabled:
+            bit = 1
+        self.write_bit(C.MPU6050_RA_USER_CTRL,
+                       C.MPU6050_USERCTRL_I2C_MST_EN_BIT, bit)
+
+    def reset_I2C_master(self):
+        self.write_bit(C.MPU6050_RA_USER_CTRL,
+                       C.MPU6050_USERCTRL_I2C_MST_RESET_BIT, 1)
+
+    def write_prog_memory_block(self, a_data_list, a_data_size, a_bank=0,
+                                a_address=0, a_verify=True):
+        return self.write_memory_block(a_data_list, a_data_size, a_bank,
+                                       a_address, a_verify)
+
+    def write_DMP_configuration_set(self, a_data_list, a_data_size):
+        index = 0
+        while index < a_data_size:
+            bank = a_data_list[index]
+            offset = a_data_list[index + 1]
+            length = a_data_list[index + 2]
+            index += 3
+            success = False
+
+            # Normal case
+            if length > 0:
+                data_selection = list()
+                for subindex in range(0, length):
+                    data_selection.append(a_data_list[index + subindex])
+                success = self.write_memory_block(data_selection, length, bank,
+                                                  offset, True)
+                index += length
+            # Special undocumented case
+            else:
+                special = a_data_list[index]
+                index += 1
+                if special == 0x01:
+                    # TODO Figure out if write8 can return True/False
+                    success = self.__bus.write_byte_data(
+                        self.__dev_id, C.MPU6050_RA_INT_ENABLE, 0x32)
+
+            if success == False:
+                # TODO implement error messagemajigger
+                return False
+                pass
+        return True
+
+    def write_prog_dmp_configuration(self, a_data_list, a_data_size):
+        return self.write_DMP_configuration_set(a_data_list, a_data_size)
+
+    def set_int_enable(self, a_enabled):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_INT_ENABLE, a_enabled)
+
+    def set_rate(self, a_rate):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_SMPLRT_DIV, a_rate)
+
+    def set_external_frame_sync(self, a_sync):
+        self.write_bits(C.MPU6050_RA_CONFIG,
+                        C.MPU6050_CFG_EXT_SYNC_SET_BIT,
+                        C.MPU6050_CFG_EXT_SYNC_SET_LENGTH, a_sync)
+
+    def set_DLF_mode(self, a_mode):
+        self.write_bits(C.MPU6050_RA_CONFIG, C.MPU6050_CFG_DLPF_CFG_BIT,
+                        C.MPU6050_CFG_DLPF_CFG_LENGTH, a_mode)
+
+    def get_DMP_config_1(self):
+        return self.__bus.read_byte_data(self.__dev_id, C.MPU6050_RA_DMP_CFG_1)
+
+    def set_DMP_config_1(self, a_config):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_DMP_CFG_1, a_config)
+
+    def get_DMP_config_2(self):
+        return self.__bus.read_byte_data(self.__dev_id, C.MPU6050_RA_DMP_CFG_2)
+
+    def set_DMP_config_2(self, a_config):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_DMP_CFG_2, a_config)
+
+    def set_OTP_bank_valid(self, a_enabled):
+        bit = 0
+        if a_enabled:
+            bit = 1
+        self.write_bit(C.MPU6050_RA_XG_OFFS_TC,
+                       C.MPU6050_TC_OTP_BNK_VLD_BIT, bit)
+
+    def get_OTP_bank_valid(self):
+        return self.read_bit(C.MPU6050_RA_XG_OFFS_TC,
+                             C.MPU6050_TC_OTP_BNK_VLD_BIT)
+
+    def set_motion_detection_threshold(self, a_threshold):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_MOT_THR, a_threshold)
+
+    def set_zero_motion_detection_threshold(self, a_threshold):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_ZRMOT_THR, a_threshold)
+
+    def set_motion_detection_duration(self, a_duration):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_MOT_DUR, a_duration)
+
+    def set_zero_motion_detection_duration(self, a_duration):
+        self.__bus.write_byte_data(
+            self.__dev_id, C.MPU6050_RA_ZRMOT_DUR, a_duration)
+
+    def set_FIFO_enabled(self, a_enabled):
+        bit = 0
+        if a_enabled:
+            bit = 1
+        self.write_bit(C.MPU6050_RA_USER_CTRL,
+                       C.MPU6050_USERCTRL_FIFO_EN_BIT, bit)
+
+    def set_DMP_enabled(self, a_enabled):
+        bit = 0
+        if a_enabled:
+            bit = 1
+        self.write_bit(C.MPU6050_RA_USER_CTRL,
+                       C.MPU6050_USERCTRL_DMP_EN_BIT, bit)
+
+    def reset_DMP(self):
+        self.write_bit(C.MPU6050_RA_USER_CTRL,
+                       C.MPU6050_USERCTRL_DMP_RESET_BIT, True)
+
+    def dmp_initialize(self):
+        # Reset the MPU
+        self.reset()
+        # time.Sleep a bit while resetting
+        time.sleep(50 / 1000)
+        # Disable time.sleep mode
+        self.set_sleep_enabled(0)
+
+        # get MPU hardware revision
+        if self.__debug:
+            print('Selecting user bank 16')
+        self.set_memory_bank(0x10, True, True)
+
+        if self.__debug:
+            print('Selecting memory byte 6')
+        self.set_memory_start_address(0x6)
+
+        if self.__debug:
+            print('Checking hardware revision')
+        HW_revision = self.read_memory_byte()
+        if self.__debug:
+            print('Revision @ user[16][6] = ' + hex(HW_revision))
+
+        if self.__debug:
+            print('Resetting memory bank selection to 0')
+        self.set_memory_bank(0)
+
+        # check OTP bank valid
+        # TODO Find out what OTP is
+        OTP_valid = self.get_OTP_bank_valid()
+        if self.__debug:
+            if OTP_valid:
+                print('OTP bank is valid')
+            else:
+                print('OTP bank is invalid')
+
+        # get X/Y/Z gyro offsets
+        if self.__debug:
+            print('Reading gyro offet TC values')
+        x_g_offset_TC = self.get_x_gyro_offset_TC()
+        y_g_offset_TC = self.get_y_gyro_offset_TC()
+        z_g_offset_TC = self.get_z_gyro_offset_TC()
+        if self.__debug:
+            print("X gyro offset = ", repr(x_g_offset_TC))
+            print("Y gyro offset = ", repr(y_g_offset_TC))
+            print("Z gyro offset = ", repr(z_g_offset_TC))
+
+        # setup weird slave stuff (?)
+        if self.__debug:
+            print('Setting slave 0 address to 0x7F')
+        self.set_slave_address(0, 0x7F)
+        if self.__debug:
+            print('Disabling I2C Master mode')
+        self.set_I2C_master_mode_enabled(False)
+        if self.__debug:
+            print('Setting slave 0 address to 0x68 (self)')
+        self.set_slave_address(0, 0x68)
+        if self.__debug:
+            print('Resetting I2C Master control')
+        self.reset_I2C_master()
+        # Wait a bit for the device to register the changes
+        time.sleep(20 / 1000)
+
+        # load DMP code into memory banks
+        if self.__debug:
+            print('Writing DMP code to MPU memory banks ' +
+                  repr(C.MPU6050_DMP_CODE_SIZE) + ' bytes')
+        if self.write_prog_memory_block(C.dmpMemory, C.MPU6050_DMP_CODE_SIZE):
+            # TODO Check if we've actually verified this
+            if self.__debug:
+                print('Success! DMP code written and verified')
+
+            # Write DMP configuration
+            if self.__debug:
+                print('Writing DMP configuration to MPU memory banks ' +
+                      repr(C.MPU6050_DMP_CONFIG_SIZE) + ' bytes in config')
+            if self.write_prog_dmp_configuration(C.dmpConfig,
+                                                 C.MPU6050_DMP_CONFIG_SIZE):
+                if self.__debug:
+                    print('Success! DMP configuration written and verified.')
+                    print('Setting clock source to Z gyro')
+                self.set_clock_source(C.MPU6050_CLOCK_PLL_ZGYRO)
+
+                if self.__debug:
+                    print('Setting DMP and FIFO_OFLOW interrupts enabled')
+                self.set_int_enable(0x12)
+
+                if self.__debug:
+                    print('Setting sample rate to 200Hz')
+                self.set_rate(4)
+
+                if self.__debug:
+                    print('Setting external frame sync to TEMP_OUT_L[0]')
+                self.set_external_frame_sync(C.MPU6050_EXT_SYNC_TEMP_OUT_L)
+
+                if self.__debug:
+                    print('Setting DLPF bandwidth to 42Hz')
+                self.set_DLF_mode(C.MPU6050_DLPF_BW_42)
+
+                if self.__debug:
+                    print('Setting gyro sensitivity to +/- 2000 deg/sec')
+                self.set_full_scale_gyro_range(C.MPU6050_GYRO_FS_2000)
+
+                if self.__debug:
+                    print('Setting DMP configuration bytes (function unknown)')
+                self.set_DMP_config_1(0x03)
+                self.set_DMP_config_2(0x00)
+
+                if self.__debug:
+                    print('Clearing OTP Bank flag')
+                self.set_OTP_bank_valid(False)
+
+                if self.__debug:
+                    print('Setting X/Y/Z gyro offset TCs to previous values')
+                self.set_x_gyro_offset_TC(x_g_offset_TC)
+                self.set_y_gyro_offset_TC(y_g_offset_TC)
+                self.set_z_gyro_offset_TC(z_g_offset_TC)
+
+                # Uncomment this to zero offsets when dmp_initialize is called
+                # if self.__debug:
+                #    print('Setting X/Y/Z gyro user offsets to zero')
+                # self.set_x_gyro_offset(0)
+                # self.set_y_gyro_offset(0)
+                # self.set_z_gyro_offset(0)
+
+                if self.__debug:
+                    print('Writing final memory update 1/7 (function unknown)')
+                pos = 0
+                j = 0
+                dmp_update = [0] * 16
+                while (j < 4) or (j < dmp_update[2] + 3):
+                    dmp_update[j] = C.dmpUpdates[pos]
+                    pos += 1
+                    j += 1
+                # Write as block from pos 3
+                self.write_memory_block(dmp_update[3:], dmp_update[2],
+                                        dmp_update[0], dmp_update[1], True)
+
+                if self.__debug:
+                    print('Writing final memory update 2/7 (function unknown)')
+                j = 0
+                while (j < 4) or (j < dmp_update[2] + 3):
+                    dmp_update[j] = C.dmpUpdates[pos]
+                    pos += 1
+                    j += 1
+                # Write as block from pos 3
+                self.write_memory_block(dmp_update[3:], dmp_update[2],
+                                        dmp_update[0], dmp_update[1], True)
+
+                if self.__debug:
+                    print('Resetting FIFO')
+                self.reset_FIFO()
+
+                if self.__debug:
+                    print('Reading FIFO count')
+                FIFO_count = self.get_FIFO_count()
+
+                if self.__debug:
+                    print('FIFO count: ' + repr(FIFO_count))
+
+                if self.__debug:
+                    print('Getting FIFO buffer')
+                FIFO_buffer = [0] * 128
+                FIFO_buffer = self.get_FIFO_bytes(FIFO_count)
+
+                if self.__debug:
+                    print('Setting motion detection threshold to 2')
+                self.set_motion_detection_threshold(2)
+
+                if self.__debug:
+                    print('Setting zero-motion detection threshold to 156')
+                self.set_zero_motion_detection_threshold(156)
+
+                if self.__debug:
+                    print('Setting motion detection duration to 80')
+                self.set_motion_detection_duration(80)
+
+                if self.__debug:
+                    print('Setting zero-motion detection duration to 0')
+                self.set_zero_motion_detection_duration(0)
+
+                if self.__debug:
+                    print('Resetting FIFO')
+                self.reset_FIFO()
+
+                if self.__debug:
+                    print('Enabling FIFO')
+                self.set_FIFO_enabled(True)
+
+                if self.__debug:
+                    print('Enabling DMP')
+                self.set_DMP_enabled(True)
+
+                if self.__debug:
+                    print('Resetting DMP')
+                self.reset_DMP()
+
+                if self.__debug:
+                    print('Writing final memory update 3/7 (function unknown)')
+                j = 0
+                while (j < 4) or (j < dmp_update[2] + 3):
+                    dmp_update[j] = C.dmpUpdates[pos]
+                    pos += 1
+                    j += 1
+                # Write as block from pos 3
+                self.write_memory_block(dmp_update[3:], dmp_update[2],
+                                        dmp_update[0], dmp_update[1], True)
+
+                if self.__debug:
+                    print('Writing final memory update 4/7 (function unknown)')
+                j = 0
+                while (j < 4) or (j < dmp_update[2] + 3):
+                    dmp_update[j] = C.dmpUpdates[pos]
+                    pos += 1
+                    j += 1
+                # Write as block from pos 3
+                self.write_memory_block(dmp_update[3:], dmp_update[2],
+                                        dmp_update[0], dmp_update[1], True)
+
+                if self.__debug:
+                    print('Writing final memory update 5/7 (function unknown)')
+                j = 0
+                while (j < 4) or (j < dmp_update[2] + 3):
+                    dmp_update[j] = C.dmpUpdates[pos]
+                    pos += 1
+                    j += 1
+                # Write as block from pos 3
+                self.write_memory_block(dmp_update[3:], dmp_update[2],
+                                        dmp_update[0], dmp_update[1], True)
+
+                if self.__debug:
+                    print('Waiting for FIFO count > 2')
+                FIFO_count = self.get_FIFO_count()
+                while FIFO_count < 3:
+                    FIFO_count = self.get_FIFO_count()
+
+                if self.__debug:
+                    print('Current FIFO count = ' + repr(FIFO_count))
+                    print('Reading FIFO data')
+                FIFO_buffer = self.get_FIFO_bytes(FIFO_count)
+
+                if self.__debug:
+                    print('Reading interrupt status')
+                MPU_int_status = self.get_int_status()
+
+                if self.__debug:
+                    print('Current interrupt status = ' + hex(MPU_int_status))
+                    print('Writing final memory update 6/7 (function unknown)')
+                j = 0
+                while (j < 4) or (j < dmp_update[2] + 3):
+                    dmp_update[j] = C.dmpUpdates[pos]
+                    pos += 1
+                    j += 1
+                # Write as block from pos 3
+                self.write_memory_block(dmp_update[3:], dmp_update[2],
+                                        dmp_update[0], dmp_update[1], True)
+
+                if self.__debug:
+                    print('Waiting for FIFO count > 2')
+                FIFO_count = self.get_FIFO_count()
+                while FIFO_count < 3:
+                    FIFO_count = self.get_FIFO_count()
+
+                if self.__debug:
+                    print('Current FIFO count = ' + repr(FIFO_count))
+                    print('Reading FIFO count')
+                FIFO_buffer = self.get_FIFO_bytes(FIFO_count)
+
+                if self.__debug:
+                    print('Reading interrupt status')
+                MPU_int_status = self.get_int_status()
+
+                if self.__debug:
+                    print('Current interrupt status = ' + hex(MPU_int_status))
+                    print('Writing final memory update 7/7 (function unknown)')
+                j = 0
+                while (j < 4) or (j < dmp_update[2] + 3):
+                    dmp_update[j] = C.dmpUpdates[pos]
+                    pos += 1
+                    j += 1
+                # Write as block from pos 3
+                self.write_memory_block(dmp_update[3:], dmp_update[2],
+                                        dmp_update[0], dmp_update[1], True)
+
+                if self.__debug:
+                    print('DMP is good to go! Finally.')
+                    print('Disabling DMP (you turn it on later)')
+                self.set_DMP_enabled(False)
+
+                if self.__debug:
+                    print('Setting up internal 42 byte DMP packet buffer')
+                self.__DMP_packet_size = 42
+
+                if self.__debug:
+                    print(
+                        'Resetting FIFO and clearing INT status one last time')
+                self.reset_FIFO()
+                self.get_int_status()
+
+            else:
+                if self.__debug:
+                    print('Configuration block loading failed')
+                return 2
+
+        else:
+            if self.__debug:
+                print('Main binary block loading failed')
+            return 1
+
+        if self.__debug:
+            print('DMP initialization was successful')
+        return 0
+
+    # Acceleration and gyro offset setters and getters
+    def set_x_accel_offset(self, a_offset):
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XA_OFFS_H,
+                                   ctypes.c_int8(a_offset >> 8).value)
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XA_OFFS_L_TC,
+                                   ctypes.c_int8(a_offset).value)
+
+    def set_y_accel_offset(self, a_offset):
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YA_OFFS_H,
+                                   ctypes.c_int8(a_offset >> 8).value)
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YA_OFFS_L_TC,
+                                   ctypes.c_int8(a_offset).value)
+
+    def set_z_accel_offset(self, a_offset):
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZA_OFFS_H,
+                                   ctypes.c_int8(a_offset >> 8).value)
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZA_OFFS_L_TC,
+                                   ctypes.c_int8(a_offset).value)
+
+    def set_x_gyro_offset(self, a_offset):
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XG_OFFS_USRH,
+                                   ctypes.c_int8(a_offset >> 8).value)
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XG_OFFS_USRL,
+                                   ctypes.c_int8(a_offset).value)
+
+    def set_y_gyro_offset(self, a_offset):
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YG_OFFS_USRH,
+                                   ctypes.c_int8(a_offset >> 8).value)
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YG_OFFS_USRL,
+                                   ctypes.c_int8(a_offset).value)
+
+    def set_z_gyro_offset(self, a_offset):
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZG_OFFS_USRH,
+                                   ctypes.c_int8(a_offset >> 8).value)
+        self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZG_OFFS_USRL,
+                                   ctypes.c_int8(a_offset).value)
+
+    # Main interfacing functions to get raw data from MPU
+    def get_acceleration(self):
+        raw_data = self.__bus.read_i2c_block_data(self.__dev_id,
+                                                  C.MPU6050_RA_ACCEL_XOUT_H, 6)
+        accel = [0] * 3
+        accel[0] = ctypes.c_int16(raw_data[0] << 8 | raw_data[1]).value
+        accel[1] = ctypes.c_int16(raw_data[2] << 8 | raw_data[3]).value
+        accel[2] = ctypes.c_int16(raw_data[4] << 8 | raw_data[5]).value
+        return accel
+
+    def get_rotation(self):
+        raw_data = self.__bus.read_i2c_block_data(self.__dev_id,
+                                                  C.MPU6050_RA_GYRO_XOUT_H, 6)
+        gyro = [0] * 3
+        gyro[0] = ctypes.c_int16(raw_data[0] << 8 | raw_data[1]).value
+        gyro[1] = ctypes.c_int16(raw_data[2] << 8 | raw_data[3]).value
+        gyro[2] = ctypes.c_int16(raw_data[4] << 8 | raw_data[5]).value
+        return gyro
+
+    # Interfacing functions to get data from FIFO buffer
+    def DMP_get_FIFO_packet_size(self):
+        return self.__DMP_packet_size
+
+    def reset_FIFO(self):
+        self.write_bit(C.MPU6050_RA_USER_CTRL,
+                       C.MPU6050_USERCTRL_FIFO_RESET_BIT, True)
+
+    def get_FIFO_count(self):
+        data = [0] * 2
+        data = self.read_bytes(data, C.MPU6050_RA_FIFO_COUNTH, 2)
+        return (data[0] << 8) | data[1]
+
+    def get_FIFO_bytes(self, a_FIFO_count):
+        return_list = list()
+        for index in range(0, a_FIFO_count):
+            return_list.append(
+                self.__bus.read_byte_data(self.__dev_id,
+                                          C.MPU6050_RA_FIFO_R_W))
+        return return_list
+
+    def get_int_status(self):
+        return self.__bus.read_byte_data(self.__dev_id,
+                                         C.MPU6050_RA_INT_STATUS)
+
+    # Data retrieval from received FIFO buffer
+    def DMP_get_quaternion_int16(self, a_FIFO_buffer):
+        w = ctypes.c_int16((a_FIFO_buffer[0] << 8) | a_FIFO_buffer[1]).value
+        x = ctypes.c_int16((a_FIFO_buffer[4] << 8) | a_FIFO_buffer[5]).value
+        y = ctypes.c_int16((a_FIFO_buffer[8] << 8) | a_FIFO_buffer[9]).value
+        z = ctypes.c_int16((a_FIFO_buffer[12] << 8) | a_FIFO_buffer[13]).value
+        return Q(w, x, y, z)
+
+    def DMP_get_quaternion(self, a_FIFO_buffer):
+        quat = self.DMP_get_quaternion_int16(a_FIFO_buffer)
+        w = quat.w / 16384.0
+        x = quat.x / 16384.0
+        y = quat.y / 16384.0
+        z = quat.z / 16384.0
+        return Q(w, x, y, z)
+
+    def DMP_get_acceleration_int16(self, a_FIFO_buffer):
+        x = ctypes.c_int16(a_FIFO_buffer[28] << 8 | a_FIFO_buffer[29]).value
+        y = ctypes.c_int16(a_FIFO_buffer[32] << 8 | a_FIFO_buffer[33]).value
+        z = ctypes.c_int16(a_FIFO_buffer[36] << 8 | a_FIFO_buffer[37]).value
+        return V(x, y, z)
+
+    def DMP_get_gravity(self, a_quat):
+        x = 2.0 * (a_quat.x * a_quat.z - a_quat.w * a_quat.y)
+        y = 2.0 * (a_quat.w * a_quat.x + a_quat.y * a_quat.z)
+        z = 1.0 * (a_quat.w * a_quat.w - a_quat.x * a_quat.x -
+                   a_quat.y * a_quat.y + a_quat.z * a_quat.z)
+        return V(x, y, z)
+
+    def DMP_get_linear_accel_int16(self, a_v_raw, a_grav):
+        x = ctypes.c_int16(a_v_raw.x - (a_grav.x*8192)).value
+        y = ctypes.c_int16(a_v_raw.y - (a_grav.y*8192)).value
+        y = ctypes.c_int16(a_v_raw.y - (a_grav.y*8192)).value
+        return V(x, y, z)
+
+    def DMP_get_euler(self, a_quat):
+        psi = math.atan2(2*a_quat.x*a_quat.y - 2*a_quat.w*a_quat.z,
+                         2*a_quat.w*a_quat.w + 2*a_quat.x*a_quat.x - 1)
+        theta = -asin(2*a_quat.x*a_quat.z + 2*a_quat.w*a_quat.y)
+        phi = math.atan2(2*a_quat.y*a_quat.z - 2*a_quat.w*a_quat.x,
+                         2*a_quat.w*a_quat.w + 2*a_quat.z*a_quat.z - 1)
+        return V(psi, theta, phi)
+
+    def DMP_get_roll_pitch_yaw(self, a_quat, a_grav_vect):
+        # roll: (tilt left/right, about X axis)
+        roll = math.atan(a_grav_vect.y /
+                         math.sqrt(a_grav_vect.x*a_grav_vect.x +
+                              a_grav_vect.z*a_grav_vect.z))
+        # pitch: (nose up/down, about Y axis)
+        pitch = math.atan(a_grav_vect.x /
+                          math.sqrt(a_grav_vect.y*a_grav_vect.y +
+                               a_grav_vect.z*a_grav_vect.z))
+        # yaw: (about Z axis)
+        yaw = math.atan2(2*a_quat.x*a_quat.y - 2*a_quat.w*a_quat.z,
+                         2*a_quat.w*a_quat.w + 2*a_quat.x*a_quat.x - 1)
+        return V(roll, pitch, yaw)
+
+    def DMP_get_euler_roll_pitch_yaw(self, a_quat, a_grav_vect):
+        rad_ypr = self.DMP_get_roll_pitch_yaw(a_quat, a_grav_vect)
+        roll = rad_ypr.x * (180.0/math.pi)
+        pitch = rad_ypr.y * (180.0/math.pi)
+        yaw = rad_ypr.z * (180.0/math.pi)
+        return V(roll, pitch, yaw)
+
+    def DMP_get_linear_accel(self, a_vector_raw, a_vect_grav):
+        x = a_vector_raw.x - a_vect_grav.x*8192
+        y = a_vector_raw.y - a_vect_grav.y*8192
+        z = a_vector_raw.z - a_vect_grav.z*8192
+        return V(x, y, z)
+
+
+class MPU6050IRQHandler:
+    __mpu = MPU6050
+    __FIFO_buffer = list()
+    __count = 0
+    __packet_size = None
+    __detected_error = False
+    __logging = False
+    __log_file = None
+    __csv_writer = None
+    __start_time = None
+    __debug = None
+
+    # def __init__(self, a_i2c_bus, a_device_address, a_x_accel_offset,
+    #             a_y_accel_offset, a_z_accel_offset, a_x_gyro_offset,
+    #             a_y_gyro_offset, a_z_gyro_offset, a_enable_debug_output):
+    #    self.__mpu = MPU6050(a_i2c_bus, a_device_address, a_x_accel_offset,
+    #                         a_y_accel_offset, a_z_accel_offset,
+    #                         a_x_gyro_offset, a_y_gyro_offset, a_z_gyro_offset,
+    #                         a_enable_debug_output)
+    def __init__(self, a_mpu, a_logging=False, a_log_file='log.csv',
+                 a_debug=False):
+        self.__mpu = a_mpu
+        self.__FIFO_buffer = [0]*64
+        self.__mpu.dmp_initialize()
+        self.__mpu.set_DMP_enabled(True)
+        self.__packet_size = self.__mpu.DMP_get_FIFO_packet_size()
+        mpu_int_status = self.__mpu.get_int_status()
+        if a_logging:
+            self.__start_time = time.clock()
+            self.__logging = True
+            self.__log_file = open(a_log_file, 'ab')
+            self.__csv_writer = csv.writer(self.__log_file, delimiter=',',
+                                           quotechar='|',
+                                           quoting=csv.QUOTE_MINIMAL)
+        self.__debug = a_debug
+
+    def action(self, channel):
+        if self.__detected_error:
+            # Clear FIFO and reset MPU
+            mpu_int_status = self.__mpu.get_int_status()
+            self.__mpu.reset_FIFO()
+            self.__detected_error = False
+            return
+
+        try:
+            FIFO_count = self.__mpu.get_FIFO_count()
+            mpu_int_status = self.__mpu.get_int_status()
+        except:
+            self.__detected_error = True
+            return
+
+        # If overflow is detected by status or fifo count we want to reset
+        if (FIFO_count == 1024) or (mpu_int_status & 0x10):
+            try:
+                self.__mpu.reset_FIFO()
+            except:
+                self.__detected_error = True
+                return
+
+        elif (mpu_int_status & 0x02):
+            # Wait until packet_size number of bytes are ready for reading,
+            # default is 42 bytes
+            while FIFO_count < self.__packet_size:
+                try:
+                    FIFO_count = self.__mpu.get_FIFO_count()
+                except:
+                    self.__detected_error = True
+                    return
+
+            while FIFO_count > self.__packet_size:
+
+                try:
+                    self.__FIFO_buffer = \
+                        self.__mpu.get_FIFO_bytes(self.__packet_size)
+                except:
+                    self.__detected_error = True
+                    return
+                accel = \
+                    self.__mpu.DMP_get_acceleration_int16(self.__FIFO_buffer)
+                quat = self.__mpu.DMP_get_quaternion_int16(self.__FIFO_buffer)
+                grav = self.__mpu.DMP_get_gravity(quat)
+                roll_pitch_yaw = self.__mpu.DMP_get_euler_roll_pitch_yaw(quat,
+                                                                         grav)
+                if self.__logging:
+                    delta_time = time.clock() - self.__start_time
+                    data_concat = ['%.4f' % delta_time] + \
+                        [accel.x, accel.y, accel.z] + \
+                        ['%.3f' % roll_pitch_yaw.x,
+                         '%.3f' % roll_pitch_yaw.y,
+                         '%.3f' % roll_pitch_yaw.z]
+                    self.__csv_writer.writerow(data_concat)
+
+                if (self.__debug) and (self.__count % 100 == 0):
+                    print('roll: ' + str(roll_pitch_yaw.x))
+                    print('pitch: ' + str(roll_pitch_yaw.y))
+                    print('yaw: ' + str(roll_pitch_yaw.z))
+                self.__count += 1
+                FIFO_count -= self.__packet_size
diff --git a/Code/Python_Code/25.1.1_MPU6050/MPU6050RAW.py b/Code/Python_Code/25.1.1_MPU6050/MPU6050RAW.py
new file mode 100644
index 0000000..5dd5ae0
--- /dev/null
+++ b/Code/Python_Code/25.1.1_MPU6050/MPU6050RAW.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+########################################################################
+# Filename    : MPU6050RAW.py
+# Description : Read the Raw data of MPU6050.
+# Author      : freenove
+# modification: 2016/07/18
+########################################################################
+import MPU6050 
+import time
+
+mpu = MPU6050.MPU6050()     #instantiate a MPU6050 class object
+accel = [0]*3               #store accelerometer data
+gyro = [0]*3                #store gyroscope data
+def setup():
+    mpu.dmp_initialize()    #initialize MPU6050
+    
+def loop():
+    while(True):
+        accel = mpu.get_acceleration()      #get accelerometer data
+        gyro = mpu.get_rotation()           #get gyroscope data
+        print"a/g:%d\t%d\t%d\t%d\t%d\t%d "%(accel[0],accel[1],accel[2],gyro[0],gyro[1],gyro[2])
+        print"a/g:%.2f g\t%.2f g\t%.2f g\t%.2f d/s\t%.2f d/s\t%.2f d/s"%(accel[0]/16384.0,accel[1]/16384.0,
+            accel[2]/16384.0,gyro[0]/131.0,gyro[1]/131.0,gyro[2]/131.0)
+        time.sleep(0.1)
+        
+if __name__ == '__main__':     # Program start from here
+    print"Program is starting ... "
+    setup()
+    try:
+        loop()
+    except KeyboardInterrupt:  # When 'Ctrl+C' is pressed,the program will exit.
+        pass
+
diff --git a/Code/Python_Code/25.1.1_MPU6050/MPU6050_cal.py b/Code/Python_Code/25.1.1_MPU6050/MPU6050_cal.py
new file mode 100644
index 0000000..e73f0ed
--- /dev/null
+++ b/Code/Python_Code/25.1.1_MPU6050/MPU6050_cal.py
@@ -0,0 +1,186 @@
+from MPU6050 import MPU6050
+from SimplePID import SimplePID
+
+
+def avg_from_array(a_array):
+    sum = 0.0
+    for index in range(0, len(a_array)):
+        sum += a_array[index]
+
+    return sum/len(a_array)
+
+
+i2c_bus = 1
+device_address = 0x68
+# The offsets are different for each device and should be changed
+# accordingly using a calibration procedure
+x_accel_offset = 0
+y_accel_offset = 0
+z_accel_offset =0
+x_gyro_offset = 0
+y_gyro_offset = 0
+z_gyro_offset = 0
+enable_debug_output = True
+
+mpu = MPU6050(i2c_bus, device_address, x_accel_offset, y_accel_offset,
+              z_accel_offset, x_gyro_offset, y_gyro_offset, z_gyro_offset,
+              enable_debug_output)
+
+kp = 0.03125
+ki = 0.25
+kd = 0
+
+pidax = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True)
+piday = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True)
+pidaz = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True)
+pidgx = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True)
+pidgy = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True)
+pidgz = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True)
+
+accel_reading = mpu.get_acceleration()
+
+x_accel_reading = accel_reading[0]
+y_accel_reading = accel_reading[1]
+z_accel_reading = accel_reading[2]
+
+x_accel_avg = [0]*100
+y_accel_avg = [0]*100
+z_accel_avg = [0]*100
+
+x_accel_offset_avg = [0]*100
+y_accel_offset_avg = [0]*100
+z_accel_offset_avg = [0]*100
+
+axindex = 0
+ayindex = 0
+azindex = 0
+
+gyro_reading = mpu.get_rotation()
+
+x_gyro_reading = gyro_reading[0]
+y_gyro_reading = gyro_reading[1]
+z_gyro_reading = gyro_reading[2]
+
+x_gyro_avg = [0]*100
+y_gyro_avg = [0]*100
+z_gyro_avg = [0]*100
+
+x_gyro_offset_avg = [0]*100
+y_gyro_offset_avg = [0]*100
+z_gyro_offset_avg = [0]*100
+
+gxindex = 0
+gyindex = 0
+gzindex = 0
+
+try:
+    while True:
+        accel_reading = mpu.get_acceleration()
+        x_accel_reading = accel_reading[0]
+        y_accel_reading = accel_reading[1]
+        z_accel_reading = accel_reading[2]
+
+        gyro_reading = mpu.get_rotation()
+        x_gyro_reading = gyro_reading[0]
+        y_gyro_reading = gyro_reading[1]
+        z_gyro_reading = gyro_reading[2]
+
+        if pidax.check_time():
+            x_accel_offset = pidax.get_output_value(x_accel_reading)
+
+            mpu.set_x_accel_offset(int(x_accel_offset))
+
+            x_accel_avg[axindex] = x_accel_reading
+            x_accel_offset_avg[axindex] = x_accel_offset
+
+            axindex += 1
+            if axindex == len(x_accel_avg):
+                axindex = 0
+                print('x_avg_read: ' +
+                      str(avg_from_array(x_accel_avg)) +
+                      ' x_avg_offset: ' +
+                      str(avg_from_array(x_accel_offset_avg)))
+                print('y_avg_read: ' +
+                      str(avg_from_array(y_accel_avg)) +
+                      ' y_avg_offset: ' +
+                      str(avg_from_array(y_accel_offset_avg)))
+                print('z_avg_read: ' +
+                      str(avg_from_array(z_accel_avg)) +
+                      ' z_avg_offset: ' +
+                      str(avg_from_array(z_accel_offset_avg)))
+
+        if piday.check_time():
+            y_accel_offset = piday.get_output_value(y_accel_reading)
+
+            mpu.set_y_accel_offset(int(y_accel_offset))
+
+            y_accel_avg[ayindex] = y_accel_reading
+            y_accel_offset_avg[ayindex] = y_accel_offset
+
+            ayindex += 1
+            if ayindex == len(y_accel_avg):
+                ayindex = 0
+
+        if pidaz.check_time():
+            z_accel_offset = pidaz.get_output_value(z_accel_reading)
+
+            mpu.set_z_accel_offset(int(z_accel_offset))
+
+            z_accel_avg[azindex] = z_accel_reading
+            z_accel_offset_avg[azindex] = z_accel_offset
+
+            azindex += 1
+            if azindex == len(z_accel_avg):
+                azindex = 0
+
+        # Gyro calibration
+        if pidgx.check_time():
+            x_gyro_offset = pidgx.get_output_value(x_gyro_reading)
+
+            mpu.set_x_gyro_offset(int(x_gyro_offset))
+
+            x_gyro_avg[gxindex] = x_gyro_reading
+            x_gyro_offset_avg[gxindex] = x_gyro_offset
+
+            gxindex += 1
+            if gxindex == len(x_gyro_avg):
+                gxindex = 0
+                print('x_avg_read: ' +
+                      str(avg_from_array(x_gyro_avg)) +
+                      ' x_avg_offset: ' +
+                      str(avg_from_array(x_gyro_offset_avg)))
+                print('y_avg_read: ' +
+                      str(avg_from_array(y_gyro_avg)) +
+                      ' y_avg_offset: ' +
+                      str(avg_from_array(y_gyro_offset_avg)))
+                print('z_avg_read: ' +
+                      str(avg_from_array(z_gyro_avg)) +
+                      ' z_avg_offset: ' +
+                      str(avg_from_array(z_gyro_offset_avg)))
+
+        if pidgy.check_time():
+            y_gyro_offset = pidgy.get_output_value(y_gyro_reading)
+
+            mpu.set_y_gyro_offset(int(y_gyro_offset))
+
+            y_gyro_avg[gyindex] = y_gyro_reading
+            y_gyro_offset_avg[gyindex] = y_gyro_offset
+
+            gyindex += 1
+            if gyindex == len(y_gyro_avg):
+                gyindex = 0
+
+        if pidgz.check_time():
+            z_gyro_offset = pidgz.get_output_value(z_gyro_reading)
+
+            mpu.set_z_gyro_offset(int(z_gyro_offset))
+
+            z_gyro_avg[gzindex] = z_gyro_reading
+            z_gyro_offset_avg[gzindex] = z_gyro_offset
+
+            gzindex += 1
+            if gzindex == len(z_gyro_avg):
+                gzindex = 0
+
+except KeyboardInterrupt:
+    pass
diff --git a/Code/Python_Code/25.1.1_MPU6050/MPUConstants.py b/Code/Python_Code/25.1.1_MPU6050/MPUConstants.py
new file mode 100644
index 0000000..0387993
--- /dev/null
+++ b/Code/Python_Code/25.1.1_MPU6050/MPUConstants.py
@@ -0,0 +1,755 @@
+__author__ = 'Geir Istad'
+
+'''
+MPU6050 Python I2C Class
+Copyright (c) 2015 Geir Istad
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+Code based on I2Cdev library collection - MPU6050 I2C device class
+by Jeff Rowberg 
+============================================
+I2Cdev device library code is placed under the MIT license
+Copyright (c) 2012 Jeff Rowberg
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+===============================================
+'''
+
+
+class MPUConstants:
+    # From MPU6050.h
+    MPU6050_ADDRESS_AD0_LOW = 0x68  # address pin low (GND), default
+    MPU6050_ADDRESS_AD0_HIGH = 0x69  # address pin high (VCC)
+    MPU6050_DEFAULT_ADDRESS = MPU6050_ADDRESS_AD0_LOW
+
+    # [7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD
+    MPU6050_RA_XG_OFFS_TC = 0x00
+    # [7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD
+    MPU6050_RA_YG_OFFS_TC = 0x01
+    # [7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD
+    MPU6050_RA_ZG_OFFS_TC = 0x02
+    # [7:0] X_FINE_GAIN
+    MPU6050_RA_X_FINE_GAIN = 0x03
+    # [7:0] Y_FINE_GAIN
+    MPU6050_RA_Y_FINE_GAIN = 0x04
+    # [7:0] Z_FINE_GAIN
+    MPU6050_RA_Z_FINE_GAIN = 0x05
+    # [15:0] XA_OFFS
+    MPU6050_RA_XA_OFFS_H = 0x06
+    MPU6050_RA_XA_OFFS_L_TC = 0x07
+    # [15:0] YA_OFFS
+    MPU6050_RA_YA_OFFS_H = 0x08
+    MPU6050_RA_YA_OFFS_L_TC = 0x09
+    # [15:0] ZA_OFFS
+    MPU6050_RA_ZA_OFFS_H = 0x0A
+    MPU6050_RA_ZA_OFFS_L_TC = 0x0B
+    # [15:0] XG_OFFS_USR
+    MPU6050_RA_XG_OFFS_USRH = 0x13
+    MPU6050_RA_XG_OFFS_USRL = 0x14
+    # [15:0] YG_OFFS_USR
+    MPU6050_RA_YG_OFFS_USRH = 0x15
+    MPU6050_RA_YG_OFFS_USRL = 0x16
+    # [15:0] ZG_OFFS_USR
+    MPU6050_RA_ZG_OFFS_USRH = 0x17
+    MPU6050_RA_ZG_OFFS_USRL = 0x18
+    MPU6050_RA_SMPLRT_DIV = 0x19
+    MPU6050_RA_CONFIG = 0x1A
+    MPU6050_RA_GYRO_CONFIG = 0x1B
+    MPU6050_RA_ACCEL_CONFIG = 0x1C
+    MPU6050_RA_FF_THR = 0x1D
+    MPU6050_RA_FF_DUR = 0x1E
+    MPU6050_RA_MOT_THR = 0x1F
+    MPU6050_RA_MOT_DUR = 0x20
+    MPU6050_RA_ZRMOT_THR = 0x21
+    MPU6050_RA_ZRMOT_DUR = 0x22
+    MPU6050_RA_FIFO_EN = 0x23
+    MPU6050_RA_I2C_MST_CTRL = 0x24
+    MPU6050_RA_I2C_SLV0_ADDR = 0x25
+    MPU6050_RA_I2C_SLV0_REG = 0x26
+    MPU6050_RA_I2C_SLV0_CTRL = 0x27
+    MPU6050_RA_I2C_SLV1_ADDR = 0x28
+    MPU6050_RA_I2C_SLV1_REG = 0x29
+    MPU6050_RA_I2C_SLV1_CTRL = 0x2A
+    MPU6050_RA_I2C_SLV2_ADDR = 0x2B
+    MPU6050_RA_I2C_SLV2_REG = 0x2C
+    MPU6050_RA_I2C_SLV2_CTRL = 0x2D
+    MPU6050_RA_I2C_SLV3_ADDR = 0x2E
+    MPU6050_RA_I2C_SLV3_REG = 0x2F
+    MPU6050_RA_I2C_SLV3_CTRL = 0x30
+    MPU6050_RA_I2C_SLV4_ADDR = 0x31
+    MPU6050_RA_I2C_SLV4_REG = 0x32
+    MPU6050_RA_I2C_SLV4_DO = 0x33
+    MPU6050_RA_I2C_SLV4_CTRL = 0x34
+    MPU6050_RA_I2C_SLV4_DI = 0x35
+    MPU6050_RA_I2C_MST_STATUS = 0x36
+    MPU6050_RA_INT_PIN_CFG = 0x37
+    MPU6050_RA_INT_ENABLE = 0x38
+    MPU6050_RA_DMP_INT_STATUS = 0x39
+    MPU6050_RA_INT_STATUS = 0x3A
+    MPU6050_RA_ACCEL_XOUT_H = 0x3B
+    MPU6050_RA_ACCEL_XOUT_L = 0x3C
+    MPU6050_RA_ACCEL_YOUT_H = 0x3D
+    MPU6050_RA_ACCEL_YOUT_L = 0x3E
+    MPU6050_RA_ACCEL_ZOUT_H = 0x3F
+    MPU6050_RA_ACCEL_ZOUT_L = 0x40
+    MPU6050_RA_TEMP_OUT_H = 0x41
+    MPU6050_RA_TEMP_OUT_L = 0x42
+    MPU6050_RA_GYRO_XOUT_H = 0x43
+    MPU6050_RA_GYRO_XOUT_L = 0x44
+    MPU6050_RA_GYRO_YOUT_H = 0x45
+    MPU6050_RA_GYRO_YOUT_L = 0x46
+    MPU6050_RA_GYRO_ZOUT_H = 0x47
+    MPU6050_RA_GYRO_ZOUT_L = 0x48
+    MPU6050_RA_EXT_SENS_DATA_00 = 0x49
+    MPU6050_RA_EXT_SENS_DATA_01 = 0x4A
+    MPU6050_RA_EXT_SENS_DATA_02 = 0x4B
+    MPU6050_RA_EXT_SENS_DATA_03 = 0x4C
+    MPU6050_RA_EXT_SENS_DATA_04 = 0x4D
+    MPU6050_RA_EXT_SENS_DATA_05 = 0x4E
+    MPU6050_RA_EXT_SENS_DATA_06 = 0x4F
+    MPU6050_RA_EXT_SENS_DATA_07 = 0x50
+    MPU6050_RA_EXT_SENS_DATA_08 = 0x51
+    MPU6050_RA_EXT_SENS_DATA_09 = 0x52
+    MPU6050_RA_EXT_SENS_DATA_10 = 0x53
+    MPU6050_RA_EXT_SENS_DATA_11 = 0x54
+    MPU6050_RA_EXT_SENS_DATA_12 = 0x55
+    MPU6050_RA_EXT_SENS_DATA_13 = 0x56
+    MPU6050_RA_EXT_SENS_DATA_14 = 0x57
+    MPU6050_RA_EXT_SENS_DATA_15 = 0x58
+    MPU6050_RA_EXT_SENS_DATA_16 = 0x59
+    MPU6050_RA_EXT_SENS_DATA_17 = 0x5A
+    MPU6050_RA_EXT_SENS_DATA_18 = 0x5B
+    MPU6050_RA_EXT_SENS_DATA_19 = 0x5C
+    MPU6050_RA_EXT_SENS_DATA_20 = 0x5D
+    MPU6050_RA_EXT_SENS_DATA_21 = 0x5E
+    MPU6050_RA_EXT_SENS_DATA_22 = 0x5F
+    MPU6050_RA_EXT_SENS_DATA_23 = 0x60
+    MPU6050_RA_MOT_DETECT_STATUS = 0x61
+    MPU6050_RA_I2C_SLV0_DO = 0x63
+    MPU6050_RA_I2C_SLV1_DO = 0x64
+    MPU6050_RA_I2C_SLV2_DO = 0x65
+    MPU6050_RA_I2C_SLV3_DO = 0x66
+    MPU6050_RA_I2C_MST_DELAY_CTRL = 0x67
+    MPU6050_RA_SIGNAL_PATH_RESET = 0x68
+    MPU6050_RA_MOT_DETECT_CTRL = 0x69
+    MPU6050_RA_USER_CTRL = 0x6A
+    MPU6050_RA_PWR_MGMT_1 = 0x6B
+    MPU6050_RA_PWR_MGMT_2 = 0x6C
+    MPU6050_RA_BANK_SEL = 0x6D
+    MPU6050_RA_MEM_START_ADDR = 0x6E
+    MPU6050_RA_MEM_R_W = 0x6F
+    MPU6050_RA_DMP_CFG_1 = 0x70
+    MPU6050_RA_DMP_CFG_2 = 0x71
+    MPU6050_RA_FIFO_COUNTH = 0x72
+    MPU6050_RA_FIFO_COUNTL = 0x73
+    MPU6050_RA_FIFO_R_W = 0x74
+    MPU6050_RA_WHO_AM_I = 0x75
+
+    MPU6050_TC_PWR_MODE_BIT = 7
+    MPU6050_TC_OFFSET_BIT = 6
+    MPU6050_TC_OFFSET_LENGTH = 6
+    MPU6050_TC_OTP_BNK_VLD_BIT = 0
+
+    MPU6050_VDDIO_LEVEL_VLOGIC = 0
+    MPU6050_VDDIO_LEVEL_VDD = 1
+
+    MPU6050_CFG_EXT_SYNC_SET_BIT = 5
+    MPU6050_CFG_EXT_SYNC_SET_LENGTH = 3
+    MPU6050_CFG_DLPF_CFG_BIT = 2
+    MPU6050_CFG_DLPF_CFG_LENGTH = 3
+
+    MPU6050_EXT_SYNC_DISABLED = 0x0
+    MPU6050_EXT_SYNC_TEMP_OUT_L = 0x1
+    MPU6050_EXT_SYNC_GYRO_XOUT_L = 0x2
+    MPU6050_EXT_SYNC_GYRO_YOUT_L = 0x3
+    MPU6050_EXT_SYNC_GYRO_ZOUT_L = 0x4
+    MPU6050_EXT_SYNC_ACCEL_XOUT_L = 0x5
+    MPU6050_EXT_SYNC_ACCEL_YOUT_L = 0x6
+    MPU6050_EXT_SYNC_ACCEL_ZOUT_L = 0x7
+
+    MPU6050_DLPF_BW_256 = 0x00
+    MPU6050_DLPF_BW_188 = 0x01
+    MPU6050_DLPF_BW_98 = 0x02
+    MPU6050_DLPF_BW_42 = 0x03
+    MPU6050_DLPF_BW_20 = 0x04
+    MPU6050_DLPF_BW_10 = 0x05
+    MPU6050_DLPF_BW_5 = 0x06
+
+    MPU6050_GCONFIG_FS_SEL_BIT = 4
+    MPU6050_GCONFIG_FS_SEL_LENGTH = 2
+
+    MPU6050_GYRO_FS_250 = 0x00
+    MPU6050_GYRO_FS_500 = 0x01
+    MPU6050_GYRO_FS_1000 = 0x02
+    MPU6050_GYRO_FS_2000 = 0x03
+
+    MPU6050_ACONFIG_XA_ST_BIT = 7
+    MPU6050_ACONFIG_YA_ST_BIT = 6
+    MPU6050_ACONFIG_ZA_ST_BIT = 5
+    MPU6050_ACONFIG_AFS_SEL_BIT = 4
+    MPU6050_ACONFIG_AFS_SEL_LENGTH = 2
+    MPU6050_ACONFIG_ACCEL_HPF_BIT = 2
+    MPU6050_ACONFIG_ACCEL_HPF_LENGTH = 3
+
+    MPU6050_ACCEL_FS_2 = 0x00
+    MPU6050_ACCEL_FS_4 = 0x01
+    MPU6050_ACCEL_FS_8 = 0x02
+    MPU6050_ACCEL_FS_16 = 0x03
+
+    MPU6050_DHPF_RESET = 0x00
+    MPU6050_DHPF_5 = 0x01
+    MPU6050_DHPF_2P5 = 0x02
+    MPU6050_DHPF_1P25 = 0x03
+    MPU6050_DHPF_0P63 = 0x04
+    MPU6050_DHPF_HOLD = 0x07
+
+    MPU6050_TEMP_FIFO_EN_BIT = 7
+    MPU6050_XG_FIFO_EN_BIT = 6
+    MPU6050_YG_FIFO_EN_BIT = 5
+    MPU6050_ZG_FIFO_EN_BIT = 4
+    MPU6050_ACCEL_FIFO_EN_BIT = 3
+    MPU6050_SLV2_FIFO_EN_BIT = 2
+    MPU6050_SLV1_FIFO_EN_BIT = 1
+    MPU6050_SLV0_FIFO_EN_BIT = 0
+
+    MPU6050_MULT_MST_EN_BIT = 7
+    MPU6050_WAIT_FOR_ES_BIT = 6
+    MPU6050_SLV_3_FIFO_EN_BIT = 5
+    MPU6050_I2C_MST_P_NSR_BIT = 4
+    MPU6050_I2C_MST_CLK_BIT = 3
+    MPU6050_I2C_MST_CLK_LENGTH = 4
+
+    MPU6050_CLOCK_DIV_348 = 0x0
+    MPU6050_CLOCK_DIV_333 = 0x1
+    MPU6050_CLOCK_DIV_320 = 0x2
+    MPU6050_CLOCK_DIV_308 = 0x3
+    MPU6050_CLOCK_DIV_296 = 0x4
+    MPU6050_CLOCK_DIV_286 = 0x5
+    MPU6050_CLOCK_DIV_276 = 0x6
+    MPU6050_CLOCK_DIV_267 = 0x7
+    MPU6050_CLOCK_DIV_258 = 0x8
+    MPU6050_CLOCK_DIV_500 = 0x9
+    MPU6050_CLOCK_DIV_471 = 0xA
+    MPU6050_CLOCK_DIV_444 = 0xB
+    MPU6050_CLOCK_DIV_421 = 0xC
+    MPU6050_CLOCK_DIV_400 = 0xD
+    MPU6050_CLOCK_DIV_381 = 0xE
+    MPU6050_CLOCK_DIV_364 = 0xF
+
+    MPU6050_I2C_SLV_RW_BIT = 7
+    MPU6050_I2C_SLV_ADDR_BIT = 6
+    MPU6050_I2C_SLV_ADDR_LENGTH = 7
+    MPU6050_I2C_SLV_EN_BIT = 7
+    MPU6050_I2C_SLV_BYTE_SW_BIT = 6
+    MPU6050_I2C_SLV_REG_DIS_BIT = 5
+    MPU6050_I2C_SLV_GRP_BIT = 4
+    MPU6050_I2C_SLV_LEN_BIT = 3
+    MPU6050_I2C_SLV_LEN_LENGTH = 4
+
+    MPU6050_I2C_SLV4_RW_BIT = 7
+    MPU6050_I2C_SLV4_ADDR_BIT = 6
+    MPU6050_I2C_SLV4_ADDR_LENGTH = 7
+    MPU6050_I2C_SLV4_EN_BIT = 7
+    MPU6050_I2C_SLV4_INT_EN_BIT = 6
+    MPU6050_I2C_SLV4_REG_DIS_BIT = 5
+    MPU6050_I2C_SLV4_MST_DLY_BIT = 4
+    MPU6050_I2C_SLV4_MST_DLY_LENGTH = 5
+
+    MPU6050_MST_PASS_THROUGH_BIT = 7
+    MPU6050_MST_I2C_SLV4_DONE_BIT = 6
+    MPU6050_MST_I2C_LOST_ARB_BIT = 5
+    MPU6050_MST_I2C_SLV4_NACK_BIT = 4
+    MPU6050_MST_I2C_SLV3_NACK_BIT = 3
+    MPU6050_MST_I2C_SLV2_NACK_BIT = 2
+    MPU6050_MST_I2C_SLV1_NACK_BIT = 1
+    MPU6050_MST_I2C_SLV0_NACK_BIT = 0
+
+    MPU6050_INTCFG_INT_LEVEL_BIT = 7
+    MPU6050_INTCFG_INT_OPEN_BIT = 6
+    MPU6050_INTCFG_LATCH_INT_EN_BIT = 5
+    MPU6050_INTCFG_INT_RD_CLEAR_BIT = 4
+    MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT = 3
+    MPU6050_INTCFG_FSYNC_INT_EN_BIT = 2
+    MPU6050_INTCFG_I2C_BYPASS_EN_BIT = 1
+    MPU6050_INTCFG_CLKOUT_EN_BIT = 0
+
+    MPU6050_INTMODE_ACTIVEHIGH = 0x00
+    MPU6050_INTMODE_ACTIVELOW = 0x01
+
+    MPU6050_INTDRV_PUSHPULL = 0x00
+    MPU6050_INTDRV_OPENDRAIN = 0x01
+
+    MPU6050_INTLATCH_50USPULSE = 0x00
+    MPU6050_INTLATCH_WAITCLEAR = 0x01
+
+    MPU6050_INTCLEAR_STATUSREAD = 0x00
+    MPU6050_INTCLEAR_ANYREAD = 0x01
+
+    MPU6050_INTERRUPT_FF_BIT = 7
+    MPU6050_INTERRUPT_MOT_BIT = 6
+    MPU6050_INTERRUPT_ZMOT_BIT = 5
+    MPU6050_INTERRUPT_FIFO_OFLOW_BIT = 4
+    MPU6050_INTERRUPT_I2C_MST_INT_BIT = 3
+    MPU6050_INTERRUPT_PLL_RDY_INT_BIT = 2
+    MPU6050_INTERRUPT_DMP_INT_BIT = 1
+    MPU6050_INTERRUPT_DATA_RDY_BIT = 0
+
+    # TODO: figure out what these actually do
+    # UMPL source code is not very obivous
+    MPU6050_DMPINT_5_BIT = 5
+    MPU6050_DMPINT_4_BIT = 4
+    MPU6050_DMPINT_3_BIT = 3
+    MPU6050_DMPINT_2_BIT = 2
+    MPU6050_DMPINT_1_BIT = 1
+    MPU6050_DMPINT_0_BIT = 0
+
+    MPU6050_MOTION_MOT_XNEG_BIT = 7
+    MPU6050_MOTION_MOT_XPOS_BIT = 6
+    MPU6050_MOTION_MOT_YNEG_BIT = 5
+    MPU6050_MOTION_MOT_YPOS_BIT = 4
+    MPU6050_MOTION_MOT_ZNEG_BIT = 3
+    MPU6050_MOTION_MOT_ZPOS_BIT = 2
+    MPU6050_MOTION_MOT_ZRMOT_BIT = 0
+
+    MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT = 7
+    MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT = 4
+    MPU6050_DELAYCTRL_I2C_SLV3_DLY_EN_BIT = 3
+    MPU6050_DELAYCTRL_I2C_SLV2_DLY_EN_BIT = 2
+    MPU6050_DELAYCTRL_I2C_SLV1_DLY_EN_BIT = 1
+    MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT = 0
+
+    MPU6050_PATHRESET_GYRO_RESET_BIT = 2
+    MPU6050_PATHRESET_ACCEL_RESET_BIT = 1
+    MPU6050_PATHRESET_TEMP_RESET_BIT = 0
+
+    MPU6050_DETECT_ACCEL_ON_DELAY_BIT = 5
+    MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH = 2
+    MPU6050_DETECT_FF_COUNT_BIT = 3
+    MPU6050_DETECT_FF_COUNT_LENGTH = 2
+    MPU6050_DETECT_MOT_COUNT_BIT = 1
+    MPU6050_DETECT_MOT_COUNT_LENGTH = 2
+
+    MPU6050_DETECT_DECREMENT_RESET = 0x0
+    MPU6050_DETECT_DECREMENT_1 = 0x1
+    MPU6050_DETECT_DECREMENT_2 = 0x2
+    MPU6050_DETECT_DECREMENT_4 = 0x3
+
+    MPU6050_USERCTRL_DMP_EN_BIT = 7
+    MPU6050_USERCTRL_FIFO_EN_BIT = 6
+    MPU6050_USERCTRL_I2C_MST_EN_BIT = 5
+    MPU6050_USERCTRL_I2C_IF_DIS_BIT = 4
+    MPU6050_USERCTRL_DMP_RESET_BIT = 3
+    MPU6050_USERCTRL_FIFO_RESET_BIT = 2
+    MPU6050_USERCTRL_I2C_MST_RESET_BIT = 1
+    MPU6050_USERCTRL_SIG_COND_RESET_BIT = 0
+
+    MPU6050_PWR1_DEVICE_RESET_BIT = 7
+    MPU6050_PWR1_SLEEP_BIT = 6
+    MPU6050_PWR1_CYCLE_BIT = 5
+    MPU6050_PWR1_TEMP_DIS_BIT = 3
+    MPU6050_PWR1_CLKSEL_BIT = 2
+    MPU6050_PWR1_CLKSEL_LENGTH = 3
+
+    MPU6050_CLOCK_INTERNAL = 0x00
+    MPU6050_CLOCK_PLL_XGYRO = 0x01
+    MPU6050_CLOCK_PLL_YGYRO = 0x02
+    MPU6050_CLOCK_PLL_ZGYRO = 0x03
+    MPU6050_CLOCK_PLL_EXT32K = 0x04
+    MPU6050_CLOCK_PLL_EXT19M = 0x05
+    MPU6050_CLOCK_KEEP_RESET = 0x07
+
+    MPU6050_PWR2_LP_WAKE_CTRL_BIT = 7
+    MPU6050_PWR2_LP_WAKE_CTRL_LENGTH = 2
+    MPU6050_PWR2_STBY_XA_BIT = 5
+    MPU6050_PWR2_STBY_YA_BIT = 4
+    MPU6050_PWR2_STBY_ZA_BIT = 3
+    MPU6050_PWR2_STBY_XG_BIT = 2
+    MPU6050_PWR2_STBY_YG_BIT = 1
+    MPU6050_PWR2_STBY_ZG_BIT = 0
+
+    MPU6050_WAKE_FREQ_1P25 = 0x0
+    MPU6050_WAKE_FREQ_2P5 = 0x1
+    MPU6050_WAKE_FREQ_5 = 0x2
+    MPU6050_WAKE_FREQ_10 = 0x3
+
+    MPU6050_BANKSEL_PRFTCH_EN_BIT = 6
+    MPU6050_BANKSEL_CFG_USER_BANK_BIT = 5
+    MPU6050_BANKSEL_MEM_SEL_BIT = 4
+    MPU6050_BANKSEL_MEM_SEL_LENGTH = 5
+
+    MPU6050_WHO_AM_I_BIT = 6
+    MPU6050_WHO_AM_I_LENGTH = 6
+
+    MPU6050_DMP_MEMORY_BANKS = 8
+    MPU6050_DMP_MEMORY_BANK_SIZE = 256
+    MPU6050_DMP_MEMORY_CHUNK_SIZE = 16
+
+    # From MPU6050_6Axis_MotionApps20.h
+    MPU6050_DMP_CODE_SIZE = 1929  # dmpMemory[]
+    MPU6050_DMP_CONFIG_SIZE = 192  # dmpConfig[]
+    MPU6050_DMP_UPDATES_SIZE = 47  # dmpUpdates[]
+    '''
+     * ================================================================================================ *
+     | Default MotionApps v2.0 42-byte FIFO packet structure:                                           |
+     |                                                                                                  |
+     | [QUAT W][      ][QUAT X][      ][QUAT Y][      ][QUAT Z][      ][GYRO X][      ][GYRO Y][      ] |
+     |   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  |
+     |                                                                                                  |
+     | [GYRO Z][      ][ACC X ][      ][ACC Y ][      ][ACC Z ][      ][      ]                         |
+     |  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41                          |
+     * ================================================================================================ *
+     '''
+    # dmpMemory has size MPU6050_DMP_CODE_SIZE = 1929
+    dmpMemory = [
+        # bank 0, 256 bytes
+        0xFB, 0x00, 0x00, 0x3E, 0x00, 0x0B, 0x00, 0x36, 0x00, 0x01, 0x00, 0x02,
+        0x00, 0x03, 0x00, 0x00,
+        0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, 0xFA, 0x80, 0x00, 0x0B,
+        0x12, 0x82, 0x00, 0x01,
+        0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x28, 0x00, 0x00, 0xFF, 0xFF, 0x45, 0x81, 0xFF, 0xFF, 0xFA, 0x72,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7F, 0xFF,
+        0xFF, 0xFE, 0x80, 0x01,
+        0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x3E, 0x03, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xCA, 0xE3, 0x09,
+        0x3E, 0x80, 0x00, 0x00,
+        0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x60, 0x00, 0x00, 0x00,
+        0x41, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x2A, 0x00, 0x00, 0x16, 0x55,
+        0x00, 0x00, 0x21, 0x82,
+        0xFD, 0x87, 0x26, 0x50, 0xFD, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00,
+        0x00, 0x05, 0x80, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+        0x00, 0x03, 0x00, 0x00,
+        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x6F, 0x00, 0x02, 0x65, 0x32,
+        0x00, 0x00, 0x5E, 0xC0,
+        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0xFB, 0x8C, 0x6F, 0x5D, 0xFD, 0x5D, 0x08, 0xD9, 0x00, 0x7C, 0x73, 0x3B,
+        0x00, 0x6C, 0x12, 0xCC,
+        0x32, 0x00, 0x13, 0x9D, 0x32, 0x00, 0xD0, 0xD6, 0x32, 0x00, 0x08, 0x00,
+        0x40, 0x00, 0x01, 0xF4,
+        0xFF, 0xE6, 0x80, 0x79, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xD6,
+        0x00, 0x00, 0x27, 0x10,
+
+        # bank 1, 256 bytes
+        0xFB, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0xFA, 0x36, 0xFF, 0xBC, 0x30, 0x8E, 0x00, 0x05, 0xFB, 0xF0,
+        0xFF, 0xD9, 0x5B, 0xC8,
+        0xFF, 0xD0, 0x9A, 0xBE, 0x00, 0x00, 0x10, 0xA9, 0xFF, 0xF4, 0x1E, 0xB2,
+        0x00, 0xCE, 0xBB, 0xF7,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02,
+        0x02, 0x00, 0x00, 0x0C,
+        0xFF, 0xC2, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xCF, 0x80, 0x00,
+        0x40, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+        0x00, 0x00, 0x00, 0x14,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x03, 0x3F, 0x68, 0xB6, 0x79, 0x35, 0x28, 0xBC,
+        0xC6, 0x7E, 0xD1, 0x6C,
+        0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x6A,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0,
+        0x00, 0x00, 0x00, 0x30,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x25, 0x4D, 0x00, 0x2F, 0x70, 0x6D, 0x00, 0x00, 0x05, 0xAE,
+        0x00, 0x0C, 0x02, 0xD0,
+
+        # bank 2, 256 bytes
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00,
+        0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00,
+        0xFF, 0xEF, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x40, 0x00, 0x00, 0x00,
+        0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+
+        # bank 3, 256 bytes
+        0xD8, 0xDC, 0xBA, 0xA2, 0xF1, 0xDE, 0xB2, 0xB8, 0xB4, 0xA8, 0x81, 0x91,
+        0xF7, 0x4A, 0x90, 0x7F,
+        0x91, 0x6A, 0xF3, 0xF9, 0xDB, 0xA8, 0xF9, 0xB0, 0xBA, 0xA0, 0x80, 0xF2,
+        0xCE, 0x81, 0xF3, 0xC2,
+        0xF1, 0xC1, 0xF2, 0xC3, 0xF3, 0xCC, 0xA2, 0xB2, 0x80, 0xF1, 0xC6, 0xD8,
+        0x80, 0xBA, 0xA7, 0xDF,
+        0xDF, 0xDF, 0xF2, 0xA7, 0xC3, 0xCB, 0xC5, 0xB6, 0xF0, 0x87, 0xA2, 0x94,
+        0x24, 0x48, 0x70, 0x3C,
+        0x95, 0x40, 0x68, 0x34, 0x58, 0x9B, 0x78, 0xA2, 0xF1, 0x83, 0x92, 0x2D,
+        0x55, 0x7D, 0xD8, 0xB1,
+        0xB4, 0xB8, 0xA1, 0xD0, 0x91, 0x80, 0xF2, 0x70, 0xF3, 0x70, 0xF2, 0x7C,
+        0x80, 0xA8, 0xF1, 0x01,
+        0xB0, 0x98, 0x87, 0xD9, 0x43, 0xD8, 0x86, 0xC9, 0x88, 0xBA, 0xA1, 0xF2,
+        0x0E, 0xB8, 0x97, 0x80,
+        0xF1, 0xA9, 0xDF, 0xDF, 0xDF, 0xAA, 0xDF, 0xDF, 0xDF, 0xF2, 0xAA, 0xC5,
+        0xCD, 0xC7, 0xA9, 0x0C,
+        0xC9, 0x2C, 0x97, 0x97, 0x97, 0x97, 0xF1, 0xA9, 0x89, 0x26, 0x46, 0x66,
+        0xB0, 0xB4, 0xBA, 0x80,
+        0xAC, 0xDE, 0xF2, 0xCA, 0xF1, 0xB2, 0x8C, 0x02, 0xA9, 0xB6, 0x98, 0x00,
+        0x89, 0x0E, 0x16, 0x1E,
+        0xB8, 0xA9, 0xB4, 0x99, 0x2C, 0x54, 0x7C, 0xB0, 0x8A, 0xA8, 0x96, 0x36,
+        0x56, 0x76, 0xF1, 0xB9,
+        0xAF, 0xB4, 0xB0, 0x83, 0xC0, 0xB8, 0xA8, 0x97, 0x11, 0xB1, 0x8F, 0x98,
+        0xB9, 0xAF, 0xF0, 0x24,
+        0x08, 0x44, 0x10, 0x64, 0x18, 0xF1, 0xA3, 0x29, 0x55, 0x7D, 0xAF, 0x83,
+        0xB5, 0x93, 0xAF, 0xF0,
+        0x00, 0x28, 0x50, 0xF1, 0xA3, 0x86, 0x9F, 0x61, 0xA6, 0xDA, 0xDE, 0xDF,
+        0xD9, 0xFA, 0xA3, 0x86,
+        0x96, 0xDB, 0x31, 0xA6, 0xD9, 0xF8, 0xDF, 0xBA, 0xA6, 0x8F, 0xC2, 0xC5,
+        0xC7, 0xB2, 0x8C, 0xC1,
+        0xB8, 0xA2, 0xDF, 0xDF, 0xDF, 0xA3, 0xDF, 0xDF, 0xDF, 0xD8, 0xD8, 0xF1,
+        0xB8, 0xA8, 0xB2, 0x86,
+
+        # bank 4, 256 bytes
+        0xB4, 0x98, 0x0D, 0x35, 0x5D, 0xB8, 0xAA, 0x98, 0xB0, 0x87, 0x2D, 0x35,
+        0x3D, 0xB2, 0xB6, 0xBA,
+        0xAF, 0x8C, 0x96, 0x19, 0x8F, 0x9F, 0xA7, 0x0E, 0x16, 0x1E, 0xB4, 0x9A,
+        0xB8, 0xAA, 0x87, 0x2C,
+        0x54, 0x7C, 0xB9, 0xA3, 0xDE, 0xDF, 0xDF, 0xA3, 0xB1, 0x80, 0xF2, 0xC4,
+        0xCD, 0xC9, 0xF1, 0xB8,
+        0xA9, 0xB4, 0x99, 0x83, 0x0D, 0x35, 0x5D, 0x89, 0xB9, 0xA3, 0x2D, 0x55,
+        0x7D, 0xB5, 0x93, 0xA3,
+        0x0E, 0x16, 0x1E, 0xA9, 0x2C, 0x54, 0x7C, 0xB8, 0xB4, 0xB0, 0xF1, 0x97,
+        0x83, 0xA8, 0x11, 0x84,
+        0xA5, 0x09, 0x98, 0xA3, 0x83, 0xF0, 0xDA, 0x24, 0x08, 0x44, 0x10, 0x64,
+        0x18, 0xD8, 0xF1, 0xA5,
+        0x29, 0x55, 0x7D, 0xA5, 0x85, 0x95, 0x02, 0x1A, 0x2E, 0x3A, 0x56, 0x5A,
+        0x40, 0x48, 0xF9, 0xF3,
+        0xA3, 0xD9, 0xF8, 0xF0, 0x98, 0x83, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18,
+        0x97, 0x82, 0xA8, 0xF1,
+        0x11, 0xF0, 0x98, 0xA2, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xDA, 0xF3,
+        0xDE, 0xD8, 0x83, 0xA5,
+        0x94, 0x01, 0xD9, 0xA3, 0x02, 0xF1, 0xA2, 0xC3, 0xC5, 0xC7, 0xD8, 0xF1,
+        0x84, 0x92, 0xA2, 0x4D,
+        0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32,
+        0xD8, 0x50, 0x71, 0xD9,
+        0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8,
+        0x78, 0x93, 0xA3, 0x4D,
+        0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32,
+        0xD8, 0x50, 0x71, 0xD9,
+        0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8,
+        0x78, 0xA8, 0x8A, 0x9A,
+        0xF0, 0x28, 0x50, 0x78, 0x9E, 0xF3, 0x88, 0x18, 0xF1, 0x9F, 0x1D, 0x98,
+        0xA8, 0xD9, 0x08, 0xD8,
+        0xC8, 0x9F, 0x12, 0x9E, 0xF3, 0x15, 0xA8, 0xDA, 0x12, 0x10, 0xD8, 0xF1,
+        0xAF, 0xC8, 0x97, 0x87,
+
+        # bank 5, 256 bytes
+        0x34, 0xB5, 0xB9, 0x94, 0xA4, 0x21, 0xF3, 0xD9, 0x22, 0xD8, 0xF2, 0x2D,
+        0xF3, 0xD9, 0x2A, 0xD8,
+        0xF2, 0x35, 0xF3, 0xD9, 0x32, 0xD8, 0x81, 0xA4, 0x60, 0x60, 0x61, 0xD9,
+        0x61, 0xD8, 0x6C, 0x68,
+        0x69, 0xD9, 0x69, 0xD8, 0x74, 0x70, 0x71, 0xD9, 0x71, 0xD8, 0xB1, 0xA3,
+        0x84, 0x19, 0x3D, 0x5D,
+        0xA3, 0x83, 0x1A, 0x3E, 0x5E, 0x93, 0x10, 0x30, 0x81, 0x10, 0x11, 0xB8,
+        0xB0, 0xAF, 0x8F, 0x94,
+        0xF2, 0xDA, 0x3E, 0xD8, 0xB4, 0x9A, 0xA8, 0x87, 0x29, 0xDA, 0xF8, 0xD8,
+        0x87, 0x9A, 0x35, 0xDA,
+        0xF8, 0xD8, 0x87, 0x9A, 0x3D, 0xDA, 0xF8, 0xD8, 0xB1, 0xB9, 0xA4, 0x98,
+        0x85, 0x02, 0x2E, 0x56,
+        0xA5, 0x81, 0x00, 0x0C, 0x14, 0xA3, 0x97, 0xB0, 0x8A, 0xF1, 0x2D, 0xD9,
+        0x28, 0xD8, 0x4D, 0xD9,
+        0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x84, 0x0D, 0xDA, 0x0E, 0xD8,
+        0xA3, 0x29, 0x83, 0xDA,
+        0x2C, 0x0E, 0xD8, 0xA3, 0x84, 0x49, 0x83, 0xDA, 0x2C, 0x4C, 0x0E, 0xD8,
+        0xB8, 0xB0, 0xA8, 0x8A,
+        0x9A, 0xF5, 0x20, 0xAA, 0xDA, 0xDF, 0xD8, 0xA8, 0x40, 0xAA, 0xD0, 0xDA,
+        0xDE, 0xD8, 0xA8, 0x60,
+        0xAA, 0xDA, 0xD0, 0xDF, 0xD8, 0xF1, 0x97, 0x86, 0xA8, 0x31, 0x9B, 0x06,
+        0x99, 0x07, 0xAB, 0x97,
+        0x28, 0x88, 0x9B, 0xF0, 0x0C, 0x20, 0x14, 0x40, 0xB8, 0xB0, 0xB4, 0xA8,
+        0x8C, 0x9C, 0xF0, 0x04,
+        0x28, 0x51, 0x79, 0x1D, 0x30, 0x14, 0x38, 0xB2, 0x82, 0xAB, 0xD0, 0x98,
+        0x2C, 0x50, 0x50, 0x78,
+        0x78, 0x9B, 0xF1, 0x1A, 0xB0, 0xF0, 0x8A, 0x9C, 0xA8, 0x29, 0x51, 0x79,
+        0x8B, 0x29, 0x51, 0x79,
+        0x8A, 0x24, 0x70, 0x59, 0x8B, 0x20, 0x58, 0x71, 0x8A, 0x44, 0x69, 0x38,
+        0x8B, 0x39, 0x40, 0x68,
+        0x8A, 0x64, 0x48, 0x31, 0x8B, 0x30, 0x49, 0x60, 0xA5, 0x88, 0x20, 0x09,
+        0x71, 0x58, 0x44, 0x68,
+
+        # bank 6, 256 bytes
+        0x11, 0x39, 0x64, 0x49, 0x30, 0x19, 0xF1, 0xAC, 0x00, 0x2C, 0x54, 0x7C,
+        0xF0, 0x8C, 0xA8, 0x04,
+        0x28, 0x50, 0x78, 0xF1, 0x88, 0x97, 0x26, 0xA8, 0x59, 0x98, 0xAC, 0x8C,
+        0x02, 0x26, 0x46, 0x66,
+        0xF0, 0x89, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69,
+        0x38, 0x64, 0x48, 0x31,
+        0xA9, 0x88, 0x09, 0x20, 0x59, 0x70, 0xAB, 0x11, 0x38, 0x40, 0x69, 0xA8,
+        0x19, 0x31, 0x48, 0x60,
+        0x8C, 0xA8, 0x3C, 0x41, 0x5C, 0x20, 0x7C, 0x00, 0xF1, 0x87, 0x98, 0x19,
+        0x86, 0xA8, 0x6E, 0x76,
+        0x7E, 0xA9, 0x99, 0x88, 0x2D, 0x55, 0x7D, 0x9E, 0xB9, 0xA3, 0x8A, 0x22,
+        0x8A, 0x6E, 0x8A, 0x56,
+        0x8A, 0x5E, 0x9F, 0xB1, 0x83, 0x06, 0x26, 0x46, 0x66, 0x0E, 0x2E, 0x4E,
+        0x6E, 0x9D, 0xB8, 0xAD,
+        0x00, 0x2C, 0x54, 0x7C, 0xF2, 0xB1, 0x8C, 0xB4, 0x99, 0xB9, 0xA3, 0x2D,
+        0x55, 0x7D, 0x81, 0x91,
+        0xAC, 0x38, 0xAD, 0x3A, 0xB5, 0x83, 0x91, 0xAC, 0x2D, 0xD9, 0x28, 0xD8,
+        0x4D, 0xD9, 0x48, 0xD8,
+        0x6D, 0xD9, 0x68, 0xD8, 0x8C, 0x9D, 0xAE, 0x29, 0xD9, 0x04, 0xAE, 0xD8,
+        0x51, 0xD9, 0x04, 0xAE,
+        0xD8, 0x79, 0xD9, 0x04, 0xD8, 0x81, 0xF3, 0x9D, 0xAD, 0x00, 0x8D, 0xAE,
+        0x19, 0x81, 0xAD, 0xD9,
+        0x01, 0xD8, 0xF2, 0xAE, 0xDA, 0x26, 0xD8, 0x8E, 0x91, 0x29, 0x83, 0xA7,
+        0xD9, 0xAD, 0xAD, 0xAD,
+        0xAD, 0xF3, 0x2A, 0xD8, 0xD8, 0xF1, 0xB0, 0xAC, 0x89, 0x91, 0x3E, 0x5E,
+        0x76, 0xF3, 0xAC, 0x2E,
+        0x2E, 0xF1, 0xB1, 0x8C, 0x5A, 0x9C, 0xAC, 0x2C, 0x28, 0x28, 0x28, 0x9C,
+        0xAC, 0x30, 0x18, 0xA8,
+        0x98, 0x81, 0x28, 0x34, 0x3C, 0x97, 0x24, 0xA7, 0x28, 0x34, 0x3C, 0x9C,
+        0x24, 0xF2, 0xB0, 0x89,
+        0xAC, 0x91, 0x2C, 0x4C, 0x6C, 0x8A, 0x9B, 0x2D, 0xD9, 0xD8, 0xD8, 0x51,
+        0xD9, 0xD8, 0xD8, 0x79,
+
+        # bank 7, 138 bytes (remainder)
+        0xD9, 0xD8, 0xD8, 0xF1, 0x9E, 0x88, 0xA3, 0x31, 0xDA, 0xD8, 0xD8, 0x91,
+        0x2D, 0xD9, 0x28, 0xD8,
+        0x4D, 0xD9, 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x83, 0x93, 0x35,
+        0x3D, 0x80, 0x25, 0xDA,
+        0xD8, 0xD8, 0x85, 0x69, 0xDA, 0xD8, 0xD8, 0xB4, 0x93, 0x81, 0xA3, 0x28,
+        0x34, 0x3C, 0xF3, 0xAB,
+        0x8B, 0xF8, 0xA3, 0x91, 0xB6, 0x09, 0xB4, 0xD9, 0xAB, 0xDE, 0xFA, 0xB0,
+        0x87, 0x9C, 0xB9, 0xA3,
+        0xDD, 0xF1, 0xA3, 0xA3, 0xA3, 0xA3, 0x95, 0xF1, 0xA3, 0xA3, 0xA3, 0x9D,
+        0xF1, 0xA3, 0xA3, 0xA3,
+        0xA3, 0xF2, 0xA3, 0xB4, 0x90, 0x80, 0xF2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+        0xA3, 0xA3, 0xA3, 0xA3,
+        0xA3, 0xB2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xB0, 0x87, 0xB5, 0x99,
+        0xF1, 0xA3, 0xA3, 0xA3,
+        0x98, 0xF1, 0xA3, 0xA3, 0xA3, 0xA3, 0x97, 0xA3, 0xA3, 0xA3, 0xA3, 0xF3,
+        0x9B, 0xA3, 0xA3, 0xDC,
+        0xB9, 0xA7, 0xF1, 0x26, 0x26, 0x26, 0xD8, 0xD8, 0xFF]
+
+    # dmpConfig has size MPU6050_DMP_CONFIG_SIZE = 192
+    dmpConfig = [
+        #  BANK    OFFSET  LENGTH  [DATA]
+        0x03, 0x7B, 0x03, 0x4C, 0xCD, 0x6C,  # FCFG_1 inv_set_gyro_calibration
+        0x03, 0xAB, 0x03, 0x36, 0x56, 0x76,  # FCFG_3 inv_set_gyro_calibration
+        0x00, 0x68, 0x04, 0x02, 0xCB, 0x47, 0xA2,
+        # D_0_104 inv_set_gyro_calibration
+        0x02, 0x18, 0x04, 0x00, 0x05, 0x8B, 0xC1,
+        # D_0_24 inv_set_gyro_calibration
+        0x01, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00,
+        # D_1_152 inv_set_accel_calibration
+        0x03, 0x7F, 0x06, 0x0C, 0xC9, 0x2C, 0x97, 0x97, 0x97,
+        # FCFG_2 inv_set_accel_calibration
+        0x03, 0x89, 0x03, 0x26, 0x46, 0x66,  # FCFG_7 inv_set_accel_calibration
+        0x00, 0x6C, 0x02, 0x20, 0x00,  # D_0_108 inv_set_accel_calibration
+        0x02, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00,
+        # CPASS_MTX_00 inv_set_compass_calibration
+        0x02, 0x44, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_01
+        0x02, 0x48, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_02
+        0x02, 0x4C, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_10
+        0x02, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_11
+        0x02, 0x54, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_12
+        0x02, 0x58, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_20
+        0x02, 0x5C, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_21
+        0x02, 0xBC, 0x04, 0x00, 0x00, 0x00, 0x00,  # CPASS_MTX_22
+        0x01, 0xEC, 0x04, 0x00, 0x00, 0x40, 0x00,
+        # D_1_236 inv_apply_endian_accel
+        0x03, 0x7F, 0x06, 0x0C, 0xC9, 0x2C, 0x97, 0x97, 0x97,
+        # FCFG_2 inv_set_mpu_sensors
+        0x04, 0x02, 0x03, 0x0D, 0x35, 0x5D,
+        # CFG_MOTION_BIAS inv_turn_on_bias_from_no_motion
+        0x04, 0x09, 0x04, 0x87, 0x2D, 0x35, 0x3D,  # FCFG_5 inv_set_bias_update
+        0x00, 0xA3, 0x01, 0x00,  # D_0_163 inv_set_dead_zone
+        # SPECIAL 0x01 = enable interrupts
+        0x00, 0x00, 0x00, 0x01,  # SET INT_ENABLE at i=22, SPECIAL INSTRUCTION
+        0x07, 0x86, 0x01, 0xFE,  # CFG_6 inv_set_fifo_interupt
+        0x07, 0x41, 0x05, 0xF1, 0x20, 0x28, 0x30, 0x38,
+        # CFG_8 inv_send_quaternion
+        0x07, 0x7E, 0x01, 0x30,  # CFG_16 inv_set_footer
+        0x07, 0x46, 0x01, 0x9A,  # CFG_GYRO_SOURCE inv_send_gyro
+        0x07, 0x47, 0x04, 0xF1, 0x28, 0x30, 0x38,
+        # CFG_9 inv_send_gyro -> inv_construct3_fifo
+        0x07, 0x6C, 0x04, 0xF1, 0x28, 0x30, 0x38,
+        # CFG_12 inv_send_accel -> inv_construct3_fifo
+        0x02, 0x16, 0x02, 0x00, 0x01]  # D_0_22 inv_set_fifo_rate
+
+    # This very last 0x01 WAS a 0x09, which drops the FIFO rate down to 20 Hz.
+    # 0x07 is 25 Hz, 0x01 is 100Hz. Going faster than 100Hz (0x00=200Hz) tends
+    # to result in very noisy data. DMP output frequency is calculated easily
+    # using this equation: (200Hz / (1 + value))
+
+    # It is important to make sure the host processor can keep up with reading
+    # and processing the FIFO output at the desired rate. Handling FIFO overflow
+    # cleanly is also a good idea.
+
+    # dmpUpdates has size MPU6050_DMP_UPDATES_SIZE = 47
+    dmpUpdates = [
+        0x01, 0xB2, 0x02, 0xFF, 0xFF,
+        0x01, 0x90, 0x04, 0x09, 0x23, 0xA1, 0x35,
+        0x01, 0x6A, 0x02, 0x06, 0x00,
+        0x01, 0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x60, 0x04, 0x40, 0x00, 0x00, 0x00,
+        0x01, 0x62, 0x02, 0x00, 0x00,
+        0x00, 0x60, 0x04, 0x00, 0x40, 0x00, 0x00]
diff --git a/Code/Python_Code/25.1.1_MPU6050/Quaternion.py b/Code/Python_Code/25.1.1_MPU6050/Quaternion.py
new file mode 100644
index 0000000..6f8b9bd
--- /dev/null
+++ b/Code/Python_Code/25.1.1_MPU6050/Quaternion.py
@@ -0,0 +1,135 @@
+__author__ = 'Geir Istad'
+"""
+MPU6050 Python I2C Class
+Copyright (c) 2015 Geir Istad
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+Code based on
+I2Cdev library collection - 3D math helper
+by Jeff Rowberg 
+============================================
+I2Cdev device library code is placed under the MIT license
+Copyright (c) 2012 Jeff Rowberg
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+===============================================
+"""
+from math import sqrt
+
+
+class Quaternion:
+    w = 0.0
+    x = 0.0
+    y = 0.0
+    z = 0.0
+
+    def __init__(self, a_w=1.0, a_x=0.0, a_y=0.0, a_z=0.0):
+        self.w = a_w
+        self.x = a_x
+        self.y = a_y
+        self.z = a_z
+
+    def get_product(self, a_quat):
+        result = Quaternion(
+            self.w * a_quat.w - self.x * a_quat.x -
+            self.y * a_quat.y - self.z * a_quat.z,
+
+            self.w * a_quat.x + self.x * a_quat.w +
+            self.y * a_quat.z - self.z * a_quat.y,
+
+            self.w * a_quat.y - self.x * a_quat.z +
+            self.y * a_quat.w + self.z * a_quat.x,
+
+            self.w * a_quat.z + self.x * a_quat.y -
+            self.y * a_quat.x + self.z * a_quat.w)
+        return result
+
+    def get_conjugate(self):
+        result = Quaternion(self.w, -self.x, -self.y, -self.z)
+        return result
+
+    def get_magnitude(self):
+        return sqrt(self.w * self.w + self.x * self.x + self.y * self.y +
+                    self.z * self.z)
+
+    def normalize(self):
+        m = self.get_magnitude()
+        self.w = self.w / m
+        self.x = self.x / m
+        self.y = self.y / m
+        self.z = self.z / m
+
+    def get_normalized(self):
+        result = Quaternion(self.w, self.x, self.y, self.z)
+        result.normalize()
+        return result
+
+
+class XYZVector:
+    x = 0.0
+    y = 0.0
+    z = 0.0
+
+    def __init__(self, a_x=0.0, a_y=0.0, a_z=0.0):
+        self.x = a_x
+        self.y = a_y
+        self.z = a_z
+
+    def get_magnitude(self):
+        return sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
+
+    def normalize(self):
+        m = self.get_magnitude()
+        self.x = self.x / m
+        self.y = self.y / m
+        self.z = self.z / m
+
+    def get_normalized(self):
+        result = XYZVector(self.x, self.y, self.z)
+        result.normalize()
+        return result
+
+    def rotate(self, a_quat):
+        p = Quaternion(0.0, self.x, self.y, self.z)
+        p = a_quat.get_product(p)
+        p = p.get_product(a_quat.get_conjugate())
+        # By magic quaternion p is now [0, x', y', z']
+        self.x = p.x
+        self.y = p.y
+        self.z = p.z
+
+    def get_rotated(self, a_quat):
+        r = XYZVector(self.x, self.y, self.z)
+        r.rotate(a_quat)
+        return r
diff --git a/Code/Python_Code/27.2.1_LightWater03/LightWater03.py b/Code/Python_Code/27.2.1_LightWater03/LightWater03.py
new file mode 100644
index 0000000..07e6c66
--- /dev/null
+++ b/Code/Python_Code/27.2.1_LightWater03/LightWater03.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+#############################################################################
+# Filename    : LightWater03.py
+# Description : Control LED by 74HC595 on the DIY circuit board
+# Author      : freenove
+# modification: 2016/08/16
+########################################################################
+import RPi.GPIO as GPIO
+import time
+
+LSBFIRST = 1
+MSBFIRST = 2
+#define the pins connect to 74HC595
+dataPin   = 11		#DS Pin of 74HC595(Pin14)
+latchPin  = 13		#ST_CP Pin of 74HC595(Pin12)
+clockPin = 15		#SH_CP Pin of 74HC595(Pin11)
+#Define an array to save the pulse width of LED. Output the signal to the 8 adjacent LEDs in order.
+pluseWidth = [0,0,0,0,0,0,0,0,64,32,16,8,4,2,1,0,0,0,0,0,0,0,0]
+
+def setup():
+	GPIO.setmode(GPIO.BOARD)    # Number GPIOs by its physical location
+	GPIO.setup(dataPin, GPIO.OUT)
+	GPIO.setup(latchPin, GPIO.OUT)
+	GPIO.setup(clockPin, GPIO.OUT)
+	
+def shiftOut(dPin,cPin,order,val):
+	for i in range(0,8):
+		GPIO.output(cPin,GPIO.LOW);
+		if(order == LSBFIRST):
+			GPIO.output(dPin,(0x01&(val>>i)==0x01) and GPIO.HIGH or GPIO.LOW)
+		elif(order == MSBFIRST):
+			GPIO.output(dPin,(0x80&(val< moveSpeed):	#speed control
+			lastMove = time.time()		#Record the time point of the move
+			index +=1			#move to next 
+			if(index > 15): 	#index to 0
+				index = 0
+			
+		for i in range(0,64):	#The cycle of PWM is 64 cycles
+			data = 0			#This loop of output data
+			for j in range(0,8):	#Calculate the output state of this loop
+				if(i < pluseWidth[j+index]):	#Calculate the LED state according to the pulse width 
+					data |= 1<