commit
108 files changed, 17410 insertions(+), 0 deletions(-)

A => libraries/AmebaArduCAM.zip
A => libraries/AmebaLedStrip.zip
A => libraries/AmebaLoRa.zip
A => libraries/AmebaLoRa/API.md
A => libraries/AmebaLoRa/LICENSE
A => libraries/AmebaLoRa/LICENSE_RTK
A => libraries/AmebaLoRa/README.md
A => libraries/AmebaLoRa/examples/LoRaReceiver/LoRaReceiver.ino
A => libraries/AmebaLoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino
A => libraries/AmebaLoRa/examples/LoRaSender/LoRaSender.ino
A => libraries/AmebaLoRa/keywords.txt
A => libraries/AmebaLoRa/library.properties
A => libraries/AmebaLoRa/src/LoRa.cpp
A => libraries/AmebaLoRa/src/LoRa.h
A => libraries/AmebaMotors-1.0.0.zip
A => libraries/AmebaMotors-1.0.1.zip
A => libraries/AmebaMotors-1.0.2.zip
A => libraries/AmebaMotors-1.0.3.zip
A => libraries/AmebaMotors/Car2wd.cpp
A => libraries/AmebaMotors/Car2wd.h
A => libraries/AmebaMotors/README.md
A => libraries/AmebaMotors/examples/car2wd_basic/car2wd_basic.ino
A => libraries/AmebaMotors/examples/car2wd_digit_control/car2wd_digit_control.ino
A => libraries/AmebaMotors/examples/car2wd_mobile_control/car2wd_mobile_control.ino
A => libraries/AmebaMotors/examples/car2wd_mobile_plus_uvc/car2wd_mobile_plus_uvc.ino
A => libraries/AmebaMotors/examples/car2wd_mpu6050_control/car2wd_mpu6050_control.ino
A => libraries/AmebaMotors/examples/car2wd_web_control/car2wd_web_control.ino
A => libraries/AmebaMotors/examples/misc/car2wd_2_controller/car2wd_2_controller.ino
A => libraries/AmebaMotors/examples/misc/car2wd_mpu6050_control_udp/car2wd_mpu6050_control_udp.ino
A => libraries/AmebaMotors/examples/thunder_tiger_jackal/thunder_tiger_jackal.ino
A => libraries/AmebaMotors/keywords.txt
A => libraries/AmebaMotors/library.properties
A => libraries/ArduinoJson.zip
A => libraries/DS1307RTC-1.0.0.zip
A => libraries/DS1307RTC/DS1307RTC.cpp
A => libraries/DS1307RTC/DS1307RTC.h
A => libraries/DS1307RTC/LICENSE.txt
A => libraries/DS1307RTC/examples/ReadTest/ReadTest.ino
A => libraries/DS1307RTC/examples/SetTime/SetTime.ino
A => libraries/DS1307RTC/examples/SetTimeNtp/SetTimeNtp.ino
A => libraries/DS1307RTC/keywords.txt
A => libraries/DS1307RTC/library.json
A => libraries/DS1307RTC/library.properties
A => libraries/DS1307RTC/readme.txt
A => libraries/I2Cdev.zip
A => libraries/I2Cdev/I2Cdev.cpp
A => libraries/I2Cdev/I2Cdev.h
A => libraries/I2Cdev/LICENSE.txt
A => libraries/I2Cdev/README
A => libraries/I2Cdev/keywords.txt
A => libraries/I2Cdev/library.json
A => libraries/LCD12864.zip
A => libraries/LCD12864/LCD12864.cpp
A => libraries/LCD12864/LCD12864.h
A => libraries/LCD12864/LCDSprite.h
A => libraries/LCD12864/examples/drawsketch/drawsketch.ino
A => libraries/LCD12864/library.properties
A => libraries/LCD12864/src/LCD12864.cpp
A => libraries/LCD12864/src/LCD12864.h
A => libraries/LCD12864/src/LCDSprite.h
A => libraries/LSM9DS1.zip
A => libraries/MPU6050.zip
A => libraries/MPU6050/Examples/MPU6050_DMP6/MPU6050_DMP6.ino
A => libraries/MPU6050/Examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde
A => libraries/MPU6050/Examples/MPU6050_DMP6_Ethernet/MPU6050_DMP6_Ethernet.ino
A => libraries/MPU6050/Examples/MPU6050_raw/MPU6050_raw.ino
A => libraries/MPU6050/LICENSE.txt
A => libraries/MPU6050/MPU6050.cpp
A => libraries/MPU6050/MPU6050.h
A => libraries/MPU6050/MPU6050_6Axis_MotionApps20.h
A => libraries/MPU6050/MPU6050_9Axis_MotionApps41.h
A => libraries/MPU6050/helper_3dmath.h
A => libraries/MPU6050/library.json
A => libraries/MPU6050/library.properties
A => libraries/ModbusTCP-1.0.0.zip
A => libraries/ModbusTCP/.gitignore
A => libraries/ModbusTCP/LICENSE
A => libraries/ModbusTCP/LICENSE_RTK.txt
A => libraries/ModbusTCP/ModbusTCP.cpp
A => libraries/ModbusTCP/ModbusTCP.h
A => libraries/ModbusTCP/README.md
A => libraries/ModbusTCP/examples/simpletest/simpletest.ino
A => libraries/ModbusTCP/keywords.txt
A => libraries/ModbusTCP/library.properties
A => libraries/OneWire.zip
A => libraries/OneWire/LICENSE.txt
A => libraries/OneWire/OneWire.cpp
A => libraries/OneWire/OneWire.h
A => libraries/OneWire/examples/DS18x20_Temperature/DS18x20_Temperature.pde
A => libraries/OneWire/examples/DS2408_Switch/DS2408_Switch.pde
A => libraries/OneWire/examples/DS250x_PROM/DS250x_PROM.pde
A => libraries/OneWire/keywords.txt
A => libraries/OneWire/library.json
A => libraries/OneWire/library.properties
A => libraries/README.md
A => libraries/RFID-RC522.zip
A => libraries/Time-1.0.0.zip
A => libraries/Time/DateStrings.cpp
A => libraries/Time/LICENSE.txt
A => libraries/Time/Readme.txt
A => libraries/Time/Time.cpp
A => libraries/Time/Time.h
A => libraries/Time/TimeLib.h
A => libraries/Time/examples/TimeNTP/TimeNTP.ino
A => libraries/Time/examples/TimeRTC/TimeRTC.ino
A => libraries/Time/keywords.txt
A => libraries/Time/library.json
A => libraries/Time/library.properties
A => libraries/AmebaArduCAM.zip +0 -0

        
A => libraries/AmebaLedStrip.zip +0 -0

        
A => libraries/AmebaLoRa.zip +0 -0

        
A => libraries/AmebaLoRa/API.md +299 -0
@@ 0,0 1,299 @@ 
+# LoRa API
+
+## Include Library
+
+```arduino
+#include <LoRa.h>
+```
+
+## Setup
+
+### Begin
+
+Initialize the library with the specified frequency.
+
+```arduino
+LoRa.begin(frequency);
+```
+ * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`)
+
+Returns `1` on success, `0` on failure.
+
+### Set pins
+
+Override the default `NSS`, `NRESET`, and `DIO0` pins used by the library. **Must** be called before `LoRa.begin()`.
+
+```arduino
+LoRa.setPins(ss, reset, dio0);
+```
+ * `ss` - new slave select pin to use, defaults to `10`
+ * `reset` - new reset pin to use, defaults to `9`
+ * `dio0` - new DIO0 pin to use, defaults to `2`
+
+This call is optional and only needs to be used if you need to change the default pins used.
+
+### End
+
+Stop the library
+
+```arduino
+LoRa.end()
+```
+
+## Sending data
+
+### Begin packet
+
+Start the sequence of sending a packet.
+
+```arduino
+LoRa.beginPacket();
+
+LoRa.beginPacket(implicitHeader);
+```
+
+ * `implicitHeader` - (optional) `true` enables implicit header mode, `false` enables explicit header mode (default)
+
+Returns `1` on success, `0` on failure.
+
+### Writing
+
+Write data to the packet. Each packet can contain up to 255 bytes.
+
+```arduino
+LoRa.write(byte);
+
+LoRa.write(buffer, length);
+```
+* `byte` - single byte to write to packet
+
+or
+
+* `buffer` - data to write to packet
+* `length` - size of data to write
+
+Returns the number of bytes written.
+
+**Note:** Other Arduino `Print` API's can also be used to write data into the packet
+
+### End packet
+
+End the sequence of sending a packet.
+
+```arduino
+LoRa.endPacket()
+```
+
+Returns `1` on success, `0` on failure.
+
+## Receiving data
+
+### Parsing packet
+
+Check if a packet has been received.
+
+```arduino
+int packetSize = LoRa.parsePacket();
+
+int packetSize = LoRa.parsePacket(size);
+```
+
+ * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode
+
+
+Returns the packet size in bytes or `0` if no packet was received.
+
+### Continuous receive mode
+
+#### Register callback
+
+Register a callback function for when a packet is received.
+
+```arduino
+LoRa.onReceive(onReceive);
+
+void onReceive(int packetSize) {
+ // ...
+}
+```
+
+ * `onReceive` - function to call when a packet is received.
+
+#### Receive mode
+
+Puts the radio in continuous receive mode.
+
+```arduino
+LoRa.receive();
+
+LoRa.receive(int size);
+```
+
+ * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode
+
+The `onReceive` callback will be called when a packet is received.
+
+### Packet RSSI
+
+```arduino
+int rssi = LoRa.packetRssi();
+```
+
+Returns the RSSI of the received packet.
+
+### Packet SNR
+
+```arduino
+float snr = LoRa.packetSnr();
+```
+
+Returns the estimated SNR of the received packet in dB.
+
+### Available
+
+```arduino
+int availableBytes = LoRa.available()
+```
+
+Returns number of bytes available for reading.
+
+### Peeking
+
+Peek at the next byte in the packet.
+
+```arduino
+byte b = LoRa.peek();
+```
+
+Returns the next byte in the packet or `-1` if no bytes are available.
+
+### Reading
+
+Read the next byte from the packet.
+
+```arduino
+byte b = LoRa.read();
+```
+
+Returns the next byte in the packet or `-1` if no bytes are available.
+
+**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet
+
+## Other radio modes
+
+### Idle mode
+
+Put the radio in idle (standby) mode.
+
+```arduino
+LoRa.idle();
+```
+
+### Sleep mode
+
+Put the radio in sleep mode.
+
+```arduino
+LoRa.sleep();
+```
+
+## Radio parameters
+
+### TX Power
+
+Change the TX power of the radio.
+
+```arduino
+LoRa.setTxPower(txPower);
+```
+ * `txPower` - TX power in dB, defaults to `17`
+
+ Supported values are between `2` and `17`.
+
+### Frequency
+
+Change the frequency of the radio.
+
+```arduino
+LoRa.setFrequency(frequency);
+```
+ * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`)
+
+### Spreading Factor
+
+Change the spreading factor of the radio.
+
+```arduino
+LoRa.setSpreadingFactor(spreadingFactor);
+```
+ * `spreadingFactor` - spreading factor, defaults to `7`
+
+Supported values are between `6` and `12`. If a spreading factor of `6` is set, implicit header mode must be used to transmit and receive packets.
+
+### Signal Bandwidth
+
+Change the signal bandwidth of the radio.
+
+```arduino
+LoRa.setSignalBandwidth(signalBandwidth);
+```
+
+ * `signalBandwidth` - signal bandwidth in Hz, defaults to `125E3`.
+
+Supported values are `7.8E3`, `10.4E3`, `15.6E3`, `20.8E3`, `31.25E3`, `41.7E3`, `62.5E3`, `125E3`, and `250E3`.
+
+### Coding Rate
+
+Change the coding rate of the radio.
+
+```arduino
+LoRa.setCodingRate4(codingRateDenominator);
+```
+
+ * `codingRateDenominator` - denominator of the coding rate, defaults to `5`
+
+Supported values are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. The coding rate numerator is fixed at `4`.
+
+### Preamble Length
+
+Change the preamble length of the radio.
+
+```arduino
+LoRa.setPreambleLength(preambleLength);
+```
+
+ * `preambleLength` - preamble length in symbols, defaults to `8`
+
+Supported values are between `6` and `65535`.
+
+### Sync Word
+
+Change the sync word of the radio.
+
+```arduino
+LoRa.setSyncWord(syncWord);
+```
+
+ * `syncWord` - byte value to use as the sync word, defaults to `0x34`
+
+### CRC
+
+Enable or disable CRC usage, by default a CRC is not used.
+
+```arduino
+LoRa.crc();
+
+LoRa.noCrc();
+```
+
+## Other functions
+
+### Random
+
+Generate a random byte, based on the Wideband RSSI measurement.
+
+```
+byte b = LoRa.random();
+```
+
+Returns random byte.

          
A => libraries/AmebaLoRa/LICENSE +21 -0
@@ 0,0 1,21 @@ 
+MIT License
+
+Copyright (c) 2016 Sandeep Mistry
+
+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.

          
A => libraries/AmebaLoRa/LICENSE_RTK +1 -0
@@ 0,0 1,1 @@ 
+This library is forked from "https://github.com/sandeepmistry/arduino-LoRa". The source codes under folder "AmebaLoRa" inherit the license from any license claim of "https://github.com/sandeepmistry/arduino-LoRa".
  No newline at end of file

          
A => libraries/AmebaLoRa/README.md +56 -0
@@ 0,0 1,56 @@ 
+# Arduino LoRa
+
+[![Build Status](https://travis-ci.org/sandeepmistry/arduino-LoRa.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-LoRa)
+
+An [Arduino](http://arduino.cc/) library for sending and receiving data using [LoRa](https://www.lora-alliance.org/) radios.
+
+## Compatible Hardware
+
+ * [Semtech SX1276/77/78/79](http://www.semtech.com/apps/product.php?pn=SX1276) based boards including:
+   * [Dragino Lora Shield](http://www.dragino.com/products/module/item/102-lora-shield.html)
+   * [HopeRF](http://www.hoperf.com/rf_transceiver/lora/) [RFM95W](http://www.hoperf.com/rf_transceiver/lora/RFM95W.html), [RFM96W](http://www.hoperf.com/rf_transceiver/lora/RFM96W.html), and [RFM98W](http://www.hoperf.com/rf_transceiver/lora/RFM98W.html)
+   * [Modtronix](http://modtronix.com/) [inAir4](http://modtronix.com/inair4.html), [inAir9](http://modtronix.com/inair9.html), and [inAir9B](http://modtronix.com/inair9b.html)
+
+### Semtech SX1276/77/78/79 wiring
+
+| Semtech SX1276/77/78/79 | Arduino | Ameba |
+| :---------------------: | :------:| :----:|
+| VCC | 3.3V | 3.3V |
+| GND | GND | GND |
+| SCK | SCK | SCK |
+| MISO | MISO | MISO |
+| MOSI | MOSI | MOSI |
+| NSS | 10 | 0 | 
+| NRESET | 9 | 9 |
+| DIO0 | 2 | 3 |
+
+
+`NSS`, `NRESET`, and `DIO0` pins can be changed by using `LoRa.setPins(ss, reset, dio0)`. `DIO0` pin is optional, it is only needed for receive callback mode.
+
+## Installation
+
+### Using the Arduino IDE Library Manager
+
+1. Choose `Sketch` -> `Include Library` -> `Manage Libraries...`
+2. Type `LoRa` into the search box.
+3. Click the row to select the library.
+4. Click the `Install` button to install the library.
+
+### Using Git
+
+```sh
+cd ~/Documents/Arduino/libraries/
+git clone https://github.com/sandeepmistry/arduino-LoRa LoRa
+```
+
+## API
+
+See [API.md](API.md).
+
+## Examples
+
+See [examples](examples) folder.
+
+## License
+
+This libary is [licensed](LICENSE) under the [MIT Licence](http://en.wikipedia.org/wiki/MIT_License).

          
A => libraries/AmebaLoRa/examples/LoRaReceiver/LoRaReceiver.ino +32 -0
@@ 0,0 1,32 @@ 
+#include <SPI.h>
+#include <LoRa.h>
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial);
+
+  Serial.println("LoRa Receiver");
+
+  if (!LoRa.begin(915E6)) {
+    Serial.println("Starting LoRa failed!");
+    while (1);
+  }
+}
+
+void loop() {
+  // try to parse packet
+  int packetSize = LoRa.parsePacket();
+  if (packetSize) {
+    // received a packet
+    Serial.print("Received packet '");
+
+    // read packet
+    while (LoRa.available()) {
+      Serial.print((char)LoRa.read());
+    }
+
+    // print RSSI of packet
+    Serial.print("' with RSSI ");
+    Serial.println(LoRa.packetRssi());
+  }
+}

          
A => libraries/AmebaLoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino +39 -0
@@ 0,0 1,39 @@ 
+#include <SPI.h>
+#include <LoRa.h>
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial);
+
+  Serial.println("LoRa Receiver Callback");
+
+  if (!LoRa.begin(915E6)) {
+    Serial.println("Starting LoRa failed!");
+    while (1);
+  }
+
+  // register the receive callback
+  LoRa.onReceive(onReceive);
+
+  // put the radio into receive mode
+  LoRa.receive();
+}
+
+void loop() {
+  // do nothing
+}
+
+void onReceive(int packetSize) {
+  // received a packet
+  Serial.print("Received packet '");
+
+  // read packet
+  for (int i = 0; i < packetSize; i++) {
+    Serial.print((char)LoRa.read());
+  }
+
+  // print RSSI of packet
+  Serial.print("' with RSSI ");
+  Serial.println(LoRa.packetRssi());
+}
+

          
A => libraries/AmebaLoRa/examples/LoRaSender/LoRaSender.ino +31 -0
@@ 0,0 1,31 @@ 
+#include <SPI.h>
+#include <LoRa.h>
+
+int counter = 0;
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial);
+
+  Serial.println("LoRa Sender");
+
+  if (!LoRa.begin(915E6)) {
+    Serial.println("Starting LoRa failed!");
+    while (1);
+  }
+}
+
+void loop() {
+  Serial.print("Sending packet: ");
+  Serial.println(counter);
+
+  // send packet
+  LoRa.beginPacket();
+  LoRa.print("hello ");
+  LoRa.print(counter);
+  LoRa.endPacket();
+
+  counter++;
+
+  delay(5000);
+}

          
A => libraries/AmebaLoRa/keywords.txt +53 -0
@@ 0,0 1,53 @@ 
+#######################################
+# Syntax Coloring Map For LoRa
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+LoRa	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin	KEYWORD2
+end	KEYWORD2
+
+beginPacket	KEYWORD2
+endPacket	KEYWORD2
+
+parsePacket	KEYWORD2
+packetRssi	KEYWORD2
+packetSnr	KEYWORD2
+
+write	KEYWORD2
+
+available	KEYWORD2
+read	KEYWORD2
+peek	KEYWORD2
+flush	KEYWORD2
+
+onReceive	KEYWORD2
+receive	KEYWORD2
+idle	KEYWORD2
+sleep	KEYWORD2
+
+setTxPower	KEYWORD2
+setFrequency	KEYWORD2
+setSpreadingFactor	KEYWORD2
+setSignalBandwidth	KEYWORD2
+setCodingRate4	KEYWORD2
+setPreambleLength	KEYWORD2
+setSyncWord	KEYWORD2
+crc	KEYWORD2
+noCrc	KEYWORD2
+
+random	KEYWORD2
+setPins	KEYWORD2
+dumpRegisters	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################

          
A => libraries/AmebaLoRa/library.properties +10 -0
@@ 0,0 1,10 @@ 
+name=AmebaLoRa
+version=0.1.0
+author=Realtek
+maintainer=Realtek <ameba.arduino@gmail.com>
+sentence=An Arduino library for sending and receiving data using LoRa radios.
+paragraph=Supports Semtech SX1276/77/78/79 based boards/shields.
+category=Communication
+url=http://www.amebaiot.com/ameba-arduino-peripherals-examples/
+architectures=ameba
+includes=LoRa.h

          
A => libraries/AmebaLoRa/src/LoRa.cpp +503 -0
@@ 0,0 1,503 @@ 
+#include <LoRa.h>
+
+// registers
+#define REG_FIFO                 0x00
+#define REG_OP_MODE              0x01
+#define REG_FRF_MSB              0x06
+#define REG_FRF_MID              0x07
+#define REG_FRF_LSB              0x08
+#define REG_PA_CONFIG            0x09
+#define REG_LNA                  0x0c
+#define REG_FIFO_ADDR_PTR        0x0d
+#define REG_FIFO_TX_BASE_ADDR    0x0e
+#define REG_FIFO_RX_BASE_ADDR    0x0f
+#define REG_FIFO_RX_CURRENT_ADDR 0x10
+#define REG_IRQ_FLAGS            0x12
+#define REG_RX_NB_BYTES          0x13
+#define REG_PKT_RSSI_VALUE       0x1a
+#define REG_PKT_SNR_VALUE        0x1b
+#define REG_MODEM_CONFIG_1       0x1d
+#define REG_MODEM_CONFIG_2       0x1e
+#define REG_PREAMBLE_MSB         0x20
+#define REG_PREAMBLE_LSB         0x21
+#define REG_PAYLOAD_LENGTH       0x22
+#define REG_RSSI_WIDEBAND        0x2c
+#define REG_DETECTION_OPTIMIZE   0x31
+#define REG_DETECTION_THRESHOLD  0x37
+#define REG_SYNC_WORD            0x39
+#define REG_DIO_MAPPING_1        0x40
+#define REG_VERSION              0x42
+
+// modes
+#define MODE_LONG_RANGE_MODE     0x80
+#define MODE_SLEEP               0x00
+#define MODE_STDBY               0x01
+#define MODE_TX                  0x03
+#define MODE_RX_CONTINUOUS       0x05
+#define MODE_RX_SINGLE           0x06
+
+// PA config
+#define PA_BOOST                 0x80
+
+// IRQ masks
+#define IRQ_TX_DONE_MASK           0x08
+#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
+#define IRQ_RX_DONE_MASK           0x40
+
+#define MAX_PKT_LENGTH           255
+
+/************For realtek ameba*************/
+#define digitalPinToInterrupt(p)  ((p) == LORA_DEFAULT_DIO0_PIN ? p : NOT_AN_INTERRUPT)	
+#define NOT_AN_INTERRUPT -1	
+/******************************************/
+
+
+LoRaClass::LoRaClass() :
+  _spiSettings(10E6, MSBFIRST, SPI_MODE0),
+  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
+  _frequency(0),
+  _packetIndex(0),
+  _implicitHeaderMode(0),
+  _onReceive(NULL)
+{
+}
+
+int LoRaClass::begin(long frequency)
+{
+  // setup pins
+  pinMode(_ss, OUTPUT);
+  pinMode(_reset, OUTPUT);
+
+  // perform reset
+  digitalWrite(_reset, LOW);
+  delay(10);
+  digitalWrite(_reset, HIGH);
+  delay(10);
+
+  // set SS high
+  digitalWrite(_ss, HIGH);
+	
+  // start SPI
+  SPI.begin();
+	
+  // check version
+  uint8_t version = readRegister(REG_VERSION);
+  if (version != 0x12) {
+    return 0;
+  }
+
+  // put in sleep mode
+  sleep();
+
+  // set frequency
+  setFrequency(frequency);
+
+  // set base addresses
+  writeRegister(REG_FIFO_TX_BASE_ADDR, 0);
+  writeRegister(REG_FIFO_RX_BASE_ADDR, 0);
+
+  // set LNA boost
+  writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03);
+
+  // set output power to 17 dBm
+  setTxPower(17);
+
+  // put in standby mode
+  idle();
+
+  return 1;
+}
+
+void LoRaClass::end()
+{
+  // put in sleep mode
+  sleep();
+
+  // stop SPI
+  SPI.end();
+}
+
+int LoRaClass::beginPacket(int implicitHeader)
+{
+  // put in standby mode
+  idle();
+
+  if (implicitHeader) {
+    implicitHeaderMode();
+  } else {
+    explicitHeaderMode();
+  }
+
+  // reset FIFO address and paload length
+  writeRegister(REG_FIFO_ADDR_PTR, 0);
+  writeRegister(REG_PAYLOAD_LENGTH, 0);
+
+  return 1;
+}
+
+int LoRaClass::endPacket()
+{
+  // put in TX mode
+  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);
+
+  // wait for TX done
+  while((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0);
+
+  // clear IRQ's
+  writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);
+
+  return 1;
+}
+
+int LoRaClass::parsePacket(int size)
+{
+  int packetLength = 0;
+  int irqFlags = readRegister(REG_IRQ_FLAGS);
+
+  if (size > 0) {
+    implicitHeaderMode();
+
+    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
+  } else {
+    explicitHeaderMode();
+  }
+
+  // clear IRQ's
+  writeRegister(REG_IRQ_FLAGS, irqFlags);
+
+  if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
+    // received a packet
+    _packetIndex = 0;
+
+    // read packet length
+    if (_implicitHeaderMode) {
+      packetLength = readRegister(REG_PAYLOAD_LENGTH);
+    } else {
+      packetLength = readRegister(REG_RX_NB_BYTES);
+    }
+
+    // set FIFO address to current RX address
+    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
+
+    // put in standby mode
+    idle();
+  } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {
+    // not currently in RX mode
+
+    // reset FIFO address
+    writeRegister(REG_FIFO_ADDR_PTR, 0);
+
+    // put in single RX mode
+    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);
+  }
+
+  return packetLength;
+}
+
+int LoRaClass::packetRssi()
+{
+  return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157));
+}
+
+float LoRaClass::packetSnr()
+{
+  return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25;
+}
+
+size_t LoRaClass::write(uint8_t byte)
+{
+  return write(&byte, sizeof(byte));
+}
+
+size_t LoRaClass::write(const uint8_t *buffer, size_t size)
+{
+  int currentLength = readRegister(REG_PAYLOAD_LENGTH);
+
+  // check size
+  if ((currentLength + size) > MAX_PKT_LENGTH) {
+    size = MAX_PKT_LENGTH - currentLength;
+  }
+
+  // write data
+  for (size_t i = 0; i < size; i++) {
+    writeRegister(REG_FIFO, buffer[i]);
+  }
+
+  // update length
+  writeRegister(REG_PAYLOAD_LENGTH, currentLength + size);
+
+  return size;
+}
+
+int LoRaClass::available()
+{
+  return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
+}
+
+int LoRaClass::read()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  _packetIndex++;
+
+  return readRegister(REG_FIFO);
+}
+
+int LoRaClass::peek()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  // store current FIFO address
+  int currentAddress = readRegister(REG_FIFO_ADDR_PTR);
+
+  // read
+  uint8_t b = readRegister(REG_FIFO);
+
+  // restore FIFO address
+  writeRegister(REG_FIFO_ADDR_PTR, currentAddress);
+
+  return b;
+}
+
+void LoRaClass::flush()
+{
+}
+
+void LoRaClass::onReceive(void(*callback)(int))
+{
+  _onReceive = callback;
+
+  if (callback) {
+    writeRegister(REG_DIO_MAPPING_1, 0x00);
+
+    attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
+  } else {
+    detachInterrupt(digitalPinToInterrupt(_dio0));
+  }
+}
+
+void LoRaClass::receive(int size)
+{
+  if (size > 0) {
+    implicitHeaderMode();
+
+    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
+  } else {
+    explicitHeaderMode();
+  }
+
+  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
+}
+
+void LoRaClass::idle()
+{
+  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
+}
+
+void LoRaClass::sleep()
+{
+  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
+}
+
+void LoRaClass::setTxPower(int level)
+{
+  if (level < 2) {
+    level = 2;
+  } else if (level > 17) {
+    level = 17;
+  }
+
+  writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2));
+}
+
+void LoRaClass::setFrequency(long frequency)
+{
+  _frequency = frequency;
+
+  uint64_t frf = ((uint64_t)frequency << 19) / 32000000;
+
+  writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
+  writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
+  writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
+}
+
+void LoRaClass::setSpreadingFactor(int sf)
+{
+  if (sf < 6) {
+    sf = 6;
+  } else if (sf > 12) {
+    sf = 12;
+  }
+
+  if (sf == 6) {
+    writeRegister(REG_DETECTION_OPTIMIZE, 0xc5);
+    writeRegister(REG_DETECTION_THRESHOLD, 0x0c);
+  } else {
+    writeRegister(REG_DETECTION_OPTIMIZE, 0xc3);
+    writeRegister(REG_DETECTION_THRESHOLD, 0x0a);
+  }
+
+  writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0));
+}
+
+void LoRaClass::setSignalBandwidth(long sbw)
+{
+  int bw;
+
+  if (sbw <= 7.8E3) {
+    bw = 0;
+  } else if (sbw <= 10.4E3) {
+    bw = 1;
+  } else if (sbw <= 15.6E3) {
+    bw = 2;
+  } else if (sbw <= 20.8E3) {
+    bw = 3;
+  } else if (sbw <= 31.25E3) {
+    bw = 4;
+  } else if (sbw <= 41.7E3) {
+    bw = 5;
+  } else if (sbw <= 62.5E3) {
+    bw = 6;
+  } else if (sbw <= 125E3) {
+    bw = 7;
+  } else if (sbw <= 250E3) {
+    bw = 8;
+  } else /*if (sbw <= 250E3)*/ {
+    bw = 9;
+  }
+
+  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4));
+}
+
+void LoRaClass::setCodingRate4(int denominator)
+{
+  if (denominator < 5) {
+    denominator = 5;
+  } else if (denominator > 8) {
+    denominator = 8;
+  }
+
+  int cr = denominator - 4;
+
+  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1));
+}
+
+void LoRaClass::setPreambleLength(long length)
+{
+  writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8));
+  writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
+}
+
+void LoRaClass::setSyncWord(int sw)
+{
+  writeRegister(REG_SYNC_WORD, sw);
+}
+
+void LoRaClass::crc()
+{
+  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
+}
+
+void LoRaClass::noCrc()
+{
+  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
+}
+
+byte LoRaClass::random()
+{
+  return readRegister(REG_RSSI_WIDEBAND);
+}
+
+void LoRaClass::setPins(int ss, int reset, int dio0)
+{
+  _ss = ss;
+  _reset = reset;
+  _dio0 = dio0;
+}
+
+void LoRaClass::dumpRegisters(Stream& out)
+{
+  for (int i = 0; i < 128; i++) {
+    out.print("0x");
+    out.print(i, HEX);
+    out.print(": 0x");
+    out.println(readRegister(i), HEX);
+  }
+}
+
+void LoRaClass::explicitHeaderMode()
+{
+  _implicitHeaderMode = 0;
+
+  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
+}
+
+void LoRaClass::implicitHeaderMode()
+{
+  _implicitHeaderMode = 1;
+
+  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
+}
+
+void LoRaClass::handleDio0Rise()
+{
+  int irqFlags = readRegister(REG_IRQ_FLAGS);
+
+  // clear IRQ's
+  writeRegister(REG_IRQ_FLAGS, irqFlags);
+
+  if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
+    // received a packet
+    _packetIndex = 0;
+
+    // read packet length
+    int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES);
+
+    // set FIFO address to current RX address
+    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
+
+    if (_onReceive) {
+      _onReceive(packetLength);
+    }
+
+    // reset FIFO address
+    writeRegister(REG_FIFO_ADDR_PTR, 0);
+  }
+}
+
+uint8_t LoRaClass::readRegister(uint8_t address)
+{
+  return singleTransfer(address & 0x7f, 0x00);
+}
+
+void LoRaClass::writeRegister(uint8_t address, uint8_t value)
+{
+  singleTransfer(address | 0x80, value);
+}
+
+uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value)
+{
+  uint8_t response;
+
+  digitalWrite(_ss, LOW);
+
+  SPI.beginTransaction(_spiSettings);
+  SPI.transfer(address);
+  response = SPI.transfer(value);
+  SPI.endTransaction();
+
+  digitalWrite(_ss, HIGH);
+
+  return response;
+}
+
+void LoRaClass::onDio0Rise()
+{
+  LoRa.handleDio0Rise();
+}
+
+LoRaClass LoRa;

          
A => libraries/AmebaLoRa/src/LoRa.h +93 -0
@@ 0,0 1,93 @@ 
+#ifndef LORA_H
+#define LORA_H
+
+#include <Arduino.h>
+#include <SPI.h>
+
+#if defined(BOARD_RTL8195A)
+
+#define LORA_DEFAULT_SS_PIN    0	//for realtek ameba 
+#define LORA_DEFAULT_RESET_PIN 9
+#define LORA_DEFAULT_DIO0_PIN  3	//for realtek ameba
+
+#elif defined(BOARD_RTL8710)
+
+#define LORA_DEFAULT_SS_PIN    5	//for realtek ameba 
+#define LORA_DEFAULT_RESET_PIN 2
+#define LORA_DEFAULT_DIO0_PIN  17	//for realtek ameba
+
+#else
+#endif
+
+class LoRaClass : public Stream {
+public:
+  LoRaClass();
+
+  int begin(long frequency);
+  void end();
+
+  int beginPacket(int implicitHeader = false);
+  int endPacket();
+
+  int parsePacket(int size = 0);
+  int packetRssi();
+  float packetSnr();
+
+  // from Print
+  virtual size_t write(uint8_t byte);
+  virtual size_t write(const uint8_t *buffer, size_t size);
+
+  // from Stream
+  virtual int available();
+  virtual int read();
+  virtual int peek();
+  virtual void flush();
+
+  void onReceive(void(*callback)(int));
+
+  void receive(int size = 0);
+  void idle();
+  void sleep();
+
+  void setTxPower(int level);
+  void setFrequency(long frequency);
+  void setSpreadingFactor(int sf);
+  void setSignalBandwidth(long sbw);
+  void setCodingRate4(int denominator);
+  void setPreambleLength(long length);
+  void setSyncWord(int sw);
+  void crc();
+  void noCrc();
+
+  byte random();
+
+  void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
+
+  void dumpRegisters(Stream& out);
+
+private:
+  void explicitHeaderMode();
+  void implicitHeaderMode();
+
+  void handleDio0Rise();
+
+  uint8_t readRegister(uint8_t address);
+  void writeRegister(uint8_t address, uint8_t value);
+  uint8_t singleTransfer(uint8_t address, uint8_t value);
+
+  static void onDio0Rise();
+
+private:
+  SPISettings _spiSettings;
+  int _ss;
+  int _reset;
+  int _dio0;
+  int _frequency;
+  int _packetIndex;
+  int _implicitHeaderMode;
+  void (*_onReceive)(int);
+};
+
+extern LoRaClass LoRa;
+
+#endif

          
A => libraries/AmebaMotors-1.0.0.zip +0 -0

        
A => libraries/AmebaMotors-1.0.1.zip +0 -0

        
A => libraries/AmebaMotors-1.0.2.zip +0 -0

        
A => libraries/AmebaMotors-1.0.3.zip +0 -0

        
A => libraries/AmebaMotors/Car2wd.cpp +190 -0
@@ 0,0 1,190 @@ 
+#include "Car2wd.h"
+#include "Arduino.h"
+
+#define CAR_ACTION_CHANGE 0x01
+#define CAR_SPEED_CHANGE  0x02
+
+/* The pwm output to enA and enB of L28N can only output to motoer with 2.5V~3V.
+ * It cannot make signaficantly forward right/left.
+ * We use solution that make the slow moter run a little then stop.
+ **/
+#define DELAY_TURN (1)
+
+// A thread which handle car's actions without interfear main thread.
+void carTask(const void *arg) {
+
+    Car2wd *pCar = (Car2wd *) arg;
+    os_event_t evt;
+
+    uint32_t dfactor;
+    uint32_t dport;
+    uint32_t dbitmask;
+
+    while(1) {
+        // wait signal for infinite timeout
+        evt = os_signal_wait(0, 0xFFFFFFFF);
+
+        if (evt.status != OS_EVENT_SIGNAL) {
+            continue;
+        }
+
+        if (evt.value.signals & CAR_SPEED_CHANGE) {
+            analogWrite(pCar->enA, pCar->currentSpeed);
+            analogWrite(pCar->enB, pCar->currentSpeed);
+
+            /* only apply to forward right/left and backward right/left
+             * The motor run time is 20ms, and stop time is 20ms~360ms.
+             *                           speed - min_speed
+             *     dfactor = 20 + 360 * -------------------
+             *                            255 - min_speed
+             */ 
+            dfactor = 20 + (360 * (pCar->currentSpeed - CAR_MIN_SPEED))/(255 - CAR_MIN_SPEED);
+        }
+
+        if (evt.value.signals & CAR_ACTION_CHANGE) {
+            switch(pCar->currentAction) {
+                case CAR_STOP:
+                    *portOutputRegister(pCar->in1_port) &= ~(pCar->in1_bitmask);
+                    *portOutputRegister(pCar->in2_port) &= ~(pCar->in2_bitmask);
+                    *portOutputRegister(pCar->in3_port) &= ~(pCar->in3_bitmask);
+                    *portOutputRegister(pCar->in4_port) &= ~(pCar->in4_bitmask);
+                    break;
+                case CAR_FORWARD:
+                case CAR_FORWARD_RIGHT:
+                case CAR_FORWARD_LEFT:
+                    *portOutputRegister(pCar->in1_port) |=  (pCar->in1_bitmask);
+                    *portOutputRegister(pCar->in2_port) &= ~(pCar->in2_bitmask);
+                    *portOutputRegister(pCar->in3_port) |=  (pCar->in3_bitmask);
+                    *portOutputRegister(pCar->in4_port) &= ~(pCar->in4_bitmask);
+                    if (pCar->currentAction == CAR_FORWARD_RIGHT) {
+                        dport = pCar->in3_port;
+                        dbitmask = pCar->in3_bitmask;
+                    }
+                    if (pCar->currentAction == CAR_FORWARD_LEFT) {
+                        dport = pCar->in1_port;
+                        dbitmask = pCar->in1_bitmask;
+                    }
+                    break;
+                case CAR_BACKWARD:
+                case CAR_BACKWARD_RIGHT:
+                case CAR_BACKWARD_LEFT:
+                    *portOutputRegister(pCar->in1_port) &= ~(pCar->in1_bitmask);
+                    *portOutputRegister(pCar->in2_port) |=  (pCar->in2_bitmask);
+                    *portOutputRegister(pCar->in3_port) &= ~(pCar->in3_bitmask);
+                    *portOutputRegister(pCar->in4_port) |=  (pCar->in4_bitmask);
+                    if (pCar->currentAction == CAR_BACKWARD_RIGHT) {
+                        dport = pCar->in4_port;
+                        dbitmask = pCar->in4_bitmask;
+                    }
+                    if (pCar->currentAction == CAR_BACKWARD_LEFT) {
+                        dport = pCar->in2_port;
+                        dbitmask = pCar->in2_bitmask;
+                    }
+                    break;
+                case CAR_ROTATE_RIGHT:
+                    *portOutputRegister(pCar->in1_port) |=  (pCar->in1_bitmask);
+                    *portOutputRegister(pCar->in2_port) &= ~(pCar->in2_bitmask);
+                    *portOutputRegister(pCar->in3_port) &= ~(pCar->in3_bitmask);
+                    *portOutputRegister(pCar->in4_port) |=  (pCar->in4_bitmask);
+                    break;
+                case CAR_ROTATE_LEFT:
+                    *portOutputRegister(pCar->in1_port) &= ~(pCar->in1_bitmask);
+                    *portOutputRegister(pCar->in2_port) |=  (pCar->in2_bitmask);
+                    *portOutputRegister(pCar->in3_port) |=  (pCar->in3_bitmask);
+                    *portOutputRegister(pCar->in4_port) &= ~(pCar->in4_bitmask);
+                    break;
+            }
+        }
+        if (pCar->currentAction >= CAR_FORWARD_RIGHT && pCar->currentAction <= CAR_BACKWARD_LEFT) {
+#if DELAY_TURN
+            while(1) {
+                *portOutputRegister(dport) |=  dbitmask;
+                evt = os_signal_wait(0, 20);
+                if (evt.status == OS_EVENT_SIGNAL) {
+                    os_signal_set(pCar->tid, evt.value.signals);
+                    break;
+                }
+
+                *portOutputRegister(dport) &= ~dbitmask;
+                evt = os_signal_wait(0, dfactor);
+                if (evt.status == OS_EVENT_SIGNAL) {
+                    *portOutputRegister(dport) |=  dbitmask;
+                    os_signal_set(pCar->tid, evt.value.signals);
+                    break;
+                }
+            }
+#else
+            *portOutputRegister(dport) &= ~dbitmask;
+#endif
+        }
+    }
+}
+
+Car2wd::Car2wd(int _enA, int _in1, int _in2, int _in3, int _in4, int _enB) {
+    enA = _enA;
+    in1 = _in1;
+    in2 = _in2;
+    in3 = _in3;
+    in4 = _in4;
+    enB = _enB;
+
+    currentAction = CAR_STOP;
+}
+
+Car2wd::~Car2wd() {
+    end();
+}
+
+void Car2wd::begin() {
+    // set all the motor control pins to outputs with value 0
+    pinMode(enA, OUTPUT);
+    analogWrite(enA, 0);
+    pinMode(enB, OUTPUT);
+    analogWrite(enB, 0);
+
+    pinMode(in1, OUTPUT);
+    pinMode(in2, OUTPUT);
+    pinMode(in3, OUTPUT);
+    pinMode(in4, OUTPUT);
+
+    // cache each pin's port and bit mask for faster gpio control
+    in1_port = digitalPinToPort(in1);
+    in1_bitmask = digitalPinToBitMask(in1);
+    in2_port = digitalPinToPort(in2);
+    in2_bitmask = digitalPinToBitMask(in2);
+    in3_port = digitalPinToPort(in3);
+    in3_bitmask = digitalPinToBitMask(in3);
+    in4_port = digitalPinToPort(in4);
+    in4_bitmask = digitalPinToBitMask(in4);
+
+    // create a thread to control the car
+    tid = os_thread_create(carTask, this, OS_PRIORITY_REALTIME, 1024);
+}
+
+void Car2wd::end() {
+    os_thread_terminate(tid);
+}
+
+void Car2wd::setAction(unsigned char action, unsigned char speed) {
+
+    int32_t sig = 0;
+
+    if (currentAction != action) {
+        currentAction = action;
+        sig |= CAR_ACTION_CHANGE;
+    }
+    if (currentSpeed != speed) {
+        currentSpeed = speed;
+        sig |= CAR_SPEED_CHANGE;
+    }
+    if (sig != 0) {
+        // send signal to car thread
+        os_signal_set(tid, sig);
+    }
+}
+
+void Car2wd::stop() {
+    setAction(CAR_STOP, currentSpeed);
+}
+
+

          
A => libraries/AmebaMotors/Car2wd.h +58 -0
@@ 0,0 1,58 @@ 
+#ifndef _CAR2WD_H_
+#define _CAR2WD_H_
+
+#include <stdint.h>
+#include "wiring_os.h"
+
+// The min pwm value. If value lower than this, then the motor won't move.
+#define CAR_MIN_SPEED 150
+
+#define CAR_STOP           0x00
+#define CAR_FORWARD        0x01
+#define CAR_BACKWARD       0x02
+#define CAR_ROTATE_RIGHT   0x03
+#define CAR_ROTATE_LEFT    0x04
+#define CAR_FORWARD_RIGHT  0x05
+#define CAR_FORWARD_LEFT   0x06
+#define CAR_BACKWARD_RIGHT 0x07
+#define CAR_BACKWARD_LEFT  0x08
+
+class Car2wd {
+public:
+    // These pins correspond pins of L298N brige. "_enA" and "_enB" has to be PWM pin.
+	Car2wd(int _enA, int _in1, int _in2, int _in3, int _in4, int _enB);
+    ~Car2wd();
+
+	void begin();
+    void end();
+
+    void setAction(unsigned char action, unsigned char speed=200);
+    void stop();
+
+    friend void carTask(const void *arg);
+
+private:
+    int enA;
+    int in1;
+    int in2;
+    int in3;
+    int in4;
+    int enB;
+
+    uint32_t in1_port;
+    uint32_t in1_bitmask;
+    uint32_t in2_port;
+    uint32_t in2_bitmask;
+    uint32_t in3_port;
+    uint32_t in3_bitmask;
+    uint32_t in4_port;
+    uint32_t in4_bitmask;
+
+    uint32_t tid;
+
+    unsigned char currentAction;
+    int           currentSpeed;
+};
+
+#endif
+

          
A => libraries/AmebaMotors/README.md +3 -0
@@ 0,0 1,3 @@ 
+This library provide examples of how to use L298N to control motors.
+It also applied to 2WD cars which includes the way to adding thread,
+thread signaling, register level gpio output, pwm control.
  No newline at end of file

          
A => libraries/AmebaMotors/examples/car2wd_basic/car2wd_basic.ino +39 -0
@@ 0,0 1,39 @@ 
+/*
+ *  This sketch shows how to use Car2wd class to control L298N on 2WD car
+ *
+ **/
+
+#include "Car2wd.h"
+
+Car2wd car(8,9,10,11,12,13);
+
+void setup() {
+  car.begin();
+}
+
+void loop() {
+  car.setAction(CAR_FORWARD);
+  delay(2000);
+  car.setAction(CAR_STOP);
+  delay(1000);
+  car.setAction(CAR_BACKWARD);
+  delay(2000);
+  car.setAction(CAR_STOP);
+  delay(1000);
+  car.setAction(CAR_FORWARD_RIGHT);
+  delay(2000);  
+  car.setAction(CAR_STOP);
+  delay(1000);
+  car.setAction(CAR_FORWARD_LEFT);
+  delay(2000);  
+  car.setAction(CAR_STOP);
+  delay(1000);
+  car.setAction(CAR_BACKWARD_LEFT);
+  delay(2000);  
+  car.setAction(CAR_STOP);
+  delay(1000);
+  car.setAction(CAR_BACKWARD_RIGHT);
+  delay(2000);  
+  car.setAction(CAR_STOP);
+  delay(1000);
+}
  No newline at end of file

          
A => libraries/AmebaMotors/examples/car2wd_digit_control/car2wd_digit_control.ino +199 -0
@@ 0,0 1,199 @@ 
+/*
+ * This sketch shows how to use L298N to control 2 wheels car (or 4 wheels by 2 phases)
+ *
+ * Materials needed:
+ *     L298N x 1
+ *     Car kit:
+ *         car body x 1
+ *         DC motors x 2 (or x 4)
+ *         wheels x 2 (or x4)
+ *
+ * L298N has 6 input controls pins:
+ *     ENA - control the strength of motor A
+ *     IN1 - control line of motor A
+ *     IN2 - control line of motor A
+ *     IN3 - control line of motor B
+ *     IN4 - control line of motor B
+ *     ENA - control the strength of motor B
+ *
+ * IN1:1 IN2:0 - motor clockwise rotation
+ * IN1:0 IN2:1 - motor counter-clockwirse rotation
+ * IN1:0 IN2:0 - motor stops
+ * 
+ * The wheel may have poor grip or the motors has poor sensibility.
+ * It makes "forward right" cannot performed by makeing 2 motors with different 
+ * motor rotating speed. To achieve "forward right" we can just make right 
+ * wheels work in partial of times.
+ **/
+
+// motor left
+int enA = 8;
+int in1 = 9;
+int in2 = 10;
+
+// motor right
+int in3 = 11;
+int in4 = 12;
+int enB = 13;
+
+uint32_t in1_port, in1_bitmask;
+uint32_t in2_port, in2_bitmask;
+uint32_t in3_port, in3_bitmask;
+uint32_t in4_port, in4_bitmask;
+
+void initMotor()
+{
+  // set all the motor control pins to outputs with value 0
+  pinMode(enA, OUTPUT);
+  analogWrite(enA, 0);
+  pinMode(enB, OUTPUT);
+  analogWrite(enB, 0);
+
+  pinMode(in1, OUTPUT);
+  digitalWrite(in1, LOW);
+  pinMode(in2, OUTPUT);
+  digitalWrite(in2, LOW);
+  pinMode(in3, OUTPUT);
+  digitalWrite(in3, LOW);
+  pinMode(in4, OUTPUT);
+  digitalWrite(in4, LOW);
+
+  in1_port = digitalPinToPort(in1);
+  in1_bitmask = digitalPinToBitMask(in1);
+  in2_port = digitalPinToPort(in2);
+  in2_bitmask = digitalPinToBitMask(in2);
+  in3_port = digitalPinToPort(in3);
+  in3_bitmask = digitalPinToBitMask(in3);
+  in4_port = digitalPinToPort(in4);
+  in4_bitmask = digitalPinToBitMask(in4);
+}
+
+void motorsControl(int speedLeft, int speedRight) {
+  analogWrite(enA, abs(speedLeft));
+  analogWrite(enB, abs(speedRight));
+
+  if (speedLeft > 0) {
+    *portOutputRegister(in1_port) |=  in1_bitmask;
+    *portOutputRegister(in2_port) &= ~in2_bitmask;
+  } else if (speedLeft < 0) {
+    *portOutputRegister(in1_port) &= ~in1_bitmask;
+    *portOutputRegister(in2_port) |=  in2_bitmask;
+  } else {
+    *portOutputRegister(in1_port) &= ~in1_bitmask;
+    *portOutputRegister(in2_port) &= ~in2_bitmask;
+  }
+
+  if (speedRight > 0) {
+    *portOutputRegister(in3_port) |=  in3_bitmask;
+    *portOutputRegister(in4_port) &= ~in4_bitmask;
+  } else if (speedRight < 0) {
+    *portOutputRegister(in3_port) &= ~in3_bitmask;
+    *portOutputRegister(in4_port) |=  in4_bitmask;
+  } else {
+    *portOutputRegister(in3_port) &= ~in3_bitmask;
+    *portOutputRegister(in4_port) &= ~in4_bitmask;
+  }
+}
+
+void forward() {
+  motorsControl(200,200);
+}
+
+void backward() {
+  motorsControl(-200,-200);
+}
+
+void stop() {
+  motorsControl(0,0);
+}
+
+void rotateRight() {
+  motorsControl(200,-200);
+}
+
+void rotateLeft() {
+  motorsControl(-200,200);
+}
+
+void setup()
+{
+  initMotor();
+}
+
+void demo1() {
+  forward();
+  delay(2000);
+  stop();
+  delay(1000);
+
+  backward();
+  delay(2000);
+  stop();
+  delay(1000);
+
+  rotateRight();
+  delay(2000);
+  stop();
+  delay(1000);
+
+  rotateLeft();
+  delay(2000);
+  stop();
+  delay(1000);
+}
+
+void demo2() {
+  /*  This function demo "forward right", "forward left", "backward left", "backward right".
+   *  If the motors on the car have good sensitiy on input signal and high grip on the ground, 
+   *  then you can just adjust the ratio of 2 pwm values.
+   *    Ex. motorControl(255,150) makes car forward right.
+   *  Otherwise, we can just adjust the working ratio of tire.
+   *    Ex. work 20ms, then stop 80ms, ...
+   **/
+
+  forward();
+  // forward right
+  for (int i=0; i<40; i++) {
+    *portOutputRegister(in3_port) |=  in3_bitmask;
+    delay(20);
+    *portOutputRegister(in3_port) &= ~in3_bitmask;
+    delay(80);
+  }
+  *portOutputRegister(in3_port) |=  in3_bitmask;
+  // forward left
+  for (int i=0; i<40; i++) {
+    *portOutputRegister(in1_port) |=  in1_bitmask;
+    delay(20);
+    *portOutputRegister(in1_port) &= ~in1_bitmask;
+    delay(80);
+  }
+  *portOutputRegister(in1_port) |=  in1_bitmask;
+  stop();
+  delay(1000);
+
+  backward();
+  // backward left
+  for (int i=0; i<40; i++) {
+    *portOutputRegister(in2_port) |=  in2_bitmask;
+    delay(20);
+    *portOutputRegister(in2_port) &= ~in2_bitmask;
+    delay(80);
+  }
+  *portOutputRegister(in2_port) |=  in2_bitmask;
+  // backward right
+  for (int i=0; i<40; i++) {
+    *portOutputRegister(in4_port) |=  in4_bitmask;
+    delay(20);
+    *portOutputRegister(in4_port) &= ~in4_bitmask;
+    delay(80);
+  }
+  *portOutputRegister(in4_port) |=  in4_bitmask;
+  stop();
+  delay(1000);
+}
+
+void loop()
+{
+  demo1();
+  demo2();
+}
  No newline at end of file

          
A => libraries/AmebaMotors/examples/car2wd_mobile_control/car2wd_mobile_control.ino +195 -0
@@ 0,0 1,195 @@ 
+/*
+ * This sketch use mobile phone to control car
+ *
+ * This sketch bring up wifi in AP mode.
+ * User can change mobile phone's wifi AP with correspond ssid.
+ * Device open TCP socket on port 5001, and mobile app connect to
+ * it on default AP IP 192.168.1.1
+ * Mobile app send raw data when control data is changed.
+ * The Raw data has these format:
+ *     <direction>:<speed>
+ *
+ *     Y:12
+ *     It's forward with max speed. (Speed has range -12~12)
+ *
+ *     Y:-6
+ *     It's backward with half speed.
+ *
+ *     X:12
+ *     It's right rotate with max speed.
+ *
+ *     Y:12
+ *     X:-6
+ *     It's forward left with max speed. (When X and Y has speed value, use Y speed only)
+ *     
+ **/
+
+#include <WiFi.h>
+#include <WiFiUdp.h>
+#include <Car2wd.h>
+
+char ssid[] = "mycar";        // Set the AP's SSID
+char pass[] = "12345678";     // Set the AP's password
+char channel[] = "6";         // Set the AP's channel
+int status = WL_IDLE_STATUS;  // the Wifi radio's status
+
+Car2wd car(8,9,10,11,12,13);
+
+int port_ctrl = 5001;
+//#define CTRL_USE_TCP
+#ifdef CTRL_USE_TCP
+WiFiServer server(port_ctrl);
+WiFiClient client;
+#else
+WiFiUDP udp;
+#endif
+
+uint8_t buffer[256];
+
+void setup() {
+  car.begin();
+
+  status = WiFi.status();
+
+  // attempt to start AP:
+  while (status != WL_CONNECTED) {
+    Serial.print("Attempting to start AP with SSID: ");
+    Serial.println(ssid);
+    status = WiFi.apbegin(ssid, pass, channel);
+    if (status == WL_CONNECTED) {
+      break;
+    }
+    delay(10000);
+  }
+  //AP MODE already started:
+  Serial.println("AP mode already started");
+
+#ifdef CTRL_USE_TCP
+  server.begin();
+#else
+  udp.begin(port_ctrl);
+#endif
+}
+
+#ifdef CTRL_USE_TCP
+void tcpHandler() {
+  int len;
+  if (!client.connected()) {
+    client = server.available();
+  }
+  while (client.connected()) {
+    memset(buffer, 0, 256);
+    len = client.read(buffer, sizeof(buffer)-1);
+    if (len > 0) {
+      printf("[TCP]:%s\r\n", buffer);
+      buffer[len] = '\0';
+    }
+    handleData((const char *)buffer);
+  }
+  Serial.println("control socket broken");
+  memset(buffer, 0, 256);
+  handleData((const char *)buffer);
+  delay(1000);  
+}
+#else
+void udpHandler() {
+  int len;
+  while(1) {
+    memset(buffer, 0, sizeof(buffer));
+    len = udp.read(buffer, sizeof(buffer)-1);
+    if (len > 0) {
+      printf("[UDP]:%s\r\n", buffer);
+      buffer[len] = '\0';
+    }
+    handleData((const char *)buffer);
+    delay(100);
+  }
+}
+#endif
+
+void loop() {
+#ifdef CTRL_USE_TCP
+  tcpHandler();
+#else
+  udpHandler();
+#endif
+}
+
+int speedMapping(int rawspeed) {
+  /* Original data range: -12 ~ + 12
+   * Convert it to PWM range: 150~255
+   **/
+  if (rawspeed == 0) {
+    return 0;
+  } else {
+    return (rawspeed * 105) / 12 + ((rawspeed > 0) ? 150 : -150);
+  }
+}
+
+int lastXspeed = 0;
+int lastYspeed = 0;
+void handleData(const char *buf) {
+  int len;
+  bool xchange = false, ychange = false;;
+  int xspeed = 0, yspeed = 0;
+
+  if (buf[0] != '\0') {
+    len = strlen(buf);
+    for (int i=0; i<len; i++) {
+      if (buf[i] == 'X') {
+        xspeed = lastXspeed = speedMapping(parseSpeed(&buf[i+2]));
+        xchange = true;
+      }
+      if (buf[i] == 'Y') {
+        yspeed = lastYspeed = speedMapping(parseSpeed(&buf[i+2]));
+        ychange = true;
+      }
+    }
+    if (xchange == false) {
+      xspeed = lastXspeed;
+    }
+    if (ychange == false) {
+      yspeed = lastYspeed;
+    }
+  } else {
+    xspeed = lastXspeed = 0;
+    yspeed = lastYspeed = 0;
+  }
+
+  if (xspeed == 0 && yspeed == 0) {
+    car.stop();
+  } else if (xspeed == 0 && yspeed > 0) {
+    car.setAction(CAR_FORWARD, abs(yspeed));
+  } else if (xspeed == 0 && yspeed < 0) {
+    car.setAction(CAR_BACKWARD, abs(yspeed));
+  } else if (xspeed > 0 && yspeed < 0 ) {
+    car.setAction(CAR_BACKWARD_RIGHT, abs(yspeed));
+  } else if (xspeed > 0 && yspeed == 0) {
+    car.setAction(CAR_ROTATE_RIGHT, abs(xspeed));
+  } else if (xspeed > 0 && yspeed > 0 ) {
+    car.setAction(CAR_FORWARD_RIGHT, abs(yspeed));
+  } else if (xspeed < 0 && yspeed < 0 ) {
+    car.setAction(CAR_BACKWARD_LEFT, abs(yspeed));
+  } else if (xspeed < 0 && yspeed == 0) {
+    car.setAction(CAR_ROTATE_LEFT, abs(xspeed));
+  } else if (xspeed < 0 && yspeed > 0 ) {
+    car.setAction(CAR_FORWARD_LEFT, abs(yspeed));
+  }
+}
+
+int parseSpeed(const char *buf) {
+  int s = 0;
+  if (buf[0] == '-') {
+    s = buf[1] - '0';
+    if (buf[2] >= '0' && buf[2] <= '9') {
+      s = s * 10 + (buf[2] - '0');
+    }
+    s = -s;
+  } else {
+    s = buf[0] - '0';
+    if (buf[1] >= '0' && buf[1] <= '9') {
+      s = s * 10 + (buf[1] - '0');
+    }
+  }
+  return s;
+}

          
A => libraries/AmebaMotors/examples/car2wd_mobile_plus_uvc/car2wd_mobile_plus_uvc.ino +198 -0
@@ 0,0 1,198 @@ 
+/*
+  This sketch use mobile phone to control car with streaming video.
+
+  At first Ameba connect to AP.
+  Then Ameba open UVC service.
+  After UVC service is enabled, user can use rtsp player and open streaming with address:
+    rtsp://192.168.1.123/test.sdp
+  (NOTE: the IP address depends on Ameba's IP")
+
+  For car related control method, please refer example car2wd_mobile_control
+*/
+
+#include <WiFi.h>
+#include <WiFiUdp.h>
+#include <Car2wd.h>
+#include <UVC.h>
+
+char ssid[] = "mycar";        // Set the AP's SSID
+char pass[] = "12345678";     // Set the AP's password
+char channel[] = "6";         // Set the AP's channel
+int status = WL_IDLE_STATUS;  // the Wifi radio's status
+
+Car2wd car(8,9,10,11,12,13);
+
+int port_ctrl = 5001;
+#define CTRL_USE_TCP
+#ifdef CTRL_USE_TCP
+WiFiServer server(port_ctrl);
+WiFiClient client;
+#else
+WiFiUDP udp;
+#endif
+
+uint8_t buffer[256];
+
+void setup() {
+  car.begin();
+
+  status = WiFi.status();
+
+  // attempt to start AP:
+  while (status != WL_CONNECTED) {
+    Serial.print("Attempting to start AP with SSID: ");
+    Serial.println(ssid);
+    status = WiFi.apbegin(ssid, pass, channel);
+    if (status == WL_CONNECTED) {
+      break;
+    }
+    delay(10000);
+  }
+
+  //AP MODE already started:
+  Serial.println("AP mode already started");
+
+  // Default setting is motion jpeg with 320x240 resolution and frame rate is 30fps
+  UVC.begin();
+  // Try below setting if you want better resolution
+  //UVC.begin(UVC_MJPEG, 640, 480, 30, 0);
+
+  // wait until UVC is ready for streaming
+  while (!UVC.available()) {
+    delay(100);
+  }
+  Serial.println("UVC is ready");
+  Serial.println("Open your rtsp player with this address:");
+  Serial.print("\trtsp://");
+  Serial.print(WiFi.localIP());
+  Serial.println("/test.sdp");
+
+#ifdef CTRL_USE_TCP
+  server.begin();
+#else
+  udp.begin(port_ctrl);
+#endif
+}
+
+#ifdef CTRL_USE_TCP
+void tcpHandler() {
+  int len;
+  if (!client.connected()) {
+    client = server.available();
+  }
+  while (client.connected()) {
+    memset(buffer, 0, 256);
+    len = client.read(buffer, sizeof(buffer)-1);
+    if (len > 0) {
+      printf("[TCP]:%s\r\n", buffer);
+      buffer[len] = '\0';
+    }
+    handleData((const char *)buffer);
+  }
+  Serial.println("control socket broken");
+  memset(buffer, 0, 256);
+  handleData((const char *)buffer);
+  delay(1000);  
+}
+#else
+void udpHandler() {
+  int len;
+  while(1) {
+    memset(buffer, 0, sizeof(buffer));
+    len = udp.read(buffer, sizeof(buffer)-1);
+    if (len > 0) {
+      printf("[UDP]:%s\r\n", buffer);
+      buffer[len] = '\0';
+    }
+    handleData((const char *)buffer);
+    delay(100);
+  }
+}
+#endif
+
+void loop() {
+#ifdef CTRL_USE_TCP
+  tcpHandler();
+#else
+  udpHandler();
+#endif
+}
+
+int speedMapping(int rawspeed) {
+  /* Original data range: -12 ~ + 12
+   * Convert it to PWM range: 150~255
+   **/
+  if (rawspeed == 0) {
+    return 0;
+  } else {
+    return (rawspeed * 105) / 12 + ((rawspeed > 0) ? 150 : -150);
+  }
+}
+
+int lastXspeed = 0;
+int lastYspeed = 0;
+void handleData(const char *buf) {
+  int len;
+  bool xchange = false, ychange = false;;
+  int xspeed = 0, yspeed = 0;
+
+  if (buf[0] != '\0') {
+    len = strlen(buf);
+    for (int i=0; i<len; i++) {
+      if (buf[i] == 'X') {
+        xspeed = lastXspeed = speedMapping(parseSpeed(&buf[i+2]));
+        xchange = true;
+      }
+      if (buf[i] == 'Y') {
+        yspeed = lastYspeed = speedMapping(parseSpeed(&buf[i+2]));
+        ychange = true;
+      }
+    }
+    if (xchange == false) {
+      xspeed = lastXspeed;
+    }
+    if (ychange == false) {
+      yspeed = lastYspeed;
+    }
+  } else {
+    xspeed = lastXspeed = 0;
+    yspeed = lastYspeed = 0;
+  }
+
+  if (xspeed == 0 && yspeed == 0) {
+    car.stop();
+  } else if (xspeed == 0 && yspeed > 0) {
+    car.setAction(CAR_FORWARD, abs(yspeed));
+  } else if (xspeed == 0 && yspeed < 0) {
+    car.setAction(CAR_BACKWARD, abs(yspeed));
+  } else if (xspeed > 0 && yspeed < 0 ) {
+    car.setAction(CAR_BACKWARD_RIGHT, abs(yspeed));
+  } else if (xspeed > 0 && yspeed == 0) {
+    car.setAction(CAR_ROTATE_RIGHT, abs(xspeed));
+  } else if (xspeed > 0 && yspeed > 0 ) {
+    car.setAction(CAR_FORWARD_RIGHT, abs(yspeed));
+  } else if (xspeed < 0 && yspeed < 0 ) {
+    car.setAction(CAR_BACKWARD_LEFT, abs(yspeed));
+  } else if (xspeed < 0 && yspeed == 0) {
+    car.setAction(CAR_ROTATE_LEFT, abs(xspeed));
+  } else if (xspeed < 0 && yspeed > 0 ) {
+    car.setAction(CAR_FORWARD_LEFT, abs(yspeed));
+  }
+}
+
+int parseSpeed(const char *buf) {
+  int s = 0;
+  if (buf[0] == '-') {
+    s = buf[1] - '0';
+    if (buf[2] >= '0' && buf[2] <= '9') {
+      s = s * 10 + (buf[2] - '0');
+    }
+    s = -s;
+  } else {
+    s = buf[0] - '0';
+    if (buf[1] >= '0' && buf[1] <= '9') {
+      s = s * 10 + (buf[1] - '0');
+    }
+  }
+  return s;
+}
  No newline at end of file

          
A => libraries/AmebaMotors/examples/car2wd_mpu6050_control/car2wd_mpu6050_control.ino +304 -0
@@ 0,0 1,304 @@ 
+/*
+   This sketch shows how to use control car with MPU6050 6 axis gyro/accelerometer,
+   and send out results via UDP
+
+   This sketch requires library include Ameba version of I2Cdev and MPU6050
+
+   We enable MPU (Motion Processing Tech) of MPU6050 that it can trigger interrupt with correspond sample rate.
+   And then gather the data from fifo and convert it to yaw/pitch/poll values.
+   We only need pitch and poll, convert it to car data format and send out via UDP.
+
+   To start control car, you need roll Ameba twice within 3 seconds.
+
+   The correspond car use Ameba example "car2wd_mobile_control".
+
+ **/
+
+#include <WiFi.h>
+#include "I2Cdev.h"
+#include "MPU6050_6Axis_MotionApps20.h"
+#include "Wire.h"
+
+/* WiFi related variables*/
+char ssid[] = "mycar";          // mycar ssid
+char pass[] = "12345678";       // mycar password
+IPAddress server(192,168,1,1);  // numeric IP for mycar AP mode
+int status = WL_IDLE_STATUS;
+
+WiFiClient client;
+char sendbuf[12];
+
+/* MPU6050 related variables */
+MPU6050 mpu;
+
+// MPU control/status vars
+bool dmpReady = false;  // set true if DMP init was successful
+uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
+uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
+uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
+uint16_t fifoCount;     // count of all bytes currently in FIFO
+uint8_t fifoBuffer[64]; // FIFO storage buffer
+
+// orientation/motion vars
+Quaternion q;           // [w, x, y, z]         quaternion container
+VectorFloat gravity;    // [x, y, z]            gravity vector
+float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
+
+/* car control variable */
+int carx = 0, cary = 0;
+int prev_carx = 0, prev_cary = 0;
+uint32_t timestamp = 0, last_send_timestamp = 0;
+
+volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
+void dmpDataReady() {
+    mpuInterrupt = true;
+}
+
+void setup() {
+  checkAndReconnectServer();
+  initMPU6050();
+}
+
+void loop() {
+  if (!dmpReady) return; // if programming failed, don't try to do anything
+
+  safeWaitMPU6050();
+  mpuIntStatus = mpu.getIntStatus();
+  fifoCount = mpu.getFIFOCount();
+
+  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
+    mpu.resetFIFO();
+    Serial.println(F("FIFO overflow!"));
+  } else if (mpuIntStatus & 0x02 && fifoCount >= packetSize) {
+    while (fifoCount >= packetSize){
+      mpu.getFIFOBytes(fifoBuffer, packetSize);
+      fifoCount -= packetSize;
+    }
+
+    // get Euler angles
+    mpu.dmpGetQuaternion(&q, fifoBuffer);
+    mpu.dmpGetGravity(&gravity, &q);
+    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
+
+    if (hasStarted(ypr[1], ypr[2])) {
+      mapPitchRolltoXY(ypr[1], ypr[2], &carx, &cary);
+    } else {
+      carx = cary = 0;
+    }
+  
+    memset(sendbuf, 0, 12);
+    timestamp = millis();
+    if ((timestamp - last_send_timestamp) > 500 || (carx != prev_carx && cary != prev_cary)) {
+      // If there is no value changed after 500ms, then send command to keepalive.
+      sprintf(sendbuf, "X:%dY:%d", carx, cary);
+    } else if (carx != prev_carx) {
+      sprintf(sendbuf, "X:%d", carx);
+    } else if (cary != prev_cary) {
+      sprintf(sendbuf, "Y:%d", cary);
+    }
+    if (strlen(sendbuf) > 0) {
+      if (checkAndReconnectServer()) {
+        safeResetMPU6050();
+      }
+      client.write(sendbuf, strlen(sendbuf));
+      last_send_timestamp = timestamp;
+      delay(10);
+
+      // ignore previous interrupt
+    }
+    prev_carx = carx;
+    prev_cary = cary;
+
+    mpuInterrupt = false;
+  }
+}
+
+#define CAR_STATE_OFF             0
+#define CAR_STATE_OFF_FIRST_ROLL  1
+#define CAR_STATE_OFF_FIRST_FLAT  2
+#define CAR_STATE_OFF_SECOND_ROLL 3
+#define CAR_STATE_ON              4
+
+// If user cannot complete action within 3s, then break to init state
+#define CAR_STATE_BREAK_TIMEOUT 3000
+
+int carState = CAR_STATE_OFF;
+uint32_t initChangeStateTimestamp = 0;
+
+int isFlat(float pitch, float roll) {
+  return (pitch < 24 && pitch > -24 && roll < 24 && roll > -24);
+}
+
+int isRoll(float pitch, float roll) {
+  return (pitch > 70 || pitch < -70 || roll > 70 || roll < -70);
+}
+
+// return 1 if we can control car
+int hasStarted(float pitch, float roll) {
+  int ret = 0;
+  pitch = pitch * 180 / M_PI;
+  roll = roll * 180 / M_PI;
+  switch(carState) {
+    case CAR_STATE_OFF:
+      if (isRoll(pitch, roll)) {
+        carState = CAR_STATE_OFF_FIRST_ROLL;
+        initChangeStateTimestamp = millis();
+      }
+      ret = 0;
+      break;
+    case CAR_STATE_OFF_FIRST_ROLL:
+      if (millis() - initChangeStateTimestamp > CAR_STATE_BREAK_TIMEOUT) {
+        carState = CAR_STATE_OFF;
+      } else {
+        if (isFlat(pitch, roll)) {
+          carState = CAR_STATE_OFF_FIRST_FLAT;
+        }
+      }
+      ret = 0;
+      break;
+    case CAR_STATE_OFF_FIRST_FLAT:
+      if (millis() - initChangeStateTimestamp > CAR_STATE_BREAK_TIMEOUT) {
+        carState = CAR_STATE_OFF;
+      } else {
+        if (isRoll(pitch, roll)) {
+          carState = CAR_STATE_OFF_SECOND_ROLL;
+        }
+      }
+      ret = 0;
+      break;
+    case CAR_STATE_OFF_SECOND_ROLL:
+      if (millis() - initChangeStateTimestamp > CAR_STATE_BREAK_TIMEOUT) {
+        carState = CAR_STATE_OFF;
+        ret = 0;
+      } else {
+        if (isFlat(pitch, roll)) {
+          carState = CAR_STATE_ON;
+          Serial.println("ON");
+          ret = 1;
+        } else {
+          ret = 0;
+        }
+      }
+      break;
+    case CAR_STATE_ON:
+      ret = 1;
+      break;
+  }
+
+  return ret;
+}
+
+int mapRange(float angle) {
+  /* Here is the mapping table.
+   * Pitch/Roll(degree): -48...-24...24...48
+   * Car:                -12.....0....0...12
+   */
+  if (angle < -48) {
+    return -12;
+  } else if ( angle >= -48 && angle < -24) {
+    return ((angle + 24)) / 2;
+  } else if ( angle > 24 && angle <= 48) {
+    return (angle - 24) / 2;
+  } else if (angle > 48) {
+    return 12;
+  } else {
+    return 0;
+  }
+}
+
+void mapPitchRolltoXY(float pitch, float roll, int *carx, int *cary) {
+  pitch = pitch * 180 / M_PI;
+  roll = roll * 180 / M_PI;
+  *carx = -mapRange(pitch);
+  *cary = -mapRange(roll);
+}
+
+int checkAndReconnectServer() {
+  int ret = 0;
+  status = WiFi.status();
+  if (status != WL_CONNECTED) {
+    while (status != WL_CONNECTED) {
+      Serial.print("Attempting to connect to SSID: ");
+      Serial.println(ssid);
+      // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
+      status = WiFi.begin(ssid, pass);
+      if (status == WL_CONNECTED) {
+        break;
+      }
+      Serial.println("Reconnect to wifi...");
+      delay(1000);
+    }
+    Serial.println("Connected to wifi");
+    ret = 1;
+  }
+  if ( !client.connected()) {
+    while (!client.connect(server, 5001)) {
+      Serial.println("reconnect to server...");
+      delay(1000);
+    }
+    Serial.println("connected to server");  
+    ret = 1;
+  }
+  return ret;
+}
+
+void initMPU6050() {
+  Wire.begin();
+  Wire.setClock(400000); // 400kHz I2C clock (200kHz if CPU is 8MHz).
+
+  Serial.println(F("Initializing I2C devices..."));
+  mpu.initialize();
+
+  Serial.println(F("Testing device connections..."));
+  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
+
+  Serial.println(F("Initializing DMP..."));
+  devStatus = mpu.dmpInitialize();
+
+  // make sure it worked (returns 0 if so)
+  if (devStatus == 0) {
+    // turn on the DMP, now that it's ready
+    Serial.println(F("Enabling DMP..."));
+    mpu.setDMPEnabled(true);
+
+    // enable Arduino interrupt detection
+    Serial.println(F("Enabling interrupt detection (Ameba D16 pin)..."));
+    attachInterrupt(16, dmpDataReady, RISING);
+    mpuIntStatus = mpu.getIntStatus();
+
+    // set our DMP Ready flag so the main loop() function knows it's okay to use it
+    Serial.println(F("DMP ready! Waiting for first interrupt..."));
+    dmpReady = true;
+
+    // get expected DMP packet size for later comparison
+    packetSize = mpu.dmpGetFIFOPacketSize();
+
+    mpu.setRate(5); // 1khz / (1 + 5) = 166 Hz
+  } else {
+    // ERROR!
+    // 1 = initial memory load failed
+    // 2 = DMP configuration updates failed
+    // (if it's going to break, usually the code will be 1)
+    Serial.print(F("DMP Initialization failed (code "));
+    Serial.print(devStatus);
+    Serial.println(F(")"));
+  }  
+}
+
+void safeWaitMPU6050() {
+  while (!mpuInterrupt) {
+    os_thread_yield(); // without yield, the empty busy loop might make CPU behave un-expected
+  }  
+  mpuInterrupt = false;
+}
+
+void safeResetMPU6050() {
+  /* If dmp interrupt happends at I2C send/recv, then MPU6050 would hang and can only recover by plug out/in VCC.
+   * To avoid this happen we wait next interrupt and then reset buffer */
+  mpuInterrupt = false;
+    while (!mpuInterrupt) {
+    os_thread_yield(); // without yield, the empty busy loop might make CPU behave un-expected
+  }
+  mpu.resetFIFO();
+  mpuInterrupt = false;
+}
  No newline at end of file

          
A => libraries/AmebaMotors/examples/car2wd_web_control/car2wd_web_control.ino +111 -0
@@ 0,0 1,111 @@ 
+/*
+ * This sketch shows how to control car on web page (via http protocol)
+ *
+ * At first we connect device to AP, start a web server at port 80.
+ * In client side we input device's IP at browser, and a page is show up.
+ * There are several buttons which can control the car.
+ * As we type one of these buttons, a request is append at the hyper link.
+ * And device parse the link and do correspond actions.
+ * 
+ * The web control part reference from http://www.instructables.com/id/Internet-Controlled-RC-Car/
+ */
+
+#include <WiFi.h>
+#include "Car2wd.h"
+
+char ssid[] = "yourNetwork";      // your network SSID (name)
+char pass[] = "secretPassword";   // your network password
+int keyIndex = 0;                 // your network key Index number (needed only for WEP)
+
+int status = WL_IDLE_STATUS;
+WiFiServer server(80);
+
+Car2wd car(8,9,10,11,12,13);
+
+void setup() {
+  car.begin();
+
+  // attempt to connect to Wifi network:
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to Network named: ");
+    Serial.println(ssid);
+
+    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
+    status = WiFi.begin(ssid, pass);
+    if (status == WL_CONNECTED) {
+      break;
+    }
+    // wait 10 seconds for connection:
+    delay(10000);
+  }
+
+  server.begin();
+}
+
+char buffer[1024];
+void loop() {
+  WiFiClient client = server.available();
+  while (client.connected()) {
+    int n = client.read((uint8_t*)(&buffer[0]), sizeof(buffer));
+
+    client.println("HTTP/1.1 200 OK"); 
+    client.println("Content-Type: text/html");
+    client.println();
+    client.println("<HTML>");
+    client.println("<HEAD>");
+    client.println("<TITLE>Internet Controlled 2WD Car</TITLE>");
+    client.println("<STYLE>");
+    client.println("body{margin:50px 0px; padding:0px; text-align:center;}");
+    client.println("h1{text-align: center; font-family:\"Trebuchet MS\",Arial, Helvetica, sans-serif; font-size:24px;}");
+    client.println("a{text-decoration:none; width:75px; height:50px; border-color:black; font-family:\"Trebuchet MS\",Arial, Helvetica, sans-serif; padding:6px; background-color:#aaaaaa; text-align:center; border-radius:10px 10px 10px; font-size:24px;}");
+    client.println("a:link {color:white;}");
+    client.println("a:visited {color:white;}");
+    client.println("a:hover {color:red;}");
+    client.println("a:active {color:white;}");
+    client.println("</STYLE>");
+    client.println("</HEAD>");
+    client.println("<BODY>");
+    client.println("<H1>Internet Controlled 2WD Car</H1>");
+    client.println("<br />");
+    client.println("<br />");        
+    client.println("<a href=\"/?forwardLeft\"\">FORWARD LEFT</a>");
+    client.println(" ");
+    client.println("<a href=\"/?forward\"\">FORWARD</a>");  
+    client.println(" ");      
+    client.println("<a href=\"/?forwardRight\"\">FORWARD RIGHT</a>");
+    client.println("<br />");
+    client.println("<br />");
+    client.println("<br />");
+    client.println("<a href=\"/?stop\"\">STOP</a>");
+    client.println("<br />");
+    client.println("<br />");
+    client.println("<br />");
+    client.println("<a href=\"/?backwardLeft\"\">BACKWARD LEFT</a>");
+    client.println(" ");
+    client.println("<a href=\"/?backward\"\">BACK</a>");
+    client.println(" ");  
+    client.println("<a href=\"/?backwardRight\"\">BACKWARD RIGHT</a>");
+    client.println("</BODY>");
+    client.println("</HTML>");
+
+    //Stop loading the website     
+    delay(1);
+    client.stop();
+
+    if(strstr(buffer, "?stop") != NULL) {
+      car.stop();
+    } else if(strstr(buffer, "?forwardLeft") != NULL) {
+      car.setAction(CAR_FORWARD_LEFT);
+    } else if(strstr(buffer, "?forwardRight") != NULL) {
+      car.setAction(CAR_FORWARD_RIGHT);
+    } else if(strstr(buffer, "?backwardLeft") != NULL) {
+      car.setAction(CAR_BACKWARD_LEFT);
+    } else if(strstr(buffer, "?backwardRight") != NULL) {
+      car.setAction(CAR_BACKWARD_RIGHT);
+    } else if(strstr(buffer, "?forward") != NULL) {
+      car.setAction(CAR_FORWARD);
+    } else if(strstr(buffer, "?backward") != NULL) {
+      car.setAction(CAR_BACKWARD);
+    }
+  }
+}
  No newline at end of file

          
A => libraries/AmebaMotors/examples/misc/car2wd_2_controller/car2wd_2_controller.ino +222 -0
@@ 0,0 1,222 @@ 
+#include <WiFi.h>
+#include <WiFiUdp.h>
+#include <Car2wd.h>
+#include <UVC.h>
+
+#define NO_CONTROLLER  0
+#define CONTROLLER_1   1
+#define CONTROLLER_2   2
+
+char ssid[] = "mycar";        // Set the AP's SSID
+char pass[] = "12345678";     // Set the AP's password
+char channel[] = "11";        // Set the AP's channel
+int status = WL_IDLE_STATUS;  // the Wifi radio's status
+
+/* control 1 */
+int port_ctrl1 = 5001;
+//#define CTRL1_USE_TCP
+#ifdef CTRL1_USE_TCP
+WiFiServer server(port_ctrl1);
+#else
+// Use UDP in control 1
+WiFiUDP udp1;
+#endif
+
+/* control 2 */
+WiFiUDP udp2;
+int port_ctrl2 = 5002;
+
+Car2wd car(8,9,10,11,12,13);
+
+#define DBG (1)
+
+void setup() {
+
+  car.begin();
+
+  status = WiFi.status();
+  while (status != WL_CONNECTED) {
+    Serial.print("Attempting to start AP with SSID: ");
+    Serial.println(ssid);
+    status = WiFi.apbegin(ssid, pass, channel);
+    if (status == WL_CONNECTED) break;
+    delay(1000);
+  }
+  Serial.println("AP mode already started");
+
+  UVC.begin();
+  while (!UVC.available()) delay(1000);
+  Serial.println("UVC is ready");
+
+#ifdef CTRL1_USE_TCP
+  server.begin();
+  os_thread_create(tcp_ctrl_task, NULL, OS_PRIORITY_REALTIME, 2048);
+#else
+  udp1.begin(port_ctrl1);
+  os_thread_create(udp1_ctrl_task, NULL, OS_PRIORITY_REALTIME, 2048);
+#endif
+
+  udp2.begin(port_ctrl2);
+  os_thread_create(udp2_ctrl_task, NULL, OS_PRIORITY_REALTIME, 2048);
+
+}
+
+#ifdef CTRL1_USE_TCP
+uint8_t tcpbuf[256];
+void tcp_ctrl_task(const void *argument) {
+  int len;
+  WiFiClient client;
+  while (1) {
+    if (!client.connected()) {
+      client = server.available();
+    }
+    while (client.connected()) {
+      client.setRecvTimeout(1800);
+      memset(tcpbuf, 0, sizeof(tcpbuf));
+      len = client.read(tcpbuf, sizeof(tcpbuf)-1);
+      if (len > 0) {
+        if (DBG) printf("[TCP]:%s\r\n", tcpbuf);
+        tcpbuf[len] = '\0';
+      }
+      handleData((const char *)tcpbuf, CONTROLLER_1);
+    }
+    delay(100);
+  }
+}
+#else
+uint8_t udpbuf1[256];
+void udp1_ctrl_task(const void *argument) {
+  int len;
+  while(1) {
+    memset(udpbuf1, 0, sizeof(udpbuf1));
+    len = udp1.read(udpbuf1, sizeof(udpbuf1)-1);
+    if (len > 0) {
+      if (DBG) printf("[UDP1]:%s\r\n", udpbuf1);
+      udpbuf1[len] = '\0';
+    }
+    handleData((const char *)udpbuf1, CONTROLLER_1);
+    delay(100);
+  }
+}
+#endif
+
+uint8_t udpbuf2[256];
+void udp2_ctrl_task(const void *argument) {
+  int len;
+  while(1) {
+    memset(udpbuf2, 0, sizeof(udpbuf2));
+    len = udp2.read(udpbuf2, sizeof(udpbuf2)-1);
+    if (len > 0) {
+      if (DBG) printf("[UDP2]:%s\r\n", udpbuf2);
+      udpbuf2[len] = '\0';
+    }
+    handleData((const char *)udpbuf2, CONTROLLER_2);
+    delay(100);
+  }
+}
+
+void loop() {
+  delay(1000);
+}
+
+int speedMapping(int rawspeed) {
+  /* Original data range: -12 ~ + 12
+   * Convert it to PWM range: 150~255
+   **/
+  if (rawspeed == 0) {
+    return 0;
+  } else {
+    return (rawspeed * 105) / 12 + ((rawspeed > 0) ? 150 : -150);
+  }
+}
+
+#define NO_CONTROL_TIMEOUT 2000
+int currentController = NO_CONTROLLER;
+uint32_t lastActionTimestamp = 0;
+int lastXspeed = 0;
+int lastYspeed = 0;
+void handleData(const char *buf, int controller) {
+
+  int len;
+  bool xchange = false, ychange = false;;
+  int xspeed = 0, yspeed = 0;
+  uint32_t currentTimestamp;
+
+  currentTimestamp = millis();
+
+  if ((currentController != NO_CONTROLLER) && (currentController != controller)) {
+    if (currentTimestamp - lastActionTimestamp > NO_CONTROL_TIMEOUT) {
+      // There is other controller, but there is no action for a while. Switch control to current controller.
+      currentController = controller;
+    } else {
+      return;
+    }
+  }
+
+  if (buf[0] != '\0') {
+    len = strlen(buf);
+    for (int i=0; i<len; i++) {
+      if (buf[i] == 'X') {
+        xspeed = lastXspeed = speedMapping(parseSpeed(&buf[i+2]));
+        xchange = true;
+      }
+      if (buf[i] == 'Y') {
+        yspeed = lastYspeed = speedMapping(parseSpeed(&buf[i+2]));
+        ychange = true;
+      }
+    }
+    if (xchange == false) xspeed = lastXspeed;
+    if (ychange == false) yspeed = lastYspeed;
+  } else {
+    xspeed = lastXspeed = 0;
+    yspeed = lastYspeed = 0;
+  }
+
+  if (xspeed != 0 || yspeed != 0) {
+    lastActionTimestamp = currentTimestamp;
+    if (currentController == NO_CONTROLLER) {
+      currentController = controller;
+    }
+  } else {
+    if (currentTimestamp - lastActionTimestamp > NO_CONTROL_TIMEOUT) {
+      currentController = NO_CONTROLLER;
+    }
+  }
+
+  if (xspeed == 0 && yspeed == 0) {
+    car.stop();
+  } else if (xspeed == 0 && yspeed > 0) {
+    car.setAction(CAR_FORWARD, abs(yspeed));
+  } else if (xspeed == 0 && yspeed < 0) {
+    car.setAction(CAR_BACKWARD, abs(yspeed));
+  } else if (xspeed > 0 && yspeed < 0 ) {
+    car.setAction(CAR_BACKWARD_RIGHT, abs(yspeed));
+  } else if (xspeed > 0 && yspeed == 0) {
+    car.setAction(CAR_ROTATE_RIGHT, abs(xspeed));
+  } else if (xspeed > 0 && yspeed > 0 ) {
+    car.setAction(CAR_FORWARD_RIGHT, abs(yspeed));
+  } else if (xspeed < 0 && yspeed < 0 ) {
+    car.setAction(CAR_BACKWARD_LEFT, abs(yspeed));
+  } else if (xspeed < 0 && yspeed == 0) {
+    car.setAction(CAR_ROTATE_LEFT, abs(xspeed));
+  } else if (xspeed < 0 && yspeed > 0 ) {
+    car.setAction(CAR_FORWARD_LEFT, abs(yspeed));
+  }
+}
+
+int parseSpeed(const char *buf) {
+  int s = 0;
+  if (buf[0] == '-') {
+    s = buf[1] - '0';
+    if (buf[2] >= '0' && buf[2] <= '9') {
+      s = s * 10 + (buf[2] - '0');
+    }
+    s = -s;
+  } else {
+    s = buf[0] - '0';
+    if (buf[1] >= '0' && buf[1] <= '9') {
+      s = s * 10 + (buf[1] - '0');
+    }
+  }
+  return s;
+}
  No newline at end of file

          
A => libraries/AmebaMotors/examples/misc/car2wd_mpu6050_control_udp/car2wd_mpu6050_control_udp.ino +284 -0
@@ 0,0 1,284 @@ 
+#include <WiFi.h>
+#include <WiFiUdp.h>
+#include "I2Cdev.h"
+#include "MPU6050_6Axis_MotionApps20.h"
+#include "Wire.h"
+
+/* WiFi related variables*/
+char ssid[] = "mycar";          // mycar ssid
+char pass[] = "12345678";       // mycar password
+IPAddress server(192,168,1,1);  // numeric IP for mycar AP mode
+int udpport = 5002;
+int status = WL_IDLE_STATUS;
+
+WiFiUDP udp;
+char sendbuf[12];
+
+/* MPU6050 related variables */
+MPU6050 mpu;
+
+// MPU control/status vars
+bool dmpReady = false;  // set true if DMP init was successful
+uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
+uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
+uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
+uint16_t fifoCount;     // count of all bytes currently in FIFO
+uint8_t fifoBuffer[64]; // FIFO storage buffer
+
+// orientation/motion vars
+Quaternion q;           // [w, x, y, z]         quaternion container
+VectorFloat gravity;    // [x, y, z]            gravity vector
+float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
+
+/* car control variable */
+int carx = 0, cary = 0;
+int prev_carx = 0, prev_cary = 0;
+uint32_t timestamp = 0, last_send_timestamp = 0;
+
+volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
+void dmpDataReady() {
+    mpuInterrupt = true;
+}
+
+void setup() {
+  checkAndReconnectServer();
+  initMPU6050();
+}
+
+void loop() {
+  if (!dmpReady) return; // if programming failed, don't try to do anything
+
+  safeWaitMPU6050();
+  mpuIntStatus = mpu.getIntStatus();
+  fifoCount = mpu.getFIFOCount();
+
+  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
+    mpu.resetFIFO();
+    Serial.println(F("FIFO overflow!"));
+  } else if (mpuIntStatus & 0x02 && fifoCount >= packetSize) {
+    while (fifoCount >= packetSize){
+      mpu.getFIFOBytes(fifoBuffer, packetSize);
+      fifoCount -= packetSize;
+    }
+
+    // get Euler angles
+    mpu.dmpGetQuaternion(&q, fifoBuffer);
+    mpu.dmpGetGravity(&gravity, &q);
+    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
+
+    if (hasStarted(ypr[1], ypr[2])) {
+      mapPitchRolltoXY(ypr[1], ypr[2], &carx, &cary);
+    } else {
+      carx = cary = 0;
+    }
+  
+    memset(sendbuf, 0, 12);
+    timestamp = millis();
+    if ((timestamp - last_send_timestamp) > 500 || (carx != prev_carx && cary != prev_cary)) {
+      // If there is no value changed after 500ms, then send command to keepalive.
+      sprintf(sendbuf, "X:%dY:%d", carx, cary);
+    } else if (carx != prev_carx) {
+      sprintf(sendbuf, "X:%d", carx);
+    } else if (cary != prev_cary) {
+      sprintf(sendbuf, "Y:%d", cary);
+    }
+    if (strlen(sendbuf) > 0) {
+      if (checkAndReconnectServer()) {
+        safeResetMPU6050();
+      }
+      udp.beginPacket(server, udpport);
+      udp.write(sendbuf, strlen(sendbuf));
+      udp.endPacket();
+      last_send_timestamp = timestamp;
+      delay(10);
+
+    }
+    prev_carx = carx;
+    prev_cary = cary;
+
+    // ignore previous interrupt
+    mpuInterrupt = false;
+  }
+}
+
+#define CAR_STATE_OFF             0
+#define CAR_STATE_OFF_FIRST_ROLL  1
+#define CAR_STATE_OFF_FIRST_FLAT  2
+#define CAR_STATE_OFF_SECOND_ROLL 3
+#define CAR_STATE_ON              4
+
+// If user cannot complete action within 3s, then break to init state
+#define CAR_STATE_BREAK_TIMEOUT 3000
+
+int carState = CAR_STATE_OFF;
+uint32_t initChangeStateTimestamp = 0;
+
+int isFlat(float pitch, float roll) {
+  return (pitch < 24 && pitch > -24 && roll < 24 && roll > -24);
+}
+
+int isRoll(float pitch, float roll) {
+  return (pitch > 70 || pitch < -70 || roll > 70 || roll < -70);
+}
+
+// return 1 if we can control car
+int hasStarted(float pitch, float roll) {
+  int ret = 0;
+  pitch = pitch * 180 / M_PI;
+  roll = roll * 180 / M_PI;
+  switch(carState) {
+    case CAR_STATE_OFF:
+      if (isRoll(pitch, roll)) {
+        carState = CAR_STATE_OFF_FIRST_ROLL;
+        initChangeStateTimestamp = millis();
+      }
+      ret = 0;
+      break;
+    case CAR_STATE_OFF_FIRST_ROLL:
+      if (millis() - initChangeStateTimestamp > CAR_STATE_BREAK_TIMEOUT) {
+        carState = CAR_STATE_OFF;
+      } else {
+        if (isFlat(pitch, roll)) {
+          carState = CAR_STATE_OFF_FIRST_FLAT;
+        }
+      }
+      ret = 0;
+      break;
+    case CAR_STATE_OFF_FIRST_FLAT:
+      if (millis() - initChangeStateTimestamp > CAR_STATE_BREAK_TIMEOUT) {
+        carState = CAR_STATE_OFF;
+      } else {
+        if (isRoll(pitch, roll)) {
+          carState = CAR_STATE_OFF_SECOND_ROLL;
+        }
+      }
+      ret = 0;
+      break;
+    case CAR_STATE_OFF_SECOND_ROLL:
+      if (millis() - initChangeStateTimestamp > CAR_STATE_BREAK_TIMEOUT) {
+        carState = CAR_STATE_OFF;
+        ret = 0;
+      } else {
+        if (isFlat(pitch, roll)) {
+          carState = CAR_STATE_ON;
+          Serial.println("ON");
+          ret = 1;
+        } else {
+          ret = 0;
+        }
+      }
+      break;
+    case CAR_STATE_ON:
+      ret = 1;
+      break;
+  }
+
+  return ret;
+}
+
+int mapRange(float angle) {
+  /* Here is the mapping table.
+   * Pitch/Roll(degree): -48...-24...24...48
+   * Car:                -12.....0....0...12
+   */
+  if (angle < -48) {
+    return -12;
+  } else if ( angle >= -48 && angle < -24) {
+    return ((angle + 24)) / 2;
+  } else if ( angle > 24 && angle <= 48) {
+    return (angle - 24) / 2;
+  } else if (angle > 48) {
+    return 12;
+  } else {
+    return 0;
+  }
+}
+
+void mapPitchRolltoXY(float pitch, float roll, int *carx, int *cary) {
+  pitch = pitch * 180 / M_PI;
+  roll = roll * 180 / M_PI;
+  *carx = -mapRange(pitch);
+  *cary = -mapRange(roll);
+}
+
+int checkAndReconnectServer() {
+  int ret = 0;
+  status = WiFi.status();
+  if (status != WL_CONNECTED) {
+    while (status != WL_CONNECTED) {
+      Serial.print("Attempting to connect to SSID: ");
+      Serial.println(ssid);
+      // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
+      status = WiFi.begin(ssid, pass);
+      if (status == WL_CONNECTED) {
+        break;
+      }
+      Serial.println("Reconnect to wifi...");
+      delay(1000);
+    }
+    Serial.println("Connected to wifi");
+    ret = 1;
+  }
+  return ret;
+}
+
+void initMPU6050() {
+  Wire.begin();
+  Wire.setClock(400000); // 400kHz I2C clock (200kHz if CPU is 8MHz).
+
+  Serial.println(F("Initializing I2C devices..."));
+  mpu.initialize();
+
+  Serial.println(F("Testing device connections..."));
+  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
+
+  Serial.println(F("Initializing DMP..."));
+  devStatus = mpu.dmpInitialize();
+
+  // make sure it worked (returns 0 if so)
+  if (devStatus == 0) {
+    // turn on the DMP, now that it's ready
+    Serial.println(F("Enabling DMP..."));
+    mpu.setDMPEnabled(true);
+
+    // enable Arduino interrupt detection
+    Serial.println(F("Enabling interrupt detection (Ameba D16 pin)..."));
+    attachInterrupt(16, dmpDataReady, RISING);
+    mpuIntStatus = mpu.getIntStatus();
+
+    // set our DMP Ready flag so the main loop() function knows it's okay to use it
+    Serial.println(F("DMP ready! Waiting for first interrupt..."));
+    dmpReady = true;
+
+    // get expected DMP packet size for later comparison
+    packetSize = mpu.dmpGetFIFOPacketSize();
+
+    mpu.setRate(5); // 1khz / (1 + 5) = 166 Hz
+  } else {
+    // ERROR!
+    // 1 = initial memory load failed
+    // 2 = DMP configuration updates failed
+    // (if it's going to break, usually the code will be 1)
+    Serial.print(F("DMP Initialization failed (code "));
+    Serial.print(devStatus);
+    Serial.println(F(")"));
+  }  
+}
+
+void safeWaitMPU6050() {
+  while (!mpuInterrupt) {
+    os_thread_yield(); // without yield, the empty busy loop might make CPU behave un-expected
+  }  
+  mpuInterrupt = false;
+}
+
+void safeResetMPU6050() {
+  /* If dmp interrupt happends at I2C send/recv, then MPU6050 would hang and can only recover by plug out/in VCC.
+   * To avoid this happen we wait next interrupt and then reset buffer */
+  mpuInterrupt = false;
+    while (!mpuInterrupt) {
+    os_thread_yield(); // without yield, the empty busy loop might make CPU behave un-expected
+  }
+  mpu.resetFIFO();
+  mpuInterrupt = false;
+}
  No newline at end of file

          
A => libraries/AmebaMotors/examples/thunder_tiger_jackal/thunder_tiger_jackal.ino +219 -0
@@ 0,0 1,219 @@ 
+/*
+
+ This sketch require these items:
+   - Ameba x 1
+   - PCA9685 x 1
+   - Thunder Tiger Jackal car x 1
+
+ There are 2 components need to be control in Thunder Tiger.
+ One is direction servo. This servo require a 6V power source. And the servo working level is 3.3V.
+ The servo works at 54Hz, and the range of pulse width is 1ms~2ms.
+ The other is motor control. The power line provide 6V power source.
+ The signal line use pwm with same property as direction servo.
+
+ The PCA9685 is for smooth the action.
+
+ The direction servo may drain power from low to high rapidly. So we need a 100uF capacity.
+ So the whole connection is as below:
+
+    Motor power line --- Ameba 5V pin --- 100uF capacity positive side --- direction servo power line
+    Motor GND --- Ameba GND --- 100uF capacity negative side --- direction servo GND
+
+    Ameba 3.3V pin --- PCA9685 VCC
+    Ameba GND --- PCA9685 GND
+    Ameba SDA --- PCA9685 SDA
+    Ameba SCL --- PCA9685 SCL
+
+    PCA9685 PWM 0 signal --- Motor signal line
+    PCA9685 PWM 1 signal --- direction servo signal line
+
+ */
+
+#include <WiFi.h>
+#include <WiFiUdp.h>
+#include <UVC.h>
+#include <Wire.h>
+#include <Adafruit_PWMServoDriver.h>
+
+#define DBG (1)
+//#define CTRL_USE_TCP
+#define ENABLE_UVC
+
+char ssid[] = "myJackal";     // Set the AP's SSID
+char pass[] = "12345678";     // Set the AP's password
+char channel[] = "6";         // Set the AP's channel
+int status = WL_IDLE_STATUS;  // the Wifi radio's status
+
+int port_ctrl = 5001;
+#ifdef CTRL_USE_TCP
+WiFiServer server(port_ctrl);
+WiFiClient client;
+#else
+WiFiUDP udp;
+#endif
+
+#define PWM_THR 0
+#define PWM_STR 1
+Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
+
+const int ctrl_range_min = -127;
+const int ctrl_range_max = +127;
+
+const int xspeed_min  = 221; // 4096 *  5.383 / 100
+const int xspeed_stop = 307; // 4096 *  8.013 / 100
+const int xspeed_max  = 439; // 4096 * 10.724 / 100
+
+const int yspeed_break  = 213; // 4096 *  5.190 / 100 (Full Speed)
+const int yspeed_min  = 300; // (Limited Speed)
+//const int yspeed_min  = 213; // 4096 *  5.190 / 100 (Full Speed)
+const int yspeed_stop = 328; // 4096 *  8.018 / 100
+const int yspeed_max  = 354; // (Limited Speed)
+//const int yspeed_max  = 434; // 4096 * 10.600 / 100 (Full Speed)
+
+uint8_t buffer[256];
+
+void setup() {
+  pwm.begin();
+  pwm.setPWMFreq(54);
+  pwm.setPWM(PWM_THR, 0, yspeed_stop);
+  pwm.setPWM(PWM_STR, 1, xspeed_stop);
+
+  status = WiFi.status();
+  while (status != WL_CONNECTED) {
+    Serial.print("Attempting to start AP with SSID: ");
+    Serial.println(ssid);
+    status = WiFi.apbegin(ssid, pass, channel);
+    if (status == WL_CONNECTED) break;
+    delay(1000);
+  }
+  Serial.println("AP mode already started");
+
+#ifdef CTRL_USE_TCP
+  server.begin();
+#else
+  udp.begin(port_ctrl);
+#endif
+
+#ifdef ENABLE_UVC
+  UVC.begin();
+  while (!UVC.available()) delay(100);
+  Serial.println("UVC is ready");
+#endif
+}
+
+#ifdef CTRL_USE_TCP
+void tcpHandler() {
+  int len;
+  if (!client.connected()) {
+    client = server.available();
+  }
+  while (client.connected()) {
+    memset(buffer, 0, 256);
+    len = client.read(buffer, sizeof(buffer)-1);
+    if (len > 0) {
+      if (DBG) printf("%s\r\n", buffer);
+      buffer[len] = '\0';
+    }
+    handleData((const char *)buffer);
+  }
+  Serial.println("control socket broken");
+  memset(buffer, 0, 256);
+  handleData((const char *)buffer);
+  delay(1000);  
+}
+#else
+void udpHandler() {
+  int len;
+  while(1) {
+    memset(buffer, 0, sizeof(buffer));
+    len = udp.read(buffer, sizeof(buffer)-1);
+    if (len > 0) {
+      if (DBG) printf("%s\r\n", buffer);
+      buffer[len] = '\0';
+    }
+    handleData((const char *)buffer);
+  }
+}
+#endif
+
+void loop() {
+#ifdef CTRL_USE_TCP
+  tcpHandler();
+#else
+  udpHandler();
+#endif
+}
+
+int thrMapping(int rawspeed) {
+  if (rawspeed == 0) {
+    return yspeed_stop;
+  } else {
+    return yspeed_min + (rawspeed - ctrl_range_min) * (yspeed_max - yspeed_min) / (ctrl_range_max - ctrl_range_min);
+  }
+}
+
+int strMapping(int rawspeed) {
+  return xspeed_min + (rawspeed - ctrl_range_min) * (xspeed_max - xspeed_min) / (ctrl_range_max - ctrl_range_min);
+}
+
+int lastXspeed = xspeed_stop;
+int lastYspeed = yspeed_stop;
+int breakCounter = 0;
+void handleData(const char *buf) {
+  int len;
+  bool xchange = false, ychange = false;;
+  int xspeed = lastXspeed;
+  int yspeed = yspeed_stop;
+
+  if (buf[0] != '\0') {
+    len = strlen(buf);
+
+    if (strcmp(buf, "R?") == 0) {
+      char rangebuf[64];
+      sprintf(rangebuf, "R=X:%d,%d;Y%d,%d", ctrl_range_min, ctrl_range_max, ctrl_range_min, ctrl_range_max);
+#ifdef CTRL_USE_TCP
+      client.write(rangebuf, strlen(rangebuf));
+#else
+      udp.beginPacket(udp.remoteIP(), udp.remotePort());
+      udp.write(rangebuf, strlen(rangebuf));
+      udp.endPacket();
+#endif
+      return;
+    }
+
+    for (int i=0; i<len; i++) {
+      if (buf[i] == 'X') {
+        xspeed = lastXspeed = strMapping(atoi(&buf[i+2]));
+        xchange = true;
+      }
+      if (buf[i] == 'Y') {
+        yspeed = lastYspeed = thrMapping(atoi(&buf[i+2]));
+        ychange = true;
+      }
+    }
+    if (xchange == false) {
+      xspeed = lastXspeed;
+    }
+    if (ychange == false) {
+      yspeed = lastYspeed;
+    }
+  } else {
+    yspeed = lastYspeed = yspeed_stop;
+  }
+
+  if (yspeed == yspeed_min && lastYspeed > yspeed_stop + 10) {
+    breakCounter = 3;
+  }
+  if (breakCounter > 0) {
+    if (yspeed == yspeed_min) {
+      yspeed = yspeed_break;
+      breakCounter--;
+    } else {
+      breakCounter = 0;
+    }
+  }
+
+  if (DBG) printf("(%d,%d)\r\n", xspeed, yspeed);
+  pwm.setPWM(PWM_THR, 0, yspeed);
+  pwm.setPWM(PWM_STR, 0, xspeed);
+}
  No newline at end of file

          
A => libraries/AmebaMotors/keywords.txt +33 -0
@@ 0,0 1,33 @@ 
+#######################################
+# Syntax Coloring Map for Motors
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+Car2wd	KEYWORD1	Car2wd
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin	KEYWORD2
+end	KEYWORD2
+setAction	KEYWORD2
+stop	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+CAR_MIN_SPEED	LITERAL1
+CAR_STOP	LITERAL1
+CAR_FORWARD	LITERAL1
+CAR_BACKWARD	LITERAL1
+CAR_ROTATE_RIGHT	LITERAL1
+CAR_ROTATE_LEFT	LITERAL1
+CAR_FORWARD_RIGHT	LITERAL1
+CAR_FORWARD_LEFT	LITERAL1
+CAR_BACKWARD_RIGHT	LITERAL1
+CAR_BACKWARD_LEFT	LITERAL1
  No newline at end of file

          
A => libraries/AmebaMotors/library.properties +10 -0
@@ 0,0 1,10 @@ 
+name=AmebaMotors
+version=1.0.1
+author=Realtek
+maintainer=Realtek <ameba.arduino@gmail.com>
+sentence=With this library you can find useful ditial application.
+paragraph=
+category=Signal Input/Output
+url=http://www.amebaiot.com/ameba-arduino-peripherals-examples/
+architectures=ameba
+

          
A => libraries/ArduinoJson.zip +0 -0

        
A => libraries/DS1307RTC-1.0.0.zip +0 -0

        
A => libraries/DS1307RTC/DS1307RTC.cpp +218 -0
@@ 0,0 1,218 @@ 
+/*
+ * DS1307RTC.h - library for DS1307 RTC
+  
+  Copyright (c) Michael Margolis 2009
+  This library is intended to be uses with Arduino Time library functions
+
+  The 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; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  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
+  
+  30 Dec 2009 - Initial release
+  5 Sep 2011 updated for Arduino 1.0
+ */
+
+
+#if defined (__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || (__AVR_ATtiny2313__)
+#include <TinyWireM.h>
+#define Wire TinyWireM
+#else
+#include <Wire.h>
+#endif
+#include "DS1307RTC.h"
+
+#define DS1307_CTRL_ID 0x68 
+
+DS1307RTC::DS1307RTC()
+{
+  Wire.begin();
+}
+  
+// PUBLIC FUNCTIONS
+time_t DS1307RTC::get()   // Aquire data from buffer and convert to time_t
+{
+  tmElements_t tm;
+  if (read(tm) == false) return 0;
+  return(makeTime(tm));
+}
+
+bool DS1307RTC::set(time_t t)
+{
+  tmElements_t tm;
+  breakTime(t, tm);
+  return write(tm); 
+}
+
+// Aquire data from the RTC chip in BCD format
+bool DS1307RTC::read(tmElements_t &tm)
+{
+  uint8_t sec;
+  Wire.beginTransmission(DS1307_CTRL_ID);
+#if ARDUINO >= 100  
+  Wire.write((uint8_t)0x00); 
+#else
+  Wire.send(0x00);
+#endif  
+  if (Wire.endTransmission() != 0) {
+    exists = false;
+    return false;
+  }
+  exists = true;
+
+  // request the 7 data fields   (secs, min, hr, dow, date, mth, yr)
+  Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields);
+  if (Wire.available() < tmNbrFields) return false;
+#if ARDUINO >= 100
+  sec = Wire.read();
+  tm.Second = bcd2dec(sec & 0x7f);   
+  tm.Minute = bcd2dec(Wire.read() );
+  tm.Hour =   bcd2dec(Wire.read() & 0x3f);  // mask assumes 24hr clock
+  tm.Wday = bcd2dec(Wire.read() );
+  tm.Day = bcd2dec(Wire.read() );
+  tm.Month = bcd2dec(Wire.read() );
+  tm.Year = y2kYearToTm((bcd2dec(Wire.read())));
+#else
+  sec = Wire.receive();
+  tm.Second = bcd2dec(sec & 0x7f);   
+  tm.Minute = bcd2dec(Wire.receive() );
+  tm.Hour =   bcd2dec(Wire.receive() & 0x3f);  // mask assumes 24hr clock
+  tm.Wday = bcd2dec(Wire.receive() );
+  tm.Day = bcd2dec(Wire.receive() );
+  tm.Month = bcd2dec(Wire.receive() );
+  tm.Year = y2kYearToTm((bcd2dec(Wire.receive())));
+#endif
+  if (sec & 0x80) return false; // clock is halted
+  return true;
+}
+
+bool DS1307RTC::write(tmElements_t &tm)
+{
+  // To eliminate any potential race conditions,
+  // stop the clock before writing the values,
+  // then restart it after.
+  Wire.beginTransmission(DS1307_CTRL_ID);
+#if ARDUINO >= 100  
+  Wire.write((uint8_t)0x00); // reset register pointer  
+  Wire.write((uint8_t)0x80); // Stop the clock. The seconds will be written last
+  Wire.write(dec2bcd(tm.Minute));
+  Wire.write(dec2bcd(tm.Hour));      // sets 24 hour format
+  Wire.write(dec2bcd(tm.Wday));   
+  Wire.write(dec2bcd(tm.Day));
+  Wire.write(dec2bcd(tm.Month));
+  Wire.write(dec2bcd(tmYearToY2k(tm.Year))); 
+#else  
+  Wire.send(0x00); // reset register pointer  
+  Wire.send(0x80); // Stop the clock. The seconds will be written last
+  Wire.send(dec2bcd(tm.Minute));
+  Wire.send(dec2bcd(tm.Hour));      // sets 24 hour format
+  Wire.send(dec2bcd(tm.Wday));   
+  Wire.send(dec2bcd(tm.Day));
+  Wire.send(dec2bcd(tm.Month));
+  Wire.send(dec2bcd(tmYearToY2k(tm.Year)));   
+#endif
+  if (Wire.endTransmission() != 0) {
+    exists = false;
+    return false;
+  }
+  exists = true;
+
+  // Now go back and set the seconds, starting the clock back up as a side effect
+  Wire.beginTransmission(DS1307_CTRL_ID);
+#if ARDUINO >= 100  
+  Wire.write((uint8_t)0x00); // reset register pointer  
+  Wire.write(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart
+#else  
+  Wire.send(0x00); // reset register pointer  
+  Wire.send(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart
+#endif
+  if (Wire.endTransmission() != 0) {
+    exists = false;
+    return false;
+  }
+  exists = true;
+  return true;
+}
+
+unsigned char DS1307RTC::isRunning()
+{
+  Wire.beginTransmission(DS1307_CTRL_ID);
+#if ARDUINO >= 100  
+  Wire.write((uint8_t)0x00); 
+#else
+  Wire.send(0x00);
+#endif  
+  Wire.endTransmission();
+
+  // Just fetch the seconds register and check the top bit
+  Wire.requestFrom(DS1307_CTRL_ID, 1);
+#if ARDUINO >= 100
+  return !(Wire.read() & 0x80);
+#else
+  return !(Wire.receive() & 0x80);
+#endif  
+}
+
+void DS1307RTC::setCalibration(char calValue)
+{
+  unsigned char calReg = abs(calValue) & 0x1f;
+  if (calValue >= 0) calReg |= 0x20; // S bit is positive to speed up the clock
+  Wire.beginTransmission(DS1307_CTRL_ID);
+#if ARDUINO >= 100  
+  Wire.write((uint8_t)0x07); // Point to calibration register
+  Wire.write(calReg);
+#else  
+  Wire.send(0x07); // Point to calibration register
+  Wire.send(calReg);
+#endif
+  Wire.endTransmission();  
+}
+
+char DS1307RTC::getCalibration()
+{
+  Wire.beginTransmission(DS1307_CTRL_ID);
+#if ARDUINO >= 100  
+  Wire.write((uint8_t)0x07); 
+#else
+  Wire.send(0x07);
+#endif  
+  Wire.endTransmission();
+
+  Wire.requestFrom(DS1307_CTRL_ID, 1);
+#if ARDUINO >= 100
+  unsigned char calReg = Wire.read();
+#else
+  unsigned char calReg = Wire.receive();
+#endif
+  char out = calReg & 0x1f;
+  if (!(calReg & 0x20)) out = -out; // S bit clear means a negative value
+  return out;
+}
+
+// PRIVATE FUNCTIONS
+
+// Convert Decimal to Binary Coded Decimal (BCD)
+uint8_t DS1307RTC::dec2bcd(uint8_t num)
+{
+  return ((num/10 * 16) + (num % 10));
+}
+
+// Convert Binary Coded Decimal (BCD) to Decimal
+uint8_t DS1307RTC::bcd2dec(uint8_t num)
+{
+  return ((num/16 * 10) + (num % 16));
+}
+
+bool DS1307RTC::exists = false;
+
+DS1307RTC RTC = DS1307RTC(); // create an instance for the user
+

          
A => libraries/DS1307RTC/DS1307RTC.h +40 -0
@@ 0,0 1,40 @@ 
+/*
+ * DS1307RTC.h - library for DS1307 RTC
+ * This library is intended to be uses with Arduino Time library functions
+ */
+
+#ifndef DS1307RTC_h
+#define DS1307RTC_h
+
+#include <TimeLib.h>
+
+// library interface description
+class DS1307RTC
+{
+  // user-accessible "public" interface
+  public:
+    DS1307RTC();
+    static time_t get();
+    static bool set(time_t t);
+    static bool read(tmElements_t &tm);
+    static bool write(tmElements_t &tm);
+    static bool chipPresent() { return exists; }
+    static unsigned char isRunning();
+    static void setCalibration(char calValue);
+    static char getCalibration();
+
+  private:
+    static bool exists;
+    static uint8_t dec2bcd(uint8_t num);
+    static uint8_t bcd2dec(uint8_t num);
+};
+
+#ifdef RTC
+#undef RTC // workaround for Arduino Due, which defines "RTC"...
+#endif
+
+extern DS1307RTC RTC;
+
+#endif
+ 
+

          
A => libraries/DS1307RTC/LICENSE.txt +1 -0
@@ 0,0 1,1 @@ 
+The Time library is forked from works at "http://www.pjrc.com/teensy/td_libs_DS1307RTC.html". The source codes under folder "Time" inherit the license from any license claim of "http://www.pjrc.com/teensy/td_libs_DS1307RTC.html".
  No newline at end of file

          
A => libraries/DS1307RTC/examples/ReadTest/ReadTest.ino +49 -0
@@ 0,0 1,49 @@ 
+#include <Wire.h>
+#include <TimeLib.h>
+#include <DS1307RTC.h>
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial) ; // wait for serial
+  delay(200);
+  Serial.println("DS1307RTC Read Test");
+  Serial.println("-------------------");
+}
+
+void loop() {
+  tmElements_t tm;
+
+  if (RTC.read(tm)) {
+    Serial.print("Ok, Time = ");
+    print2digits(tm.Hour);
+    Serial.write(':');
+    print2digits(tm.Minute);
+    Serial.write(':');
+    print2digits(tm.Second);
+    Serial.print(", Date (D/M/Y) = ");
+    Serial.print(tm.Day);
+    Serial.write('/');
+    Serial.print(tm.Month);
+    Serial.write('/');
+    Serial.print(tmYearToCalendar(tm.Year));
+    Serial.println();
+  } else {
+    if (RTC.chipPresent()) {
+      Serial.println("The DS1307 is stopped.  Please run the SetTime");
+      Serial.println("example to initialize the time and begin running.");
+      Serial.println();
+    } else {
+      Serial.println("DS1307 read error!  Please check the circuitry.");
+      Serial.println();
+    }
+    delay(9000);
+  }
+  delay(1000);
+}
+
+void print2digits(int number) {
+  if (number >= 0 && number < 10) {
+    Serial.write('0');
+  }
+  Serial.print(number);
+}

          
A => libraries/DS1307RTC/examples/SetTime/SetTime.ino +75 -0
@@ 0,0 1,75 @@ 
+#include <Wire.h>
+#include <TimeLib.h>
+#include <DS1307RTC.h>
+
+const char *monthName[12] = {
+  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+tmElements_t tm;
+
+void setup() {
+  bool parse=false;
+  bool config=false;
+
+  // get the date and time the compiler was run
+  if (getDate(__DATE__) && getTime(__TIME__)) {
+    parse = true;
+    // and configure the RTC with this info
+    if (RTC.write(tm)) {
+      config = true;
+    }
+  }
+
+  Serial.begin(9600);
+  while (!Serial) ; // wait for Arduino Serial Monitor
+  delay(200);
+  if (parse && config) {
+    Serial.print("DS1307 configured Time=");
+    Serial.print(__TIME__);
+    Serial.print(", Date=");
+    Serial.println(__DATE__);
+  } else if (parse) {
+    Serial.println("DS1307 Communication Error :-{");
+    Serial.println("Please check your circuitry");
+  } else {
+    Serial.print("Could not parse info from the compiler, Time=\"");
+    Serial.print(__TIME__);
+    Serial.print("\", Date=\"");
+    Serial.print(__DATE__);
+    Serial.println("\"");
+  }
+}
+
+void loop() {
+}
+
+bool getTime(const char *str)
+{
+  int Hour, Min, Sec;
+
+  if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
+  tm.Hour = Hour;
+  tm.Minute = Min;
+  tm.Second = Sec;
+  return true;
+}
+
+bool getDate(const char *str)
+{
+  char Month[12];
+  int Day, Year;
+  uint8_t monthIndex;
+
+  if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
+  for (monthIndex = 0; monthIndex < 12; monthIndex++) {
+    if (strcmp(Month, monthName[monthIndex]) == 0) break;
+  }
+  if (monthIndex >= 12) return false;
+  tm.Day = Day;
+  tm.Month = monthIndex + 1;
+  tm.Year = CalendarYrToTm(Year);
+  return true;
+}
+

          
A => libraries/DS1307RTC/examples/SetTimeNtp/SetTimeNtp.ino +147 -0
@@ 0,0 1,147 @@ 
+/*
+ * Example showing RTC sync from NTP time source
+ *
+ */
+
+#include <Wire.h>
+#include <TimeLib.h>
+#include <WiFi.h>
+#include <WiFiUdp.h>
+#include <DS1307RTC.h>
+
+int status = WL_IDLE_STATUS;
+char ssid[] = "yourNetwork";          // your network SSID (name)
+char pass[] = "secretPassword";       // your network password
+
+// NTP Servers:
+IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
+
+const int timeZone = 8;     // Beijing Time, Taipei Time
+//const int timeZone = 1;   // Central European Time
+//const int timeZone = -5;  // Eastern Standard Time (USA)
+//const int timeZone = -4;  // Eastern Daylight Time (USA)
+//const int timeZone = -8;  // Pacific Standard Time (USA)
+//const int timeZone = -7;  // Pacific Daylight Time (USA)
+
+// A UDP instance to let us send and receive packets over UDP
+WiFiUDP Udp;
+unsigned int localPort = 2390;  // local port to listen for UDP packets
+
+void setup() 
+{
+  while (status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
+    status = WiFi.begin(ssid, pass);
+    if (status == WL_CONNECTED) break;
+    // wait 1 seconds for retry
+    delay(1000);
+  }
+
+  Udp.begin(localPort);
+  Serial.println("waiting for sync");
+  setSyncProvider(getNtpTime);
+
+  // calibrate RTC time from NTP time
+  if (timeStatus() != timeNotSet) {
+    tmElements_t tm;
+    tm.Hour = hour();
+    tm.Minute = minute();
+    tm.Second = second();
+    tm.Day = day();
+    tm.Month = month();
+    tm.Year = CalendarYrToTm(year());
+    // configure RTC time
+    RTC.write(tm);
+  }
+}
+
+
+void loop()
+{
+  tmElements_t tm;
+
+  if (RTC.read(tm)) {
+    Serial.print("Ok, Time = ");
+    print2digits(tm.Hour);
+    Serial.write(':');
+    print2digits(tm.Minute);
+    Serial.write(':');
+    print2digits(tm.Second);
+    Serial.print(", Date (D/M/Y) = ");
+    Serial.print(tm.Day);
+    Serial.write('/');
+    Serial.print(tm.Month);
+    Serial.write('/');
+    Serial.print(tmYearToCalendar(tm.Year));
+    Serial.println();
+  } else {
+    if (RTC.chipPresent()) {
+      Serial.println("The DS1307 is stopped.  Please run the SetTime");
+      Serial.println("example to initialize the time and begin running.");
+      Serial.println();
+    } else {
+      Serial.println("DS1307 read error!  Please check the circuitry.");
+      Serial.println();
+    }
+    delay(9000);
+  }
+  delay(1000);
+}
+
+void print2digits(int number) {
+  if (number >= 0 && number < 10) {
+    Serial.write('0');
+  }
+  Serial.print(number);
+}
+
+/*-------- NTP code ----------*/
+
+const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
+byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
+
+time_t getNtpTime()
+{
+  Serial.println("Transmit NTP Request");
+  Udp.setRecvTimeout(1500);
+  sendNTPpacket(timeServer);
+  if ( Udp.read(packetBuffer, NTP_PACKET_SIZE) > 0 ) {
+      unsigned long secsSince1900;
+      // convert four bytes starting at location 40 to a long integer
+      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
+      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
+      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
+      secsSince1900 |= (unsigned long)packetBuffer[43];
+      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
+  } else {
+    Serial.println("No NTP Response :-(");
+    return 0; // return 0 if unable to get the time
+  }
+}
+
+// send an NTP request to the time server at the given address
+void sendNTPpacket(IPAddress &address)
+{
+  // set all bytes in the buffer to 0
+  memset(packetBuffer, 0, NTP_PACKET_SIZE);
+  // Initialize values needed to form NTP request
+  // (see URL above for details on the packets)
+  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
+  packetBuffer[1] = 0;     // Stratum, or type of clock
+  packetBuffer[2] = 6;     // Polling Interval
+  packetBuffer[3] = 0xEC;  // Peer Clock Precision
+  // 8 bytes of zero for Root Delay & Root Dispersion
+  packetBuffer[12]  = 49;
+  packetBuffer[13]  = 0x4E;
+  packetBuffer[14]  = 49;
+  packetBuffer[15]  = 52;
+  // all NTP fields have been given values, now
+  // you can send a packet requesting a timestamp:                 
+  Udp.beginPacket(address, 123); //NTP requests are to port 123
+  Udp.write(packetBuffer, NTP_PACKET_SIZE);
+  Udp.endPacket();
+}

          
A => libraries/DS1307RTC/keywords.txt +23 -0
@@ 0,0 1,23 @@ 
+#######################################
+# Syntax Coloring Map For DS1307RTC
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+get	KEYWORD2
+set	KEYWORD2
+read	KEYWORD2
+write	KEYWORD2
+chipPresent	KEYWORD2
+#######################################
+# Instances (KEYWORD2)
+#######################################
+RTC	KEYWORD2
+#######################################
+# Constants (LITERAL1)
+#######################################

          
A => libraries/DS1307RTC/library.json +12 -0
@@ 0,0 1,12 @@ 
+{
+  "name": "DS1307RTC",
+  "keywords": "i2c, rtc, time, clock",
+  "description": "DS1307 RTC (Real-Time-Clock)",
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/PaulStoffregen/DS1307RTC.git"
+  },
+  "frameworks": "arduino",
+  "platforms": "atmelavr"
+}

          
A => libraries/DS1307RTC/library.properties +10 -0
@@ 0,0 1,10 @@ 
+name=AmebaDS1307RTC
+version=1.4
+author=Realtek
+maintainer=Realtek <ameba.arduino@gmail.com>
+sentence=Use a DS1307 Real Time Clock chip with the Time library
+paragraph=
+category=Timing
+url=http://playground.arduino.cc/code/time
+architectures=*
+

          
A => libraries/DS1307RTC/readme.txt +7 -0
@@ 0,0 1,7 @@ 
+Readme file for DS1307RTC Library
+
+The DS1307RTC library is provided to demonstrate the Arduino Time library.
+
+See the TimeRTC example sketches privided with the Time library download for usage
+
+

          
A => libraries/I2Cdev.zip +0 -0

        
A => libraries/I2Cdev/I2Cdev.cpp +1457 -0
@@ 0,0 1,1457 @@ 
+// I2Cdev library collection - Main I2C device class
+// Abstracts bit and byte I2C R/W functions into a convenient class
+// 2013-06-05 by Jeff Rowberg <jeff@rowberg.net>
+//
+// Changelog:
+//      2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
+//      2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan)
+//      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) 2013 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 "I2Cdev.h"
+
+#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
+
+    #ifdef I2CDEV_IMPLEMENTATION_WARNINGS
+        #if ARDUINO < 100
+            #warning Using outdated Arduino IDE with Wire library is functionally limiting.
+            #warning Arduino IDE v1.6.5+ with I2Cdev Fastwire implementation is recommended.
+            #warning This I2Cdev implementation does not support:
+            #warning - Repeated starts conditions
+            #warning - Timeout detection (some Wire requests block forever)
+        #elif ARDUINO == 100
+            #warning Using outdated Arduino IDE with Wire library is functionally limiting.
+            #warning Arduino IDE v1.6.5+ with I2Cdev Fastwire implementation is recommended.
+            #warning This I2Cdev implementation does not support:
+            #warning - Repeated starts conditions
+            #warning - Timeout detection (some Wire requests block forever)
+        #elif ARDUINO > 100
+            /*#warning Using current Arduino IDE with Wire library is functionally limiting.
+            #warning Arduino IDE v1.6.5+ with I2CDEV_BUILTIN_FASTWIRE implementation is recommended.
+            #warning This I2Cdev implementation does not support:
+            #warning - Timeout detection (some Wire requests block forever)*/
+        #endif
+    #endif
+
+#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
+
+    //#error The I2CDEV_BUILTIN_FASTWIRE implementation is known to be broken right now. Patience, Iago!
+
+#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
+
+    #ifdef I2CDEV_IMPLEMENTATION_WARNINGS
+        #warning Using I2CDEV_BUILTIN_NBWIRE implementation may adversely affect interrupt detection.
+        #warning This I2Cdev implementation does not support:
+        #warning - Repeated starts conditions
+    #endif
+
+    // NBWire implementation based heavily on code by Gene Knight <Gene@Telobot.com>
+    // Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html
+    // Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html
+    TwoWire Wire;
+
+#endif
+
+/** 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) {
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.print("I2C (0x");
+        Serial.print(devAddr, HEX);
+        Serial.print(") reading ");
+        Serial.print(length, DEC);
+        Serial.print(" bytes from 0x");
+        Serial.print(regAddr, HEX);
+        Serial.print("...");
+    #endif
+
+    int8_t count = 0;
+    uint32_t t1 = millis();
+
+    #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE)
+
+        #if (ARDUINO < 100)
+            // Arduino v00xx (before v1.0), Wire library
+
+            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
+            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
+            // smaller chunks instead of all at once
+            for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {
+                Wire.beginTransmission(devAddr);
+                Wire.send(regAddr);
+                Wire.endTransmission();
+                Wire.beginTransmission(devAddr);
+                Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));
+
+                for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
+                    data[count] = Wire.receive();
+                    #ifdef I2CDEV_SERIAL_DEBUG
+                        Serial.print(data[count], HEX);
+                        if (count + 1 < length) Serial.print(" ");
+                    #endif
+                }
+
+                Wire.endTransmission();
+            }
+        #elif (ARDUINO == 100)
+            // Arduino v1.0.0, Wire library
+            // Adds standardized write() and read() stream methods instead of send() and receive()
+
+            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
+            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
+            // smaller chunks instead of all at once
+            for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {
+                Wire.beginTransmission(devAddr);
+                Wire.write(regAddr);
+                Wire.endTransmission();
+                Wire.beginTransmission(devAddr);
+                Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));
+        
+                for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
+                    data[count] = Wire.read();
+                    #ifdef I2CDEV_SERIAL_DEBUG
+                        Serial.print(data[count], HEX);
+                        if (count + 1 < length) Serial.print(" ");
+                    #endif
+                }
+        
+                Wire.endTransmission();
+            }
+        #elif (ARDUINO > 100)
+            // Arduino v1.0.1+, Wire library
+            // Adds official support for repeated start condition, yay!
+
+            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
+            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
+            // smaller chunks instead of all at once
+            for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {
+                Wire.beginTransmission(devAddr);
+                Wire.write(regAddr);
+                Wire.endTransmission();
+                Wire.beginTransmission(devAddr);
+                Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));
+        
+                for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
+                    data[count] = Wire.read();
+                    #ifdef I2CDEV_SERIAL_DEBUG
+                        Serial.print(data[count], HEX);
+                        if (count + 1 < length) Serial.print(" ");
+                    #endif
+                }
+            }
+        #endif
+
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+
+        // Fastwire library
+        // no loop required for fastwire
+        uint8_t status = Fastwire::readBuf(devAddr << 1, regAddr, data, length);
+        if (status == 0) {
+            count = length; // success
+        } else {
+            count = -1; // error
+        }
+
+    #endif
+
+    // check for timeout
+    if (timeout > 0 && millis() - t1 >= timeout && count < length) count = -1; // timeout
+
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.print(". Done (");
+        Serial.print(count, DEC);
+        Serial.println(" read).");
+    #endif
+
+    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 (-1 indicates failure)
+ */
+int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout) {
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.print("I2C (0x");
+        Serial.print(devAddr, HEX);
+        Serial.print(") reading ");
+        Serial.print(length, DEC);
+        Serial.print(" words from 0x");
+        Serial.print(regAddr, HEX);
+        Serial.print("...");
+    #endif
+
+    int8_t count = 0;
+    uint32_t t1 = millis();
+
+    #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE)
+
+        #if (ARDUINO < 100)
+            // Arduino v00xx (before v1.0), Wire library
+
+            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
+            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
+            // smaller chunks instead of all at once
+            for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) {
+                Wire.beginTransmission(devAddr);
+                Wire.send(regAddr);
+                Wire.endTransmission();
+                Wire.beginTransmission(devAddr);
+                Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
+    
+                bool msb = true; // starts with MSB, then LSB
+                for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
+                    if (msb) {
+                        // first byte is bits 15-8 (MSb=15)
+                        data[count] = Wire.receive() << 8;
+                    } else {
+                        // second byte is bits 7-0 (LSb=0)
+                        data[count] |= Wire.receive();
+                        #ifdef I2CDEV_SERIAL_DEBUG
+                            Serial.print(data[count], HEX);
+                            if (count + 1 < length) Serial.print(" ");
+                        #endif
+                        count++;
+                    }
+                    msb = !msb;
+                }
+
+                Wire.endTransmission();
+            }
+        #elif (ARDUINO == 100)
+            // Arduino v1.0.0, Wire library
+            // Adds standardized write() and read() stream methods instead of send() and receive()
+    
+            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
+            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
+            // smaller chunks instead of all at once
+            for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) {
+                Wire.beginTransmission(devAddr);
+                Wire.write(regAddr);
+                Wire.endTransmission();
+                Wire.beginTransmission(devAddr);
+                Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
+    
+                bool msb = true; // starts with MSB, then LSB
+                for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
+                    if (msb) {
+                        // first byte is bits 15-8 (MSb=15)
+                        data[count] = Wire.read() << 8;
+                    } else {
+                        // second byte is bits 7-0 (LSb=0)
+                        data[count] |= Wire.read();
+                        #ifdef I2CDEV_SERIAL_DEBUG
+                            Serial.print(data[count], HEX);
+                            if (count + 1 < length) Serial.print(" ");
+                        #endif
+                        count++;
+                    }
+                    msb = !msb;
+                }
+        
+                Wire.endTransmission();
+            }
+        #elif (ARDUINO > 100)
+            // Arduino v1.0.1+, Wire library
+            // Adds official support for repeated start condition, yay!
+
+            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
+            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
+            // smaller chunks instead of all at once
+            for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) {
+                Wire.beginTransmission(devAddr);
+                Wire.write(regAddr);
+                Wire.endTransmission();
+                Wire.beginTransmission(devAddr);
+                Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes
+        
+                bool msb = true; // starts with MSB, then LSB
+                for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) {
+                    if (msb) {
+                        // first byte is bits 15-8 (MSb=15)
+                        data[count] = Wire.read() << 8;
+                    } else {
+                        // second byte is bits 7-0 (LSb=0)
+                        data[count] |= Wire.read();
+                        #ifdef I2CDEV_SERIAL_DEBUG
+                            Serial.print(data[count], HEX);
+                            if (count + 1 < length) Serial.print(" ");
+                        #endif
+                        count++;
+                    }
+                    msb = !msb;
+                }
+        
+                Wire.endTransmission();
+            }
+        #endif
+
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+
+        // Fastwire library
+        // no loop required for fastwire
+        uint16_t intermediate[(uint8_t)length];
+        uint8_t status = Fastwire::readBuf(devAddr << 1, regAddr, (uint8_t *)intermediate, (uint8_t)(length * 2));
+        if (status == 0) {
+            count = length; // success
+            for (uint8_t i = 0; i < length; i++) {
+                data[i] = (intermediate[2*i] << 8) | intermediate[2*i + 1];
+            }
+        } else {
+            count = -1; // error
+        }
+
+    #endif
+
+    if (timeout > 0 && millis() - t1 >= timeout && count < length) count = -1; // timeout
+
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.print(". Done (");
+        Serial.print(count, DEC);
+        Serial.println(" read).");
+    #endif
+    
+    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 word
+    // 1010111110010110 original value (sample)
+    // 1010001110010110 original & ~mask
+    // 1010101110010110 masked | value
+    uint16_t w;
+    if (readWord(devAddr, regAddr, &w) != 0) {
+        uint16_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) {
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.print("I2C (0x");
+        Serial.print(devAddr, HEX);
+        Serial.print(") writing ");
+        Serial.print(length, DEC);
+        Serial.print(" bytes to 0x");
+        Serial.print(regAddr, HEX);
+        Serial.print("...");
+    #endif
+    uint8_t status = 0;
+    #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
+        Wire.beginTransmission(devAddr);
+        Wire.send((uint8_t) regAddr); // send address
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
+        Wire.beginTransmission(devAddr);
+        Wire.write((uint8_t) regAddr); // send address
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+        Fastwire::beginTransmission(devAddr);
+        Fastwire::write(regAddr);
+    #endif
+    for (uint8_t i = 0; i < length; i++) {
+        #ifdef I2CDEV_SERIAL_DEBUG
+            Serial.print(data[i], HEX);
+            if (i + 1 < length) Serial.print(" ");
+        #endif
+        #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
+            Wire.send((uint8_t) data[i]);
+        #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
+            Wire.write((uint8_t) data[i]);
+        #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+            Fastwire::write((uint8_t) data[i]);
+        #endif
+    }
+    #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
+        Wire.endTransmission();
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
+        status = Wire.endTransmission();
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+        Fastwire::stop();
+        //status = Fastwire::endTransmission();
+    #endif
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.println(". Done.");
+    #endif
+    return status == 0;
+}
+
+/** 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) {
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.print("I2C (0x");
+        Serial.print(devAddr, HEX);
+        Serial.print(") writing ");
+        Serial.print(length, DEC);
+        Serial.print(" words to 0x");
+        Serial.print(regAddr, HEX);
+        Serial.print("...");
+    #endif
+    uint8_t status = 0;
+    #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
+        Wire.beginTransmission(devAddr);
+        Wire.send(regAddr); // send address
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
+        Wire.beginTransmission(devAddr);
+        Wire.write(regAddr); // send address
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+        Fastwire::beginTransmission(devAddr);
+        Fastwire::write(regAddr);
+    #endif
+    for (uint8_t i = 0; i < length * 2; i++) {
+        #ifdef I2CDEV_SERIAL_DEBUG
+            Serial.print(data[i], HEX);
+            if (i + 1 < length) Serial.print(" ");
+        #endif
+        #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
+            Wire.send((uint8_t)(data[i] >> 8));     // send MSB
+            Wire.send((uint8_t)data[i++]);          // send LSB
+        #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
+            Wire.write((uint8_t)(data[i] >> 8));    // send MSB
+            Wire.write((uint8_t)data[i++]);         // send LSB
+        #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+            Fastwire::write((uint8_t)(data[i] >> 8));       // send MSB
+            status = Fastwire::write((uint8_t)data[i++]);   // send LSB
+            if (status != 0) break;
+        #endif
+    }
+    #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE)
+        Wire.endTransmission();
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100)
+        status = Wire.endTransmission();
+    #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)
+        Fastwire::stop();
+        //status = Fastwire::endTransmission();
+    #endif
+    #ifdef I2CDEV_SERIAL_DEBUG
+        Serial.println(". Done.");
+    #endif
+    return status == 0;
+}
+
+/** Default timeout value for read operations.
+ * Set this to 0 to disable timeout detection.
+ */
+uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT;
+
+#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
+    // I2C library
+    //////////////////////
+    // Copyright(C) 2012
+    // Francesco Ferrara
+    // ferrara[at]libero[point]it
+    //////////////////////
+
+    /*
+    FastWire
+    - 0.24 added stop
+    - 0.23 added reset
+
+     This is a library to help faster programs to read I2C devices.
+     Copyright(C) 2012 Francesco Ferrara
+     occhiobello at gmail dot com
+     [used by Jeff Rowberg for I2Cdevlib with permission]
+     */
+
+    boolean Fastwire::waitInt() {
+        int l = 250;
+        while (!(TWCR & (1 << TWINT)) && l-- > 0);
+        return l > 0;
+    }
+
+    void Fastwire::setup(int khz, boolean pullup) {
+        TWCR = 0;
+        #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
+            // activate internal pull-ups for twi (PORTC bits 4 & 5)
+            // as per note from atmega8 manual pg167
+            if (pullup) PORTC |= ((1 << 4) | (1 << 5));
+            else        PORTC &= ~((1 << 4) | (1 << 5));
+        #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
+            // activate internal pull-ups for twi (PORTC bits 0 & 1)
+            if (pullup) PORTC |= ((1 << 0) | (1 << 1));
+            else        PORTC &= ~((1 << 0) | (1 << 1));
+        #else
+            // activate internal pull-ups for twi (PORTD bits 0 & 1)
+            // as per note from atmega128 manual pg204
+            if (pullup) PORTD |= ((1 << 0) | (1 << 1));
+            else        PORTD &= ~((1 << 0) | (1 << 1));
+        #endif
+
+        TWSR = 0; // no prescaler => prescaler = 1
+        TWBR = ((16000L / khz) - 16) / 2; // change the I2C clock rate
+        TWCR = 1 << TWEN; // enable twi module, no interrupt
+    }
+
+    // added by Jeff Rowberg 2013-05-07:
+    // Arduino Wire-style "beginTransmission" function
+    // (takes 7-bit device address like the Wire method, NOT 8-bit: 0x68, not 0xD0/0xD1)
+    byte Fastwire::beginTransmission(byte device) {
+        byte twst, retry;
+        retry = 2;
+        do {
+            TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA);
+            if (!waitInt()) return 1;
+            twst = TWSR & 0xF8;
+            if (twst != TW_START && twst != TW_REP_START) return 2;
+
+            //Serial.print(device, HEX);
+            //Serial.print(" ");
+            TWDR = device << 1; // send device address without read bit (1)
+            TWCR = (1 << TWINT) | (1 << TWEN);
+            if (!waitInt()) return 3;
+            twst = TWSR & 0xF8;
+        } while (twst == TW_MT_SLA_NACK && retry-- > 0);
+        if (twst != TW_MT_SLA_ACK) return 4;
+        return 0;
+    }
+
+    byte Fastwire::writeBuf(byte device, byte address, byte *data, byte num) {
+        byte twst, retry;
+
+        retry = 2;
+        do {
+            TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA);
+            if (!waitInt()) return 1;
+            twst = TWSR & 0xF8;
+            if (twst != TW_START && twst != TW_REP_START) return 2;
+
+            //Serial.print(device, HEX);
+            //Serial.print(" ");
+            TWDR = device & 0xFE; // send device address without read bit (1)
+            TWCR = (1 << TWINT) | (1 << TWEN);
+            if (!waitInt()) return 3;
+            twst = TWSR & 0xF8;
+        } while (twst == TW_MT_SLA_NACK && retry-- > 0);
+        if (twst != TW_MT_SLA_ACK) return 4;
+
+        //Serial.print(address, HEX);
+        //Serial.print(" ");
+        TWDR = address; // send data to the previously addressed device
+        TWCR = (1 << TWINT) | (1 << TWEN);
+        if (!waitInt()) return 5;
+        twst = TWSR & 0xF8;
+        if (twst != TW_MT_DATA_ACK) return 6;
+
+        for (byte i = 0; i < num; i++) {
+            //Serial.print(data[i], HEX);
+            //Serial.print(" ");
+            TWDR = data[i]; // send data to the previously addressed device
+            TWCR = (1 << TWINT) | (1 << TWEN);
+            if (!waitInt()) return 7;
+            twst = TWSR & 0xF8;
+            if (twst != TW_MT_DATA_ACK) return 8;
+        }
+        //Serial.print("\n");
+
+        return 0;
+    }
+
+    byte Fastwire::write(byte value) {
+        byte twst;
+        //Serial.println(value, HEX);
+        TWDR = value; // send data
+        TWCR = (1 << TWINT) | (1 << TWEN);
+        if (!waitInt()) return 1;
+        twst = TWSR & 0xF8;
+        if (twst != TW_MT_DATA_ACK) return 2;
+        return 0;
+    }
+
+    byte Fastwire::readBuf(byte device, byte address, byte *data, byte num) {
+        byte twst, retry;
+
+        retry = 2;
+        do {
+            TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA);
+            if (!waitInt()) return 16;
+            twst = TWSR & 0xF8;
+            if (twst != TW_START && twst != TW_REP_START) return 17;
+
+            //Serial.print(device, HEX);
+            //Serial.print(" ");
+            TWDR = device & 0xfe; // send device address to write
+            TWCR = (1 << TWINT) | (1 << TWEN);
+            if (!waitInt()) return 18;
+            twst = TWSR & 0xF8;
+        } while (twst == TW_MT_SLA_NACK && retry-- > 0);
+        if (twst != TW_MT_SLA_ACK) return 19;
+
+        //Serial.print(address, HEX);
+        //Serial.print(" ");
+        TWDR = address; // send data to the previously addressed device
+        TWCR = (1 << TWINT) | (1 << TWEN);
+        if (!waitInt()) return 20;
+        twst = TWSR & 0xF8;
+        if (twst != TW_MT_DATA_ACK) return 21;
+
+        /***/
+
+        retry = 2;
+        do {
+            TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA);
+            if (!waitInt()) return 22;
+            twst = TWSR & 0xF8;
+            if (twst != TW_START && twst != TW_REP_START) return 23;
+
+            //Serial.print(device, HEX);
+            //Serial.print(" ");
+            TWDR = device | 0x01; // send device address with the read bit (1)
+            TWCR = (1 << TWINT) | (1 << TWEN);
+            if (!waitInt()) return 24;
+            twst = TWSR & 0xF8;
+        } while (twst == TW_MR_SLA_NACK && retry-- > 0);
+        if (twst != TW_MR_SLA_ACK) return 25;
+
+        for (uint8_t i = 0; i < num; i++) {
+            if (i == num - 1)
+                TWCR = (1 << TWINT) | (1 << TWEN);
+            else
+                TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
+            if (!waitInt()) return 26;
+            twst = TWSR & 0xF8;
+            if (twst != TW_MR_DATA_ACK && twst != TW_MR_DATA_NACK) return twst;
+            data[i] = TWDR;
+            //Serial.print(data[i], HEX);
+            //Serial.print(" ");
+        }
+        //Serial.print("\n");
+        stop();
+
+        return 0;
+    }
+
+    void Fastwire::reset() {
+        TWCR = 0;
+    }
+
+    byte Fastwire::stop() {
+        TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+        if (!waitInt()) return 1;
+        return 0;
+    }
+#endif
+
+#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
+    // NBWire implementation based heavily on code by Gene Knight <Gene@Telobot.com>
+    // Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html
+    // Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html
+
+    /*
+    call this version 1.0
+    
+    Offhand, the only funky part that I can think of is in nbrequestFrom, where the buffer
+    length and index are set *before* the data is actually read. The problem is that these
+    are variables local to the TwoWire object, and by the time we actually have read the
+    data, and know what the length actually is, we have no simple access to the object's 
+    variables. The actual bytes read *is* given to the callback function, though.
+    
+    The ISR code for a slave receiver is commented out. I don't have that setup, and can't
+    verify it at this time. Save it for 2.0!
+    
+    The handling of the read and write processes here is much like in the demo sketch code: 
+    the process is broken down into sequential functions, where each registers the next as a
+    callback, essentially.
+    
+    For example, for the Read process, twi_read00 just returns if TWI is not yet in a 
+    ready state. When there's another interrupt, and the interface *is* ready, then it
+    sets up the read, starts it, and registers twi_read01 as the function to call after
+    the *next* interrupt. twi_read01, then, just returns if the interface is still in a
+    "reading" state. When the reading is done, it copies the information to the buffer,
+    cleans up, and calls the user-requested callback function with the actual number of 
+    bytes read.
+    
+    The writing is similar.
+    
+    Questions, comments and problems can go to Gene@Telobot.com.
+    
+    Thumbs Up!
+    Gene Knight
+    
+    */
+    
+    uint8_t TwoWire::rxBuffer[NBWIRE_BUFFER_LENGTH];
+    uint8_t TwoWire::rxBufferIndex = 0;
+    uint8_t TwoWire::rxBufferLength = 0;
+    
+    uint8_t TwoWire::txAddress = 0;
+    uint8_t TwoWire::txBuffer[NBWIRE_BUFFER_LENGTH];
+    uint8_t TwoWire::txBufferIndex = 0;
+    uint8_t TwoWire::txBufferLength = 0;
+    
+    //uint8_t TwoWire::transmitting = 0;
+    void (*TwoWire::user_onRequest)(void);
+    void (*TwoWire::user_onReceive)(int);
+    
+    static volatile uint8_t twi_transmitting;
+    static volatile uint8_t twi_state;
+    static uint8_t twi_slarw;
+    static volatile uint8_t twi_error;
+    static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
+    static volatile uint8_t twi_masterBufferIndex;
+    static uint8_t twi_masterBufferLength;
+    static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
+    static volatile uint8_t twi_rxBufferIndex;
+    //static volatile uint8_t twi_Interrupt_Continue_Command;
+    static volatile uint8_t twi_Return_Value;
+    static volatile uint8_t twi_Done;
+    void (*twi_cbendTransmissionDone)(int);
+    void (*twi_cbreadFromDone)(int);
+    
+    void twi_init() {
+        // initialize state
+        twi_state = TWI_READY;
+
+        // activate internal pull-ups for twi
+        // as per note from atmega8 manual pg167
+        sbi(PORTC, 4);
+        sbi(PORTC, 5);
+
+        // initialize twi prescaler and bit rate
+        cbi(TWSR, TWPS0); // TWI Status Register - Prescaler bits
+        cbi(TWSR, TWPS1);
+
+        /* twi bit rate formula from atmega128 manual pg 204
+        SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
+        note: TWBR should be 10 or higher for master mode
+        It is 72 for a 16mhz Wiring board with 100kHz TWI */
+
+        TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2; // bitrate register
+        // enable twi module, acks, and twi interrupt
+
+        TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
+
+        /* TWEN - TWI Enable Bit
+        TWIE - TWI Interrupt Enable
+        TWEA - TWI Enable Acknowledge Bit
+        TWINT - TWI Interrupt Flag
+        TWSTA - TWI Start Condition
+        */
+    }
+    
+    typedef struct {
+        uint8_t address;
+        uint8_t* data;
+        uint8_t length;
+        uint8_t wait;
+        uint8_t i;
+    } twi_Write_Vars;
+
+    twi_Write_Vars *ptwv = 0;
+    static void (*fNextInterruptFunction)(void) = 0;
+
+    void twi_Finish(byte bRetVal) {
+        if (ptwv) {
+            free(ptwv);
+            ptwv = 0;
+        }
+        twi_Done = 0xFF;
+        twi_Return_Value = bRetVal;
+        fNextInterruptFunction = 0;
+    }
+    
+    uint8_t twii_WaitForDone(uint16_t timeout) {
+        uint32_t endMillis = millis() + timeout;
+        while (!twi_Done && (timeout == 0 || millis() < endMillis)) continue;
+        return twi_Return_Value;
+    }
+    
+    void twii_SetState(uint8_t ucState) {
+        twi_state = ucState;
+    }
+
+    void twii_SetError(uint8_t ucError) {
+        twi_error = ucError ;
+    }
+
+    void twii_InitBuffer(uint8_t ucPos, uint8_t ucLength) {
+        twi_masterBufferIndex = 0;
+        twi_masterBufferLength = ucLength;
+    }
+
+    void twii_CopyToBuf(uint8_t* pData, uint8_t ucLength) {
+        uint8_t i;
+        for (i = 0; i < ucLength; ++i) {
+            twi_masterBuffer[i] = pData[i];
+        }
+    }
+
+    void twii_CopyFromBuf(uint8_t *pData, uint8_t ucLength) {
+        uint8_t i;
+        for (i = 0; i < ucLength; ++i) {
+            pData[i] = twi_masterBuffer[i];
+        }
+    }
+
+    void twii_SetSlaRW(uint8_t ucSlaRW) {
+        twi_slarw = ucSlaRW;
+    }
+
+    void twii_SetStart() {
+        TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
+    }
+
+    void twi_write01() {
+        if (TWI_MTX == twi_state) return; // blocking test
+        twi_transmitting = 0 ;
+        if (twi_error == 0xFF)
+            twi_Finish (0);    // success
+        else if (twi_error == TW_MT_SLA_NACK)
+            twi_Finish (2);    // error: address send, nack received
+        else if (twi_error == TW_MT_DATA_NACK)
+            twi_Finish (3);    // error: data send, nack received
+        else
+            twi_Finish (4);    // other twi error
+        if (twi_cbendTransmissionDone) return twi_cbendTransmissionDone(twi_Return_Value);
+        return;
+    }
+    
+    
+    void twi_write00() {
+        if (TWI_READY != twi_state) return; // blocking test
+        if (TWI_BUFFER_LENGTH < ptwv -> length) {
+            twi_Finish(1); // end write with error 1
+            return;
+        }
+        twi_Done = 0x00; // show as working
+        twii_SetState(TWI_MTX); // to transmitting
+        twii_SetError(0xFF); // to No Error
+        twii_InitBuffer(0, ptwv -> length); // pointer and length
+        twii_CopyToBuf(ptwv -> data, ptwv -> length); // get the data
+        twii_SetSlaRW((ptwv -> address << 1) | TW_WRITE); // write command
+        twii_SetStart(); // start the cycle
+        fNextInterruptFunction = twi_write01; // next routine
+        return twi_write01();
+    }
+    
+    void twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait) {
+        uint8_t i;
+        ptwv = (twi_Write_Vars *)malloc(sizeof(twi_Write_Vars));
+        ptwv -> address = address;
+        ptwv -> data = data;
+        ptwv -> length = length;
+        ptwv -> wait = wait;
+        fNextInterruptFunction = twi_write00;
+        return twi_write00();
+    }
+
+    void twi_read01() {
+        if (TWI_MRX == twi_state) return; // blocking test
+        if (twi_masterBufferIndex < ptwv -> length) ptwv -> length = twi_masterBufferIndex;
+        twii_CopyFromBuf(ptwv -> data, ptwv -> length);
+        twi_Finish(ptwv -> length);
+        if (twi_cbreadFromDone) return twi_cbreadFromDone(twi_Return_Value);
+        return;
+    }
+    
+    void twi_read00() {
+        if (TWI_READY != twi_state) return; // blocking test
+        if (TWI_BUFFER_LENGTH < ptwv -> length) twi_Finish(0); // error return
+        twi_Done = 0x00; // show as working
+        twii_SetState(TWI_MRX); // reading
+        twii_SetError(0xFF); // reset error
+        twii_InitBuffer(0, ptwv -> length - 1); // init to one less than length
+        twii_SetSlaRW((ptwv -> address << 1) | TW_READ); // read command
+        twii_SetStart(); // start cycle
+        fNextInterruptFunction = twi_read01;
+        return twi_read01();
+    }
+
+    void twi_readFrom(uint8_t address, uint8_t* data, uint8_t length) {
+        uint8_t i;
+
+        ptwv = (twi_Write_Vars *)malloc(sizeof(twi_Write_Vars));
+        ptwv -> address = address;
+        ptwv -> data = data;
+        ptwv -> length = length;
+        fNextInterruptFunction = twi_read00;
+        return twi_read00();
+    }
+
+    void twi_reply(uint8_t ack) {
+        // transmit master read ready signal, with or without ack
+        if (ack){
+            TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
+        } else {
+            TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
+        }
+    }
+    
+    void twi_stop(void) {
+        // send stop condition
+        TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
+    
+        // wait for stop condition to be exectued on bus
+        // TWINT is not set after a stop condition!
+        while (TWCR & _BV(TWSTO)) {
+            continue;
+        }
+    
+        // update twi state
+        twi_state = TWI_READY;
+    }
+
+    void twi_releaseBus(void) {
+        // release bus
+        TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
+    
+        // update twi state
+        twi_state = TWI_READY;
+    }
+    
+    SIGNAL(TWI_vect) {
+        switch (TW_STATUS) {
+            // All Master
+            case TW_START:     // sent start condition
+            case TW_REP_START: // sent repeated start condition
+                // copy device address and r/w bit to output register and ack
+                TWDR = twi_slarw;
+                twi_reply(1);
+                break;
+    
+            // Master Transmitter
+            case TW_MT_SLA_ACK:  // slave receiver acked address
+            case TW_MT_DATA_ACK: // slave receiver acked data
+                // if there is data to send, send it, otherwise stop
+                if (twi_masterBufferIndex < twi_masterBufferLength) {
+                    // copy data to output register and ack
+                    TWDR = twi_masterBuffer[twi_masterBufferIndex++];
+                    twi_reply(1);
+                } else {
+                    twi_stop();
+                }
+                break;
+
+            case TW_MT_SLA_NACK:  // address sent, nack received
+                twi_error = TW_MT_SLA_NACK;
+                twi_stop();
+                break;
+
+            case TW_MT_DATA_NACK: // data sent, nack received
+                twi_error = TW_MT_DATA_NACK;
+                twi_stop();
+                break;
+
+            case TW_MT_ARB_LOST: // lost bus arbitration
+                twi_error = TW_MT_ARB_LOST;
+                twi_releaseBus();
+                break;
+    
+            // Master Receiver
+            case TW_MR_DATA_ACK: // data received, ack sent
+                // put byte into buffer
+                twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
+
+            case TW_MR_SLA_ACK:  // address sent, ack received
+                // ack if more bytes are expected, otherwise nack
+                if (twi_masterBufferIndex < twi_masterBufferLength) {
+                    twi_reply(1);
+                } else {
+                    twi_reply(0);
+                }
+                break;
+
+            case TW_MR_DATA_NACK: // data received, nack sent
+                // put final byte into buffer
+                twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
+
+            case TW_MR_SLA_NACK: // address sent, nack received
+                twi_stop();
+                break;
+
+        // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case
+
+        // Slave Receiver (NOT IMPLEMENTED YET)
+        /*
+            case TW_SR_SLA_ACK:   // addressed, returned ack
+            case TW_SR_GCALL_ACK: // addressed generally, returned ack
+            case TW_SR_ARB_LOST_SLA_ACK:   // lost arbitration, returned ack
+            case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
+                // enter slave receiver mode
+                twi_state = TWI_SRX;
+
+                // indicate that rx buffer can be overwritten and ack
+                twi_rxBufferIndex = 0;
+                twi_reply(1);
+                break;
+
+            case TW_SR_DATA_ACK:       // data received, returned ack
+            case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
+                // if there is still room in the rx buffer
+                if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) {
+                    // put byte in buffer and ack
+                    twi_rxBuffer[twi_rxBufferIndex++] = TWDR;
+                    twi_reply(1);
+                } else {
+                    // otherwise nack
+                    twi_reply(0);
+                }
+                break;
+
+            case TW_SR_STOP: // stop or repeated start condition received
+                // put a null char after data if there's room
+                if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) {
+                    twi_rxBuffer[twi_rxBufferIndex] = 0;
+                }
+
+                // sends ack and stops interface for clock stretching
+                twi_stop();
+
+                // callback to user defined callback
+                twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
+
+                // since we submit rx buffer to "wire" library, we can reset it
+                twi_rxBufferIndex = 0;
+
+                // ack future responses and leave slave receiver state
+                twi_releaseBus();
+                break;
+
+            case TW_SR_DATA_NACK:       // data received, returned nack
+            case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
+                // nack back at master
+                twi_reply(0);
+                break;
+
+            // Slave Transmitter
+            case TW_ST_SLA_ACK:          // addressed, returned ack
+            case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
+                // enter slave transmitter mode
+                twi_state = TWI_STX;
+
+                // ready the tx buffer index for iteration
+                twi_txBufferIndex = 0;
+
+                // set tx buffer length to be zero, to verify if user changes it
+                twi_txBufferLength = 0;
+
+                // request for txBuffer to be filled and length to be set
+                // note: user must call twi_transmit(bytes, length) to do this
+                twi_onSlaveTransmit();
+
+                // if they didn't change buffer & length, initialize it
+                if (0 == twi_txBufferLength) {
+                    twi_txBufferLength = 1;
+                    twi_txBuffer[0] = 0x00;
+                }
+                
+                // transmit first byte from buffer, fall through
+
+            case TW_ST_DATA_ACK: // byte sent, ack returned
+                // copy data to output register
+                TWDR = twi_txBuffer[twi_txBufferIndex++];
+
+                // if there is more to send, ack, otherwise nack
+                if (twi_txBufferIndex < twi_txBufferLength) {
+                    twi_reply(1);
+                } else {
+                    twi_reply(0);
+                }
+                break;
+
+            case TW_ST_DATA_NACK: // received nack, we are done
+            case TW_ST_LAST_DATA: // received ack, but we are done already!
+                // ack future responses
+                twi_reply(1);
+                // leave slave receiver state
+                twi_state = TWI_READY;
+                break;
+            */
+
+            // all
+            case TW_NO_INFO:   // no state information
+                break;
+
+            case TW_BUS_ERROR: // bus error, illegal stop/start
+                twi_error = TW_BUS_ERROR;
+                twi_stop();
+                break;
+        }
+
+        if (fNextInterruptFunction) return fNextInterruptFunction();
+    }
+
+    TwoWire::TwoWire() { }
+    
+    void TwoWire::begin(void) {
+        rxBufferIndex = 0;
+        rxBufferLength = 0;
+    
+        txBufferIndex = 0;
+        txBufferLength = 0;
+
+        twi_init();
+    }
+    
+    void TwoWire::beginTransmission(uint8_t address) {
+        //beginTransmission((uint8_t)address);
+
+        // indicate that we are transmitting
+        twi_transmitting = 1;
+        
+        // set address of targeted slave
+        txAddress = address;
+        
+        // reset tx buffer iterator vars
+        txBufferIndex = 0;
+        txBufferLength = 0;
+    }
+
+    uint8_t TwoWire::endTransmission(uint16_t timeout) {
+        // transmit buffer (blocking)
+        //int8_t ret =
+        twi_cbendTransmissionDone = NULL;
+        twi_writeTo(txAddress, txBuffer, txBufferLength, 1);
+        int8_t ret = twii_WaitForDone(timeout);
+
+        // reset tx buffer iterator vars
+        txBufferIndex = 0;
+        txBufferLength = 0;
+
+        // indicate that we are done transmitting
+        // twi_transmitting = 0;
+        return ret;
+    }
+
+    void TwoWire::nbendTransmission(void (*function)(int)) {
+        twi_cbendTransmissionDone = function;
+        twi_writeTo(txAddress, txBuffer, txBufferLength, 1);
+        return;
+    }
+    
+    void TwoWire::send(uint8_t data) {
+        if (twi_transmitting) {
+            // in master transmitter mode
+            // don't bother if buffer is full
+            if (txBufferLength >= NBWIRE_BUFFER_LENGTH) {
+                return;
+            }
+
+            // put byte in tx buffer
+            txBuffer[txBufferIndex] = data;
+            ++txBufferIndex;
+
+            // update amount in buffer
+            txBufferLength = txBufferIndex;
+        } else {
+            // in slave send mode
+            // reply to master
+            //twi_transmit(&data, 1);
+        }
+    }
+    
+    uint8_t TwoWire::receive(void) {
+        // default to returning null char
+        // for people using with char strings
+        uint8_t value = 0;
+      
+        // get each successive byte on each call
+        if (rxBufferIndex < rxBufferLength) {
+            value = rxBuffer[rxBufferIndex];
+            ++rxBufferIndex;
+        }
+    
+        return value;
+    }
+    
+    uint8_t TwoWire::requestFrom(uint8_t address, int quantity, uint16_t timeout) {
+        // clamp to buffer length
+        if (quantity > NBWIRE_BUFFER_LENGTH) {
+            quantity = NBWIRE_BUFFER_LENGTH;
+        }
+
+        // perform blocking read into buffer
+        twi_cbreadFromDone = NULL;
+        twi_readFrom(address, rxBuffer, quantity);
+        uint8_t read = twii_WaitForDone(timeout);
+
+        // set rx buffer iterator vars
+        rxBufferIndex = 0;
+        rxBufferLength = read;
+    
+        return read;
+    }
+    
+    void TwoWire::nbrequestFrom(uint8_t address, int quantity, void (*function)(int)) {
+        // clamp to buffer length
+        if (quantity > NBWIRE_BUFFER_LENGTH) {
+            quantity = NBWIRE_BUFFER_LENGTH;
+        }
+
+        // perform blocking read into buffer
+        twi_cbreadFromDone = function;
+        twi_readFrom(address, rxBuffer, quantity);
+        //uint8_t read = twii_WaitForDone();
+
+        // set rx buffer iterator vars
+        //rxBufferIndex = 0;
+        //rxBufferLength = read;
+
+        rxBufferIndex = 0;
+        rxBufferLength = quantity; // this is a hack
+
+        return; //read;
+    }
+
+    uint8_t TwoWire::available(void) {
+        return rxBufferLength - rxBufferIndex;
+    }
+
+#endif

          
A => libraries/I2Cdev/I2Cdev.h +281 -0
@@ 0,0 1,281 @@ 
+// I2Cdev library collection - Main I2C device class header file
+// Abstracts bit and byte I2C R/W functions into a convenient class
+// 2013-06-05 by Jeff Rowberg <jeff@rowberg.net>
+//
+// Changelog:
+//      2015-10-30 - simondlevy : support i2c_t3 for Teensy3.1
+//      2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
+//      2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan)
+//      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) 2013 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_
+
+// -----------------------------------------------------------------------------
+// I2C interface implementation setting
+// -----------------------------------------------------------------------------
+#ifndef I2CDEV_IMPLEMENTATION
+#define I2CDEV_IMPLEMENTATION       I2CDEV_ARDUINO_WIRE
+//#define I2CDEV_IMPLEMENTATION       I2CDEV_BUILTIN_FASTWIRE
+#endif // I2CDEV_IMPLEMENTATION
+
+// comment this out if you are using a non-optimal IDE/implementation setting
+// but want the compiler to shut up about it
+#define I2CDEV_IMPLEMENTATION_WARNINGS
+
+// -----------------------------------------------------------------------------
+// I2C interface implementation options
+// -----------------------------------------------------------------------------
+#define I2CDEV_ARDUINO_WIRE         1 // Wire object from Arduino
+#define I2CDEV_BUILTIN_NBWIRE       2 // Tweaked Wire object from Gene Knight's NBWire project
+                                      // ^^^ NBWire implementation is still buggy w/some interrupts!
+#define I2CDEV_BUILTIN_FASTWIRE     3 // FastWire object from Francesco Ferrara's project
+#define I2CDEV_I2CMASTER_LIBRARY    4 // I2C object from DSSCircuits I2C-Master Library at https://github.com/DSSCircuits/I2C-Master-Library
+
+// -----------------------------------------------------------------------------
+// Arduino-style "Serial.print" debug constant (uncomment to enable)
+// -----------------------------------------------------------------------------
+//#define I2CDEV_SERIAL_DEBUG
+
+#ifdef ARDUINO
+    #if ARDUINO < 100
+        #include "WProgram.h"
+    #else
+        #include "Arduino.h"
+    #endif
+    #if defined(CORE_TEENSY) && defined(__MK20DX256__)
+        #include <i2c_t3.h>
+        #define BUFFER_LENGTH 32
+    #elif I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
+        #include <Wire.h>
+    #endif
+    #if I2CDEV_IMPLEMENTATION == I2CDEV_I2CMASTER_LIBRARY
+        #include <I2C.h>
+    #endif
+#endif
+
+#ifdef SPARK
+    #include <spark_wiring_i2c.h>
+    #define ARDUINO 101
+#endif
+
+
+// 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];")
+#define I2CDEV_DEFAULT_READ_TIMEOUT     1000
+
+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;
+};
+
+#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
+    //////////////////////
+    // FastWire 0.24
+    // This is a library to help faster programs to read I2C devices.
+    // Copyright(C) 2012
+    // Francesco Ferrara
+    //////////////////////
+
+    /* Master */
+    #define TW_START                0x08
+    #define TW_REP_START            0x10
+
+    /* Master Transmitter */
+    #define TW_MT_SLA_ACK           0x18
+    #define TW_MT_SLA_NACK          0x20
+    #define TW_MT_DATA_ACK          0x28
+    #define TW_MT_DATA_NACK         0x30
+    #define TW_MT_ARB_LOST          0x38
+
+    /* Master Receiver */
+    #define TW_MR_ARB_LOST          0x38
+    #define TW_MR_SLA_ACK           0x40
+    #define TW_MR_SLA_NACK          0x48
+    #define TW_MR_DATA_ACK          0x50
+    #define TW_MR_DATA_NACK         0x58
+
+    #define TW_OK                   0
+    #define TW_ERROR                1
+
+    class Fastwire {
+        private:
+            static boolean waitInt();
+
+        public:
+            static void setup(int khz, boolean pullup);
+            static byte beginTransmission(byte device);
+            static byte write(byte value);
+            static byte writeBuf(byte device, byte address, byte *data, byte num);
+            static byte readBuf(byte device, byte address, byte *data, byte num);
+            static void reset();
+            static byte stop();
+    };
+#endif
+
+#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
+    // NBWire implementation based heavily on code by Gene Knight <Gene@Telobot.com>
+    // Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html
+    // Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html
+
+    #define NBWIRE_BUFFER_LENGTH 32
+
+    class TwoWire {
+        private:
+            static uint8_t rxBuffer[];
+            static uint8_t rxBufferIndex;
+            static uint8_t rxBufferLength;
+
+            static uint8_t txAddress;
+            static uint8_t txBuffer[];
+            static uint8_t txBufferIndex;
+            static uint8_t txBufferLength;
+
+            // static uint8_t transmitting;
+            static void (*user_onRequest)(void);
+            static void (*user_onReceive)(int);
+            static void onRequestService(void);
+            static void onReceiveService(uint8_t*, int);
+
+        public:
+            TwoWire();
+            void begin();
+            void begin(uint8_t);
+            void begin(int);
+            void beginTransmission(uint8_t);
+            //void beginTransmission(int);
+            uint8_t endTransmission(uint16_t timeout=0);
+            void nbendTransmission(void (*function)(int)) ;
+            uint8_t requestFrom(uint8_t, int, uint16_t timeout=0);
+            //uint8_t requestFrom(int, int);
+            void nbrequestFrom(uint8_t, int, void (*function)(int));
+            void send(uint8_t);
+            void send(uint8_t*, uint8_t);
+            //void send(int);
+            void send(char*);
+            uint8_t available(void);
+            uint8_t receive(void);
+            void onReceive(void (*)(int));
+            void onRequest(void (*)(void));
+    };
+
+    #define TWI_READY   0
+    #define TWI_MRX     1
+    #define TWI_MTX     2
+    #define TWI_SRX     3
+    #define TWI_STX     4
+
+    #define TW_WRITE    0
+    #define TW_READ     1
+
+    #define TW_MT_SLA_NACK      0x20
+    #define TW_MT_DATA_NACK     0x30
+
+    #define CPU_FREQ            16000000L
+    #define TWI_FREQ            100000L
+    #define TWI_BUFFER_LENGTH   32
+
+    /* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */
+
+    #define TW_STATUS_MASK              (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3))
+    #define TW_STATUS                   (TWSR & TW_STATUS_MASK)
+    #define TW_START                    0x08
+    #define TW_REP_START                0x10
+    #define TW_MT_SLA_ACK               0x18
+    #define TW_MT_SLA_NACK              0x20
+    #define TW_MT_DATA_ACK              0x28
+    #define TW_MT_DATA_NACK             0x30
+    #define TW_MT_ARB_LOST              0x38
+    #define TW_MR_ARB_LOST              0x38
+    #define TW_MR_SLA_ACK               0x40
+    #define TW_MR_SLA_NACK              0x48
+    #define TW_MR_DATA_ACK              0x50
+    #define TW_MR_DATA_NACK             0x58
+    #define TW_ST_SLA_ACK               0xA8
+    #define TW_ST_ARB_LOST_SLA_ACK      0xB0
+    #define TW_ST_DATA_ACK              0xB8
+    #define TW_ST_DATA_NACK             0xC0
+    #define TW_ST_LAST_DATA             0xC8
+    #define TW_SR_SLA_ACK               0x60
+    #define TW_SR_ARB_LOST_SLA_ACK      0x68
+    #define TW_SR_GCALL_ACK             0x70
+    #define TW_SR_ARB_LOST_GCALL_ACK    0x78
+    #define TW_SR_DATA_ACK              0x80
+    #define TW_SR_DATA_NACK             0x88
+    #define TW_SR_GCALL_DATA_ACK        0x90
+    #define TW_SR_GCALL_DATA_NACK       0x98
+    #define TW_SR_STOP                  0xA0
+    #define TW_NO_INFO                  0xF8
+    #define TW_BUS_ERROR                0x00
+
+    //#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
+    //#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
+
+    #ifndef sbi // set bit
+        #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
+    #endif // sbi
+
+    #ifndef cbi // clear bit
+        #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
+    #endif // cbi
+
+    extern TwoWire Wire;
+
+#endif // I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
+
+#endif /* _I2CDEV_H_ */

          
A => libraries/I2Cdev/LICENSE.txt +1 -0
@@ 0,0 1,1 @@ 
+The I2Cdev library is forked from works at "http://www.i2cdevlib.com/". The source codes under folder "I2Cdev" inherit the license from any license claim of "http://www.i2cdevlib.com/".
  No newline at end of file

          
A => libraries/I2Cdev/README +17 -0
@@ 0,0 1,17 @@ 
+Jennic platform added!
+
+============================================================================
+Note: for details about this project, please visit: http://www.i2cdevlib.com
+============================================================================
+
+The I2C Device Library (i2cdevlib) is a collection of uniform and well-documented classes to provide simple and intuitive interfaces to I2C devices. Each device is built to make use of the generic "I2Cdev" class, which abstracts the I2C bit- and byte-level communication away from each specific device class, making it easy to keep the device class clean while providing a simple way to modify just one class to port the I2C communication code onto different platforms (Arduino, PIC, MSP430, Jennic, simple bit-banging, etc.). Device classes are designed to provide complete coverage of all functionality described by each device's documentation, plus any generic convenience functions that are helpful.
+
+The code is written primarily to support the Arduino/Wiring implementation, but it may be useful in other circumstances. There are multiple I2C/TWI implementations selectable in the I2Cdev.h header file if the default Arduino "Wire.h" is not available or preferable for any reason.
+
+There are examples in many of the classes that demonstrate basic usage patterns. The I2Cdev class is built to be used statically, reducing the memory requirement if you have multiple I2C devices in your project. Only one instance of the I2Cdev class is required.
+
+Documentation for each class is created using Doxygen-style comments placed in each class definition file, based on the information available in each device's datasheet. This documentation is available in HTML format on the i2cdevlib.com website, which also holds helpful information for most of the classes present here on the repository.
+
+To use the library, just place the I2Cdev .cpp/.h or .c/.h source files and any device library .cpp/.h or .c/.h source files in the same folder as your sketch (or a suitable place relative to your project build tool), and include just the device library headers that you need. Arduino users will also need to include <Wire.h> in your main sketch source file. Create a single device object (e.g. "ADXL345 accel();"), place any appropriate init function calls in your setup() routine (e.g. "accel.initialize();"), and off you go! See the example sketches inside many of the device class directories for reference.
+
+Want a library for a device that isn't up on the repository? Request it, or fork the code and contribute! Better yet, send me a device on a breakout board to test the code during development. No guarantees on how fast I can get it done, but I'd love to make this the biggest consistent and well-documented I2C device library around.

          
A => libraries/I2Cdev/keywords.txt +38 -0
@@ 0,0 1,38 @@ 
+#######################################
+# Syntax Coloring Map For I2Cdev
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+I2Cdev	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+readBit	KEYWORD2
+readBitW	KEYWORD2
+readBits	KEYWORD2
+readBitsW	KEYWORD2
+readByte	KEYWORD2
+readBytes	KEYWORD2
+readWord	KEYWORD2
+readWords	KEYWORD2
+writeBit	KEYWORD2
+writeBitW	KEYWORD2
+writeBits	KEYWORD2
+writeBitsW	KEYWORD2
+writeByte	KEYWORD2
+writeBytes	KEYWORD2
+writeWord	KEYWORD2
+writeWords	KEYWORD2
+
+#######################################
+# Instances (KEYWORD2)
+#######################################
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+

          
A => libraries/I2Cdev/library.json +13 -0
@@ 0,0 1,13 @@ 
+{
+  "name": "I2Cdevlib-Core",
+  "keywords": "i2cdevlib, i2c",
+  "description": "The I2C Device Library (I2Cdevlib) is a collection of uniform and well-documented classes to provide simple and intuitive interfaces to I2C devices.",
+  "include": "Arduino/I2Cdev",
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/jrowberg/i2cdevlib.git"
+  },
+  "frameworks": "arduino",
+  "platforms": "atmelavr"
+}

          
A => libraries/LCD12864.zip +0 -0

        
A => libraries/LCD12864/LCD12864.cpp +515 -0
@@ 0,0 1,515 @@ 
+/*
+LCD12864
+Created by Markos Kyritsis
+This code is completely open source, and you may edit it however you like. 
+Please support the Arduino community, and upload any bug fixes to the relative
+page. 
+Keep in mind that the code is for demonstration purposes only, I can't be
+held responsible if your device suddenly zaps you into millions of little
+atoms, and you live the rest of your existence in a quantum universe where
+nothing makes sense anymore (you know, you exist in more than one place, changing
+between matter and waves, while at the same time half your electrons are entangled
+in some other point in space and time). 
+Other than that, enjoy... =D
+*/
+
+#include "LCD12864.h"
+#include "LCDSprite.h"
+
+#include "Arduino.h"
+
+extern "C" {
+//#include <wiring.h> 
+#include <inttypes.h>
+#include <avr/pgmspace.h>
+}
+
+
+LCD12864::LCD12864() {
+
+this->DEFAULTTIME = 80; // 80 ms default time
+this->delaytime = DEFAULTTIME;
+}
+
+/*
+void LCD12864::configPins(uint8_t tCSEL1, uint8_t tCSEL2, uint8_t tEN, uint8_t tRS, uint8_t tRW, uint8_t tD7, uint8_t tD6, uint8_t tD5, uint8_t tD4, uint8_t tD3, uint8_t tD2, uint8_t tD1, uint8_t tD0) {
+
+this->EN = tEN;
+this->RS = tRS;
+this->RW = tRW;
+this->D7 = tD7;
+this->D6 = tD6;
+this->D5 = tD5;
+this->D4 = tD4;
+this->D3 = tD3;
+this->D2 = tD2;
+this->D1 = tD1;
+this->D0 = tD0;
+this->CSEL1 = tCSEL1; 
+this->CSEL2 = tCSEL2; 
+}
+*/
+void LCD12864::setdelay(uint8_t newtime) {
+this->delaytime = newtime;
+}
+/*
+void LCD12864::configPins() {
+this->EN = 18;
+this->RS = 17;
+this->RW = 16;
+this->D7 = 7;
+this->D6 = 6;
+this->D5 = 5;
+this->D4 = 4;
+this->D3 = 11;
+this->D2 = 10;
+this->D1 = 9;
+this->D0 = 8;
+this->CSEL1 = 14; 
+}
+*/
+
+
+
+
+void LCD12864::selectCS1(void) {
+  digitalWrite(this->EN, 1);   
+delayns();
+  digitalWrite(this->CSEL1, 1);   
+delayns();
+  digitalWrite(this->EN, 0);   
+}
+
+void LCD12864::Initialise(void) {
+this->checkAND[7]= B00000001;
+this->checkAND[6]   = B00000010;
+this->checkAND[5] = B00000100;
+this->checkAND[4]  = B00001000;
+this->checkAND[3]  = B00010000;
+this->checkAND[2]  = B00100000;
+this->checkAND[1]  = B01000000;
+this->checkAND[0]  = B10000000;
+
+
+   pinMode(CSEL1, OUTPUT);    
+//   pinMode(this->CSEL2, OUTPUT);    
+   pinMode(RS, OUTPUT);     
+   pinMode(RW, OUTPUT);    
+   pinMode(EN, OUTPUT);     
+   pinMode(D0, OUTPUT);      
+   pinMode(D1, OUTPUT);    
+   pinMode(D2, OUTPUT);   
+   pinMode(D3, OUTPUT);      
+   pinMode(D4, OUTPUT);      
+   pinMode(D5, OUTPUT);      
+   pinMode(D6, OUTPUT);      
+   pinMode(D7, OUTPUT);      
+
+delayns();
+selectCS1();
+delayns();
+
+
+
+  // 5FH
+ setPins(0,0,0,1,0,1,1,1,1,1);
+  // 34H
+  setPins(0,0,0,0,1,1,0,1,0,0);
+//30H
+setPins(0,0,0,0,1,1,0,0,0,0);
+
+//01h
+setPins(0,0,0,0,0,0,0,0,0,1);
+
+//06h
+setPins(0,0,0,0,0,0,0,1,1,0);
+
+//0ch
+setPins(0,0,0,0,0,0,1,1,0,0);
+
+//try for gfx
+setPins(0,0,1,0,1,1,1,1,1,1);
+
+
+
+delayns();
+delayns();
+
+
+ setPins(0,0,0,0,1,1,0,1,0,0); // 8BIT Mode-4 ext gfx mode
+setPins(0,0,0,0,0,0,0,0,1,0); // Vertical scroll
+//  setPins(0,0,1,0,0,0,0,0,0,0); // SET 0,0
+
+delayns();
+delayns();
+
+
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X
+
+// Clear the Screen
+
+
+
+for (int j = 128; j <= 160; j++) {
+for (int i = 0; i <= 256; i+=8) {
+  setPins(1,0,0,0,0,0,0,0,0,0);
+}
+
+VectorConverter(j); // lets get the binary from the dec
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X
+
+
+}
+
+this->currentXpos = 0; // Starting coordinate lEFT of screen
+this->currentYpos = 128; // // Starting coordinate TOP of screen
+
+
+
+}
+
+
+
+
+void LCD12864::VectorConverter(uint8_t vector) {
+int result = vector;
+for (int i = 7; i >= 0; i--) {
+this->temp[i] = result % 2;
+
+result = result/2;
+}
+
+}
+
+void LCD12864::VectorConvertermany(uint8_t vector,uint8_t amount) {
+int result = vector;
+for (int i = amount-1; i >= 0; i--) {
+this->temp[i] = result % 2;
+
+result = result/2;
+}
+
+}
+
+
+void LCD12864::Render(void) {
+  setPins(0,0,0,0,1,1,0,1,1,0); // 8BIT Mode-4 ext gfx mode
+}
+void LCD12864::Draw(bool t, uint8_t x, uint8_t y) {
+if (t == true) // Graphic Select
+  setPins(0,0,0,0,1,1,0,1,1,0); // 8BIT Mode-4 ext gfx mode
+else { // Text select, are all these really necessary? I really don't know
+
+x += 128;
+y += 128;
+  // 5FH
+// setPins(0,0,0,1,0,1,1,1,1,1);
+  // 34H
+  setPins(0,0,0,0,1,1,0,1,0,0);
+//30H
+setPins(0,0,0,0,1,1,0,0,0,0);
+
+//01h
+setPins(0,0,0,0,0,0,0,0,0,1);
+
+//06h
+setPins(0,0,0,0,0,0,0,1,1,0);
+
+//0ch
+setPins(0,0,0,0,0,0,1,1,0,0);
+
+VectorConverter(y); // lets get the binary from the dec
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+VectorConverter(x); // lets get the binary from the dec
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM X
+
+/*
+//Move Y
+setPins(0,0,1,0,0,0,0,0,0,0);
+//Move X
+setPins(0,0,1,0,0,0,0,1,0,0);
+*/
+}
+
+}
+
+
+void LCD12864::FillRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height)
+{
+for (uint8_t i = y0+1; i < y0+height; i++) {
+DrawBrLine(x0, i, x0+width, i);
+}
+
+}
+
+void LCD12864::DrawRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height)
+{
+// Draw Top side
+DrawBrLine(x0, y0, x0+width+1, y0);
+
+// Draw Bottom side 
+DrawBrLine(x0, y0+height, x0+width+1, y0+height);
+
+// Now the sides
+for (uint8_t i = y0+1; i < y0+height; i++) {
+DrawPixel(x0,i);
+DrawPixel(x0+width,i);
+}
+
+
+
+
+
+}
+void LCD12864::DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius)  //Midpouint8_t circle algorithm
+  {
+    int f = 1 - radius;
+    int ddF_x = 1;
+    int ddF_y = -2 * radius;
+    int x = 0;
+    int y = radius;
+ 
+    DrawPixel(x0, y0 + radius);
+    DrawPixel(x0, y0 - radius);
+    DrawPixel(x0 + radius, y0);
+    DrawPixel(x0 - radius, y0);
+ 
+    while(x < y)
+    {
+      if(f >= 0) 
+      {
+        y--;
+        ddF_y += 2;
+        f += ddF_y;
+      }
+      x++;
+      ddF_x += 2;
+      f += ddF_x;    
+      DrawPixel(x0 + x, y0 + y);
+      DrawPixel(x0 - x, y0 + y);
+      DrawPixel(x0 + x, y0 - y);
+      DrawPixel(x0 - x, y0 - y);
+      DrawPixel(x0 + y, y0 + x);
+      DrawPixel(x0 - y, y0 + x);
+      DrawPixel(x0 + y, y0 - x);
+      DrawPixel(x0 - y, y0 - x);
+    }
+}
+
+
+
+void  LCD12864::DrawBrLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { // Bresenham Line Algorithm
+   int Dx = x1 - x0; 
+   int Dy = y1 - y0;
+   int steep = (abs(Dy) >= abs(Dx));
+   if (steep) {
+
+int a = x0;
+int b = y0;
+x0=b;
+y0=a;
+
+ a = x1;
+ b = y1;
+x1=b;
+y1=a;
+
+       // recompute Dx, Dy after swap
+       Dx = x1 - x0;
+       Dy = y1 - y0;
+   }
+   int xstep = 1;
+   if (Dx < 0) {
+       xstep = -1;
+       Dx = -Dx;
+   }
+   int ystep = 1;
+   if (Dy < 0) {
+       ystep = -1;		
+       Dy = -Dy; 
+   }
+   int TwoDy = 2*Dy; 
+   int TwoDyTwoDx = TwoDy - 2*Dx; // 2*Dy - 2*Dx
+   int E = TwoDy - Dx; //2*Dy - Dx
+   int y = y0;
+   int xDraw, yDraw;	
+   for (int x = x0; x != x1; x += xstep) {		
+       if (steep) {			
+           xDraw = y;
+           yDraw = x;
+       } else {			
+           xDraw = x;
+           yDraw = y;
+       }
+       // plot
+       DrawPixel(xDraw, yDraw);
+       // next
+       if (E > 0) {
+           E += TwoDyTwoDx; //E += 2*Dy - 2*Dx;
+           y = y + ystep;
+       } else {
+           E += TwoDy; //E += 2*Dy;
+       }
+   }
+}
+
+
+
+void LCD12864::DrawScreenBuffer(uint8_t X, uint8_t Y) {
+
+int xpos = X/8;
+int ypos = (Y-128);
+
+int leftX = X%8;
+int orit = checkAND[leftX];
+
+ScreenBuffer[xpos][ypos] |= orit;
+
+}
+
+void LCD12864::DrawSprite(uint8_t xPos, uint8_t yPos) {
+int count = 0;
+for (int j = 0; j < sheight; j++) {
+for (int i = 0; i < swidth; i++) {
+if (pgm_read_byte_near(header_data + count) == 1)
+this->DrawScreenBuffer(xPos+i,yPos+128+j);
+count++;
+}
+}
+}
+
+
+
+
+void LCD12864::RenderScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY) {
+for (int k = startY; k < maxY; k++) {
+VectorConverter(k+128); // Starting Y Position
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X at start again
+
+
+for (int i = 0; i < maxX; i++) {
+
+// Check if a pixel exists
+for (int j = 0; j < 8; j++) {
+int res = ScreenBuffer[i][k] & checkAND[j];
+
+if (res > 0) { // Looks like we found a 1
+temp[j] = 1;
+}
+else
+temp[j] = 0;
+} // end j
+setPins(1,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // draw remaining pixels =D
+} // end i
+
+
+} // end k
+
+}
+
+
+void LCD12864::RenderScreenBuffer(uint8_t screen) {
+
+for (int k = 0; k < 32; k++) {
+VectorConverter(k+128); // Starting Y Position
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X at start again
+
+
+if (screen == 2) {
+for (int i = 0; i < 16; i++) {
+setPins(1,0,0,0,0,0,0,0,0,0); // draw remaining pixels =D
+
+}
+}
+
+for (int i = 0; i < 16; i++) {
+
+// Check if a pixel exists
+for (int j = 0; j < 8; j++) {
+int res = ScreenBuffer[i][k] & checkAND[j];
+
+if (res > 0) { // Looks like we found a 1
+temp[j] = 1;
+}
+else
+temp[j] = 0;
+} // end j
+setPins(1,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // draw remaining pixels =D
+} // end i
+
+
+} // end k
+
+}
+
+
+void LCD12864::DumpScreenBuffer() {
+for (int j = 0; j < 32; j++) {
+for (int i = 0; i < 16; i++) {
+
+ScreenBuffer[i][j] = 0;
+}
+}
+}
+
+void LCD12864::DumpScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY) {
+for (int j = startY; j < maxY; j++) {
+for (int i = 0; i < maxX; i++) {
+
+ScreenBuffer[i][j] = 0;
+}
+}
+}
+
+void LCD12864::DrawPixel(uint8_t startX, uint8_t startY) {
+DrawScreenBuffer(startX,startY);
+
+this->currentXpos = startX;
+this->currentYpos = startY;
+}
+
+
+
+
+
+
+void LCD12864::Duplicate(bool x) {
+if (x == true)
+setPins(0,0,0,0,1,1,1,1,1,1); // Draw
+else
+setPins(0,0,0,0,1,1,1,1,1,0); // Draw
+
+}
+
+void LCD12864::setPins(uint8_t tRS, uint8_t tRW, uint8_t tD7, uint8_t tD6, uint8_t tD5, uint8_t tD4, uint8_t tD3, uint8_t tD2, uint8_t tD1, uint8_t tD0) {
+digitalWrite(EN,1);  
+delayns();
+
+  digitalWrite(RS, tRS);   
+  digitalWrite(RW, tRW);   
+  digitalWrite(D7, tD7);   
+  digitalWrite(D6, tD6);   
+  digitalWrite(D5, tD5);   
+  digitalWrite(D4, tD4);   
+  digitalWrite(D3, tD3);   
+  digitalWrite(D2, tD2);   
+  digitalWrite(D1, tD1);   
+  digitalWrite(D0, tD0);   
+delayns();
+  digitalWrite(EN, 0);   
+delayns();
+
+}
+
+void LCD12864::delayns(void){   
+delayMicroseconds(delaytime);
+ }
+
+
+LCD12864 LCDA = LCD12864();
+
+

          
A => libraries/LCD12864/LCD12864.h +95 -0
@@ 0,0 1,95 @@ 
+/*
+LCD12864
+Created by Markos Kyritsis
+This code is completely open source, and you may edit it however you like. 
+Please support the Arduino community, and upload any bug fixes to the relative
+page. 
+Keep in mind that the code is for demonstration purposes only, I can't be
+held responsible if your device suddenly zaps you into millions of little
+atoms, and you live the rest of your existence in a quantum universe where
+nothing makes sense anymore (you know, you exist in more than one place, changing
+between matter and waves, while at the same time half your electrons are entangled
+in some other point in space and time). 
+Other than that, enjoy... =D
+*/
+
+#if defined(BOARD_RTL8710)
+#error "RTL8710 do not support this library"
+#endif
+
+#ifndef	LCD12864_h
+#define LCD12864_h
+#include <avr/pgmspace.h>
+#include <inttypes.h>
+
+
+class LCD12864 {
+typedef unsigned char byte;
+
+
+
+public:
+
+LCD12864();
+
+ void Initialise(void);
+ void selectCS1(void);
+void setPins(uint8_t tRS, uint8_t tRW, uint8_t tD7, uint8_t tD6, uint8_t tD5, uint8_t tD4, uint8_t tD3, uint8_t tD2, uint8_t tD1, uint8_t tD0);
+void delayns(void);
+void VectorConverter(uint8_t vector);
+void VectorConvertermany(uint8_t vector, uint8_t amount);
+
+void Render(void);
+void DrawPixel(uint8_t startX, uint8_t startY);
+void DrawBrLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1);
+void DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius);
+void DrawRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height);
+void FillRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height);
+
+void DrawScreenBuffer(uint8_t X,uint8_t Y);
+void Duplicate(bool x);
+void Draw(bool t, uint8_t x, uint8_t y);
+void setdelay(uint8_t newtime);
+void DrawSprite(uint8_t xPos, uint8_t yPos);
+
+void RenderScreenBuffer(uint8_t screen);
+void RenderScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY);
+
+
+void DumpScreenBuffer(void);
+void DumpScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY);
+
+
+byte ScreenBuffer[16][32];
+
+
+byte checkAND[8];
+
+
+byte delaytime;
+byte DEFAULTTIME;
+
+static const byte RS = 17; 
+static const byte RW = 16;  
+static const byte EN = 18;  
+static const byte D0  = 8;  
+static const byte D1  = 9; 
+static const byte D2  = 10;  
+static const byte D3  = 11;  
+static const byte D4  = 4; 
+static const byte D5  = 5;  
+static const byte D6  = 6;  
+static const byte D7  = 7; 
+static const byte CSEL1  = 14; 
+
+
+
+byte currentXpos;
+byte currentYpos;
+
+byte temp[8];
+
+
+};
+extern LCD12864 LCDA;    
+#endif

          
A => libraries/LCD12864/LCDSprite.h +53 -0
@@ 0,0 1,53 @@ 
+/*  GIMP header image file format (INDEXED): /home/markos/pythonDevelopment/test4.h  */
+
+// TEST BMP, Picture of a Penguin
+
+
+static int swidth = 20; // change these
+static int sheight = 20; // change these
+
+
+static const unsigned char header_data[] = { // add data here
+/*
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,
+	0,0,0,0,
+	0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,
+	0,0,0,0,
+	0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,
+	0,0,0,0,
+	0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,
+	1,0,0,0,
+	0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,
+	1,1,0,0,
+	0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,
+	1,1,0,0,
+	0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,
+	1,1,0,0,
+	0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,
+	1,1,0,0,
+	0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,
+	1,0,0,0,
+	0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,
+	1,1,1,0,
+	0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,
+	1,1,1,0,
+	0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
+	1,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0
+*/
+	};

          
A => libraries/LCD12864/examples/drawsketch/drawsketch.ino +51 -0
@@ 0,0 1,51 @@ 
+#include <LCD12864.h>
+#include <LCDSprite.h>
+
+/*
+ * 
+ 12864 Graphics LCD library
+ 
+ 12864 is an unofficial Arduino library that supports 12864 Graphic LCDs that use the ST7920 chip.
+ 
+ Lets try to draw a Circle, and a the number 012 on screen
+  
+ * EN = Arduino Pin 18
+ * RS = Arduino Pin 17
+ * RW = Arduino Pin 16
+ * D7 = Arduino Pin 7
+ * D6 = Arduino Pin 6
+ * D5 = Arduino Pin 5
+ * D4 = Arduino Pin 4
+ * D3 = Arduino Pin 11
+ * D2 = Arduino Pin 10
+ * D1 = Arduino Pin 9
+ * D0 = Arduino Pin 8
+ * CSEL1 = Arduino Pin 14
+ You also need to connect a POT with 5V on one side, ground on the other, and 5V (perhaps with 150 Ohm resistance) + LCD Pin 19 to the wiper (middle one usually). Otherwise you won't see much.
+ 
+ This example code is in the public domain.
+ http://playground.arduino.cc/Code/LCD12864
+ */
+
+void setup() {
+  // put your setup code here, to run once:
+  LCDA.Initialise();
+  delay(500);
+  LCDA.Render();
+  LCDA.DrawCircle(30,135,5);
+
+  LCDA.RenderScreenBuffer(2); // lets draw it in the second screen
+  LCDA.Draw(false,4,0);
+
+  delay(1);
+
+  LCDA.setPins(1,0,0,0,1,1,0,0,0,0);
+  LCDA.setPins(1,0,0,0,1,1,0,0,0,1);
+  LCDA.setPins(1,0,0,0,1,1,0,0,1,0);
+  LCDA.Draw(true,4,0);
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+}

          
A => libraries/LCD12864/library.properties +9 -0
@@ 0,0 1,9 @@ 
+name=AmebaLCD12864
+version=1.0
+author=Realtek
+maintainer=Realtek <ameba.arduino@gmail.com>
+sentence=Use the LCD12864 module to draw something
+paragraph=
+category=LCD
+url=http://playground.arduino.cc/Code/LCD12864
+architectures=*

          
A => libraries/LCD12864/src/LCD12864.cpp +514 -0
@@ 0,0 1,514 @@ 
+/*
+LCD12864
+Created by Markos Kyritsis
+This code is completely open source, and you may edit it however you like. 
+Please support the Arduino community, and upload any bug fixes to the relative
+page. 
+Keep in mind that the code is for demonstration purposes only, I can't be
+held responsible if your device suddenly zaps you into millions of little
+atoms, and you live the rest of your existence in a quantum universe where
+nothing makes sense anymore (you know, you exist in more than one place, changing
+between matter and waves, while at the same time half your electrons are entangled
+in some other point in space and time). 
+Other than that, enjoy... =D
+*/
+
+#include "LCD12864.h"
+#include "LCDSprite.h"
+#include "Arduino.h"
+
+extern "C" {
+#include <wiring.h> 
+#include <inttypes.h>
+#include <avr/pgmspace.h>
+}
+
+
+LCD12864::LCD12864() {
+
+this->DEFAULTTIME = 80; // 80 ms default time
+this->delaytime = DEFAULTTIME;
+}
+
+/*
+void LCD12864::configPins(uint8_t tCSEL1, uint8_t tCSEL2, uint8_t tEN, uint8_t tRS, uint8_t tRW, uint8_t tD7, uint8_t tD6, uint8_t tD5, uint8_t tD4, uint8_t tD3, uint8_t tD2, uint8_t tD1, uint8_t tD0) {
+
+this->EN = tEN;
+this->RS = tRS;
+this->RW = tRW;
+this->D7 = tD7;
+this->D6 = tD6;
+this->D5 = tD5;
+this->D4 = tD4;
+this->D3 = tD3;
+this->D2 = tD2;
+this->D1 = tD1;
+this->D0 = tD0;
+this->CSEL1 = tCSEL1; 
+this->CSEL2 = tCSEL2; 
+}
+*/
+void LCD12864::setdelay(uint8_t newtime) {
+this->delaytime = newtime;
+}
+/*
+void LCD12864::configPins() {
+this->EN = 18;
+this->RS = 17;
+this->RW = 16;
+this->D7 = 7;
+this->D6 = 6;
+this->D5 = 5;
+this->D4 = 4;
+this->D3 = 11;
+this->D2 = 10;
+this->D1 = 9;
+this->D0 = 8;
+this->CSEL1 = 14; 
+}
+*/
+
+
+
+
+void LCD12864::selectCS1(void) {
+  digitalWrite(this->EN, 1);   
+delayns();
+  digitalWrite(this->CSEL1, 1);   
+delayns();
+  digitalWrite(this->EN, 0);   
+}
+
+void LCD12864::Initialise(void) {
+this->checkAND[7]= B00000001;
+this->checkAND[6]   = B00000010;
+this->checkAND[5] = B00000100;
+this->checkAND[4]  = B00001000;
+this->checkAND[3]  = B00010000;
+this->checkAND[2]  = B00100000;
+this->checkAND[1]  = B01000000;
+this->checkAND[0]  = B10000000;
+
+
+   pinMode(CSEL1, OUTPUT);    
+//   pinMode(this->CSEL2, OUTPUT);    
+   pinMode(RS, OUTPUT);     
+   pinMode(RW, OUTPUT);    
+   pinMode(EN, OUTPUT);     
+   pinMode(D0, OUTPUT);      
+   pinMode(D1, OUTPUT);    
+   pinMode(D2, OUTPUT);   
+   pinMode(D3, OUTPUT);      
+   pinMode(D4, OUTPUT);      
+   pinMode(D5, OUTPUT);      
+   pinMode(D6, OUTPUT);      
+   pinMode(D7, OUTPUT);      
+
+delayns();
+selectCS1();
+delayns();
+
+
+
+  // 5FH
+ setPins(0,0,0,1,0,1,1,1,1,1);
+  // 34H
+  setPins(0,0,0,0,1,1,0,1,0,0);
+//30H
+setPins(0,0,0,0,1,1,0,0,0,0);
+
+//01h
+setPins(0,0,0,0,0,0,0,0,0,1);
+
+//06h
+setPins(0,0,0,0,0,0,0,1,1,0);
+
+//0ch
+setPins(0,0,0,0,0,0,1,1,0,0);
+
+//try for gfx
+setPins(0,0,1,0,1,1,1,1,1,1);
+
+
+
+delayns();
+delayns();
+
+
+ setPins(0,0,0,0,1,1,0,1,0,0); // 8BIT Mode-4 ext gfx mode
+setPins(0,0,0,0,0,0,0,0,1,0); // Vertical scroll
+//  setPins(0,0,1,0,0,0,0,0,0,0); // SET 0,0
+
+delayns();
+delayns();
+
+
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X
+
+// Clear the Screen
+
+
+
+for (int j = 128; j <= 160; j++) {
+for (int i = 0; i <= 256; i+=8) {
+  setPins(1,0,0,0,0,0,0,0,0,0);
+}
+
+VectorConverter(j); // lets get the binary from the dec
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X
+
+
+}
+
+this->currentXpos = 0; // Starting coordinate lEFT of screen
+this->currentYpos = 128; // // Starting coordinate TOP of screen
+
+
+
+}
+
+
+
+
+void LCD12864::VectorConverter(uint8_t vector) {
+int result = vector;
+for (int i = 7; i >= 0; i--) {
+this->temp[i] = result % 2;
+
+result = result/2;
+}
+
+}
+
+void LCD12864::VectorConvertermany(uint8_t vector,uint8_t amount) {
+int result = vector;
+for (int i = amount-1; i >= 0; i--) {
+this->temp[i] = result % 2;
+
+result = result/2;
+}
+
+}
+
+
+void LCD12864::Render(void) {
+  setPins(0,0,0,0,1,1,0,1,1,0); // 8BIT Mode-4 ext gfx mode
+}
+void LCD12864::Draw(bool t, uint8_t x, uint8_t y) {
+if (t == true) // Graphic Select
+  setPins(0,0,0,0,1,1,0,1,1,0); // 8BIT Mode-4 ext gfx mode
+else { // Text select, are all these really necessary? I really don't know
+
+x += 128;
+y += 128;
+  // 5FH
+// setPins(0,0,0,1,0,1,1,1,1,1);
+  // 34H
+  setPins(0,0,0,0,1,1,0,1,0,0);
+//30H
+setPins(0,0,0,0,1,1,0,0,0,0);
+
+//01h
+setPins(0,0,0,0,0,0,0,0,0,1);
+
+//06h
+setPins(0,0,0,0,0,0,0,1,1,0);
+
+//0ch
+setPins(0,0,0,0,0,0,1,1,0,0);
+
+VectorConverter(y); // lets get the binary from the dec
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+VectorConverter(x); // lets get the binary from the dec
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM X
+
+/*
+//Move Y
+setPins(0,0,1,0,0,0,0,0,0,0);
+//Move X
+setPins(0,0,1,0,0,0,0,1,0,0);
+*/
+}
+
+}
+
+
+void LCD12864::FillRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height)
+{
+for (uint8_t i = y0+1; i < y0+height; i++) {
+DrawBrLine(x0, i, x0+width, i);
+}
+
+}
+
+void LCD12864::DrawRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height)
+{
+// Draw Top side
+DrawBrLine(x0, y0, x0+width+1, y0);
+
+// Draw Bottom side 
+DrawBrLine(x0, y0+height, x0+width+1, y0+height);
+
+// Now the sides
+for (uint8_t i = y0+1; i < y0+height; i++) {
+DrawPixel(x0,i);
+DrawPixel(x0+width,i);
+}
+
+
+
+
+
+}
+void LCD12864::DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius)  //Midpouint8_t circle algorithm
+  {
+    int f = 1 - radius;
+    int ddF_x = 1;
+    int ddF_y = -2 * radius;
+    int x = 0;
+    int y = radius;
+ 
+    DrawPixel(x0, y0 + radius);
+    DrawPixel(x0, y0 - radius);
+    DrawPixel(x0 + radius, y0);
+    DrawPixel(x0 - radius, y0);
+ 
+    while(x < y)
+    {
+      if(f >= 0) 
+      {
+        y--;
+        ddF_y += 2;
+        f += ddF_y;
+      }
+      x++;
+      ddF_x += 2;
+      f += ddF_x;    
+      DrawPixel(x0 + x, y0 + y);
+      DrawPixel(x0 - x, y0 + y);
+      DrawPixel(x0 + x, y0 - y);
+      DrawPixel(x0 - x, y0 - y);
+      DrawPixel(x0 + y, y0 + x);
+      DrawPixel(x0 - y, y0 + x);
+      DrawPixel(x0 + y, y0 - x);
+      DrawPixel(x0 - y, y0 - x);
+    }
+}
+
+
+
+void  LCD12864::DrawBrLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { // Bresenham Line Algorithm
+   int Dx = x1 - x0; 
+   int Dy = y1 - y0;
+   int steep = (abs(Dy) >= abs(Dx));
+   if (steep) {
+
+int a = x0;
+int b = y0;
+x0=b;
+y0=a;
+
+ a = x1;
+ b = y1;
+x1=b;
+y1=a;
+
+       // recompute Dx, Dy after swap
+       Dx = x1 - x0;
+       Dy = y1 - y0;
+   }
+   int xstep = 1;
+   if (Dx < 0) {
+       xstep = -1;
+       Dx = -Dx;
+   }
+   int ystep = 1;
+   if (Dy < 0) {
+       ystep = -1;		
+       Dy = -Dy; 
+   }
+   int TwoDy = 2*Dy; 
+   int TwoDyTwoDx = TwoDy - 2*Dx; // 2*Dy - 2*Dx
+   int E = TwoDy - Dx; //2*Dy - Dx
+   int y = y0;
+   int xDraw, yDraw;	
+   for (int x = x0; x != x1; x += xstep) {		
+       if (steep) {			
+           xDraw = y;
+           yDraw = x;
+       } else {			
+           xDraw = x;
+           yDraw = y;
+       }
+       // plot
+       DrawPixel(xDraw, yDraw);
+       // next
+       if (E > 0) {
+           E += TwoDyTwoDx; //E += 2*Dy - 2*Dx;
+           y = y + ystep;
+       } else {
+           E += TwoDy; //E += 2*Dy;
+       }
+   }
+}
+
+
+
+void LCD12864::DrawScreenBuffer(uint8_t X, uint8_t Y) {
+
+int xpos = X/8;
+int ypos = (Y-128);
+
+int leftX = X%8;
+int orit = checkAND[leftX];
+
+ScreenBuffer[xpos][ypos] |= orit;
+
+}
+
+void LCD12864::DrawSprite(uint8_t xPos, uint8_t yPos) {
+int count = 0;
+for (int j = 0; j < sheight; j++) {
+for (int i = 0; i < swidth; i++) {
+if (pgm_read_byte_near(header_data + count) == 1)
+this->DrawScreenBuffer(xPos+i,yPos+128+j);
+count++;
+}
+}
+}
+
+
+
+
+void LCD12864::RenderScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY) {
+for (int k = startY; k < maxY; k++) {
+VectorConverter(k+128); // Starting Y Position
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X at start again
+
+
+for (int i = 0; i < maxX; i++) {
+
+// Check if a pixel exists
+for (int j = 0; j < 8; j++) {
+int res = ScreenBuffer[i][k] & checkAND[j];
+
+if (res > 0) { // Looks like we found a 1
+temp[j] = 1;
+}
+else
+temp[j] = 0;
+} // end j
+setPins(1,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // draw remaining pixels =D
+} // end i
+
+
+} // end k
+
+}
+
+
+void LCD12864::RenderScreenBuffer(uint8_t screen) {
+
+for (int k = 0; k < 32; k++) {
+VectorConverter(k+128); // Starting Y Position
+setPins(0,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // SET DDRAM Y
+setPins(0,0,1,0,0,0,0,0,0,0); // SET DDRAM X at start again
+
+
+if (screen == 2) {
+for (int i = 0; i < 16; i++) {
+setPins(1,0,0,0,0,0,0,0,0,0); // draw remaining pixels =D
+
+}
+}
+
+for (int i = 0; i < 16; i++) {
+
+// Check if a pixel exists
+for (int j = 0; j < 8; j++) {
+int res = ScreenBuffer[i][k] & checkAND[j];
+
+if (res > 0) { // Looks like we found a 1
+temp[j] = 1;
+}
+else
+temp[j] = 0;
+} // end j
+setPins(1,0,this->temp[0],this->temp[1],this->temp[2],this->temp[3],this->temp[4],this->temp[5],this->temp[6],this->temp[7]); // draw remaining pixels =D
+} // end i
+
+
+} // end k
+
+}
+
+
+void LCD12864::DumpScreenBuffer() {
+for (int j = 0; j < 32; j++) {
+for (int i = 0; i < 16; i++) {
+
+ScreenBuffer[i][j] = 0;
+}
+}
+}
+
+void LCD12864::DumpScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY) {
+for (int j = startY; j < maxY; j++) {
+for (int i = 0; i < maxX; i++) {
+
+ScreenBuffer[i][j] = 0;
+}
+}
+}
+
+void LCD12864::DrawPixel(uint8_t startX, uint8_t startY) {
+DrawScreenBuffer(startX,startY);
+
+this->currentXpos = startX;
+this->currentYpos = startY;
+}
+
+
+
+
+
+
+void LCD12864::Duplicate(bool x) {
+if (x == true)
+setPins(0,0,0,0,1,1,1,1,1,1); // Draw
+else
+setPins(0,0,0,0,1,1,1,1,1,0); // Draw
+
+}
+
+void LCD12864::setPins(uint8_t tRS, uint8_t tRW, uint8_t tD7, uint8_t tD6, uint8_t tD5, uint8_t tD4, uint8_t tD3, uint8_t tD2, uint8_t tD1, uint8_t tD0) {
+digitalWrite(EN,1);  
+delayns();
+
+  digitalWrite(RS, tRS);   
+  digitalWrite(RW, tRW);   
+  digitalWrite(D7, tD7);   
+  digitalWrite(D6, tD6);   
+  digitalWrite(D5, tD5);   
+  digitalWrite(D4, tD4);   
+  digitalWrite(D3, tD3);   
+  digitalWrite(D2, tD2);   
+  digitalWrite(D1, tD1);   
+  digitalWrite(D0, tD0);   
+delayns();
+  digitalWrite(EN, 0);   
+delayns();
+
+}
+
+void LCD12864::delayns(void){   
+delayMicroseconds(delaytime);
+ }
+
+
+LCD12864 LCDA = LCD12864();
+
+

          
A => libraries/LCD12864/src/LCD12864.h +91 -0
@@ 0,0 1,91 @@ 
+/*
+LCD12864
+Created by Markos Kyritsis
+This code is completely open source, and you may edit it however you like. 
+Please support the Arduino community, and upload any bug fixes to the relative
+page. 
+Keep in mind that the code is for demonstration purposes only, I can't be
+held responsible if your device suddenly zaps you into millions of little
+atoms, and you live the rest of your existence in a quantum universe where
+nothing makes sense anymore (you know, you exist in more than one place, changing
+between matter and waves, while at the same time half your electrons are entangled
+in some other point in space and time). 
+Other than that, enjoy... =D
+*/
+
+#ifndef	LCD12864_h
+#define LCD12864_h
+#include <avr/pgmspace.h>
+#include <inttypes.h>
+
+
+class LCD12864 {
+typedef unsigned char byte;
+
+
+
+public:
+
+LCD12864();
+
+ void Initialise(void);
+ void selectCS1(void);
+void setPins(uint8_t tRS, uint8_t tRW, uint8_t tD7, uint8_t tD6, uint8_t tD5, uint8_t tD4, uint8_t tD3, uint8_t tD2, uint8_t tD1, uint8_t tD0);
+void delayns(void);
+void VectorConverter(uint8_t vector);
+void VectorConvertermany(uint8_t vector, uint8_t amount);
+
+void Render(void);
+void DrawPixel(uint8_t startX, uint8_t startY);
+void DrawBrLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1);
+void DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius);
+void DrawRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height);
+void FillRectangle(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height);
+
+void DrawScreenBuffer(uint8_t X,uint8_t Y);
+void Duplicate(bool x);
+void Draw(bool t, uint8_t x, uint8_t y);
+void setdelay(uint8_t newtime);
+void DrawSprite(uint8_t xPos, uint8_t yPos);
+
+void RenderScreenBuffer(uint8_t screen);
+void RenderScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY);
+
+
+void DumpScreenBuffer(void);
+void DumpScreenBuffer(uint8_t startX, uint8_t startY, uint8_t maxX, uint8_t maxY);
+
+
+byte ScreenBuffer[16][32];
+
+
+byte checkAND[8];
+
+
+byte delaytime;
+byte DEFAULTTIME;
+
+static const byte RS = 17; 
+static const byte RW = 16;  
+static const byte EN = 18;  
+static const byte D0  = 8;  
+static const byte D1  = 9; 
+static const byte D2  = 10;  
+static const byte D3  = 11;  
+static const byte D4  = 4; 
+static const byte D5  = 5;  
+static const byte D6  = 6;  
+static const byte D7  = 7; 
+static const byte CSEL1  = 14; 
+
+
+
+byte currentXpos;
+byte currentYpos;
+
+byte temp[8];
+
+
+};
+extern LCD12864 LCDA;    
+#endif

          
A => libraries/LCD12864/src/LCDSprite.h +53 -0
@@ 0,0 1,53 @@ 
+/*  GIMP header image file format (INDEXED): /home/markos/pythonDevelopment/test4.h  */
+
+// TEST BMP, Picture of a Penguin
+
+
+static int swidth = 20; // change these
+static int sheight = 20; // change these
+
+
+static const prog_char header_data[] PROGMEM = { // add data here
+/*
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,
+	0,0,0,0,
+	0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,
+	0,0,0,0,
+	0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,
+	0,0,0,0,
+	0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,
+	0,0,0,0,
+	0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,
+	1,0,0,0,
+	0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,
+	1,1,0,0,
+	0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,
+	1,1,0,0,
+	0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,
+	1,1,0,0,
+	0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,
+	1,1,0,0,
+	0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,
+	1,0,0,0,
+	0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,
+	1,1,1,0,
+	0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,
+	1,1,1,0,
+	0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
+	1,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0
+*/
+	};

          
A => libraries/LSM9DS1.zip +0 -0

        
A => libraries/MPU6050.zip +0 -0

        
A => libraries/MPU6050/Examples/MPU6050_DMP6/MPU6050_DMP6.ino +389 -0
@@ 0,0 1,389 @@ 
+// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0)
+// 6/21/2012 by Jeff Rowberg <jeff@rowberg.net>
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//      2013-05-08 - added seamless Fastwire support
+//                 - added note about gyro calibration
+//      2012-06-21 - added note about Arduino 1.0.1 + Leonardo compatibility error
+//      2012-06-20 - improved FIFO overflow handling and simplified read process
+//      2012-06-19 - completely rearranged DMP initialization code and simplification
+//      2012-06-13 - pull gyro and accel data from FIFO packet instead of reading directly
+//      2012-06-09 - fix broken FIFO read sequence and change interrupt detection to RISING
+//      2012-06-05 - add gravity-compensated initial reference frame acceleration output
+//                 - add 3D math helper file to DMP6 example sketch
+//                 - add Euler output and Yaw/Pitch/Roll output formats
+//      2012-06-04 - remove accel offset clearing for better results (thanks Sungon Lee)
+//      2012-06-01 - fixed gyro sensitivity to be 2000 deg/sec instead of 250
+//      2012-05-30 - basic DMP initialization working
+
+/* ============================================
+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.
+===============================================
+*/
+
+// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
+// for both classes must be in the include path of your project
+#include "I2Cdev.h"
+
+#include "MPU6050_6Axis_MotionApps20.h"
+//#include "MPU6050.h" // not necessary if using MotionApps include file
+
+// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
+// is used in I2Cdev.h
+#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
+    #include "Wire.h"
+#endif
+
+// class default I2C address is 0x68
+// specific I2C addresses may be passed as a parameter here
+// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
+// AD0 high = 0x69
+MPU6050 mpu;
+//MPU6050 mpu(0x69); // <-- use for AD0 high
+
+/* =========================================================================
+   NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch
+   depends on the MPU-6050's INT pin being connected to the Arduino's
+   external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is
+   digital I/O pin 2.
+ * ========================================================================= */
+
+/* =========================================================================
+   NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error
+   when using Serial.write(buf, len). The Teapot output uses this method.
+   The solution requires a modification to the Arduino USBAPI.h file, which
+   is fortunately simple, but annoying. This will be fixed in the next IDE
+   release. For more info, see these links:
+
+   http://arduino.cc/forum/index.php/topic,109987.0.html
+   http://code.google.com/p/arduino/issues/detail?id=958
+ * ========================================================================= */
+
+
+
+// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual
+// quaternion components in a [w, x, y, z] format (not best for parsing
+// on a remote host such as Processing or something though)
+//#define OUTPUT_READABLE_QUATERNION
+
+// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles
+// (in degrees) calculated from the quaternions coming from the FIFO.
+// Note that Euler angles suffer from gimbal lock (for more info, see
+// http://en.wikipedia.org/wiki/Gimbal_lock)
+//#define OUTPUT_READABLE_EULER
+
+// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/
+// pitch/roll angles (in degrees) calculated from the quaternions coming
+// from the FIFO. Note this also requires gravity vector calculations.
+// Also note that yaw/pitch/roll angles suffer from gimbal lock (for
+// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock)
+#define OUTPUT_READABLE_YAWPITCHROLL
+
+// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration
+// components with gravity removed. This acceleration reference frame is
+// not compensated for orientation, so +X is always +X according to the
+// sensor, just without the effects of gravity. If you want acceleration
+// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead.
+//#define OUTPUT_READABLE_REALACCEL
+
+// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration
+// components with gravity removed and adjusted for the world frame of
+// reference (yaw is relative to initial orientation, since no magnetometer
+// is present in this case). Could be quite handy in some cases.
+//#define OUTPUT_READABLE_WORLDACCEL
+
+// uncomment "OUTPUT_TEAPOT" if you want output that matches the
+// format used for the InvenSense teapot demo
+//#define OUTPUT_TEAPOT
+
+
+
+#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
+bool blinkState = false;
+
+// MPU control/status vars
+bool dmpReady = false;  // set true if DMP init was successful
+uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
+uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
+uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
+uint16_t fifoCount;     // count of all bytes currently in FIFO
+uint8_t fifoBuffer[64]; // FIFO storage buffer
+
+// orientation/motion vars
+Quaternion q;           // [w, x, y, z]         quaternion container
+VectorInt16 aa;         // [x, y, z]            accel sensor measurements
+VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
+VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
+VectorFloat gravity;    // [x, y, z]            gravity vector
+float euler[3];         // [psi, theta, phi]    Euler angle container
+float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
+
+// packet structure for InvenSense teapot demo
+uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
+
+
+
+// ================================================================
+// ===               INTERRUPT DETECTION ROUTINE                ===
+// ================================================================
+
+volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
+void dmpDataReady() {
+    mpuInterrupt = true;
+}
+
+
+
+// ================================================================
+// ===                      INITIAL SETUP                       ===
+// ================================================================
+
+void setup() {
+    // join I2C bus (I2Cdev library doesn't do this automatically)
+    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
+        Wire.begin();
+        Wire.setClock(400000); // 400kHz I2C clock (200kHz if CPU is 8MHz).
+    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
+        Fastwire::setup(400, true);
+    #endif
+
+    // initialize serial communication
+    // (115200 chosen because it is required for Teapot Demo output, but it's
+    // really up to you depending on your project)
+    Serial.begin(38400);
+    while (!Serial); // wait for Leonardo enumeration, others continue immediately
+
+    // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3v or Ardunio
+    // Pro Mini running at 3.3v, cannot handle this baud rate reliably due to
+    // the baud timing being too misaligned with processor ticks. You must use
+    // 38400 or slower in these cases, or use some kind of external separate
+    // crystal solution for the UART timer.
+
+    // initialize device
+    Serial.println(F("Initializing I2C devices..."));
+    mpu.initialize();
+
+    // verify connection
+    Serial.println(F("Testing device connections..."));
+    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
+
+    // wait for ready
+    Serial.println(F("\nSend any character to begin DMP programming and demo: "));
+    while (Serial.available() && Serial.read()); // empty buffer
+    while (!Serial.available());                 // wait for data
+    while (Serial.available() && Serial.read()); // empty buffer again
+
+    // load and configure the DMP
+    Serial.println(F("Initializing DMP..."));
+    devStatus = mpu.dmpInitialize();
+
+    // supply your own gyro offsets here, scaled for min sensitivity
+    mpu.setXGyroOffset(220);
+    mpu.setYGyroOffset(76);
+    mpu.setZGyroOffset(-85);
+    mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
+
+    // make sure it worked (returns 0 if so)
+    if (devStatus == 0) {
+        // turn on the DMP, now that it's ready
+        Serial.println(F("Enabling DMP..."));
+        mpu.setDMPEnabled(true);
+
+        // enable Arduino interrupt detection
+#if defined(BOARD_RTL8195A)
+        Serial.println(F("Enabling interrupt detection (Ameba RTL8195A D3 pin)..."));
+        attachInterrupt(3, dmpDataReady, RISING);
+#elif defined(BOARD_RTL8710)
+        Serial.println(F("Enabling interrupt detection (Ameba RTL8710 D12 pin)..."));
+        attachInterrupt(12, dmpDataReady, RISING);
+#else
+        Serial.println(F("Enabling interrupt detection (D3 pin)..."));
+        attachInterrupt(3, dmpDataReady, RISING);
+#endif
+        mpuIntStatus = mpu.getIntStatus();
+
+        // set our DMP Ready flag so the main loop() function knows it's okay to use it
+        Serial.println(F("DMP ready! Waiting for first interrupt..."));
+        dmpReady = true;
+
+        // get expected DMP packet size for later comparison
+        packetSize = mpu.dmpGetFIFOPacketSize();
+
+        /* We should avoid send/recv I2C data while there is an interrupt invoked.
+         * Otherwise the MPU6050 would hang and need a power down/up reset.
+         * So we set this vale big enough that we can finish task before next interrupt happend.
+         */
+        mpu.setRate(5); // 1khz / (1 + 5) = 166 Hz
+    } else {
+        // ERROR!
+        // 1 = initial memory load failed
+        // 2 = DMP configuration updates failed
+        // (if it's going to break, usually the code will be 1)
+        Serial.print(F("DMP Initialization failed (code "));
+        Serial.print(devStatus);
+        Serial.println(F(")"));
+    }
+
+    // configure LED for output
+    pinMode(LED_PIN, OUTPUT);
+}
+
+
+
+// ================================================================
+// ===                    MAIN PROGRAM LOOP                     ===
+// ================================================================
+
+void loop() {
+    // if programming failed, don't try to do anything
+    if (!dmpReady) return;
+
+    // wait for MPU interrupt or extra packet(s) available
+    while (!mpuInterrupt && fifoCount < packetSize) {
+        // other program behavior stuff here
+        // .
+        // .
+        // .
+        // if you are really paranoid you can frequently test in between other
+        // stuff to see if mpuInterrupt is true, and if so, "break;" from the
+        // while() loop to immediately process the MPU data
+        // .
+        // .
+        // .
+        os_thread_yield(); // without yield, the empty busy loop might make CPU behave un-expected
+    }
+
+    // reset interrupt flag and get INT_STATUS byte
+    mpuInterrupt = false;
+    mpuIntStatus = mpu.getIntStatus();
+
+    // get current FIFO count
+    fifoCount = mpu.getFIFOCount();
+
+    // check for overflow (this should never happen unless our code is too inefficient)
+    if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
+        // reset so we can continue cleanly
+        mpu.resetFIFO();
+        Serial.println(F("FIFO overflow!"));
+
+    // otherwise, check for DMP data ready interrupt (this should happen frequently)
+    } else if (mpuIntStatus & 0x02) {
+        // wait for correct available data length, should be a VERY short wait
+        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
+
+        while (fifoCount >= packetSize){
+            // read a packet from FIFO
+            mpu.getFIFOBytes(fifoBuffer, packetSize);
+            
+            // track FIFO count here in case there is > 1 packet available
+            // (this lets us immediately read more without waiting for an interrupt)
+            fifoCount -= packetSize;
+        }
+
+        #ifdef OUTPUT_READABLE_QUATERNION
+            // display quaternion values in easy matrix form: w x y z
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            Serial.print("quat\t");
+            Serial.print(q.w);
+            Serial.print("\t");
+            Serial.print(q.x);
+            Serial.print("\t");
+            Serial.print(q.y);
+            Serial.print("\t");
+            Serial.println(q.z);
+        #endif
+
+        #ifdef OUTPUT_READABLE_EULER
+            // display Euler angles in degrees
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetEuler(euler, &q);
+            Serial.print("euler\t");
+            Serial.print(euler[0] * 180/M_PI);
+            Serial.print("\t");
+            Serial.print(euler[1] * 180/M_PI);
+            Serial.print("\t");
+            Serial.println(euler[2] * 180/M_PI);
+        #endif
+
+        #ifdef OUTPUT_READABLE_YAWPITCHROLL
+            // display Euler angles in degrees
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetGravity(&gravity, &q);
+            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
+            Serial.print("ypr\t");
+            Serial.print(ypr[0] * 180/M_PI);
+            Serial.print("\t");
+            Serial.print(ypr[1] * 180/M_PI);
+            Serial.print("\t");
+            Serial.println(ypr[2] * 180/M_PI);
+        #endif
+
+        #ifdef OUTPUT_READABLE_REALACCEL
+            // display real acceleration, adjusted to remove gravity
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetAccel(&aa, fifoBuffer);
+            mpu.dmpGetGravity(&gravity, &q);
+            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
+            Serial.print("areal\t");
+            Serial.print(aaReal.x);
+            Serial.print("\t");
+            Serial.print(aaReal.y);
+            Serial.print("\t");
+            Serial.println(aaReal.z);
+        #endif
+
+        #ifdef OUTPUT_READABLE_WORLDACCEL
+            // display initial world-frame acceleration, adjusted to remove gravity
+            // and rotated based on known orientation from quaternion
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetAccel(&aa, fifoBuffer);
+            mpu.dmpGetGravity(&gravity, &q);
+            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
+            mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
+            Serial.print("aworld\t");
+            Serial.print(aaWorld.x);
+            Serial.print("\t");
+            Serial.print(aaWorld.y);
+            Serial.print("\t");
+            Serial.println(aaWorld.z);
+        #endif
+    
+        #ifdef OUTPUT_TEAPOT
+            // display quaternion values in InvenSense Teapot demo format:
+            teapotPacket[2] = fifoBuffer[0];
+            teapotPacket[3] = fifoBuffer[1];
+            teapotPacket[4] = fifoBuffer[4];
+            teapotPacket[5] = fifoBuffer[5];
+            teapotPacket[6] = fifoBuffer[8];
+            teapotPacket[7] = fifoBuffer[9];
+            teapotPacket[8] = fifoBuffer[12];
+            teapotPacket[9] = fifoBuffer[13];
+            Serial.write(teapotPacket, 14);
+            teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
+        #endif
+
+        // blink LED to indicate activity
+        blinkState = !blinkState;
+        digitalWrite(LED_PIN, blinkState);
+    }
+}

          
A => libraries/MPU6050/Examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde +242 -0
@@ 0,0 1,242 @@ 
+// I2C device class (I2Cdev) demonstration Processing sketch for MPU6050 DMP output
+// 6/20/2012 by Jeff Rowberg <jeff@rowberg.net>
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//     2012-06-20 - 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.
+===============================================
+*/
+
+import processing.serial.*;
+import processing.opengl.*;
+import toxi.geom.*;
+import toxi.processing.*;
+
+// NOTE: requires ToxicLibs to be installed in order to run properly.
+// 1. Download from http://toxiclibs.org/downloads
+// 2. Extract into [userdir]/Processing/libraries
+//    (location may be different on Mac/Linux)
+// 3. Run and bask in awesomeness
+
+ToxiclibsSupport gfx;
+
+Serial port;                         // The serial port
+char[] teapotPacket = new char[14];  // InvenSense Teapot packet
+int serialCount = 0;                 // current packet byte position
+int synced = 0;
+int interval = 0;
+
+float[] q = new float[4];
+Quaternion quat = new Quaternion(1, 0, 0, 0);
+
+float[] gravity = new float[3];
+float[] euler = new float[3];
+float[] ypr = new float[3];
+
+void setup() {
+    // 300px square viewport using OpenGL rendering
+    size(300, 300, OPENGL);
+    gfx = new ToxiclibsSupport(this);
+
+    // setup lights and antialiasing
+    lights();
+    smooth();
+  
+    // display serial port list for debugging/clarity
+    println(Serial.list());
+
+    // get the first available port (use EITHER this OR the specific port code below)
+    //String portName = Serial.list()[0];
+    
+    // get a specific serial port (use EITHER this OR the first-available code above)
+    String portName = "COM4";
+    
+    // open the serial port
+    port = new Serial(this, portName, 38400);
+    
+    // send single character to trigger DMP init/start
+    // (expected by MPU6050_DMP6 example Arduino sketch)
+    port.write('r');
+}
+
+void draw() {
+    if (millis() - interval > 1000) {
+        // resend single character to trigger DMP init/start
+        // in case the MPU is halted/reset while applet is running
+        port.write('r');
+        interval = millis();
+    }
+    
+    // black background
+    background(0);
+    
+    // translate everything to the middle of the viewport
+    pushMatrix();
+    translate(width / 2, height / 2);
+
+    // 3-step rotation from yaw/pitch/roll angles (gimbal lock!)
+    // ...and other weirdness I haven't figured out yet
+    //rotateY(-ypr[0]);
+    //rotateZ(-ypr[1]);
+    //rotateX(-ypr[2]);
+
+    // toxiclibs direct angle/axis rotation from quaternion (NO gimbal lock!)
+    // (axis order [1, 3, 2] and inversion [-1, +1, +1] is a consequence of
+    // different coordinate system orientation assumptions between Processing
+    // and InvenSense DMP)
+    float[] axis = quat.toAxisAngle();
+    rotate(axis[0], -axis[1], axis[3], axis[2]);
+
+    // draw main body in red
+    fill(255, 0, 0, 200);
+    box(10, 10, 200);
+    
+    // draw front-facing tip in blue
+    fill(0, 0, 255, 200);
+    pushMatrix();
+    translate(0, 0, -120);
+    rotateX(PI/2);
+    drawCylinder(0, 20, 20, 8);
+    popMatrix();
+    
+    // draw wings and tail fin in green
+    fill(0, 255, 0, 200);
+    beginShape(TRIANGLES);
+    vertex(-100,  2, 30); vertex(0,  2, -80); vertex(100,  2, 30);  // wing top layer
+    vertex(-100, -2, 30); vertex(0, -2, -80); vertex(100, -2, 30);  // wing bottom layer
+    vertex(-2, 0, 98); vertex(-2, -30, 98); vertex(-2, 0, 70);  // tail left layer
+    vertex( 2, 0, 98); vertex( 2, -30, 98); vertex( 2, 0, 70);  // tail right layer
+    endShape();
+    beginShape(QUADS);
+    vertex(-100, 2, 30); vertex(-100, -2, 30); vertex(  0, -2, -80); vertex(  0, 2, -80);
+    vertex( 100, 2, 30); vertex( 100, -2, 30); vertex(  0, -2, -80); vertex(  0, 2, -80);
+    vertex(-100, 2, 30); vertex(-100, -2, 30); vertex(100, -2,  30); vertex(100, 2,  30);
+    vertex(-2,   0, 98); vertex(2,   0, 98); vertex(2, -30, 98); vertex(-2, -30, 98);
+    vertex(-2,   0, 98); vertex(2,   0, 98); vertex(2,   0, 70); vertex(-2,   0, 70);
+    vertex(-2, -30, 98); vertex(2, -30, 98); vertex(2,   0, 70); vertex(-2,   0, 70);
+    endShape();
+    
+    popMatrix();
+}
+
+void serialEvent(Serial port) {
+    interval = millis();
+    while (port.available() > 0) {
+        int ch = port.read();
+
+        if (synced == 0 && ch != '$') return;   // initial synchronization - also used to resync/realign if needed
+        synced = 1;
+        print ((char)ch);
+
+        if ((serialCount == 1 && ch != 2)
+            || (serialCount == 12 && ch != '\r')
+            || (serialCount == 13 && ch != '\n'))  {
+            serialCount = 0;
+            synced = 0;
+            return;
+        }
+
+        if (serialCount > 0 || ch == '$') {
+            teapotPacket[serialCount++] = (char)ch;
+            if (serialCount == 14) {
+                serialCount = 0; // restart packet byte position
+                
+                // get quaternion from data packet
+                q[0] = ((teapotPacket[2] << 8) | teapotPacket[3]) / 16384.0f;
+                q[1] = ((teapotPacket[4] << 8) | teapotPacket[5]) / 16384.0f;
+                q[2] = ((teapotPacket[6] << 8) | teapotPacket[7]) / 16384.0f;
+                q[3] = ((teapotPacket[8] << 8) | teapotPacket[9]) / 16384.0f;
+                for (int i = 0; i < 4; i++) if (q[i] >= 2) q[i] = -4 + q[i];
+                
+                // set our toxilibs quaternion to new data
+                quat.set(q[0], q[1], q[2], q[3]);
+
+                /*
+                // below calculations unnecessary for orientation only using toxilibs
+                
+                // calculate gravity vector
+                gravity[0] = 2 * (q[1]*q[3] - q[0]*q[2]);
+                gravity[1] = 2 * (q[0]*q[1] + q[2]*q[3]);
+                gravity[2] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
+    
+                // calculate Euler angles
+                euler[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1);
+                euler[1] = -asin(2*q[1]*q[3] + 2*q[0]*q[2]);
+                euler[2] = atan2(2*q[2]*q[3] - 2*q[0]*q[1], 2*q[0]*q[0] + 2*q[3]*q[3] - 1);
+    
+                // calculate yaw/pitch/roll angles
+                ypr[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1);
+                ypr[1] = atan(gravity[0] / sqrt(gravity[1]*gravity[1] + gravity[2]*gravity[2]));
+                ypr[2] = atan(gravity[1] / sqrt(gravity[0]*gravity[0] + gravity[2]*gravity[2]));
+    
+                // output various components for debugging
+                //println("q:\t" + round(q[0]*100.0f)/100.0f + "\t" + round(q[1]*100.0f)/100.0f + "\t" + round(q[2]*100.0f)/100.0f + "\t" + round(q[3]*100.0f)/100.0f);
+                //println("euler:\t" + euler[0]*180.0f/PI + "\t" + euler[1]*180.0f/PI + "\t" + euler[2]*180.0f/PI);
+                //println("ypr:\t" + ypr[0]*180.0f/PI + "\t" + ypr[1]*180.0f/PI + "\t" + ypr[2]*180.0f/PI);
+                */
+            }
+        }
+    }
+}
+
+void drawCylinder(float topRadius, float bottomRadius, float tall, int sides) {
+    float angle = 0;
+    float angleIncrement = TWO_PI / sides;
+    beginShape(QUAD_STRIP);
+    for (int i = 0; i < sides + 1; ++i) {
+        vertex(topRadius*cos(angle), 0, topRadius*sin(angle));
+        vertex(bottomRadius*cos(angle), tall, bottomRadius*sin(angle));
+        angle += angleIncrement;
+    }
+    endShape();
+    
+    // If it is not a cone, draw the circular top cap
+    if (topRadius != 0) {
+        angle = 0;
+        beginShape(TRIANGLE_FAN);
+        
+        // Center point
+        vertex(0, 0, 0);
+        for (int i = 0; i < sides + 1; i++) {
+            vertex(topRadius * cos(angle), 0, topRadius * sin(angle));
+            angle += angleIncrement;
+        }
+        endShape();
+    }
+  
+    // If it is not a cone, draw the circular bottom cap
+    if (bottomRadius != 0) {
+        angle = 0;
+        beginShape(TRIANGLE_FAN);
+    
+        // Center point
+        vertex(0, tall, 0);
+        for (int i = 0; i < sides + 1; i++) {
+            vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle));
+            angle += angleIncrement;
+        }
+        endShape();
+    }
+}

          
A => libraries/MPU6050/Examples/MPU6050_DMP6_Ethernet/MPU6050_DMP6_Ethernet.ino +535 -0
@@ 0,0 1,535 @@ 
+// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0) over Ethernet
+// 2/27/2016 by hellphoenix
+// Based on I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0) (6/21/2012 by Jeff Rowberg <jeff@rowberg.net>)
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//      2016-02-28 - Cleaned up code to be in line with other example codes 
+ //                - Added Ethernet outputs for Quaternion, Euler, RealAccel, WorldAccel
+//      2016-02-27 - Initial working code compiled
+// Bugs:
+//                 - There is still a hangup after some time, though it only occurs when you are reading data from the website. 
+//                   If you only read the data from the serial port, there are no hangups.
+/* ============================================
+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 <Ethernet.h>
+// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
+// for both classes must be in the include path of your project
+#include "I2Cdev.h"
+
+#include "MPU6050_6Axis_MotionApps20.h"
+//#include "MPU6050.h" // not necessary if using MotionApps include file
+
+// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
+// is used in I2Cdev.h
+#include "Wire.h"
+#include "avr/wdt.h"// Watchdog library
+
+// class default I2C address is 0x68
+// specific I2C addresses may be passed as a parameter here
+// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
+// AD0 high = 0x69
+MPU6050 mpu;
+//MPU6050 mpu(0x69); // <-- use for AD0 high
+
+// MAC address from Ethernet shield sticker under board
+byte mac[] = {
+  0x90, 0xA2, 0xDA, 0x10, 0x26, 0x82
+}; 
+// assign an IP address for the controller:
+IPAddress ip(192,168,1,50);
+// the router's gateway address:
+byte gateway[] = { 192, 168, 1, 1 };
+// the subnet:
+byte subnet[] = { 255, 255, 0, 0 };
+
+// Initialize the Ethernet server library
+// with the IP address and port you want to use
+// (port 80 is default for HTTP):
+EthernetServer server(80);
+
+String HTTP_req;            // stores the HTTP request
+
+/* =========================================================================
+   NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch
+   depends on the MPU-6050's INT pin being connected to the Arduino's
+   external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is
+   digital I/O pin 2.
+ * ========================================================================= */
+
+/* =========================================================================
+   NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error
+   when using Serial.write(buf, len). The Teapot output uses this method.
+   The solution requires a modification to the Arduino USBAPI.h file, which
+   is fortunately simple, but annoying. This will be fixed in the next IDE
+   release. For more info, see these links:
+
+   http://arduino.cc/forum/index.php/topic,109987.0.html
+   http://code.google.com/p/arduino/issues/detail?id=958
+ * ========================================================================= */
+
+
+// uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual
+// quaternion components in a [w, x, y, z] format (not best for parsing
+// on a remote host such as Processing or something though)
+//#define OUTPUT_READABLE_QUATERNION
+
+// uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles
+// (in degrees) calculated from the quaternions coming from the FIFO.
+// Note that Euler angles suffer from gimbal lock (for more info, see
+// http://en.wikipedia.org/wiki/Gimbal_lock)
+//#define OUTPUT_READABLE_EULER
+
+// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/
+// pitch/roll angles (in degrees) calculated from the quaternions coming
+// from the FIFO. Note this also requires gravity vector calculations.
+// Also note that yaw/pitch/roll angles suffer from gimbal lock (for
+// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock)
+#define OUTPUT_READABLE_YAWPITCHROLL
+
+// uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration
+// components with gravity removed. This acceleration reference frame is
+// not compensated for orientation, so +X is always +X according to the
+// sensor, just without the effects of gravity. If you want acceleration
+// compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead.
+//#define OUTPUT_READABLE_REALACCEL
+
+// uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration
+// components with gravity removed and adjusted for the world frame of
+// reference (yaw is relative to initial orientation, since no magnetometer
+// is present in this case). Could be quite handy in some cases.
+//#define OUTPUT_READABLE_WORLDACCEL
+
+// uncomment "OUTPUT_TEAPOT" if you want output that matches the
+// format used for the InvenSense teapot demo
+//#define OUTPUT_TEAPOT
+
+#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
+bool blinkState = false;
+
+// MPU control/status vars
+bool dmpReady = false;  // set true if DMP init was successful
+uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
+uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
+uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
+uint16_t fifoCount;     // count of all bytes currently in FIFO
+uint8_t fifoBuffer[64]; // FIFO storage buffer
+
+// orientation/motion vars
+Quaternion q;           // [w, x, y, z]         quaternion container
+VectorInt16 aa;         // [x, y, z]            accel sensor measurements
+VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
+VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
+VectorFloat gravity;    // [x, y, z]            gravity vector
+float euler[3];         // [psi, theta, phi]    Euler angle container
+float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
+
+// packet structure for InvenSense teapot demo
+uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
+
+
+
+// ================================================================
+// ===               INTERRUPT DETECTION ROUTINE                ===
+// ================================================================
+
+volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
+void dmpDataReady() {
+    mpuInterrupt = true;
+}
+// ================================================================
+// ===                      INITIAL SETUP                       ===
+// ================================================================
+
+void setup() {
+    wdt_enable(WDTO_1S); //Watchdog enable. 
+    //WDTO_1S sets the watchdog timer to 1 second. The time set here is approximate.
+    // You can find more time settings at http://www.nongnu.org/avr-libc/user-manual/group__avr__watchdog.html .
+    
+    // join I2C bus (I2Cdev library doesn't do this automatically)
+    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
+        Wire.begin();
+        TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
+    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
+       Fastwire::setup(400, true);
+    #endif
+
+    // initialize serial communication
+    // (115200 chosen because it is required for Teapot Demo output, but it's
+    // really up to you depending on your project)
+    Serial.begin(115200);   
+    // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3v or Ardunio
+    // Pro Mini running at 3.3v, cannot handle this baud rate reliably due to
+    // the baud timing being too misaligned with processor ticks. You must use
+    // 38400 or slower in these cases, or use some kind of external separate
+    // crystal solution for the UART timer.
+
+    Ethernet.begin(mac, ip, gateway, subnet);
+    server.begin();
+    Serial.print("server is at ");
+    Serial.println(Ethernet.localIP());
+    while (!Serial); // wait for Leonardo enumeration, others continue immediately
+
+    // initialize device
+    Serial.println(F("Initializing I2C devices..."));
+    mpu.initialize();
+
+    // verify connection
+    Serial.println(F("Testing device connections..."));
+    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
+
+    // load and configure the DMP
+    Serial.println(F("Initializing DMP..."));
+    devStatus = mpu.dmpInitialize();
+
+    // supply your own gyro offsets here, scaled for min sensitivity
+    mpu.setXGyroOffset(220);
+    mpu.setYGyroOffset(76);
+    mpu.setZGyroOffset(-85);
+    mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
+
+    // make sure it worked (returns 0 if so)
+    if (devStatus == 0) {
+        // turn on the DMP, now that it's ready
+        Serial.println(F("Enabling DMP..."));
+        mpu.setDMPEnabled(true);
+
+        // enable Arduino interrupt detection
+        Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
+        attachInterrupt(0, dmpDataReady, RISING);
+        mpuIntStatus = mpu.getIntStatus();
+
+        // set our DMP Ready flag so the main loop() function knows it's okay to use it
+        Serial.println(F("DMP ready! Waiting for first interrupt..."));
+        dmpReady = true;
+
+        // get expected DMP packet size for later comparison
+        packetSize = mpu.dmpGetFIFOPacketSize();
+    } else {
+        // ERROR!
+        // 1 = initial memory load failed
+        // 2 = DMP configuration updates failed
+        // (if it's going to break, usually the code will be 1)
+        Serial.print(F("DMP Initialization failed (code "));
+        Serial.print(devStatus);
+        Serial.println(F(")"));
+        return;
+    }
+
+    // configure LED for output
+    pinMode(LED_PIN, OUTPUT);
+}
+
+
+
+// ================================================================
+// ===                    MAIN PROGRAM LOOP                     ===
+// ================================================================
+
+void loop() {
+    // if programming failed, don't try to do anything
+    if (!dmpReady) return;
+    wdt_reset();//Resets the watchdog timer. If the timer is not reset, and the timer expires, a watchdog-initiated device reset will occur.
+    // wait for MPU interrupt or extra packet(s) available
+    while (!mpuInterrupt && fifoCount < packetSize) {
+        // other program behavior stuff here        
+        // .
+        // .
+        // if you are really paranoid you can frequently test in between other
+        // stuff to see if mpuInterrupt is true, and if so, "break;" from the
+        // while() loop to immediately process the MPU data
+        // .
+        // .
+        // .
+    }
+
+    // reset interrupt flag and get INT_STATUS byte
+    mpuInterrupt = false;
+    mpuIntStatus = mpu.getIntStatus();
+
+    // get current FIFO count
+    fifoCount = mpu.getFIFOCount();
+
+    // check for overflow (this should never happen unless our code is too inefficient)
+    if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
+        // reset so we can continue cleanly
+        mpu.resetFIFO();
+        Serial.println(F("FIFO overflow!"));
+
+    // otherwise, check for DMP data ready interrupt (this should happen frequently)
+    } else if (mpuIntStatus & 0x02) {
+        // wait for correct available data length, should be a VERY short wait
+        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
+
+        // read a packet from FIFO, then clear the buffer
+        mpu.getFIFOBytes(fifoBuffer, packetSize);
+        //mpu.resetFIFO();
+        
+        // track FIFO count here in case there is > 1 packet available
+        // (this lets us immediately read more without waiting for an interrupt)
+        fifoCount -= packetSize;
+
+         #ifdef OUTPUT_READABLE_QUATERNION
+            // display quaternion values in easy matrix form: w x y z
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            Serial.print("quat\t");
+            Serial.print(q.w);
+            Serial.print("\t");
+            Serial.print(q.x);
+            Serial.print("\t");
+            Serial.print(q.y);
+            Serial.print("\t");
+            Serial.println(q.z);
+        #endif
+        #ifdef OUTPUT_READABLE_EULER
+            // display Euler angles in degrees
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetEuler(euler, &q);
+            Serial.print("euler\t");
+            Serial.print(euler[0] * 180/M_PI);
+            Serial.print("\t");
+            Serial.print(euler[1] * 180/M_PI);
+            Serial.print("\t");
+            Serial.println(euler[2] * 180/M_PI);
+        #endif
+        #ifdef OUTPUT_READABLE_YAWPITCHROLL
+            // display Euler angles in degrees
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetGravity(&gravity, &q);
+            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);            
+            Serial.print("ypr\t");
+            Serial.print(ypr[0] * 180/M_PI);
+            Serial.print("\t");
+            Serial.print(ypr[1] * 180/M_PI);
+            Serial.print("\t");
+            Serial.println(ypr[2] * 180/M_PI);
+        #endif
+        #ifdef OUTPUT_READABLE_REALACCEL
+            // display real acceleration, adjusted to remove gravity
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetAccel(&aa, fifoBuffer);
+            mpu.dmpGetGravity(&gravity, &q);
+            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
+            Serial.print("areal\t");
+            Serial.print(aaReal.x);
+            Serial.print("\t");
+            Serial.print(aaReal.y);
+            Serial.print("\t");
+            Serial.println(aaReal.z);
+        #endif
+        #ifdef OUTPUT_READABLE_WORLDACCEL
+            // display initial world-frame acceleration, adjusted to remove gravity
+            // and rotated based on known orientation from quaternion
+            mpu.dmpGetQuaternion(&q, fifoBuffer);
+            mpu.dmpGetAccel(&aa, fifoBuffer);
+            mpu.dmpGetGravity(&gravity, &q);
+            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
+            mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
+            Serial.print("aworld\t");
+            Serial.print(aaWorld.x);
+            Serial.print("\t");
+            Serial.print(aaWorld.y);
+            Serial.print("\t");
+            Serial.println(aaWorld.z);
+        #endif    
+        #ifdef OUTPUT_TEAPOT
+            // display quaternion values in InvenSense Teapot demo format:
+            teapotPacket[2] = fifoBuffer[0];
+            teapotPacket[3] = fifoBuffer[1];
+            teapotPacket[4] = fifoBuffer[4];
+            teapotPacket[5] = fifoBuffer[5];
+            teapotPacket[6] = fifoBuffer[8];
+            teapotPacket[7] = fifoBuffer[9];
+            teapotPacket[8] = fifoBuffer[12];
+            teapotPacket[9] = fifoBuffer[13];
+            Serial.write(teapotPacket, 14);
+            teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
+        #endif
+            serversend();
+        // blink LED to indicate activity
+        blinkState = !blinkState;
+        digitalWrite(LED_PIN, blinkState);
+    }
+}
+
+void serversend(){
+
+  EthernetClient client = server.available();  // try to get client
+  
+  if (client) {  // got client?
+        //boolean currentLineIsBlank = true;
+        while (client.connected()) {
+            if (client.available()) {   // client data available to read
+                char c = client.read(); // read 1 byte (character) from client
+                HTTP_req += c;  // save the HTTP request 1 char at a time
+                // last line of client request is blank and ends with \n
+                // respond to client only after last line received
+                if (c == '\n') {
+                    // send a standard http response header
+                    client.println("HTTP/1.1 200 OK");
+                    client.println("Content-Type: text/html");
+                    //client.println("Connection: keep-alive");
+                    client.println();
+                     // AJAX request for switch state
+                    if (HTTP_req.indexOf("ajax_switch") > -1) {
+                        // read switch state and analog input
+                        GetAjaxData(client);
+                    }
+                    else {  // HTTP request for web page
+                        // send web page - contains JavaScript with AJAX calls
+                        client.println("<!DOCTYPE html>");
+                        client.println("<html>");
+                        client.println("<head>");
+                        client.println("<title>Arduino Web Page</title>");
+                        client.println("<script>");
+                        client.println("function GetAjaxData() {");
+                        client.println(
+                            "nocache = \"&nocache=\" + Math.random() * 1000000;");
+                        client.println("var request = new XMLHttpRequest();");
+                        client.println("request.onreadystatechange = function() {");
+                        client.println("if (this.readyState == 4) {");
+                        client.println("if (this.status == 200) {");
+                        client.println("if (this.responseText != null) {");
+                        client.println("document.getElementById(\"sw_an_data\")\
+.innerHTML = this.responseText;");
+                        client.println("}}}}");
+                        client.println(
+                        "request.open(\"GET\", \"ajax_switch\" + nocache, true);");
+                        client.println("request.send(null);");
+                        client.println("setTimeout('GetAjaxData()', 10);");
+                        client.println("}");
+                        client.println("</script>");
+                        client.println("</head>");
+                        client.println("<body onload=\"GetAjaxData()\">");
+                        client.println("<h1>MPU6050 Output</h1>");
+                        client.println("<div id=\"sw_an_data\">");
+                        client.println("</div>");
+                        client.println("</body>");
+                        client.println("</html>");
+                    }
+                    // display received HTTP request on serial port
+                    Serial.print(HTTP_req);
+                    HTTP_req = "";            // finished with request, empty string
+                    client.stop(); // close the connection
+                    break;
+                }              
+            } 
+        }      
+    } 
+}
+
+void GetAjaxData(EthernetClient cl)
+{
+    #ifdef OUTPUT_READABLE_QUATERNION
+        // display quaternion values in easy matrix form: w x y z
+        cl.print("Quaternion Values:\t");
+        cl.print("<p>w:");
+        cl.print(q.w); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>x:");
+        cl.print(q.x); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>y:");
+        cl.print(q.y); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>z:");
+        cl.print(q.z); 
+        cl.print("\t");
+        cl.println("</p>");
+     #endif
+     #ifdef OUTPUT_READABLE_EULER
+        // display Euler angles in degrees
+        cl.print("Euler Angles:\t");
+        cl.print("<p>Yaw:");
+        cl.print(euler[0] * 180/M_PI); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Pitch:");
+        cl.print(euler[2] * 180/M_PI); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Roll:");
+        cl.print(euler[1] * 180/M_PI); 
+        cl.print("\t");
+        cl.println("</p>");
+     #endif
+     #ifdef OUTPUT_READABLE_YAWPITCHROLL
+        // display Yaw/Pitch/Roll values in degrees
+        cl.print("Yaw, Pitch, and Roll:\t");
+        cl.print("<p>Yaw:");
+        cl.print(ypr[0] * 180/M_PI); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Pitch:");
+        cl.print(ypr[2] * 180/M_PI); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Roll:");
+        cl.print(ypr[1] * 180/M_PI); 
+        cl.print("\t");
+        cl.println("</p>");
+     #endif
+     #ifdef OUTPUT_READABLE_REALACCEL
+        // display real acceleration, adjusted to remove gravity
+        cl.print("Real Accel:\t");
+        cl.print("<p>Yaw:");
+        cl.print(aaReal.x); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Pitch:");
+        cl.print(aaReal.z); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Roll:");
+        cl.print(aaReal.y); 
+        cl.print("\t");
+        cl.println("</p>");
+     #endif
+     #ifdef OUTPUT_READABLE_WORLDACCEL
+        // display initial world-frame acceleration, adjusted to remove gravity
+        // and rotated based on known orientation from quaternion
+        cl.print("World Accel:\t");
+        cl.print("<p>Yaw:");
+        cl.print(aaWorld.x); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Pitch:");
+        cl.print(aaWorld.z); 
+        cl.print("\t");
+        cl.println("</p>");
+        cl.print("<p>Roll:");
+        cl.print(aaWorld.y); 
+        cl.print("\t");
+        cl.println("</p>");
+     #endif
+     #ifdef OUTPUT_TEAPOT
+        cl.print("<p>teapotpacket:");
+        cl.write(teapotPacket, 14); 
+        cl.print("\t");
+        cl.println("</p>");
+    #endif
+}

          
A => libraries/MPU6050/Examples/MPU6050_raw/MPU6050_raw.ino +151 -0
@@ 0,0 1,151 @@ 
+// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class
+// 10/7/2011 by Jeff Rowberg <jeff@rowberg.net>
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//      2013-05-08 - added multiple output formats
+//                 - added seamless Fastwire support
+//      2011-10-07 - initial release
+
+/* ============================================
+I2Cdev device library code is placed under the MIT license
+Copyright (c) 2011 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.
+===============================================
+*/
+
+// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
+// for both classes must be in the include path of your project
+#include "I2Cdev.h"
+#include "MPU6050.h"
+
+// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
+// is used in I2Cdev.h
+#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
+    #include "Wire.h"
+#endif
+
+// class default I2C address is 0x68
+// specific I2C addresses may be passed as a parameter here
+// AD0 low = 0x68 (default for InvenSense evaluation board)
+// AD0 high = 0x69
+MPU6050 accelgyro;
+//MPU6050 accelgyro(0x69); // <-- use for AD0 high
+
+int16_t ax, ay, az;
+int16_t gx, gy, gz;
+
+
+
+// uncomment "OUTPUT_READABLE_ACCELGYRO" if you want to see a tab-separated
+// list of the accel X/Y/Z and then gyro X/Y/Z values in decimal. Easy to read,
+// not so easy to parse, and slow(er) over UART.
+#define OUTPUT_READABLE_ACCELGYRO
+
+// uncomment "OUTPUT_BINARY_ACCELGYRO" to send all 6 axes of data as 16-bit
+// binary, one right after the other. This is very fast (as fast as possible
+// without compression or data loss), and easy to parse, but impossible to read
+// for a human.
+//#define OUTPUT_BINARY_ACCELGYRO
+
+
+#define LED_PIN 13
+bool blinkState = false;
+
+void setup() {
+    // join I2C bus (I2Cdev library doesn't do this automatically)
+    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
+        Wire.begin();
+    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
+        Fastwire::setup(400, true);
+    #endif
+
+    // initialize serial communication
+    // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
+    // it's really up to you depending on your project)
+    Serial.begin(38400);
+
+    // initialize device
+    Serial.println("Initializing I2C devices...");
+    accelgyro.initialize();
+
+    // verify connection
+    Serial.println("Testing device connections...");
+    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
+
+    // use the code below to change accel/gyro offset values
+    /*
+    Serial.println("Updating internal sensor offsets...");
+    // -76	-2359	1688	0	0	0
+    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
+    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
+    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
+    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
+    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
+    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
+    Serial.print("\n");
+    accelgyro.setXGyroOffset(220);
+    accelgyro.setYGyroOffset(76);
+    accelgyro.setZGyroOffset(-85);
+    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
+    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
+    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
+    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
+    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
+    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
+    Serial.print("\n");
+    */
+
+    // configure Arduino LED for
+    pinMode(LED_PIN, OUTPUT);
+}
+
+void loop() {
+    // read raw accel/gyro measurements from device
+    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
+
+    // these methods (and a few others) are also available
+    //accelgyro.getAcceleration(&ax, &ay, &az);
+    //accelgyro.getRotation(&gx, &gy, &gz);
+
+    #ifdef OUTPUT_READABLE_ACCELGYRO
+        // display tab-separated accel/gyro x/y/z values
+        Serial.print("a/g:\t");
+        Serial.print(ax); Serial.print("\t");
+        Serial.print(ay); Serial.print("\t");
+        Serial.print(az); Serial.print("\t");
+        Serial.print(gx); Serial.print("\t");
+        Serial.print(gy); Serial.print("\t");
+        Serial.println(gz);
+    #endif
+
+    #ifdef OUTPUT_BINARY_ACCELGYRO
+        Serial.write((uint8_t)(ax >> 8)); Serial.write((uint8_t)(ax & 0xFF));
+        Serial.write((uint8_t)(ay >> 8)); Serial.write((uint8_t)(ay & 0xFF));
+        Serial.write((uint8_t)(az >> 8)); Serial.write((uint8_t)(az & 0xFF));
+        Serial.write((uint8_t)(gx >> 8)); Serial.write((uint8_t)(gx & 0xFF));
+        Serial.write((uint8_t)(gy >> 8)); Serial.write((uint8_t)(gy & 0xFF));
+        Serial.write((uint8_t)(gz >> 8)); Serial.write((uint8_t)(gz & 0xFF));
+    #endif
+
+    // blink LED to indicate activity
+    blinkState = !blinkState;
+    digitalWrite(LED_PIN, blinkState);
+}

          
A => libraries/MPU6050/LICENSE.txt +1 -0
@@ 0,0 1,1 @@ 
+The MPU6050 library is forked from works at "http://www.i2cdevlib.com/". The source codes under folder "MPU6050" inherit the license from any license claim of "http://www.i2cdevlib.com/".
  No newline at end of file

          
A => libraries/MPU6050/MPU6050.cpp +3213 -0
@@ 0,0 1,3213 @@ 
+// 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 <jeff@rowberg.net>
+// 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 "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.
+ *
+ * <pre>
+ * 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]
+ * </pre>
+ *
+ * @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.
+ *
+ * <pre>
+ *          |   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
+ * </pre>
+ *
+ * @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.
+ *
+ * <pre>
+ * 0 = +/- 250 degrees/sec
+ * 1 = +/- 500 degrees/sec
+ * 2 = +/- 1000 degrees/sec
+ * 3 = +/- 2000 degrees/sec
+ * </pre>
+ *
+ * @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);
+}
+
+// SELF TEST FACTORY TRIM VALUES
+
+/** Get self-test factory trim value for accelerometer X axis.
+ * @return factory trim value
+ * @see MPU6050_RA_SELF_TEST_X
+ */
+uint8_t MPU6050::getAccelXSelfTestFactoryTrim() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_X, &buffer[0]);
+	I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_A, &buffer[1]);	
+    return (buffer[0]>>3) | ((buffer[1]>>4) & 0x03);
+}
+
+/** Get self-test factory trim value for accelerometer Y axis.
+ * @return factory trim value
+ * @see MPU6050_RA_SELF_TEST_Y
+ */
+uint8_t MPU6050::getAccelYSelfTestFactoryTrim() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_Y, &buffer[0]);
+	I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_A, &buffer[1]);	
+    return (buffer[0]>>3) | ((buffer[1]>>2) & 0x03);
+}
+
+/** Get self-test factory trim value for accelerometer Z axis.
+ * @return factory trim value
+ * @see MPU6050_RA_SELF_TEST_Z
+ */
+uint8_t MPU6050::getAccelZSelfTestFactoryTrim() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_SELF_TEST_Z, 2, buffer);	
+    return (buffer[0]>>3) | (buffer[1] & 0x03);
+}
+
+/** Get self-test factory trim value for gyro X axis.
+ * @return factory trim value
+ * @see MPU6050_RA_SELF_TEST_X
+ */
+uint8_t MPU6050::getGyroXSelfTestFactoryTrim() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_X, buffer);	
+    return (buffer[0] & 0x1F);
+}
+
+/** Get self-test factory trim value for gyro Y axis.
+ * @return factory trim value
+ * @see MPU6050_RA_SELF_TEST_Y
+ */
+uint8_t MPU6050::getGyroYSelfTestFactoryTrim() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_Y, buffer);	
+    return (buffer[0] & 0x1F);
+}
+
+/** Get self-test factory trim value for gyro Z axis.
+ * @return factory trim value
+ * @see MPU6050_RA_SELF_TEST_Z
+ */
+uint8_t MPU6050::getGyroZSelfTestFactoryTrim() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_SELF_TEST_Z, buffer);	
+    return (buffer[0] & 0x1F);
+}
+
+// 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.
+ *
+ * <pre>
+ * 0 = +/- 2g
+ * 1 = +/- 4g
+ * 2 = +/- 8g
+ * 3 = +/- 16g
+ * </pre>
+ *
+ * @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:
+ *
+ * <pre>
+ *    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.
+ * </pre>
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * @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 motion detection 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:
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * @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:
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * @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:
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * @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 full motion detection status register content (all bits).
+ * @return Motion detection status byte
+ * @see MPU6050_RA_MOT_DETECT_STATUS
+ */
+uint8_t MPU6050::getMotionStatus() {
+    I2Cdev::readByte(devAddr, MPU6050_RA_MOT_DETECT_STATUS, buffer);
+    return buffer[0];
+}
+/** 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:
+ *
+ * <pre>
+ * FF_COUNT | Counter Decrement
+ * ---------+------------------
+ * 0        | Reset
+ * 1        | 1
+ * 2        | 2
+ * 3        | 4
+ * </pre>
+ *
+ * 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:
+ *
+ * <pre>
+ * MOT_COUNT | Counter Decrement
+ * ----------+------------------
+ * 0         | Reset
+ * 1         | 1
+ * 2         | 2
+ * 3         | 4
+ * </pre>
+ *
+ * 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:
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * @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:
+ *
+ * <pre>
+ * LP_WAKE_CTRL | Wake-up Frequency
+ * -------------+------------------
+ * 0            | 1.25 Hz
+ * 1            | 2.5 Hz
+ * 2            | 5 Hz
+ * 3            | 10 Hz
+ * </pre>
+ *
+ * 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) {
+    if(length > 0){
+        I2Cdev::readBytes(devAddr, MPU6050_RA_FIFO_R_W, length, data);
+    } else {
+    	*data = 0;
+    }
+}
+/** 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::getXGyroOffsetTC() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
+    return buffer[0];
+}
+void MPU6050::setXGyroOffsetTC(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::getYGyroOffsetTC() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
+    return buffer[0];
+}
+void MPU6050::setYGyroOffsetTC(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::getZGyroOffsetTC() {
+    I2Cdev::readBits(devAddr, MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
+    return buffer[0];
+}
+void MPU6050::setZGyroOffsetTC(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::getXGyroOffset() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_XG_OFFS_USRH, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setXGyroOffset(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_XG_OFFS_USRH, offset);
+}
+
+// YG_OFFS_USR* register
+
+int16_t MPU6050::getYGyroOffset() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_YG_OFFS_USRH, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setYGyroOffset(int16_t offset) {
+    I2Cdev::writeWord(devAddr, MPU6050_RA_YG_OFFS_USRH, offset);
+}
+
+// ZG_OFFS_USR* register
+
+int16_t MPU6050::getZGyroOffset() {
+    I2Cdev::readBytes(devAddr, MPU6050_RA_ZG_OFFS_USRH, 2, buffer);
+    return (((int16_t)buffer[0]) << 8) | buffer[1];
+}
+void MPU6050::setZGyroOffset(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=0;
+    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 = 0;
+	uint8_t 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);
+}

          
A => libraries/MPU6050/MPU6050.h +1032 -0
@@ 0,0 1,1032 @@ 
+// 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 <jeff@rowberg.net>
+// 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"
+
+// supporting link:  http://forum.arduino.cc/index.php?&topic=143444.msg1079517#msg1079517
+// also: http://forum.arduino.cc/index.php?&topic=141571.msg1062899#msg1062899s
+
+#ifdef __AVR__
+#include <avr/pgmspace.h>
+#else
+//#define PROGMEM /* empty */
+//#define pgm_read_byte(x) (*(x))
+//#define pgm_read_word(x) (*(x))
+//#define pgm_read_float(x) (*(x))
+//#define PSTR(STR) STR
+#endif
+
+
+#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_SELF_TEST_X      0x0D //[7:5] XA_TEST[4-2], [4:0] XG_TEST[4-0]
+#define MPU6050_RA_SELF_TEST_Y      0x0E //[7:5] YA_TEST[4-2], [4:0] YG_TEST[4-0]
+#define MPU6050_RA_SELF_TEST_Z      0x0F //[7:5] ZA_TEST[4-2], [4:0] ZG_TEST[4-0]
+#define MPU6050_RA_SELF_TEST_A      0x10 //[5:4] XA_TEST[1-0], [3:2] YA_TEST[1-0], [1:0] ZA_TEST[1-0]
+#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_SELF_TEST_XA_1_BIT     0x07
+#define MPU6050_SELF_TEST_XA_1_LENGTH  0x03
+#define MPU6050_SELF_TEST_XA_2_BIT     0x05
+#define MPU6050_SELF_TEST_XA_2_LENGTH  0x02
+#define MPU6050_SELF_TEST_YA_1_BIT     0x07
+#define MPU6050_SELF_TEST_YA_1_LENGTH  0x03
+#define MPU6050_SELF_TEST_YA_2_BIT     0x03
+#define MPU6050_SELF_TEST_YA_2_LENGTH  0x02
+#define MPU6050_SELF_TEST_ZA_1_BIT     0x07
+#define MPU6050_SELF_TEST_ZA_1_LENGTH  0x03
+#define MPU6050_SELF_TEST_ZA_2_BIT     0x01
+#define MPU6050_SELF_TEST_ZA_2_LENGTH  0x02
+
+#define MPU6050_SELF_TEST_XG_1_BIT     0x04
+#define MPU6050_SELF_TEST_XG_1_LENGTH  0x05
+#define MPU6050_SELF_TEST_YG_1_BIT     0x04
+#define MPU6050_SELF_TEST_YG_1_LENGTH  0x05
+#define MPU6050_SELF_TEST_ZG_1_BIT     0x04
+#define MPU6050_SELF_TEST_ZG_1_LENGTH  0x05
+
+#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);
+
+		// SELF_TEST registers
+		uint8_t getAccelXSelfTestFactoryTrim();
+		uint8_t getAccelYSelfTestFactoryTrim();
+		uint8_t getAccelZSelfTestFactoryTrim();
+
+		uint8_t getGyroXSelfTestFactoryTrim();
+		uint8_t getGyroYSelfTestFactoryTrim();
+		uint8_t getGyroZSelfTestFactoryTrim();
+		
+        // 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
+        uint8_t getMotionStatus();
+        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 getXGyroOffsetTC();
+        void setXGyroOffsetTC(int8_t offset);
+
+        // YG_OFFS_TC register
+        int8_t getYGyroOffsetTC();
+        void setYGyroOffsetTC(int8_t offset);
+
+        // ZG_OFFS_TC register
+        int8_t getZGyroOffsetTC();
+        void setZGyroOffsetTC(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 getXGyroOffset();
+        void setXGyroOffset(int16_t offset);
+
+        // YG_OFFS_USR* register
+        int16_t getYGyroOffset();
+        void setYGyroOffset(int16_t offset);
+
+        // ZG_OFFS_USR* register
+        int16_t getZGyroOffset();
+        void setZGyroOffset(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_ */

          
A => libraries/MPU6050/MPU6050_6Axis_MotionApps20.h +745 -0
@@ 0,0 1,745 @@ 
+// I2Cdev library collection - MPU6050 I2C device class, 6-axis MotionApps 2.0 implementation
+// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00)
+// 5/20/2013 by Jeff Rowberg <jeff@rowberg.net>
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//     ... - ongoing debug 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 _MPU6050_6AXIS_MOTIONAPPS20_H_
+#define _MPU6050_6AXIS_MOTIONAPPS20_H_
+
+#include "I2Cdev.h"
+#include "helper_3dmath.h"
+
+// MotionApps 2.0 DMP implementation, built using the MPU-6050EVB evaluation board
+#define MPU6050_INCLUDE_DMP_MOTIONAPPS20
+
+#include "MPU6050.h"
+
+// Tom Carpenter's conditional PROGMEM code
+// http://forum.arduino.cc/index.php?topic=129407.0
+#ifdef __AVR__
+    #include <avr/pgmspace.h>
+#else
+    // Teensy 3.0 library conditional PROGMEM code from Paul Stoffregen
+    #ifndef __PGMSPACE_H_
+        #define __PGMSPACE_H_ 1
+        #include <inttypes.h>
+
+        #define PROGMEM
+        #define PGM_P  const char *
+        #define PSTR(str) (str)
+        #define F(x) x
+
+        typedef void prog_void;
+        typedef char prog_char;
+        typedef unsigned char prog_uchar;
+        typedef int8_t prog_int8_t;
+        typedef uint8_t prog_uint8_t;
+        typedef int16_t prog_int16_t;
+        typedef uint16_t prog_uint16_t;
+        typedef int32_t prog_int32_t;
+        typedef uint32_t prog_uint32_t;
+        
+        #define strcpy_P(dest, src) strcpy((dest), (src))
+        #define strcat_P(dest, src) strcat((dest), (src))
+        #define strcmp_P(a, b) strcmp((a), (b))
+        
+        #define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+        #define pgm_read_word(addr) (*(const unsigned short *)(addr))
+        #define pgm_read_dword(addr) (*(const unsigned long *)(addr))
+        #define pgm_read_float(addr) (*(const float *)(addr))
+        
+        #define pgm_read_byte_near(addr) pgm_read_byte(addr)
+        #define pgm_read_word_near(addr) pgm_read_word(addr)
+        #define pgm_read_dword_near(addr) pgm_read_dword(addr)
+        #define pgm_read_float_near(addr) pgm_read_float(addr)
+        #define pgm_read_byte_far(addr) pgm_read_byte(addr)
+        #define pgm_read_word_far(addr) pgm_read_word(addr)
+        #define pgm_read_dword_far(addr) pgm_read_dword(addr)
+        #define pgm_read_float_far(addr) pgm_read_float(addr)
+    #endif
+#endif
+
+/* Source is from the InvenSense MotionApps v2 demo code. Original source is
+ * unavailable, unless you happen to be amazing as decompiling binary by
+ * hand (in which case, please contact me, and I'm totally serious).
+ *
+ * Also, I'd like to offer many, many thanks to Noah Zerkin for all of the
+ * DMP reverse-engineering he did to help make this bit of wizardry
+ * possible.
+ */
+
+// NOTE! Enabling DEBUG adds about 3.3kB to the flash program size.
+// Debug output is now working even on ATMega328P MCUs (e.g. Arduino Uno)
+// after moving string constants to flash memory storage using the F()
+// compiler macro (Arduino IDE 1.0+ required).
+
+//#define DEBUG
+#ifdef DEBUG
+    #define DEBUG_PRINT(x) Serial.print(x)
+    #define DEBUG_PRINTF(x, y) Serial.print(x, y)
+    #define DEBUG_PRINTLN(x) Serial.println(x)
+    #define DEBUG_PRINTLNF(x, y) Serial.println(x, y)
+#else
+    #define DEBUG_PRINT(x)
+    #define DEBUG_PRINTF(x, y)
+    #define DEBUG_PRINTLN(x)
+    #define DEBUG_PRINTLNF(x, y)
+#endif
+
+#define MPU6050_DMP_CODE_SIZE       1929    // dmpMemory[]
+#define MPU6050_DMP_CONFIG_SIZE     192     // dmpConfig[]
+#define 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                          |
+ * ================================================================================================ */
+
+// this block of memory gets written to the MPU on start-up, and it seems
+// to be volatile memory, so it has to be done each time (it only takes ~1
+// second though)
+const unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] PROGMEM = {
+    // 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
+};
+
+// thanks to Noah Zerkin for piecing this stuff together!
+const unsigned char dmpConfig[MPU6050_DMP_CONFIG_SIZE] PROGMEM = {
+//  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.
+};
+
+const unsigned char dmpUpdates[MPU6050_DMP_UPDATES_SIZE] PROGMEM = {
+    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
+};
+
+uint8_t MPU6050::dmpInitialize() {
+    // reset device
+    DEBUG_PRINTLN(F("\n\nResetting MPU6050..."));
+    reset();
+    delay(30); // wait after reset
+
+    // enable sleep mode and wake cycle
+    /*Serial.println(F("Enabling sleep mode..."));
+    setSleepEnabled(true);
+    Serial.println(F("Enabling wake cycle..."));
+    setWakeCycleEnabled(true);*/
+
+    // disable sleep mode
+    DEBUG_PRINTLN(F("Disabling sleep mode..."));
+    setSleepEnabled(false);
+
+    // get MPU hardware revision
+    DEBUG_PRINTLN(F("Selecting user bank 16..."));
+    setMemoryBank(0x10, true, true);
+    DEBUG_PRINTLN(F("Selecting memory byte 6..."));
+    setMemoryStartAddress(0x06);
+    DEBUG_PRINTLN(F("Checking hardware revision..."));
+    DEBUG_PRINT(F("Revision @ user[16][6] = "));
+    DEBUG_PRINTLNF(readMemoryByte(), HEX);
+    DEBUG_PRINTLN(F("Resetting memory bank selection to 0..."));
+    setMemoryBank(0, false, false);
+
+    // check OTP bank valid
+    DEBUG_PRINTLN(F("Reading OTP bank valid flag..."));
+    DEBUG_PRINT(F("OTP bank is "));
+    DEBUG_PRINTLN(getOTPBankValid() ? F("valid!") : F("invalid!"));
+
+    // get X/Y/Z gyro offsets
+    DEBUG_PRINTLN(F("Reading gyro offset TC values..."));
+    int8_t xgOffsetTC = getXGyroOffsetTC();
+    int8_t ygOffsetTC = getYGyroOffsetTC();
+    int8_t zgOffsetTC = getZGyroOffsetTC();
+    DEBUG_PRINT(F("X gyro offset = "));
+    DEBUG_PRINTLN(xgOffsetTC);
+    DEBUG_PRINT(F("Y gyro offset = "));
+    DEBUG_PRINTLN(ygOffsetTC);
+    DEBUG_PRINT(F("Z gyro offset = "));
+    DEBUG_PRINTLN(zgOffsetTC);
+
+    // setup weird slave stuff (?)
+    DEBUG_PRINTLN(F("Setting slave 0 address to 0x7F..."));
+    setSlaveAddress(0, 0x7F);
+    DEBUG_PRINTLN(F("Disabling I2C Master mode..."));
+    setI2CMasterModeEnabled(false);
+    DEBUG_PRINTLN(F("Setting slave 0 address to 0x68 (self)..."));
+    setSlaveAddress(0, 0x68);
+    DEBUG_PRINTLN(F("Resetting I2C Master control..."));
+    resetI2CMaster();
+    delay(20);
+
+    // load DMP code into memory banks
+    DEBUG_PRINT(F("Writing DMP code to MPU memory banks ("));
+    DEBUG_PRINT(MPU6050_DMP_CODE_SIZE);
+    DEBUG_PRINTLN(F(" bytes)"));
+    if (writeProgMemoryBlock(dmpMemory, MPU6050_DMP_CODE_SIZE)) {
+        DEBUG_PRINTLN(F("Success! DMP code written and verified."));
+
+        // write DMP configuration
+        DEBUG_PRINT(F("Writing DMP configuration to MPU memory banks ("));
+        DEBUG_PRINT(MPU6050_DMP_CONFIG_SIZE);
+        DEBUG_PRINTLN(F(" bytes in config def)"));
+        if (writeProgDMPConfigurationSet(dmpConfig, MPU6050_DMP_CONFIG_SIZE)) {
+            DEBUG_PRINTLN(F("Success! DMP configuration written and verified."));
+
+            DEBUG_PRINTLN(F("Setting clock source to Z Gyro..."));
+            setClockSource(MPU6050_CLOCK_PLL_ZGYRO);
+
+            DEBUG_PRINTLN(F("Setting DMP and FIFO_OFLOW interrupts enabled..."));
+            setIntEnabled(0x12);
+
+            DEBUG_PRINTLN(F("Setting sample rate to 200Hz..."));
+            setRate(4); // 1khz / (1 + 4) = 200 Hz
+
+            DEBUG_PRINTLN(F("Setting external frame sync to TEMP_OUT_L[0]..."));
+            setExternalFrameSync(MPU6050_EXT_SYNC_TEMP_OUT_L);
+
+            DEBUG_PRINTLN(F("Setting DLPF bandwidth to 42Hz..."));
+            setDLPFMode(MPU6050_DLPF_BW_42);
+
+            DEBUG_PRINTLN(F("Setting gyro sensitivity to +/- 2000 deg/sec..."));
+            setFullScaleGyroRange(MPU6050_GYRO_FS_2000);
+
+            DEBUG_PRINTLN(F("Setting DMP configuration bytes (function unknown)..."));
+            setDMPConfig1(0x03);
+            setDMPConfig2(0x00);
+
+            DEBUG_PRINTLN(F("Clearing OTP Bank flag..."));
+            setOTPBankValid(false);
+
+            DEBUG_PRINTLN(F("Setting X/Y/Z gyro offset TCs to previous values..."));
+            setXGyroOffsetTC(xgOffsetTC);
+            setYGyroOffsetTC(ygOffsetTC);
+            setZGyroOffsetTC(zgOffsetTC);
+
+            //DEBUG_PRINTLN(F("Setting X/Y/Z gyro user offsets to zero..."));
+            //setXGyroOffset(0);
+            //setYGyroOffset(0);
+            //setZGyroOffset(0);
+
+            DEBUG_PRINTLN(F("Writing final memory update 1/7 (function unknown)..."));
+            uint8_t dmpUpdate[16], j;
+            uint16_t pos = 0;
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Writing final memory update 2/7 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Resetting FIFO..."));
+            resetFIFO();
+
+            DEBUG_PRINTLN(F("Reading FIFO count..."));
+            uint16_t fifoCount = getFIFOCount();
+            uint8_t fifoBuffer[128];
+
+            DEBUG_PRINT(F("Current FIFO count="));
+            DEBUG_PRINTLN(fifoCount);
+            getFIFOBytes(fifoBuffer, fifoCount);
+
+            DEBUG_PRINTLN(F("Setting motion detection threshold to 2..."));
+            setMotionDetectionThreshold(2);
+
+            DEBUG_PRINTLN(F("Setting zero-motion detection threshold to 156..."));
+            setZeroMotionDetectionThreshold(156);
+
+            DEBUG_PRINTLN(F("Setting motion detection duration to 80..."));
+            setMotionDetectionDuration(80);
+
+            DEBUG_PRINTLN(F("Setting zero-motion detection duration to 0..."));
+            setZeroMotionDetectionDuration(0);
+
+            DEBUG_PRINTLN(F("Resetting FIFO..."));
+            resetFIFO();
+
+            DEBUG_PRINTLN(F("Enabling FIFO..."));
+            setFIFOEnabled(true);
+
+            DEBUG_PRINTLN(F("Enabling DMP..."));
+            setDMPEnabled(true);
+
+            DEBUG_PRINTLN(F("Resetting DMP..."));
+            resetDMP();
+
+            DEBUG_PRINTLN(F("Writing final memory update 3/7 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Writing final memory update 4/7 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Writing final memory update 5/7 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Waiting for FIFO count > 2..."));
+            while ((fifoCount = getFIFOCount()) < 3);
+
+            DEBUG_PRINT(F("Current FIFO count="));
+            DEBUG_PRINTLN(fifoCount);
+            DEBUG_PRINTLN(F("Reading FIFO data..."));
+            getFIFOBytes(fifoBuffer, fifoCount);
+
+            DEBUG_PRINTLN(F("Reading interrupt status..."));
+
+            DEBUG_PRINT(F("Current interrupt status="));
+            DEBUG_PRINTLNF(getIntStatus(), HEX);
+
+            DEBUG_PRINTLN(F("Reading final memory update 6/7 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            readMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Waiting for FIFO count > 2..."));
+            while ((fifoCount = getFIFOCount()) < 3);
+
+            DEBUG_PRINT(F("Current FIFO count="));
+            DEBUG_PRINTLN(fifoCount);
+
+            DEBUG_PRINTLN(F("Reading FIFO data..."));
+            getFIFOBytes(fifoBuffer, fifoCount);
+
+            DEBUG_PRINTLN(F("Reading interrupt status..."));
+
+            DEBUG_PRINT(F("Current interrupt status="));
+            DEBUG_PRINTLNF(getIntStatus(), HEX);
+
+            DEBUG_PRINTLN(F("Writing final memory update 7/7 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("DMP is good to go! Finally."));
+
+            DEBUG_PRINTLN(F("Disabling DMP (you turn it on later)..."));
+            setDMPEnabled(false);
+
+            DEBUG_PRINTLN(F("Setting up internal 42-byte (default) DMP packet buffer..."));
+            dmpPacketSize = 42;
+            /*if ((dmpPacketBuffer = (uint8_t *)malloc(42)) == 0) {
+                return 3; // TODO: proper error code for no memory
+            }*/
+
+            DEBUG_PRINTLN(F("Resetting FIFO and clearing INT status one last time..."));
+            resetFIFO();
+            getIntStatus();
+        } else {
+            DEBUG_PRINTLN(F("ERROR! DMP configuration verification failed."));
+            return 2; // configuration block loading failed
+        }
+    } else {
+        DEBUG_PRINTLN(F("ERROR! DMP code verification failed."));
+        return 1; // main binary block loading failed
+    }
+    return 0; // success
+}
+
+bool MPU6050::dmpPacketAvailable() {
+    return getFIFOCount() >= dmpGetFIFOPacketSize();
+}
+
+// uint8_t MPU6050::dmpSetFIFORate(uint8_t fifoRate);
+// uint8_t MPU6050::dmpGetFIFORate();
+// uint8_t MPU6050::dmpGetSampleStepSizeMS();
+// uint8_t MPU6050::dmpGetSampleFrequency();
+// int32_t MPU6050::dmpDecodeTemperature(int8_t tempReg);
+
+//uint8_t MPU6050::dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority);
+//uint8_t MPU6050::dmpUnregisterFIFORateProcess(inv_obj_func func);
+//uint8_t MPU6050::dmpRunFIFORateProcesses();
+
+// uint8_t MPU6050::dmpSendQuaternion(uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendPacketNumber(uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy);
+
+uint8_t MPU6050::dmpGetAccel(int32_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (((uint32_t)packet[28] << 24) | ((uint32_t)packet[29] << 16) | ((uint32_t)packet[30] << 8) | packet[31]);
+    data[1] = (((uint32_t)packet[32] << 24) | ((uint32_t)packet[33] << 16) | ((uint32_t)packet[34] << 8) | packet[35]);
+    data[2] = (((uint32_t)packet[36] << 24) | ((uint32_t)packet[37] << 16) | ((uint32_t)packet[38] << 8) | packet[39]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetAccel(int16_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (packet[28] << 8) | packet[29];
+    data[1] = (packet[32] << 8) | packet[33];
+    data[2] = (packet[36] << 8) | packet[37];
+    return 0;
+}
+uint8_t MPU6050::dmpGetAccel(VectorInt16 *v, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    v -> x = (packet[28] << 8) | packet[29];
+    v -> y = (packet[32] << 8) | packet[33];
+    v -> z = (packet[36] << 8) | packet[37];
+    return 0;
+}
+uint8_t MPU6050::dmpGetQuaternion(int32_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (((uint32_t)packet[0] << 24) | ((uint32_t)packet[1] << 16) | ((uint32_t)packet[2] << 8) | packet[3]);
+    data[1] = (((uint32_t)packet[4] << 24) | ((uint32_t)packet[5] << 16) | ((uint32_t)packet[6] << 8) | packet[7]);
+    data[2] = (((uint32_t)packet[8] << 24) | ((uint32_t)packet[9] << 16) | ((uint32_t)packet[10] << 8) | packet[11]);
+    data[3] = (((uint32_t)packet[12] << 24) | ((uint32_t)packet[13] << 16) | ((uint32_t)packet[14] << 8) | packet[15]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetQuaternion(int16_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = ((packet[0] << 8) | packet[1]);
+    data[1] = ((packet[4] << 8) | packet[5]);
+    data[2] = ((packet[8] << 8) | packet[9]);
+    data[3] = ((packet[12] << 8) | packet[13]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetQuaternion(Quaternion *q, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    int16_t qI[4];
+    uint8_t status = dmpGetQuaternion(qI, packet);
+    if (status == 0) {
+        q -> w = (float)qI[0] / 16384.0f;
+        q -> x = (float)qI[1] / 16384.0f;
+        q -> y = (float)qI[2] / 16384.0f;
+        q -> z = (float)qI[3] / 16384.0f;
+        return 0;
+    }
+    return status; // int16 return value, indicates error if this line is reached
+}
+// uint8_t MPU6050::dmpGet6AxisQuaternion(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetRelativeQuaternion(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetGyro(int32_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (((uint32_t)packet[16] << 24) | ((uint32_t)packet[17] << 16) | ((uint32_t)packet[18] << 8) | packet[19]);
+    data[1] = (((uint32_t)packet[20] << 24) | ((uint32_t)packet[21] << 16) | ((uint32_t)packet[22] << 8) | packet[23]);
+    data[2] = (((uint32_t)packet[24] << 24) | ((uint32_t)packet[25] << 16) | ((uint32_t)packet[26] << 8) | packet[27]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetGyro(int16_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (packet[16] << 8) | packet[17];
+    data[1] = (packet[20] << 8) | packet[21];
+    data[2] = (packet[24] << 8) | packet[25];
+    return 0;
+}
+uint8_t MPU6050::dmpGetGyro(VectorInt16 *v, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    v -> x = (packet[16] << 8) | packet[17];
+    v -> y = (packet[20] << 8) | packet[21];
+    v -> z = (packet[24] << 8) | packet[25];
+    return 0;
+}
+// uint8_t MPU6050::dmpSetLinearAccelFilterCoefficient(float coef);
+// uint8_t MPU6050::dmpGetLinearAccel(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) {
+    // get rid of the gravity component (+1g = +8192 in standard DMP FIFO packet, sensitivity is 2g)
+    v -> x = vRaw -> x - gravity -> x*8192;
+    v -> y = vRaw -> y - gravity -> y*8192;
+    v -> z = vRaw -> z - gravity -> z*8192;
+    return 0;
+}
+// uint8_t MPU6050::dmpGetLinearAccelInWorld(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q) {
+    // rotate measured 3D acceleration vector into original state
+    // frame of reference based on orientation quaternion
+    memcpy(v, vReal, sizeof(VectorInt16));
+    v -> rotate(q);
+    return 0;
+}
+// uint8_t MPU6050::dmpGetGyroAndAccelSensor(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetGyroSensor(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetControlData(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetTemperature(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetGravity(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetGravity(VectorFloat *v, Quaternion *q) {
+    v -> x = 2 * (q -> x*q -> z - q -> w*q -> y);
+    v -> y = 2 * (q -> w*q -> x + q -> y*q -> z);
+    v -> z = q -> w*q -> w - q -> x*q -> x - q -> y*q -> y + q -> z*q -> z;
+    return 0;
+}
+// uint8_t MPU6050::dmpGetUnquantizedAccel(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetQuantizedAccel(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetExternalSensorData(long *data, int size, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetEIS(long *data, const uint8_t* packet);
+
+uint8_t MPU6050::dmpGetEuler(float *data, Quaternion *q) {
+    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);   // psi
+    data[1] = -asin(2*q -> x*q -> z + 2*q -> w*q -> y);                              // theta
+    data[2] = atan2(2*q -> y*q -> z - 2*q -> w*q -> x, 2*q -> w*q -> w + 2*q -> z*q -> z - 1);   // phi
+    return 0;
+}
+uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) {
+    // yaw: (about Z axis)
+    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);
+    // pitch: (nose up/down, about Y axis)
+    data[1] = atan(gravity -> x / sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z));
+    // roll: (tilt left/right, about X axis)
+    data[2] = atan(gravity -> y / sqrt(gravity -> x*gravity -> x + gravity -> z*gravity -> z));
+    return 0;
+}
+
+// uint8_t MPU6050::dmpGetAccelFloat(float *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetQuaternionFloat(float *data, const uint8_t* packet);
+
+uint8_t MPU6050::dmpProcessFIFOPacket(const unsigned char *dmpData) {
+    /*for (uint8_t k = 0; k < dmpPacketSize; k++) {
+        if (dmpData[k] < 0x10) Serial.print("0");
+        Serial.print(dmpData[k], HEX);
+        Serial.print(" ");
+    }
+    Serial.print("\n");*/
+    //Serial.println((uint16_t)dmpPacketBuffer);
+    return 0;
+}
+uint8_t MPU6050::dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed) {
+    uint8_t status;
+    uint8_t buf[dmpPacketSize];
+    for (uint8_t i = 0; i < numPackets; i++) {
+        // read packet from FIFO
+        getFIFOBytes(buf, dmpPacketSize);
+
+        // process packet
+        if ((status = dmpProcessFIFOPacket(buf)) > 0) return status;
+        
+        // increment external process count variable, if supplied
+        if (processed != 0) (*processed)++;
+    }
+    return 0;
+}
+
+// uint8_t MPU6050::dmpSetFIFOProcessedCallback(void (*func) (void));
+
+// uint8_t MPU6050::dmpInitFIFOParam();
+// uint8_t MPU6050::dmpCloseFIFO();
+// uint8_t MPU6050::dmpSetGyroDataSource(uint_fast8_t source);
+// uint8_t MPU6050::dmpDecodeQuantizedAccel();
+// uint32_t MPU6050::dmpGetGyroSumOfSquare();
+// uint32_t MPU6050::dmpGetAccelSumOfSquare();
+// void MPU6050::dmpOverrideQuaternion(long *q);
+uint16_t MPU6050::dmpGetFIFOPacketSize() {
+    return dmpPacketSize;
+}
+
+#endif /* _MPU6050_6AXIS_MOTIONAPPS20_H_ */

          
A => libraries/MPU6050/MPU6050_9Axis_MotionApps41.h +852 -0
@@ 0,0 1,852 @@ 
+// I2Cdev library collection - MPU6050 I2C device class, 9-axis MotionApps 4.1 implementation
+// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00)
+// 6/18/2012 by Jeff Rowberg <jeff@rowberg.net>
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//     ... - ongoing debug 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 _MPU6050_9AXIS_MOTIONAPPS41_H_
+#define _MPU6050_9AXIS_MOTIONAPPS41_H_
+
+#include "I2Cdev.h"
+#include "helper_3dmath.h"
+
+// MotionApps 4.1 DMP implementation, built using the MPU-9150 "MotionFit" board
+#define MPU6050_INCLUDE_DMP_MOTIONAPPS41
+
+#include "MPU6050.h"
+
+// Tom Carpenter's conditional PROGMEM code
+// http://forum.arduino.cc/index.php?topic=129407.0
+#ifdef __AVR__
+    #include <avr/pgmspace.h>
+#else
+    // Teensy 3.0 library conditional PROGMEM code from Paul Stoffregen
+    #ifndef __PGMSPACE_H_
+        #define __PGMSPACE_H_ 1
+        #include <inttypes.h>
+
+        #define PROGMEM
+        #define PGM_P  const char *
+        #define PSTR(str) (str)
+        #define F(x) x
+
+        typedef void prog_void;
+        typedef char prog_char;
+        //typedef unsigned char prog_uchar;
+        typedef int8_t prog_int8_t;
+        typedef uint8_t prog_uint8_t;
+        typedef int16_t prog_int16_t;
+        typedef uint16_t prog_uint16_t;
+        typedef int32_t prog_int32_t;
+        typedef uint32_t prog_uint32_t;
+        
+        #define strcpy_P(dest, src) strcpy((dest), (src))
+        #define strcat_P(dest, src) strcat((dest), (src))
+        #define strcmp_P(a, b) strcmp((a), (b))
+        
+        #define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+        #define pgm_read_word(addr) (*(const unsigned short *)(addr))
+        #define pgm_read_dword(addr) (*(const unsigned long *)(addr))
+        #define pgm_read_float(addr) (*(const float *)(addr))
+        
+        #define pgm_read_byte_near(addr) pgm_read_byte(addr)
+        #define pgm_read_word_near(addr) pgm_read_word(addr)
+        #define pgm_read_dword_near(addr) pgm_read_dword(addr)
+        #define pgm_read_float_near(addr) pgm_read_float(addr)
+        #define pgm_read_byte_far(addr) pgm_read_byte(addr)
+        #define pgm_read_word_far(addr) pgm_read_word(addr)
+        #define pgm_read_dword_far(addr) pgm_read_dword(addr)
+        #define pgm_read_float_far(addr) pgm_read_float(addr)
+    #endif
+#endif
+
+// NOTE! Enabling DEBUG adds about 3.3kB to the flash program size.
+// Debug output is now working even on ATMega328P MCUs (e.g. Arduino Uno)
+// after moving string constants to flash memory storage using the F()
+// compiler macro (Arduino IDE 1.0+ required).
+
+//#define DEBUG
+#ifdef DEBUG
+    #define DEBUG_PRINT(x) Serial.print(x)
+    #define DEBUG_PRINTF(x, y) Serial.print(x, y)
+    #define DEBUG_PRINTLN(x) Serial.println(x)
+    #define DEBUG_PRINTLNF(x, y) Serial.println(x, y)
+#else
+    #define DEBUG_PRINT(x)
+    #define DEBUG_PRINTF(x, y)
+    #define DEBUG_PRINTLN(x)
+    #define DEBUG_PRINTLNF(x, y)
+#endif
+
+#define MPU6050_DMP_CODE_SIZE       1962    // dmpMemory[]
+#define MPU6050_DMP_CONFIG_SIZE     232     // dmpConfig[]
+#define MPU6050_DMP_UPDATES_SIZE    140     // dmpUpdates[]
+
+/* ================================================================================================ *
+ | Default MotionApps v4.1 48-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][      ][MAG X ][MAG Y ][MAG 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  42  43  44  45  46  47  |
+ * ================================================================================================ */
+
+// this block of memory gets written to the MPU on start-up, and it seems
+// to be volatile memory, so it has to be done each time (it only takes ~1
+// second though)
+const unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] PROGMEM = {
+    // 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, 0x47, 0x78, 0xA2,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    
+    // bank 3, 256 bytes
+    0xD8, 0xDC, 0xF4, 0xD8, 0xB9, 0xAB, 0xF3, 0xF8, 0xFA, 0xF1, 0xBA, 0xA2, 0xDE, 0xB2, 0xB8, 0xB4,
+    0xA8, 0x81, 0x98, 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, 0xF0, 0x00, 0x28, 0x50, 0xF5, 0xBA, 0xAD, 0x8F, 0x9F, 0x28, 0x54,
+    0x7C, 0xB9, 0xF1, 0xA3, 0x86, 0x9F, 0x61, 0xA6, 0xDA, 0xDE, 0xDF, 0xDB, 0xB2, 0xB6, 0x8E, 0x9D,
+    0xAE, 0xF5, 0x60, 0x68, 0x70, 0xB1, 0xB5, 0xF1, 0xDA, 0xA6, 0xDF, 0xD9, 0xA6, 0xFA, 0xA3, 0x86,
+    
+    // bank 4, 256 bytes
+    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,
+    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,
+    
+    // bank 5, 256 bytes
+    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,
+    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, 0x97, 0x86,
+    0xA8, 0x31, 0x9B, 0x06, 0x99, 0x07, 0xAB, 0x97, 0x28, 0x88, 0x9B, 0xF0, 0x0C, 0x20, 0x14, 0x40,
+    0xB9, 0xA3, 0x8A, 0xC3, 0xC5, 0xC7, 0x9A, 0xA3, 0x28, 0x50, 0x78, 0xF1, 0xB5, 0x93, 0x01, 0xD9,
+    0xDF, 0xDF, 0xDF, 0xD8, 0xB8, 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, 0xB1, 0x83, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0xB0, 0x8B, 0x29, 0x51, 0x79, 0xB1, 0x83, 0x24,
+
+    // bank 6, 256 bytes
+    0x70, 0x59, 0xB0, 0x8B, 0x20, 0x58, 0x71, 0xB1, 0x83, 0x44, 0x69, 0x38, 0xB0, 0x8B, 0x39, 0x40,
+    0x68, 0xB1, 0x83, 0x64, 0x48, 0x31, 0xB0, 0x8B, 0x30, 0x49, 0x60, 0xA5, 0x88, 0x20, 0x09, 0x71,
+    0x58, 0x44, 0x68, 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,
+    
+    // bank 7, 170 bytes (remainder)
+    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, 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, 0xA3, 0x91, 0xB6, 0x09, 0xB4, 0xD9, 0xAB, 0xDE, 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
+};
+
+const unsigned char dmpConfig[MPU6050_DMP_CONFIG_SIZE] PROGMEM = {
+//  BANK    OFFSET  LENGTH  [DATA]
+    0x02,   0xEC,   0x04,   0x00, 0x47, 0x7D, 0x1A,   // ?
+    0x03,   0x82,   0x03,   0x4C, 0xCD, 0x6C,         // FCFG_1 inv_set_gyro_calibration
+    0x03,   0xB2,   0x03,   0x36, 0x56, 0x76,         // FCFG_3 inv_set_gyro_calibration
+    0x00,   0x68,   0x04,   0x02, 0xCA, 0xE3, 0x09,   // D_0_104 inv_set_gyro_calibration
+    0x01,   0x0C,   0x04,   0x00, 0x00, 0x00, 0x00,   // D_1_152 inv_set_accel_calibration
+    0x03,   0x86,   0x03,   0x0C, 0xC9, 0x2C,         // FCFG_2 inv_set_accel_calibration
+    0x03,   0x90,   0x03,   0x26, 0x46, 0x66,         //   (continued)...FCFG_2 inv_set_accel_calibration
+    0x00,   0x6C,   0x02,   0x40, 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,   0x40, 0x00, 0x00, 0x00,   // CPASS_MTX_01
+    0x02,   0x48,   0x04,   0x00, 0x00, 0x00, 0x00,   // CPASS_MTX_02
+    0x02,   0x4C,   0x04,   0x40, 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,   0xC0, 0x00, 0x00, 0x00,   // CPASS_MTX_22
+
+    0x01,   0xEC,   0x04,   0x00, 0x00, 0x40, 0x00,   // D_1_236 inv_apply_endian_accel
+    0x03,   0x86,   0x06,   0x0C, 0xC9, 0x2C, 0x97, 0x97, 0x97, // FCFG_2 inv_set_mpu_sensors
+    0x04,   0x22,   0x03,   0x0D, 0x35, 0x5D,         // CFG_MOTION_BIAS inv_turn_on_bias_from_no_motion
+    0x00,   0xA3,   0x01,   0x00,                     // ?
+    0x04,   0x29,   0x04,   0x87, 0x2D, 0x35, 0x3D,   // FCFG_5 inv_set_bias_update
+    0x07,   0x62,   0x05,   0xF1, 0x20, 0x28, 0x30, 0x38, // CFG_8 inv_send_quaternion
+    0x07,   0x9F,   0x01,   0x30,                     // CFG_16 inv_set_footer
+    0x07,   0x67,   0x01,   0x9A,                     // CFG_GYRO_SOURCE inv_send_gyro
+    0x07,   0x68,   0x04,   0xF1, 0x28, 0x30, 0x38,   // CFG_9 inv_send_gyro -> inv_construct3_fifo
+    0x07,   0x62,   0x05,   0xF1, 0x20, 0x28, 0x30, 0x38, // ?
+    0x02,   0x0C,   0x04,   0x00, 0x00, 0x00, 0x00,   // ?
+    0x07,   0x83,   0x06,   0xC2, 0xCA, 0xC4, 0xA3, 0xA3, 0xA3, // ?
+                 // SPECIAL 0x01 = enable interrupts
+    0x00,   0x00,   0x00,   0x01, // SET INT_ENABLE, SPECIAL INSTRUCTION
+    0x07,   0xA7,   0x01,   0xFE,                     // ?
+    0x07,   0x62,   0x05,   0xF1, 0x20, 0x28, 0x30, 0x38, // ?
+    0x07,   0x67,   0x01,   0x9A,                     // ?
+    0x07,   0x68,   0x04,   0xF1, 0x28, 0x30, 0x38,   // CFG_12 inv_send_accel -> inv_construct3_fifo
+    0x07,   0x8D,   0x04,   0xF1, 0x28, 0x30, 0x38,   // ??? CFG_12 inv_send_mag -> inv_construct3_fifo
+    0x02,   0x16,   0x02,   0x00, 0x03                // 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.
+};
+
+const unsigned char dmpUpdates[MPU6050_DMP_UPDATES_SIZE] PROGMEM = {
+    0x01,   0xB2,   0x02,   0xFF, 0xF5,
+    0x01,   0x90,   0x04,   0x0A, 0x0D, 0x97, 0xC0,
+    0x00,   0xA3,   0x01,   0x00,
+    0x04,   0x29,   0x04,   0x87, 0x2D, 0x35, 0x3D,
+    0x01,   0x6A,   0x02,   0x06, 0x00,
+    0x01,   0x60,   0x08,   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00,   0x60,   0x04,   0x40, 0x00, 0x00, 0x00,
+    0x02,   0x60,   0x0C,   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x01,   0x08,   0x02,   0x01, 0x20,
+    0x01,   0x0A,   0x02,   0x00, 0x4E,
+    0x01,   0x02,   0x02,   0xFE, 0xB3,
+    0x02,   0x6C,   0x04,   0x00, 0x00, 0x00, 0x00, // READ
+    0x02,   0x6C,   0x04,   0xFA, 0xFE, 0x00, 0x00,
+    0x02,   0x60,   0x0C,   0xFF, 0xFF, 0xCB, 0x4D, 0x00, 0x01, 0x08, 0xC1, 0xFF, 0xFF, 0xBC, 0x2C,
+    0x02,   0xF4,   0x04,   0x00, 0x00, 0x00, 0x00,
+    0x02,   0xF8,   0x04,   0x00, 0x00, 0x00, 0x00,
+    0x02,   0xFC,   0x04,   0x00, 0x00, 0x00, 0x00,
+    0x00,   0x60,   0x04,   0x40, 0x00, 0x00, 0x00,
+    0x00,   0x60,   0x04,   0x00, 0x40, 0x00, 0x00
+};
+
+uint8_t MPU6050::dmpInitialize() {
+    // reset device
+    DEBUG_PRINTLN(F("\n\nResetting MPU6050..."));
+    reset();
+    delay(30); // wait after reset
+
+    // disable sleep mode
+    DEBUG_PRINTLN(F("Disabling sleep mode..."));
+    setSleepEnabled(false);
+
+    // get MPU product ID
+    DEBUG_PRINTLN(F("Getting product ID..."));
+    //uint8_t productID = 0; //getProductID();
+    DEBUG_PRINT(F("Product ID = "));
+    DEBUG_PRINT(productID);
+
+    // get MPU hardware revision
+    DEBUG_PRINTLN(F("Selecting user bank 16..."));
+    setMemoryBank(0x10, true, true);
+    DEBUG_PRINTLN(F("Selecting memory byte 6..."));
+    setMemoryStartAddress(0x06);
+    DEBUG_PRINTLN(F("Checking hardware revision..."));
+    uint8_t hwRevision = readMemoryByte();
+    DEBUG_PRINT(F("Revision @ user[16][6] = "));
+    DEBUG_PRINTLNF(hwRevision, HEX);
+    DEBUG_PRINTLN(F("Resetting memory bank selection to 0..."));
+    setMemoryBank(0, false, false);
+
+    // check OTP bank valid
+    DEBUG_PRINTLN(F("Reading OTP bank valid flag..."));
+    uint8_t otpValid = getOTPBankValid();
+    DEBUG_PRINT(F("OTP bank is "));
+    DEBUG_PRINTLN(otpValid ? F("valid!") : F("invalid!"));
+
+    // get X/Y/Z gyro offsets
+    DEBUG_PRINTLN(F("Reading gyro offset values..."));
+    int8_t xgOffset = getXGyroOffset();
+    int8_t ygOffset = getYGyroOffset();
+    int8_t zgOffset = getZGyroOffset();
+    DEBUG_PRINT(F("X gyro offset = "));
+    DEBUG_PRINTLN(xgOffset);
+    DEBUG_PRINT(F("Y gyro offset = "));
+    DEBUG_PRINTLN(ygOffset);
+    DEBUG_PRINT(F("Z gyro offset = "));
+    DEBUG_PRINTLN(zgOffset);
+    
+    I2Cdev::readByte(devAddr, MPU6050_RA_USER_CTRL, buffer); // ?
+    
+    DEBUG_PRINTLN(F("Enabling interrupt latch, clear on any read, AUX bypass enabled"));
+    I2Cdev::writeByte(devAddr, MPU6050_RA_INT_PIN_CFG, 0x32);
+
+    // enable MPU AUX I2C bypass mode
+    //DEBUG_PRINTLN(F("Enabling AUX I2C bypass mode..."));
+    //setI2CBypassEnabled(true);
+
+    DEBUG_PRINTLN(F("Setting magnetometer mode to power-down..."));
+    //mag -> setMode(0);
+    I2Cdev::writeByte(0x0E, 0x0A, 0x00);
+
+    DEBUG_PRINTLN(F("Setting magnetometer mode to fuse access..."));
+    //mag -> setMode(0x0F);
+    I2Cdev::writeByte(0x0E, 0x0A, 0x0F);
+
+    DEBUG_PRINTLN(F("Reading mag magnetometer factory calibration..."));
+    int8_t asax, asay, asaz;
+    //mag -> getAdjustment(&asax, &asay, &asaz);
+    I2Cdev::readBytes(0x0E, 0x10, 3, buffer);
+    asax = (int8_t)buffer[0];
+    asay = (int8_t)buffer[1];
+    asaz = (int8_t)buffer[2];
+    DEBUG_PRINT(F("Adjustment X/Y/Z = "));
+    DEBUG_PRINT(asax);
+    DEBUG_PRINT(F(" / "));
+    DEBUG_PRINT(asay);
+    DEBUG_PRINT(F(" / "));
+    DEBUG_PRINTLN(asaz);
+
+    DEBUG_PRINTLN(F("Setting magnetometer mode to power-down..."));
+    //mag -> setMode(0);
+    I2Cdev::writeByte(0x0E, 0x0A, 0x00);
+
+    // load DMP code into memory banks
+    DEBUG_PRINT(F("Writing DMP code to MPU memory banks ("));
+    DEBUG_PRINT(MPU6050_DMP_CODE_SIZE);
+    DEBUG_PRINTLN(F(" bytes)"));
+    if (writeProgMemoryBlock(dmpMemory, MPU6050_DMP_CODE_SIZE)) {
+        DEBUG_PRINTLN(F("Success! DMP code written and verified."));
+
+        DEBUG_PRINTLN(F("Configuring DMP and related settings..."));
+
+        // write DMP configuration
+        DEBUG_PRINT(F("Writing DMP configuration to MPU memory banks ("));
+        DEBUG_PRINT(MPU6050_DMP_CONFIG_SIZE);
+        DEBUG_PRINTLN(F(" bytes in config def)"));
+        if (writeProgDMPConfigurationSet(dmpConfig, MPU6050_DMP_CONFIG_SIZE)) {
+            DEBUG_PRINTLN(F("Success! DMP configuration written and verified."));
+
+            DEBUG_PRINTLN(F("Setting DMP and FIFO_OFLOW interrupts enabled..."));
+            setIntEnabled(0x12);
+
+            DEBUG_PRINTLN(F("Setting sample rate to 200Hz..."));
+            setRate(4); // 1khz / (1 + 4) = 200 Hz
+
+            DEBUG_PRINTLN(F("Setting clock source to Z Gyro..."));
+            setClockSource(MPU6050_CLOCK_PLL_ZGYRO);
+
+            DEBUG_PRINTLN(F("Setting DLPF bandwidth to 42Hz..."));
+            setDLPFMode(MPU6050_DLPF_BW_42);
+
+            DEBUG_PRINTLN(F("Setting external frame sync to TEMP_OUT_L[0]..."));
+            setExternalFrameSync(MPU6050_EXT_SYNC_TEMP_OUT_L);
+
+            DEBUG_PRINTLN(F("Setting gyro sensitivity to +/- 2000 deg/sec..."));
+            setFullScaleGyroRange(MPU6050_GYRO_FS_2000);
+
+            DEBUG_PRINTLN(F("Setting DMP configuration bytes (function unknown)..."));
+            setDMPConfig1(0x03);
+            setDMPConfig2(0x00);
+
+            DEBUG_PRINTLN(F("Clearing OTP Bank flag..."));
+            setOTPBankValid(false);
+
+            DEBUG_PRINTLN(F("Setting X/Y/Z gyro offsets to previous values..."));
+            setXGyroOffsetTC(xgOffset);
+            setYGyroOffsetTC(ygOffset);
+            setZGyroOffsetTC(zgOffset);
+
+            //DEBUG_PRINTLN(F("Setting X/Y/Z gyro user offsets to zero..."));
+            //setXGyroOffset(0);
+            //setYGyroOffset(0);
+            //setZGyroOffset(0);
+
+            DEBUG_PRINTLN(F("Writing final memory update 1/19 (function unknown)..."));
+            uint8_t dmpUpdate[16], j;
+            uint16_t pos = 0;
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Writing final memory update 2/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Resetting FIFO..."));
+            resetFIFO();
+
+            DEBUG_PRINTLN(F("Reading FIFO count..."));
+            uint8_t fifoCount = getFIFOCount();
+
+            DEBUG_PRINT(F("Current FIFO count="));
+            DEBUG_PRINTLN(fifoCount);
+            uint8_t fifoBuffer[128];
+            //getFIFOBytes(fifoBuffer, fifoCount);
+
+            DEBUG_PRINTLN(F("Writing final memory update 3/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Writing final memory update 4/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Disabling all standby flags..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_PWR_MGMT_2, 0x00);
+
+            DEBUG_PRINTLN(F("Setting accelerometer sensitivity to +/- 2g..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_ACCEL_CONFIG, 0x00);
+
+            DEBUG_PRINTLN(F("Setting motion detection threshold to 2..."));
+            setMotionDetectionThreshold(2);
+
+            DEBUG_PRINTLN(F("Setting zero-motion detection threshold to 156..."));
+            setZeroMotionDetectionThreshold(156);
+
+            DEBUG_PRINTLN(F("Setting motion detection duration to 80..."));
+            setMotionDetectionDuration(80);
+
+            DEBUG_PRINTLN(F("Setting zero-motion detection duration to 0..."));
+            setZeroMotionDetectionDuration(0);
+
+            DEBUG_PRINTLN(F("Setting AK8975 to single measurement mode..."));
+            //mag -> setMode(1);
+            I2Cdev::writeByte(0x0E, 0x0A, 0x01);
+
+            // setup AK8975 (0x0E) as Slave 0 in read mode
+            DEBUG_PRINTLN(F("Setting up AK8975 read slave 0..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV0_ADDR, 0x8E);
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV0_REG,  0x01);
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV0_CTRL, 0xDA);
+
+            // setup AK8975 (0x0E) as Slave 2 in write mode
+            DEBUG_PRINTLN(F("Setting up AK8975 write slave 2..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_ADDR, 0x0E);
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_REG,  0x0A);
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_CTRL, 0x81);
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV2_DO,   0x01);
+
+            // setup I2C timing/delay control
+            DEBUG_PRINTLN(F("Setting up slave access delay..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_SLV4_CTRL, 0x18);
+            I2Cdev::writeByte(0x68, MPU6050_RA_I2C_MST_DELAY_CTRL, 0x05);
+
+            // enable interrupts
+            DEBUG_PRINTLN(F("Enabling default interrupt behavior/no bypass..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_INT_PIN_CFG, 0x00);
+
+            // enable I2C master mode and reset DMP/FIFO
+            DEBUG_PRINTLN(F("Enabling I2C master mode..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0x20);
+            DEBUG_PRINTLN(F("Resetting FIFO..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0x24);
+            DEBUG_PRINTLN(F("Rewriting I2C master mode enabled because...I don't know"));
+            I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0x20);
+            DEBUG_PRINTLN(F("Enabling and resetting DMP/FIFO..."));
+            I2Cdev::writeByte(0x68, MPU6050_RA_USER_CTRL, 0xE8);
+
+            DEBUG_PRINTLN(F("Writing final memory update 5/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 6/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 7/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 8/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 9/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 10/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 11/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            
+            DEBUG_PRINTLN(F("Reading final memory update 12/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            readMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            #ifdef DEBUG
+                DEBUG_PRINT(F("Read bytes: "));
+                for (j = 0; j < 4; j++) {
+                    DEBUG_PRINTF(dmpUpdate[3 + j], HEX);
+                    DEBUG_PRINT(" ");
+                }
+                DEBUG_PRINTLN("");
+            #endif
+
+            DEBUG_PRINTLN(F("Writing final memory update 13/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 14/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 15/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 16/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+            DEBUG_PRINTLN(F("Writing final memory update 17/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Waiting for FIRO count >= 46..."));
+            while ((fifoCount = getFIFOCount()) < 46);
+            DEBUG_PRINTLN(F("Reading FIFO..."));
+            getFIFOBytes(fifoBuffer, min(fifoCount, 128)); // safeguard only 128 bytes
+            DEBUG_PRINTLN(F("Reading interrupt status..."));
+            getIntStatus();
+
+            DEBUG_PRINTLN(F("Writing final memory update 18/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Waiting for FIRO count >= 48..."));
+            while ((fifoCount = getFIFOCount()) < 48);
+            DEBUG_PRINTLN(F("Reading FIFO..."));
+            getFIFOBytes(fifoBuffer, min(fifoCount, 128)); // safeguard only 128 bytes
+            DEBUG_PRINTLN(F("Reading interrupt status..."));
+            getIntStatus();
+            DEBUG_PRINTLN(F("Waiting for FIRO count >= 48..."));
+            while ((fifoCount = getFIFOCount()) < 48);
+            DEBUG_PRINTLN(F("Reading FIFO..."));
+            getFIFOBytes(fifoBuffer, min(fifoCount, 128)); // safeguard only 128 bytes
+            DEBUG_PRINTLN(F("Reading interrupt status..."));
+            getIntStatus();
+
+            DEBUG_PRINTLN(F("Writing final memory update 19/19 (function unknown)..."));
+            for (j = 0; j < 4 || j < dmpUpdate[2] + 3; j++, pos++) dmpUpdate[j] = pgm_read_byte(&dmpUpdates[pos]);
+            writeMemoryBlock(dmpUpdate + 3, dmpUpdate[2], dmpUpdate[0], dmpUpdate[1]);
+
+            DEBUG_PRINTLN(F("Disabling DMP (you turn it on later)..."));
+            setDMPEnabled(false);
+
+            DEBUG_PRINTLN(F("Setting up internal 48-byte (default) DMP packet buffer..."));
+            dmpPacketSize = 48;
+            /*if ((dmpPacketBuffer = (uint8_t *)malloc(42)) == 0) {
+                return 3; // TODO: proper error code for no memory
+            }*/
+
+            DEBUG_PRINTLN(F("Resetting FIFO and clearing INT status one last time..."));
+            resetFIFO();
+            getIntStatus();
+        } else {
+            DEBUG_PRINTLN(F("ERROR! DMP configuration verification failed."));
+            return 2; // configuration block loading failed
+        }
+    } else {
+        DEBUG_PRINTLN(F("ERROR! DMP code verification failed."));
+        return 1; // main binary block loading failed
+    }
+    return 0; // success
+}
+
+bool MPU6050::dmpPacketAvailable() {
+    return getFIFOCount() >= dmpGetFIFOPacketSize();
+}
+
+// uint8_t MPU6050::dmpSetFIFORate(uint8_t fifoRate);
+// uint8_t MPU6050::dmpGetFIFORate();
+// uint8_t MPU6050::dmpGetSampleStepSizeMS();
+// uint8_t MPU6050::dmpGetSampleFrequency();
+// int32_t MPU6050::dmpDecodeTemperature(int8_t tempReg);
+
+//uint8_t MPU6050::dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority);
+//uint8_t MPU6050::dmpUnregisterFIFORateProcess(inv_obj_func func);
+//uint8_t MPU6050::dmpRunFIFORateProcesses();
+
+// uint8_t MPU6050::dmpSendQuaternion(uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendPacketNumber(uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy);
+// uint8_t MPU6050::dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy);
+
+uint8_t MPU6050::dmpGetAccel(int32_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (((uint32_t)packet[34] << 24) | ((uint32_t)packet[35] << 16) | ((uint32_t)packet[36] << 8) | packet[37]);
+    data[1] = (((uint32_t)packet[38] << 24) | ((uint32_t)packet[39] << 16) | ((uint32_t)packet[40] << 8) | packet[41]);
+    data[2] = (((uint32_t)packet[42] << 24) | ((uint32_t)packet[43] << 16) | ((uint32_t)packet[44] << 8) | packet[45]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetAccel(int16_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (packet[34] << 8) | packet[35];
+    data[1] = (packet[38] << 8) | packet[39];
+    data[2] = (packet[42] << 8) | packet[43];
+    return 0;
+}
+uint8_t MPU6050::dmpGetAccel(VectorInt16 *v, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    v -> x = (packet[34] << 8) | packet[35];
+    v -> y = (packet[38] << 8) | packet[39];
+    v -> z = (packet[42] << 8) | packet[43];
+    return 0;
+}
+uint8_t MPU6050::dmpGetQuaternion(int32_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (((uint32_t)packet[0] << 24) | ((uint32_t)packet[1] << 16) | ((uint32_t)packet[2] << 8) | packet[3]);
+    data[1] = (((uint32_t)packet[4] << 24) | ((uint32_t)packet[5] << 16) | ((uint32_t)packet[6] << 8) | packet[7]);
+    data[2] = (((uint32_t)packet[8] << 24) | ((uint32_t)packet[9] << 16) | ((uint32_t)packet[10] << 8) | packet[11]);
+    data[3] = (((uint32_t)packet[12] << 24) | ((uint32_t)packet[13] << 16) | ((uint32_t)packet[14] << 8) | packet[15]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetQuaternion(int16_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = ((packet[0] << 8) | packet[1]);
+    data[1] = ((packet[4] << 8) | packet[5]);
+    data[2] = ((packet[8] << 8) | packet[9]);
+    data[3] = ((packet[12] << 8) | packet[13]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetQuaternion(Quaternion *q, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    int16_t qI[4];
+    uint8_t status = dmpGetQuaternion(qI, packet);
+    if (status == 0) {
+        q -> w = (float)qI[0] / 16384.0f;
+        q -> x = (float)qI[1] / 16384.0f;
+        q -> y = (float)qI[2] / 16384.0f;
+        q -> z = (float)qI[3] / 16384.0f;
+        return 0;
+    }
+    return status; // int16 return value, indicates error if this line is reached
+}
+// uint8_t MPU6050::dmpGet6AxisQuaternion(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetRelativeQuaternion(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetGyro(int32_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (((uint32_t)packet[16] << 24) | ((uint32_t)packet[17] << 16) | ((uint32_t)packet[18] << 8) | packet[19]);
+    data[1] = (((uint32_t)packet[20] << 24) | ((uint32_t)packet[21] << 16) | ((uint32_t)packet[22] << 8) | packet[23]);
+    data[2] = (((uint32_t)packet[24] << 24) | ((uint32_t)packet[25] << 16) | ((uint32_t)packet[26] << 8) | packet[27]);
+    return 0;
+}
+uint8_t MPU6050::dmpGetGyro(int16_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (packet[16] << 8) | packet[17];
+    data[1] = (packet[20] << 8) | packet[21];
+    data[2] = (packet[24] << 8) | packet[25];
+    return 0;
+}
+uint8_t MPU6050::dmpGetMag(int16_t *data, const uint8_t* packet) {
+    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
+    if (packet == 0) packet = dmpPacketBuffer;
+    data[0] = (packet[28] << 8) | packet[29];
+    data[1] = (packet[30] << 8) | packet[31];
+    data[2] = (packet[32] << 8) | packet[33];
+    return 0;
+}
+// uint8_t MPU6050::dmpSetLinearAccelFilterCoefficient(float coef);
+// uint8_t MPU6050::dmpGetLinearAccel(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) {
+    // get rid of the gravity component (+1g = +4096 in standard DMP FIFO packet)
+    v -> x = vRaw -> x - gravity -> x*4096;
+    v -> y = vRaw -> y - gravity -> y*4096;
+    v -> z = vRaw -> z - gravity -> z*4096;
+    return 0;
+}
+// uint8_t MPU6050::dmpGetLinearAccelInWorld(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q) {
+    // rotate measured 3D acceleration vector into original state
+    // frame of reference based on orientation quaternion
+    memcpy(v, vReal, sizeof(VectorInt16));
+    v -> rotate(q);
+    return 0;
+}
+// uint8_t MPU6050::dmpGetGyroAndAccelSensor(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetGyroSensor(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetControlData(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetTemperature(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetGravity(long *data, const uint8_t* packet);
+uint8_t MPU6050::dmpGetGravity(VectorFloat *v, Quaternion *q) {
+    v -> x = 2 * (q -> x*q -> z - q -> w*q -> y);
+    v -> y = 2 * (q -> w*q -> x + q -> y*q -> z);
+    v -> z = q -> w*q -> w - q -> x*q -> x - q -> y*q -> y + q -> z*q -> z;
+    return 0;
+}
+// uint8_t MPU6050::dmpGetUnquantizedAccel(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetQuantizedAccel(long *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetExternalSensorData(long *data, int size, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetEIS(long *data, const uint8_t* packet);
+
+uint8_t MPU6050::dmpGetEuler(float *data, Quaternion *q) {
+    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);   // psi
+    data[1] = -asin(2*q -> x*q -> z + 2*q -> w*q -> y);                              // theta
+    data[2] = atan2(2*q -> y*q -> z - 2*q -> w*q -> x, 2*q -> w*q -> w + 2*q -> z*q -> z - 1);   // phi
+    return 0;
+}
+uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) {
+    // yaw: (about Z axis)
+    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);
+    // pitch: (nose up/down, about Y axis)
+    data[1] = atan(gravity -> x / sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z));
+    // roll: (tilt left/right, about X axis)
+    data[2] = atan(gravity -> y / sqrt(gravity -> x*gravity -> x + gravity -> z*gravity -> z));
+    return 0;
+}
+
+// uint8_t MPU6050::dmpGetAccelFloat(float *data, const uint8_t* packet);
+// uint8_t MPU6050::dmpGetQuaternionFloat(float *data, const uint8_t* packet);
+
+uint8_t MPU6050::dmpProcessFIFOPacket(const unsigned char *dmpData) {
+    /*for (uint8_t k = 0; k < dmpPacketSize; k++) {
+        if (dmpData[k] < 0x10) Serial.print("0");
+        Serial.print(dmpData[k], HEX);
+        Serial.print(" ");
+    }
+    Serial.print("\n");*/
+    //Serial.println((uint16_t)dmpPacketBuffer);
+    return 0;
+}
+uint8_t MPU6050::dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed) {
+    uint8_t status;
+    uint8_t buf[dmpPacketSize];
+    for (uint8_t i = 0; i < numPackets; i++) {
+        // read packet from FIFO
+        getFIFOBytes(buf, dmpPacketSize);
+
+        // process packet
+        if ((status = dmpProcessFIFOPacket(buf)) > 0) return status;
+        
+        // increment external process count variable, if supplied
+        if (processed != 0) *processed++;
+    }
+    return 0;
+}
+
+// uint8_t MPU6050::dmpSetFIFOProcessedCallback(void (*func) (void));
+
+// uint8_t MPU6050::dmpInitFIFOParam();
+// uint8_t MPU6050::dmpCloseFIFO();
+// uint8_t MPU6050::dmpSetGyroDataSource(uint_fast8_t source);
+// uint8_t MPU6050::dmpDecodeQuantizedAccel();
+// uint32_t MPU6050::dmpGetGyroSumOfSquare();
+// uint32_t MPU6050::dmpGetAccelSumOfSquare();
+// void MPU6050::dmpOverrideQuaternion(long *q);
+uint16_t MPU6050::dmpGetFIFOPacketSize() {
+    return dmpPacketSize;
+}
+
+#endif /* _MPU6050_9AXIS_MOTIONAPPS41_H_ */

          
A => libraries/MPU6050/helper_3dmath.h +216 -0
@@ 0,0 1,216 @@ 
+// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class, 3D math helper
+// 6/5/2012 by Jeff Rowberg <jeff@rowberg.net>
+// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
+//
+// Changelog:
+//     2012-06-05 - add 3D math helper file to DMP6 example sketch
+
+/* ============================================
+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 _HELPER_3DMATH_H_
+#define _HELPER_3DMATH_H_
+
+class Quaternion {
+    public:
+        float w;
+        float x;
+        float y;
+        float z;
+        
+        Quaternion() {
+            w = 1.0f;
+            x = 0.0f;
+            y = 0.0f;
+            z = 0.0f;
+        }
+        
+        Quaternion(float nw, float nx, float ny, float nz) {
+            w = nw;
+            x = nx;
+            y = ny;
+            z = nz;
+        }
+
+        Quaternion getProduct(Quaternion q) {
+            // Quaternion multiplication is defined by:
+            //     (Q1 * Q2).w = (w1w2 - x1x2 - y1y2 - z1z2)
+            //     (Q1 * Q2).x = (w1x2 + x1w2 + y1z2 - z1y2)
+            //     (Q1 * Q2).y = (w1y2 - x1z2 + y1w2 + z1x2)
+            //     (Q1 * Q2).z = (w1z2 + x1y2 - y1x2 + z1w2
+            return Quaternion(
+                w*q.w - x*q.x - y*q.y - z*q.z,  // new w
+                w*q.x + x*q.w + y*q.z - z*q.y,  // new x
+                w*q.y - x*q.z + y*q.w + z*q.x,  // new y
+                w*q.z + x*q.y - y*q.x + z*q.w); // new z
+        }
+
+        Quaternion getConjugate() {
+            return Quaternion(w, -x, -y, -z);
+        }
+        
+        float getMagnitude() {
+            return sqrt(w*w + x*x + y*y + z*z);
+        }
+        
+        void normalize() {
+            float m = getMagnitude();
+            w /= m;
+            x /= m;
+            y /= m;
+            z /= m;
+        }
+        
+        Quaternion getNormalized() {
+            Quaternion r(w, x, y, z);
+            r.normalize();
+            return r;
+        }
+};
+
+class VectorInt16 {
+    public:
+        int16_t x;
+        int16_t y;
+        int16_t z;
+
+        VectorInt16() {
+            x = 0;
+            y = 0;
+            z = 0;
+        }
+        
+        VectorInt16(int16_t nx, int16_t ny, int16_t nz) {
+            x = nx;
+            y = ny;
+            z = nz;
+        }
+
+        float getMagnitude() {
+            return sqrt(x*x + y*y + z*z);
+        }
+
+        void normalize() {
+            float m = getMagnitude();
+            x /= m;
+            y /= m;
+            z /= m;
+        }
+        
+        VectorInt16 getNormalized() {
+            VectorInt16 r(x, y, z);
+            r.normalize();
+            return r;
+        }
+        
+        void rotate(Quaternion *q) {
+            // http://www.cprogramming.com/tutorial/3d/quaternions.html
+            // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/index.htm
+            // http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation
+            // ^ or: http://webcache.googleusercontent.com/search?q=cache:xgJAp3bDNhQJ:content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation&hl=en&gl=us&strip=1
+        
+            // P_out = q * P_in * conj(q)
+            // - P_out is the output vector
+            // - q is the orientation quaternion
+            // - P_in is the input vector (a*aReal)
+            // - conj(q) is the conjugate of the orientation quaternion (q=[w,x,y,z], q*=[w,-x,-y,-z])
+            Quaternion p(0, x, y, z);
+
+            // quaternion multiplication: q * p, stored back in p
+            p = q -> getProduct(p);
+
+            // quaternion multiplication: p * conj(q), stored back in p
+            p = p.getProduct(q -> getConjugate());
+
+            // p quaternion is now [0, x', y', z']
+            x = p.x;
+            y = p.y;
+            z = p.z;
+        }
+
+        VectorInt16 getRotated(Quaternion *q) {
+            VectorInt16 r(x, y, z);
+            r.rotate(q);
+            return r;
+        }
+};
+
+class VectorFloat {
+    public:
+        float x;
+        float y;
+        float z;
+
+        VectorFloat() {
+            x = 0;
+            y = 0;
+            z = 0;
+        }
+        
+        VectorFloat(float nx, float ny, float nz) {
+            x = nx;
+            y = ny;
+            z = nz;
+        }
+
+        float getMagnitude() {
+            return sqrt(x*x + y*y + z*z);
+        }
+
+        void normalize() {
+            float m = getMagnitude();
+            x /= m;
+            y /= m;
+            z /= m;
+        }
+        
+        VectorFloat getNormalized() {
+            VectorFloat r(x, y, z);
+            r.normalize();
+            return r;
+        }
+        
+        void rotate(Quaternion *q) {
+            Quaternion p(0, x, y, z);
+
+            // quaternion multiplication: q * p, stored back in p
+            p = q -> getProduct(p);
+
+            // quaternion multiplication: p * conj(q), stored back in p
+            p = p.getProduct(q -> getConjugate());
+
+            // p quaternion is now [0, x', y', z']
+            x = p.x;
+            y = p.y;
+            z = p.z;
+        }
+
+        VectorFloat getRotated(Quaternion *q) {
+            VectorFloat r(x, y, z);
+            r.rotate(q);
+            return r;
+        }
+};
+
+#endif /* _HELPER_3DMATH_H_ */
  No newline at end of file

          
A => libraries/MPU6050/library.json +18 -0
@@ 0,0 1,18 @@ 
+{
+  "name": "I2Cdevlib-MPU6050",
+  "keywords": "gyroscope, accelerometer, sensor, i2cdevlib, i2c",
+  "description": "The MPU6050 combines a 3-axis gyroscope and a 3-axis accelerometer on the same silicon die together with an onboard Digital Motion Processor(DMP) which processes complex 6-axis MotionFusion algorithms",
+  "include": "Arduino/MPU6050",
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/jrowberg/i2cdevlib.git"
+  },
+  "dependencies":
+  {
+    "name": "I2Cdevlib-Core",
+    "frameworks": "arduino"
+  },
+  "frameworks": "arduino",
+  "platforms": "atmelavr"
+}

          
A => libraries/MPU6050/library.properties +10 -0
@@ 0,0 1,10 @@ 
+name=AmebaMPU6050
+version=1.0.0
+author=Realtek
+maintainer=Realtek <ameba.arduino@gmail.com>
+sentence=With this library you can find useful MPU6050 api
+paragraph=
+category=Signal Input/Output
+url=http://www.amebaiot.com/ameba-arduino-peripherals-examples/
+architectures=ameba
+

          
A => libraries/ModbusTCP-1.0.0.zip +0 -0

        
A => libraries/ModbusTCP/.gitignore +28 -0
@@ 0,0 1,28 @@ 
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app

          
A => libraries/ModbusTCP/LICENSE +22 -0
@@ 0,0 1,22 @@ 
+The MIT License (MIT)
+
+Copyright (c) 2014 Ductsoup
+
+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.
+

          
A => libraries/ModbusTCP/LICENSE_RTK.txt +1 -0
@@ 0,0 1,1 @@ 
+This library is forked from "https://github.com/ductsoup/ModbusTCP". The source codes under folder "ModbusTCP" inherit the license from any license claim of "https://github.com/ductsoup/ModbusTCP".
  No newline at end of file

          
A => libraries/ModbusTCP/ModbusTCP.cpp +388 -0
@@ 0,0 1,388 @@ 
+/*******************************************************************************
+ * ModbusTCP.cpp
+ */
+#include <Arduino.h>
+#include "ModbusTCP.h"
+
+ModbusTCP::ModbusTCP(void) : 
+#ifdef MB_ETHERNET
+mb_server(MB_PORT) 
+#endif
+#ifdef MB_CC3000
+mb_client(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIVIDER),
+mb_server(MB_PORT)
+#endif
+#ifdef MODBUSTCP_PCN001_USE_WIFI
+mb_server(MB_PORT)
+#endif
+{
+}
+#ifdef MB_ETHERNET
+void ModbusTCP::begin(uint8_t mac[6]){
+  
+  Serial.print(F("Requesting DHCP ..."));
+  if (Ethernet.begin(mac) == 0) {
+    Serial.println("failed");
+    while (1);
+  }
+  else
+    Serial.println();
+
+  Serial.print(F("Listening on "));
+  for (byte thisByte = 0; thisByte < 4; thisByte++) {
+    Serial.print(Ethernet.localIP()[thisByte], DEC);
+    Serial.print(thisByte < 3 ? "." : "");
+  }
+  Serial.print(":");
+  Serial.print(MB_PORT);
+  Serial.println(" ...");
+}
+#endif
+#ifdef MB_CC3000
+void ModbusTCP::begin(const char *ssid, const char *key, uint8_t secmode){
+
+  mb_client.begin();
+  Serial.print(F("Connecting to ")); Serial.print(ssid); Serial.println(F(" ..."));
+  mb_client.connectToAP(ssid, key, secmode); 
+  while (!mb_client.checkDHCP())
+      delay(100);
+  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
+  mb_client.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv);
+  Serial.print(F("Listening on "));
+  mb_client.printIPdotsRev(ipAddress);
+  Serial.print(F(":")); 
+  Serial.println(MB_PORT);
+  mb_server.begin();
+}
+#endif
+
+#ifdef MODBUSTCP_PCN001_USE_WIFI
+int ModbusTCP::begin(char* ssid) {
+  int status = WL_IDLE_STATUS;
+  while (status != WL_CONNECTED) {
+    status = WiFi.begin(ssid);
+    if (status == WL_CONNECTED) break;
+      printf("Retry connecting to %s\r\n", ssid);
+    delay(1000); // retry after 1s
+  }
+  printf("Connected to %s\r\n", ssid);
+  mb_server.begin();
+}
+
+int ModbusTCP::begin(char* ssid, uint8_t key_idx, const char* key) {
+  int status = WL_IDLE_STATUS;
+  while (status != WL_CONNECTED) {
+    status = WiFi.begin(ssid, key_idx, key);
+    if (status == WL_CONNECTED) break;
+    printf("Retry connecting to %s\r\n", ssid);
+    delay(1000); // retry after 1s
+  }
+  printf("Connected to %s\r\n", ssid);
+  mb_server.begin();
+}
+
+int ModbusTCP::begin(char* ssid, const char *passphrase) {
+  int status = WL_IDLE_STATUS;
+  while (status != WL_CONNECTED) {
+    status = WiFi.begin(ssid, passphrase);
+    if (status == WL_CONNECTED) break;
+    printf("Retry connecting to %s\r\n", ssid);
+    delay(1000); // retry after 1s
+  }
+  printf("Connected to %s\r\n", ssid);
+  mb_server.begin();
+}
+
+#endif
+
+void ModbusTCP::run(void){
+
+  int16_t i, iStart, iQty;
+  int16_t iTXLen = 0;       // response packet length
+  uint8_t *ptr, iFC = MB_FC_NONE, iEC = MB_EC_NONE;
+  //  
+  // Initialize and check for a request from a MODBUS master
+  //
+#ifdef MB_ETHERNET
+  EthernetClient clientrequest = mb_server.available();
+#endif
+#ifdef MB_CC3000
+  Adafruit_CC3000_ClientRef clientrequest = mb_server.available();
+#endif
+#ifdef ARDUINO_AMEBA
+  if (!clientrequest.connected()) {
+    clientrequest = mb_server.available();
+  }
+#endif
+
+#ifdef MODBUSTCP_PCN001_USE_WIFI
+  if(clientrequest.available()) {
+    int len = clientrequest.read(mb_adu, sizeof(mb_adu));
+    if (len > 0) {
+      iFC = mb_adu[MB_TCP_FUNC];
+    }
+  } else {
+    delay(10);
+  }
+
+#else
+  if(clientrequest.available()) {
+    //
+    // Retrieve request
+    //
+    for (i = 0 ; clientrequest.available() ; i++) 
+      mb_adu[i] = clientrequest.read();
+#ifdef MB_DEBUG   
+    printMB("RX: ", word(mb_adu[MB_TCP_LEN],mb_adu[MB_TCP_LEN+1])+6);  
+#endif    
+    //
+    // Unpack the function code
+    //
+    iFC = mb_adu[MB_TCP_FUNC];
+  }
+#endif // end of #ifdef ARDUINO_AMEBA
+  //
+  // Handle request
+  //
+  switch(iFC) {
+  case MB_FC_NONE:
+    break;
+  case MB_FC_READ_REGISTERS: 
+    //
+    // 03 (0x03) Read Holding Registers
+    //
+    // modpoll -m tcp -t 4:float -r 40001 -c 1 -1 192.168.x.x
+    //
+    //     [TransID] [ProtID-] [Length-] [Un] [FC] [Start--] [Qty----] 
+    // RX: 0x00 0x01 0x00 0x00 0x00 0x06 0x01 0x03 0x9C 0x40 0x00 0x02 
+    //
+    //     [TransID] [ProtID-] [Length-] [Un] [FC] [Bc] [float------------] 
+    // TX: 0x00 0x01 0x00 0x00 0x00 0x07 0x01 0x03 0x04 0x20 0x00 0x47 0xF1
+    //
+    // 123456.0 = 0x00 0x20 0xF1 0x47 (IEEE 754)
+    //
+    // Unpack the start and length
+    //
+    ptr = mb_adu + MB_TCP_DATA;
+    iStart = word(*ptr++, *ptr++) - 40000;
+    iQty = 2*word(*ptr++, *ptr);
+    //
+    // check for valid register addresses     
+    //
+    if (iStart < 0 || (iStart + iQty/2 - 1) > MB_REGISTERS_MAX) {
+      iEC = MB_EC_ILLEGAL_DATA_ADDRESS;
+      break;
+    }
+    //
+    // Write data length
+    //
+    ptr = mb_adu + MB_TCP_DATA;
+    *ptr++ = iQty;
+    //
+    // Write data
+    //
+    for (i = 0 ; i < iQty/2 ; i++) {
+      *ptr++ = highByte(mb_reg[iStart + i]);
+      *ptr++ =  lowByte(mb_reg[iStart + i]);
+    }
+    iTXLen = iQty + 9;
+    break;
+
+  case MB_FC_WRITE_REGISTER:
+    //
+    // 06 (0x06) Write register 
+    //
+    ptr = mb_adu + MB_TCP_DATA;   
+    iStart = word(*ptr++, *ptr++) - 40000;
+    //
+    // check for valid register addresses     
+    //   
+    if (iStart < 0 || (iStart - 1) > MB_REGISTERS_MAX) {
+      iEC = MB_EC_ILLEGAL_DATA_ADDRESS;
+      break;
+    }      
+    // Unpack and store data
+    //
+    mb_reg[iStart] = word(*ptr++, *ptr);
+    //
+    // Build a response 
+    //
+    iTXLen = 12;             
+    break;
+
+  case MB_FC_WRITE_MULTIPLE_REGISTERS: 
+    //
+    // 16 (0x10) Write Multiple registers
+    //
+    // modpoll -m tcp -t 4:float -r 40001 -c 1 -1 192.168.x.x 123456.0
+    //
+    //     [TransID] [ProtID-] [Length-] [Un] [FC] [Start--] [Qty----] [Bc] [float------------] 
+    // RX: 0x00 0x01 0x00 0x00 0x00 0x0B 0x01 0x10 0x9C 0x40 0x00 0x02 0x04 0x20 0x00 0x47 0xF1
+    //
+    // 123456.0 = 0x00 0x20 0xF1 0x47 (IEEE 754)
+    //
+    // Unpack the start and length
+    //
+    ptr = mb_adu + MB_TCP_DATA;   
+    iStart = word(*ptr++, *ptr++) - 40000;
+    iQty = 2*word(*ptr++, *ptr);      
+    //
+    // check for valid register addresses     
+    //   
+    if (iStart < 0 || (iStart + iQty/2 - 1) > MB_REGISTERS_MAX) {
+      iEC = MB_EC_ILLEGAL_DATA_ADDRESS;
+      break;
+    }
+    //
+    // Unpack and store data
+    //
+    ptr = mb_adu + MB_TCP_DATA+5;
+    // todo: check for valid length
+    for (i = 0 ; i < iQty/2 ; i++) {
+      mb_reg[iStart + i] = word(*ptr++, *ptr++);
+    }
+    //
+    // Build a response 
+    //
+    iTXLen = 12;
+    break;
+
+  default:
+    iEC = MB_EC_ILLEGAL_FUNCTION;
+    break;    
+  }
+  //
+  // Build exception response if necessary because we were too
+  // lazy to do it earlier. Other responses should already be
+  // built.
+  //
+  if (iEC) {
+    ptr = mb_adu + MB_TCP_FUNC;
+    *ptr = *ptr++ | 0x80;        // flag the function code
+    *ptr = iEC;                  // write the exception code
+    iTXLen = 9;
+  }
+  //
+  // If there's a response, transmit it
+  //
+  if (iFC) {
+    ptr = mb_adu + MB_TCP_LEN;    // write the header length
+    *ptr++ = 0x00;
+    *ptr = iTXLen - MB_TCP_UID;  
+    clientrequest.write(mb_adu, iTXLen); // send it        
+#ifdef MB_DEBUG   
+    printMB("TX: ", word(mb_adu[MB_TCP_LEN], mb_adu[MB_TCP_LEN+1]) + MB_TCP_UID);  
+#endif                                                
+  }
+}
+
+#ifdef MB_Float
+int ModbusTCP::setFloat(uint16_t iAddress, float fValue) {
+
+  int iRet; 
+  union {
+    float f;
+    uint16_t w[2];
+  } 
+  fw;
+
+  for (iRet = 0 ; !iRet ; iRet = 2) {
+    if ((iAddress -= 40001) < 0 || (iAddress + 1) >= MB_REGISTERS_MAX) // invalid address
+      break;
+    fw.f = fValue;
+    mb_reg[iAddress++] = fw.w[0];
+    mb_reg[iAddress] = fw.w[1]; 
+  }
+  return(iRet);  
+}
+float ModbusTCP::getFloat(uint16_t iAddress) {
+
+  union {
+    float fRet;
+    uint16_t w[2];
+  } 
+  fw;
+
+  if ((iAddress -= 40001) < 0 || (iAddress + 1) >= MB_REGISTERS_MAX) // invalid address
+    fw.fRet = NULL;
+  else {
+    fw.w[0] = mb_reg[iAddress++];  
+    fw.w[1] = mb_reg[iAddress];    
+  }
+  return(fw.fRet);  
+}
+#endif
+#ifdef MB_UInt32
+int ModbusTCP::setU32(uint16_t iAddress, uint32_t iValue) {
+
+  int iRet; 
+  for (iRet = 0 ; !iRet ; iRet = 2) {
+    if ((iAddress -= 40001) < 0 || (iAddress + 1) >= MB_REGISTERS_MAX) // invalid address
+      break;
+    mb_reg[iAddress++] = iValue & 0xffff;
+    mb_reg[iAddress] = iValue >> 16;
+  }
+  return(iRet);  
+}
+uint32_t ModbusTCP::getU32(uint16_t iAddress) {
+
+  uint32_t lRet; 
+
+  if ((iAddress -= 40001) < 0 || (iAddress + 1) >= MB_REGISTERS_MAX) // invalid address
+    lRet = NULL;
+  else {
+    lRet = (long(mb_reg[iAddress++]) | (long(mb_reg[iAddress+1]) << 16));
+  }
+  return(lRet);  
+}
+#endif
+#ifdef MB_UInt16
+int ModbusTCP::setU16(uint16_t iAddress, uint16_t iValue) {
+
+  int iRet; 
+  for (iRet = 0 ; !iRet ; iRet = 1) {
+    if ((iAddress -= 40001) < 0 || (iAddress >= MB_REGISTERS_MAX)) // invalid address
+      break;
+    mb_reg[iAddress] = iValue;
+  }
+  return(iRet);  
+}
+uint16_t ModbusTCP::getU16(uint16_t iAddress) {
+
+  uint16_t iRet; 
+
+  if ((iAddress -= 40001) < 0 || (iAddress >= MB_REGISTERS_MAX)) // invalid address
+    iRet = NULL;
+  else
+    iRet = mb_reg[iAddress];
+
+  return(iRet);  
+}
+#endif
+#ifdef MB_DEBUG
+void ModbusTCP::printHex(int num, int precision) {
+
+  char tmp[16];
+  char format[128];
+
+  sprintf(format, "0x%%.%dX ", precision);
+
+  sprintf(tmp, format, num);
+  Serial.print(tmp);
+}
+void ModbusTCP::printMB(char *s, int n) {
+
+  int i;
+
+  Serial.print(s);
+  for (i = 0 ; i < n ; i++) {
+    printHex(mb_adu[i], 2); 
+    if (i == 6) Serial.print("- ");
+    if (i == 7) Serial.print("- ");      
+  }
+  Serial.println("");      
+}
+#endif
+
+
+

          
A => libraries/ModbusTCP/ModbusTCP.h +221 -0
@@ 0,0 1,221 @@ 
+/*******************************************************************************
+ * ModbusTCP.h
+ *
+ * Lightweight Modbus TCP 32-bit slave for Arduino. 
+ * 
+ * Three MODBUS function codes are supported; reading (0x04) and writing (0x10)
+ * of holding registers and function 0x06 for compatiblilty. 
+ * 
+ * Three Modbus encodings are supported; UInt16, UInt32 and Float (Float and 
+ * Double are identical on Arduino Uno). With these three primitive types you
+ * can also support Int16, Int32, or packed bits (coils). 
+ * 
+ * Holding register addressing begins at 40001 and are contiguous.
+ * 
+ * Data is stored in native MODBUS UInt16 registers. To make it less error 
+ * prone, get and set functions are included for each encoding that also do 
+ * error checking. If you're crushed for resources feel free to comment out
+ * the define statements and write to the registers directly.
+ * 
+ * Testing
+ * This code was developed and tested using modpoll and Inductive Automation 
+ * Ignition quick client running in trial mode.
+ * 
+ * http://www.modbusdriver.com/modpoll.html
+ * https://www.inductiveautomation.com/scada-software
+ * 
+ * Credits
+ * 
+ * https://code.google.com/p/mudbus/
+ * 
+ * Thanks to Dee Wykoff and Martin Pettersson for making the Mudbus library 
+ * available as a place to start.
+ *
+ * MIT License
+ * Copyright (c) 2014 Ductsoup
+ * 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. 
+ */
+
+/*******************************************************************************
+ * 
+ * MODBUS Reference
+ * 
+ * http://www.csimn.com/CSI_pages/Modbus101.html
+ * 
+ * Modicon convention notation
+ * 
+ * 0x = Coil = 00001-09999
+ * 1x = Discrete Input = 10001-19999
+ * 3x = Input Register = 30001-39999
+ * 4x = Holding Register = 40001-49999
+ * 
+ * Modbus protocol defines a holding register as 16 bits wide; however, there is 
+ * a widely used defacto standard for reading and writing data wider than 16 
+ * bits. The most common are IEEE 754 floating point, and 32-bit integer. The 
+ * convention may also be extended to double precision floating point and 64-bit 
+ * integer data.
+ * 
+ * http://www.freemodbus.org/
+ * 
+ * ----------------------- MBAP Header --------------------------------------
+ * 
+ * <------------------------ MODBUS TCP/IP ADU(1) ------------------------->
+ * <----------- MODBUS PDU (1') ---------------->
+ * +-----------+---------------+------------------------------------------+
+ * | TID | PID | Length | UID  |Code | Data                               |
+ * +-----------+---------------+------------------------------------------+
+ * |     |     |        |      |                                           
+ * (2)   (3)   (4)      (5)    (6)                                          
+ * (2)  ... MB_TCP_TID          = 0 (Transaction Identifier - 2 Byte) 
+ * (3)  ... MB_TCP_PID          = 2 (Protocol Identifier - 2 Byte)
+ * (4)  ... MB_TCP_LEN          = 4 (Number of bytes - 2 Byte)
+ * (5)  ... MB_TCP_UID          = 6 (Unit Identifier - 1 Byte)
+ * (6)  ... MB_TCP_FUNC         = 7 (Modbus Function Code)
+ * 
+ * (1)  ... Modbus TCP/IP Application Data Unit
+ * (1') ... Modbus Protocol Data Unit
+ * 
+ *******************************************************************************/
+
+#ifndef HEADER_ModbusTCP
+#define HEADER_ModbusTCP
+
+//
+// Note: The Arduino IDE does not respect conditional included
+// header files in the main sketch so you have to select your
+// hardware there, and here.
+//
+#ifndef ARDUINO_AMEBA
+#define MB_ETHERNET
+#endif
+//#define MB_CC3000
+
+#ifdef MB_ETHERNET
+#include <Ethernet.h>
+#endif
+#ifdef MB_CC3000
+#include <Adafruit_CC3000.h>
+#include <SPI.h>
+#define ADAFRUIT_CC3000_IRQ   3
+#define ADAFRUIT_CC3000_VBAT  5
+#define ADAFRUIT_CC3000_CS    10
+#endif
+
+#ifdef ARDUINO_AMEBA
+
+#define MODBUSTCP_PCN001_USE_WIFI
+
+#endif
+
+#ifdef MODBUSTCP_PCN001_USE_WIFI
+#include <WiFi.h>
+#endif
+
+//
+// Application specific, adjust as necessary
+//
+#define MB_PORT 502
+#define MB_REGISTERS_MAX 32
+//
+// Comment out the features you don't need                            
+//
+#define MB_Float            
+#define MB_UInt32
+#define MB_UInt16
+//#define MB_DEBUG
+
+class ModbusTCP {
+public:
+  ModbusTCP(void);
+#ifdef MB_ETHERNET  
+  void begin(uint8_t mac[6]);
+#endif
+#ifdef MB_CC3000  
+  void begin(const char *ssid, const char *key, uint8_t secmode);
+#endif
+#ifdef MODBUSTCP_PCN001_USE_WIFI
+  int begin(char* ssid);
+  int begin(char* ssid, uint8_t key_idx, const char* key);
+  int begin(char* ssid, const char *passphrase);
+#endif
+
+  void run(void);
+
+#ifdef MB_Float
+  int setFloat(uint16_t iAddress, float fValue);
+  float getFloat(uint16_t iAddress);
+#endif
+#ifdef MB_UInt32    
+  int setU32(uint16_t iAddress, uint32_t iValue);
+  uint32_t getU32(uint16_t iAddress);
+#endif
+#ifdef MB_UInt16
+  int setU16(uint16_t iAddress, uint16_t iValue);
+  uint16_t getU16(uint16_t iAddress);
+#endif
+#ifdef MB_DEBUG
+  void printHex(int num, int precision);
+  void printMB(char *s, int n);
+#endif
+private:
+  uint16_t mb_reg[MB_REGISTERS_MAX];
+  uint8_t mb_adu[260];
+#ifdef MB_ETHERNET
+    EthernetServer mb_server;
+#endif
+#ifdef MB_CC3000
+    Adafruit_CC3000 mb_client;
+    Adafruit_CC3000_Server mb_server;
+#endif
+#ifdef MODBUSTCP_PCN001_USE_WIFI   
+    WiFiServer mb_server;
+    WiFiClient clientrequest;
+#endif
+};
+//
+// MODBUS Function Codes
+//
+#define MB_FC_NONE 0
+#define MB_FC_READ_COILS 1
+#define MB_FC_READ_DISCRETE_INPUT 2
+#define MB_FC_READ_REGISTERS 3 // implemented
+#define MB_FC_READ_INPUT_REGISTERS 4
+#define MB_FC_WRITE_COIL 5
+#define MB_FC_WRITE_REGISTER 6 // implemented
+#define MB_FC_WRITE_MULTIPLE_COILS 15
+#define MB_FC_WRITE_MULTIPLE_REGISTERS 16 // implemented
+//
+// MODBUS Error Codes
+//
+#define MB_EC_NONE 0
+#define MB_EC_ILLEGAL_FUNCTION 1
+#define MB_EC_ILLEGAL_DATA_ADDRESS 2
+#define MB_EC_ILLEGAL_DATA_VALUE 3
+#define MB_EC_SLAVE_DEVICE_FAILURE 4
+//
+// MODBUS MBAP offsets
+//
+#define MB_TCP_TID          0 // ignored
+#define MB_TCP_PID          2 // ignored
+#define MB_TCP_LEN          4
+#define MB_TCP_UID          6 // ignored
+#define MB_TCP_FUNC         7
+#define MB_TCP_DATA         8
+
+#endif

          
A => libraries/ModbusTCP/README.md +6 -0
@@ 0,0 1,6 @@ 
+ModbusTCP
+=========
+
+A Modbus TCP class for Arduino and either Ethernet or Adafruit CC3000 WiFi shield that includes support for 32-bit integer and float operations.
+
+If you're using an ESP8266 device check this post: https://forums.adafruit.com/viewtopic.php?f=31&t=61774&start=15

          
A => libraries/ModbusTCP/examples/simpletest/simpletest.ino +42 -0
@@ 0,0 1,42 @@ 
+/*
+
+ Simple Test
+
+ You can find modpoll command line tool here:
+   http://www.modbusdriver.com/modpoll.html
+
+ */
+
+#include "ModbusTCP.h"
+
+char ssid[] = "yourNetwork";     // your network SSID (name)
+char pass[] = "secretPassword";  // your network password
+
+// Register map for this example
+#define S1_FloatConstant 40001 // modpoll -m tcp -t 4:float -r 40001 [ip_addr] 
+#define S1_CurrentMillis 40003 // modpoll -m tcp -t 4:int -r 40003 [ip_addr]
+#define S1_FeetPerMile   40005 // modpoll -m tcp -t 4 -r 40005 [ip_addr]
+
+ModbusTCP m;
+
+void setup(void)
+{
+  // set some initial values
+  m.setFloat(S1_FloatConstant, PI);
+  m.setU32(S1_CurrentMillis, millis());
+  m.setU16(S1_FeetPerMile, 5280);
+  
+  m.begin(ssid, pass);
+}
+
+void loop(void)
+{
+  // Process MODBUS requests on every scan
+  m.run();
+  
+  // Update the MODBUS registers  
+  m.setU32(S1_CurrentMillis, millis());
+}
+
+
+

          
A => libraries/ModbusTCP/keywords.txt +21 -0
@@ 0,0 1,21 @@ 
+#######################################
+# Syntax Coloring Map for Motors
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+ModbusTCP	KEYWORD1	ModbusTCP
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+run	KEYWORD2
+setFloat	KEYWORD2
+getFloat	KEYWORD2
+setU32	KEYWORD2
+getU32	KEYWORD2
+printHex	KEYWORD2
+printMB	KEYWORD2

          
A => libraries/ModbusTCP/library.properties +10 -0
@@ 0,0 1,10 @@ 
+name=AmebaModbusTCP
+version=1.0.0
+author=Realtek
+maintainer=Realtek <ameba.arduino@gmail.com>
+sentence=With this library you can find useful ditial application.
+paragraph=
+category=Communication
+url=http://www.amebaiot.com/ameba-arduino-peripherals-examples/
+architectures=ameba
+

          
A => libraries/OneWire.zip +0 -0

        
A => libraries/OneWire/LICENSE.txt +1 -0
@@ 0,0 1,1 @@ 
+The OneWire library is forked from works at "http://www.pjrc.com/teensy/td_libs_OneWire.html". The source codes under folder "OneWire" inherit the license from any license claim of "http://www.pjrc.com/teensy/td_libs_OneWire.html".
  No newline at end of file

          
A => libraries/OneWire/OneWire.cpp +577 -0
@@ 0,0 1,577 @@ 
+/*
+Copyright (c) 2007, Jim Studt  (original old version - many contributors since)
+
+The latest version of this library may be found at:
+  http://www.pjrc.com/teensy/td_libs_OneWire.html
+
+OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since
+January 2010.  At the time, it was in need of many bug fixes, but had
+been abandoned the original author (Jim Studt).  None of the known
+contributors were interested in maintaining OneWire.  Paul typically
+works on OneWire every 6 to 12 months.  Patches usually wait that
+long.  If anyone is interested in more actively maintaining OneWire,
+please contact Paul.
+
+Version 2.3:
+  Unknonw chip fallback mode, Roger Clark
+  Teensy-LC compatibility, Paul Stoffregen
+  Search bug fix, Love Nystrom
+
+Version 2.2:
+  Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com
+  Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030
+  Fix DS18B20 example negative temperature
+  Fix DS18B20 example's low res modes, Ken Butcher
+  Improve reset timing, Mark Tillotson
+  Add const qualifiers, Bertrik Sikken
+  Add initial value input to crc16, Bertrik Sikken
+  Add target_search() function, Scott Roberts
+
+Version 2.1:
+  Arduino 1.0 compatibility, Paul Stoffregen
+  Improve temperature example, Paul Stoffregen
+  DS250x_PROM example, Guillermo Lovato
+  PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com
+  Improvements from Glenn Trewitt:
+  - crc16() now works
+  - check_crc16() does all of calculation/checking work.
+  - Added read_bytes() and write_bytes(), to reduce tedious loops.
+  - Added ds2408 example.
+  Delete very old, out-of-date readme file (info is here)
+
+Version 2.0: Modifications by Paul Stoffregen, January 2010:
+http://www.pjrc.com/teensy/td_libs_OneWire.html
+  Search fix from Robin James
+    http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27
+  Use direct optimized I/O in all cases
+  Disable interrupts during timing critical sections
+    (this solves many random communication errors)
+  Disable interrupts during read-modify-write I/O
+  Reduce RAM consumption by eliminating unnecessary
+    variables and trimming many to 8 bits
+  Optimize both crc8 - table version moved to flash
+
+Modified to work with larger numbers of devices - avoids loop.
+Tested in Arduino 11 alpha with 12 sensors.
+26 Sept 2008 -- Robin James
+http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27
+
+Updated to work with arduino-0008 and to include skip() as of
+2007/07/06. --RJL20
+
+Modified to calculate the 8-bit CRC directly, avoiding the need for
+the 256-byte lookup table to be loaded in RAM.  Tested in arduino-0010
+-- Tom Pollard, Jan 23, 2008
+
+Jim Studt's original library was modified by Josh Larios.
+
+Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008
+
+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.
+
+Much of the code was inspired by Derek Yerger's code, though I don't
+think much of that remains.  In any event that was..
+    (copyleft) 2006 by Derek Yerger - Free to distribute freely.
+
+The CRC code was excerpted and inspired by the Dallas Semiconductor
+sample code bearing this copyright.
+//---------------------------------------------------------------------------
+// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
+//
+// 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 DALLAS SEMICONDUCTOR 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.
+//
+// Except as contained in this notice, the name of Dallas Semiconductor
+// shall not be used except as stated in the Dallas Semiconductor
+// Branding Policy.
+//--------------------------------------------------------------------------
+*/
+
+#include "OneWire.h"
+
+
+OneWire::OneWire(uint8_t pin)
+{
+	pinMode(pin, INPUT);
+	bitmask = PIN_TO_BITMASK(pin);
+	baseReg = PIN_TO_BASEREG(pin);
+#if ONEWIRE_SEARCH
+	reset_search();
+#endif
+}
+
+
+// Perform the onewire reset function.  We will wait up to 250uS for
+// the bus to come high, if it doesn't then it is broken or shorted
+// and we return a 0;
+//
+// Returns 1 if a device asserted a presence pulse, 0 otherwise.
+//
+uint8_t OneWire::reset(void)
+{
+	IO_REG_TYPE mask = bitmask;
+	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
+	uint8_t r;
+	uint8_t retries = 125;
+
+	noInterrupts();
+	DIRECT_MODE_INPUT(reg, mask);
+	interrupts();
+	// wait until the wire is high... just in case
+	do {
+		if (--retries == 0) return 0;
+		delayMicroseconds(2);
+	} while ( !DIRECT_READ(reg, mask));
+
+	noInterrupts();
+	DIRECT_WRITE_LOW(reg, mask);
+	DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
+	interrupts();
+	delayMicroseconds(480);
+	noInterrupts();
+	DIRECT_MODE_INPUT(reg, mask);	// allow it to float
+	delayMicroseconds(70);
+	r = !DIRECT_READ(reg, mask);
+	interrupts();
+	delayMicroseconds(410);
+	return r;
+}
+
+//
+// Write a bit. Port and bit is used to cut lookup time and provide
+// more certain timing.
+//
+void OneWire::write_bit(uint8_t v)
+{
+	IO_REG_TYPE mask=bitmask;
+	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
+
+	if (v & 1) {
+		noInterrupts();
+		DIRECT_WRITE_LOW(reg, mask);
+		DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
+		delayMicroseconds(10);
+		DIRECT_WRITE_HIGH(reg, mask);	// drive output high
+		interrupts();
+		delayMicroseconds(55);
+	} else {
+		noInterrupts();
+		DIRECT_WRITE_LOW(reg, mask);
+		DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
+		delayMicroseconds(65);
+		DIRECT_WRITE_HIGH(reg, mask);	// drive output high
+		interrupts();
+		delayMicroseconds(5);
+	}
+}
+
+//
+// Read a bit. Port and bit is used to cut lookup time and provide
+// more certain timing.
+//
+uint8_t OneWire::read_bit(void)
+{
+	IO_REG_TYPE mask=bitmask;
+	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
+	uint8_t r;
+
+	noInterrupts();
+	DIRECT_MODE_OUTPUT(reg, mask);
+	DIRECT_WRITE_LOW(reg, mask);
+	delayMicroseconds(3);
+	DIRECT_MODE_INPUT(reg, mask);	// let pin float, pull up will raise
+	delayMicroseconds(10);
+	r = DIRECT_READ(reg, mask);
+	interrupts();
+	delayMicroseconds(53);
+	return r;
+}
+
+//
+// Write a byte. The writing code uses the active drivers to raise the
+// pin high, if you need power after the write (e.g. DS18S20 in
+// parasite power mode) then set 'power' to 1, otherwise the pin will
+// go tri-state at the end of the write to avoid heating in a short or
+// other mishap.
+//
+void OneWire::write(uint8_t v, uint8_t power /* = 0 */) {
+    uint8_t bitMask;
+
+    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
+	OneWire::write_bit( (bitMask & v)?1:0);
+    }
+    if ( !power) {
+	noInterrupts();
+	DIRECT_MODE_INPUT(baseReg, bitmask);
+	DIRECT_WRITE_LOW(baseReg, bitmask);
+	interrupts();
+    }
+}
+
+void OneWire::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) {
+  for (uint16_t i = 0 ; i < count ; i++)
+    write(buf[i]);
+  if (!power) {
+    noInterrupts();
+    DIRECT_MODE_INPUT(baseReg, bitmask);
+    DIRECT_WRITE_LOW(baseReg, bitmask);
+    interrupts();
+  }
+}
+
+//
+// Read a byte
+//
+uint8_t OneWire::read() {
+    uint8_t bitMask;
+    uint8_t r = 0;
+
+    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
+	if ( OneWire::read_bit()) r |= bitMask;
+    }
+    return r;
+}
+
+void OneWire::read_bytes(uint8_t *buf, uint16_t count) {
+  for (uint16_t i = 0 ; i < count ; i++)
+    buf[i] = read();
+}
+
+//
+// Do a ROM select
+//
+void OneWire::select(const uint8_t rom[8])
+{
+    uint8_t i;
+
+    write(0x55);           // Choose ROM
+
+    for (i = 0; i < 8; i++) write(rom[i]);
+}
+
+//
+// Do a ROM skip
+//
+void OneWire::skip()
+{
+    write(0xCC);           // Skip ROM
+}
+
+void OneWire::depower()
+{
+	noInterrupts();
+	DIRECT_MODE_INPUT(baseReg, bitmask);
+	interrupts();
+}
+
+#if ONEWIRE_SEARCH
+
+//
+// You need to use this function to start a search again from the beginning.
+// You do not need to do it for the first search, though you could.
+//
+void OneWire::reset_search()
+{
+  // reset the search state
+  LastDiscrepancy = 0;
+  LastDeviceFlag = FALSE;
+  LastFamilyDiscrepancy = 0;
+  for(int i = 7; ; i--) {
+    ROM_NO[i] = 0;
+    if ( i == 0) break;
+  }
+}
+
+// Setup the search to find the device type 'family_code' on the next call
+// to search(*newAddr) if it is present.
+//
+void OneWire::target_search(uint8_t family_code)
+{
+   // set the search state to find SearchFamily type devices
+   ROM_NO[0] = family_code;
+   for (uint8_t i = 1; i < 8; i++)
+      ROM_NO[i] = 0;
+   LastDiscrepancy = 64;
+   LastFamilyDiscrepancy = 0;
+   LastDeviceFlag = FALSE;
+}
+
+//
+// Perform a search. If this function returns a '1' then it has
+// enumerated the next device and you may retrieve the ROM from the
+// OneWire::address variable. If there are no devices, no further
+// devices, or something horrible happens in the middle of the
+// enumeration then a 0 is returned.  If a new device is found then
+// its address is copied to newAddr.  Use OneWire::reset_search() to
+// start over.
+//
+// --- Replaced by the one from the Dallas Semiconductor web site ---
+//--------------------------------------------------------------------------
+// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
+// search state.
+// Return TRUE  : device found, ROM number in ROM_NO buffer
+//        FALSE : device not found, end of search
+//
+uint8_t OneWire::search(uint8_t *newAddr, bool search_mode /* = true */)
+{
+   uint8_t id_bit_number;
+   uint8_t last_zero, rom_byte_number, search_result;
+   uint8_t id_bit, cmp_id_bit;
+
+   unsigned char rom_byte_mask, search_direction;
+
+   // initialize for search
+   id_bit_number = 1;
+   last_zero = 0;
+   rom_byte_number = 0;
+   rom_byte_mask = 1;
+   search_result = 0;
+
+   // if the last call was not the last one
+   if (!LastDeviceFlag)
+   {
+      // 1-Wire reset
+      if (!reset())
+      {
+         // reset the search
+         LastDiscrepancy = 0;
+         LastDeviceFlag = FALSE;
+         LastFamilyDiscrepancy = 0;
+         return FALSE;
+      }
+
+      // issue the search command
+      if (search_mode == true) {
+        write(0xF0);   // NORMAL SEARCH
+      } else {
+        write(0xEC);   // CONDITIONAL SEARCH
+      }
+
+      // loop to do the search
+      do
+      {
+         // read a bit and its complement
+         id_bit = read_bit();
+         cmp_id_bit = read_bit();
+
+         // check for no devices on 1-wire
+         if ((id_bit == 1) && (cmp_id_bit == 1))
+            break;
+         else
+         {
+            // all devices coupled have 0 or 1
+            if (id_bit != cmp_id_bit)
+               search_direction = id_bit;  // bit write value for search
+            else
+            {
+               // if this discrepancy if before the Last Discrepancy
+               // on a previous next then pick the same as last time
+               if (id_bit_number < LastDiscrepancy)
+                  search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
+               else
+                  // if equal to last pick 1, if not then pick 0
+                  search_direction = (id_bit_number == LastDiscrepancy);
+
+               // if 0 was picked then record its position in LastZero
+               if (search_direction == 0)
+               {
+                  last_zero = id_bit_number;
+
+                  // check for Last discrepancy in family
+                  if (last_zero < 9)
+                     LastFamilyDiscrepancy = last_zero;
+               }
+            }
+
+            // set or clear the bit in the ROM byte rom_byte_number
+            // with mask rom_byte_mask
+            if (search_direction == 1)
+              ROM_NO[rom_byte_number] |= rom_byte_mask;
+            else
+              ROM_NO[rom_byte_number] &= ~rom_byte_mask;
+
+            // serial number search direction write bit
+            write_bit(search_direction);
+
+            // increment the byte counter id_bit_number
+            // and shift the mask rom_byte_mask
+            id_bit_number++;
+            rom_byte_mask <<= 1;
+
+            // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
+            if (rom_byte_mask == 0)
+            {
+                rom_byte_number++;
+                rom_byte_mask = 1;
+            }
+         }
+      }
+      while(rom_byte_number < 8);  // loop until through all ROM bytes 0-7
+
+      // if the search was successful then
+      if (!(id_bit_number < 65))
+      {
+         // search successful so set LastDiscrepancy,LastDeviceFlag,search_result
+         LastDiscrepancy = last_zero;
+
+         // check for last device
+         if (LastDiscrepancy == 0)
+            LastDeviceFlag = TRUE;
+
+         search_result = TRUE;
+      }
+   }
+
+   // if no device found then reset counters so next 'search' will be like a first
+   if (!search_result || !ROM_NO[0])
+   {
+      LastDiscrepancy = 0;
+      LastDeviceFlag = FALSE;
+      LastFamilyDiscrepancy = 0;
+      search_result = FALSE;
+   } else {
+      for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i];
+   }
+   return search_result;
+  }
+
+#endif
+
+#if ONEWIRE_CRC
+// The 1-Wire CRC scheme is described in Maxim Application Note 27:
+// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
+//
+
+#if ONEWIRE_CRC8_TABLE
+// This table comes from Dallas sample code where it is freely reusable,
+// though Copyright (C) 2000 Dallas Semiconductor Corporation
+static const uint8_t PROGMEM dscrc_table[] = {
+      0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
+    157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
+     35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
+    190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
+     70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
+    219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
+    101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
+    248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
+    140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
+     17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
+    175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
+     50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
+    202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
+     87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
+    233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
+    116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
+
+//
+// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
+// and the registers.  (note: this might better be done without to
+// table, it would probably be smaller and certainly fast enough
+// compared to all those delayMicrosecond() calls.  But I got
+// confused, so I use this table from the examples.)
+//
+uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len)
+{
+	uint8_t crc = 0;
+
+	while (len--) {
+		crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
+	}
+	return crc;
+}
+#else
+//
+// Compute a Dallas Semiconductor 8 bit CRC directly.
+// this is much slower, but much smaller, than the lookup table.
+//
+uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len)
+{
+	uint8_t crc = 0;
+
+	while (len--) {
+#if defined(__AVR__)
+		crc = _crc_ibutton_update(crc, *addr++);
+#else
+		uint8_t inbyte = *addr++;
+		for (uint8_t i = 8; i; i--) {
+			uint8_t mix = (crc ^ inbyte) & 0x01;
+			crc >>= 1;
+			if (mix) crc ^= 0x8C;
+			inbyte >>= 1;
+		}
+#endif
+	}
+	return crc;
+}
+#endif
+
+#if ONEWIRE_CRC16
+bool OneWire::check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc)
+{
+    crc = ~crc16(input, len, crc);
+    return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1];
+}
+
+uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc)
+{
+#if defined(__AVR__)
+    for (uint16_t i = 0 ; i < len ; i++) {
+        crc = _crc16_update(crc, input[i]);
+    }
+#else
+    static const uint8_t oddparity[16] =
+        { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
+
+    for (uint16_t i = 0 ; i < len ; i++) {
+      // Even though we're just copying a byte from the input,
+      // we'll be doing 16-bit computation with it.
+      uint16_t cdata = input[i];
+      cdata = (cdata ^ crc) & 0xff;
+      crc >>= 8;
+
+      if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4])
+          crc ^= 0xC001;
+
+      cdata <<= 6;
+      crc ^= cdata;
+      cdata <<= 1;
+      crc ^= cdata;
+    }
+#endif
+    return crc;
+}
+#endif
+
+#endif

          
A => libraries/OneWire/OneWire.h +383 -0
@@ 0,0 1,383 @@ 
+#ifndef OneWire_h
+#define OneWire_h
+
+#include <inttypes.h>
+
+#if defined(__AVR__)
+#include <util/crc16.h>
+#endif
+
+#if ARDUINO >= 100
+#include "Arduino.h"       // for delayMicroseconds, digitalPinToBitMask, etc
+#else
+#include "WProgram.h"      // for delayMicroseconds
+#include "pins_arduino.h"  // for digitalPinToBitMask, etc
+#endif
+
+// You can exclude certain features from OneWire.  In theory, this
+// might save some space.  In practice, the compiler automatically
+// removes unused code (technically, the linker, using -fdata-sections
+// and -ffunction-sections when compiling, and Wl,--gc-sections
+// when linking), so most of these will not result in any code size
+// reduction.  Well, unless you try to use the missing features
+// and redesign your program to not need them!  ONEWIRE_CRC8_TABLE
+// is the exception, because it selects a fast but large algorithm
+// or a small but slow algorithm.
+
+// you can exclude onewire_search by defining that to 0
+#ifndef ONEWIRE_SEARCH
+#define ONEWIRE_SEARCH 1
+#endif
+
+// You can exclude CRC checks altogether by defining this to 0
+#ifndef ONEWIRE_CRC
+#define ONEWIRE_CRC 1
+#endif
+
+// Select the table-lookup method of computing the 8-bit CRC
+// by setting this to 1.  The lookup table enlarges code size by
+// about 250 bytes.  It does NOT consume RAM (but did in very
+// old versions of OneWire).  If you disable this, a slower
+// but very compact algorithm is used.
+#ifndef ONEWIRE_CRC8_TABLE
+#define ONEWIRE_CRC8_TABLE 1
+#endif
+
+// You can allow 16-bit CRC checks by defining this to 1
+// (Note that ONEWIRE_CRC must also be 1.)
+#ifndef ONEWIRE_CRC16
+#define ONEWIRE_CRC16 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE  1
+#endif
+
+// Platform specific I/O definitions
+
+#if defined(__AVR__)
+#define PIN_TO_BASEREG(pin)             (portInputRegister(digitalPinToPort(pin)))
+#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
+#define IO_REG_TYPE uint8_t
+#define IO_REG_ASM asm("r30")
+#define DIRECT_READ(base, mask)         (((*(base)) & (mask)) ? 1 : 0)
+#define DIRECT_MODE_INPUT(base, mask)   ((*((base)+1)) &= ~(mask))
+#define DIRECT_MODE_OUTPUT(base, mask)  ((*((base)+1)) |= (mask))
+#define DIRECT_WRITE_LOW(base, mask)    ((*((base)+2)) &= ~(mask))
+#define DIRECT_WRITE_HIGH(base, mask)   ((*((base)+2)) |= (mask))
+
+#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
+#define PIN_TO_BASEREG(pin)             (portOutputRegister(pin))
+#define PIN_TO_BITMASK(pin)             (1)
+#define IO_REG_TYPE uint8_t
+#define IO_REG_ASM
+#define DIRECT_READ(base, mask)         (*((base)+512))
+#define DIRECT_MODE_INPUT(base, mask)   (*((base)+640) = 0)
+#define DIRECT_MODE_OUTPUT(base, mask)  (*((base)+640) = 1)
+#define DIRECT_WRITE_LOW(base, mask)    (*((base)+256) = 1)
+#define DIRECT_WRITE_HIGH(base, mask)   (*((base)+128) = 1)
+
+#elif defined(__MKL26Z64__)
+#define PIN_TO_BASEREG(pin)             (portOutputRegister(pin))
+#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
+#define IO_REG_TYPE uint8_t
+#define IO_REG_ASM
+#define DIRECT_READ(base, mask)         ((*((base)+16) & (mask)) ? 1 : 0)
+#define DIRECT_MODE_INPUT(base, mask)   (*((base)+20) &= ~(mask))
+#define DIRECT_MODE_OUTPUT(base, mask)  (*((base)+20) |= (mask))
+#define DIRECT_WRITE_LOW(base, mask)    (*((base)+8) = (mask))
+#define DIRECT_WRITE_HIGH(base, mask)   (*((base)+4) = (mask))
+
+#elif defined(__SAM3X8E__)
+// Arduino 1.5.1 may have a bug in delayMicroseconds() on Arduino Due.
+// http://arduino.cc/forum/index.php/topic,141030.msg1076268.html#msg1076268
+// If you have trouble with OneWire on Arduino Due, please check the
+// status of delayMicroseconds() before reporting a bug in OneWire!
+#define PIN_TO_BASEREG(pin)             (&(digitalPinToPort(pin)->PIO_PER))
+#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
+#define IO_REG_TYPE uint32_t
+#define IO_REG_ASM
+#define DIRECT_READ(base, mask)         (((*((base)+15)) & (mask)) ? 1 : 0)
+#define DIRECT_MODE_INPUT(base, mask)   ((*((base)+5)) = (mask))
+#define DIRECT_MODE_OUTPUT(base, mask)  ((*((base)+4)) = (mask))
+#define DIRECT_WRITE_LOW(base, mask)    ((*((base)+13)) = (mask))
+#define DIRECT_WRITE_HIGH(base, mask)   ((*((base)+12)) = (mask))
+#ifndef PROGMEM
+#define PROGMEM
+#endif
+#ifndef pgm_read_byte
+#define pgm_read_byte(addr) (*(const uint8_t *)(addr))
+#endif
+
+#elif defined(__PIC32MX__)
+#define PIN_TO_BASEREG(pin)             (portModeRegister(digitalPinToPort(pin)))
+#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
+#define IO_REG_TYPE uint32_t
+#define IO_REG_ASM
+#define DIRECT_READ(base, mask)         (((*(base+4)) & (mask)) ? 1 : 0)  //PORTX + 0x10
+#define DIRECT_MODE_INPUT(base, mask)   ((*(base+2)) = (mask))            //TRISXSET + 0x08
+#define DIRECT_MODE_OUTPUT(base, mask)  ((*(base+1)) = (mask))            //TRISXCLR + 0x04
+#define DIRECT_WRITE_LOW(base, mask)    ((*(base+8+1)) = (mask))          //LATXCLR  + 0x24
+#define DIRECT_WRITE_HIGH(base, mask)   ((*(base+8+2)) = (mask))          //LATXSET + 0x28
+
+#elif defined(ARDUINO_ARCH_ESP8266)
+#define PIN_TO_BASEREG(pin)             ((volatile uint32_t*) GPO)
+#define PIN_TO_BITMASK(pin)             (1 << pin)
+#define IO_REG_TYPE uint32_t
+#define IO_REG_ASM
+#define DIRECT_READ(base, mask)         ((GPI & (mask)) ? 1 : 0)    //GPIO_IN_ADDRESS
+#define DIRECT_MODE_INPUT(base, mask)   (GPE &= ~(mask))            //GPIO_ENABLE_W1TC_ADDRESS
+#define DIRECT_MODE_OUTPUT(base, mask)  (GPE |= (mask))             //GPIO_ENABLE_W1TS_ADDRESS
+#define DIRECT_WRITE_LOW(base, mask)    (GPOC = (mask))             //GPIO_OUT_W1TC_ADDRESS
+#define DIRECT_WRITE_HIGH(base, mask)   (GPOS = (mask))             //GPIO_OUT_W1TS_ADDRESS
+
+#elif defined(__SAMD21G18A__)
+#define PIN_TO_BASEREG(pin)             portModeRegister(digitalPinToPort(pin))
+#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
+#define IO_REG_TYPE uint32_t
+#define IO_REG_ASM
+#define DIRECT_READ(base, mask)         (((*((base)+8)) & (mask)) ? 1 : 0)
+#define DIRECT_MODE_INPUT(base, mask)   ((*((base)+1)) = (mask))
+#define DIRECT_MODE_OUTPUT(base, mask)  ((*((base)+2)) = (mask))
+#define DIRECT_WRITE_LOW(base, mask)    ((*((base)+5)) = (mask))
+#define DIRECT_WRITE_HIGH(base, mask)   ((*((base)+6)) = (mask))
+
+#elif defined(RBL_NRF51822)
+#define PIN_TO_BASEREG(pin)             (0)
+#define PIN_TO_BITMASK(pin)             (pin)
+#define IO_REG_TYPE uint32_t
+#define IO_REG_ASM
+#define DIRECT_READ(base, pin)          nrf_gpio_pin_read(pin)
+#define DIRECT_WRITE_LOW(base, pin)     nrf_gpio_pin_clear(pin)
+#define DIRECT_WRITE_HIGH(base, pin)    nrf_gpio_pin_set(pin)
+#define DIRECT_MODE_INPUT(base, pin)    nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL)
+#define DIRECT_MODE_OUTPUT(base, pin)   nrf_gpio_cfg_output(pin)
+
+#elif defined(__arc__) /* Arduino101/Genuino101 specifics */
+
+#include "scss_registers.h"
+#include "portable.h"
+#include "avr/pgmspace.h"
+
+#define GPIO_ID(pin)			(g_APinDescription[pin].ulGPIOId)
+#define GPIO_TYPE(pin)			(g_APinDescription[pin].ulGPIOType)
+#define GPIO_BASE(pin)			(g_APinDescription[pin].ulGPIOBase)
+#define DIR_OFFSET_SS			0x01
+#define DIR_OFFSET_SOC			0x04
+#define EXT_PORT_OFFSET_SS		0x0A
+#define EXT_PORT_OFFSET_SOC		0x50
+
+/* GPIO registers base address */
+#define PIN_TO_BASEREG(pin)		((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase)
+#define PIN_TO_BITMASK(pin)		pin
+#define IO_REG_TYPE			uint32_t
+#define IO_REG_ASM
+
+static inline __attribute__((always_inline))
+IO_REG_TYPE directRead(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
+{
+    IO_REG_TYPE ret;
+    if (SS_GPIO == GPIO_TYPE(pin)) {
+        ret = READ_ARC_REG(((IO_REG_TYPE)base + EXT_PORT_OFFSET_SS));
+    } else {
+        ret = MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, EXT_PORT_OFFSET_SOC);
+    }
+    return ((ret >> GPIO_ID(pin)) & 0x01);
+}
+
+static inline __attribute__((always_inline))
+void directModeInput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
+{
+    if (SS_GPIO == GPIO_TYPE(pin)) {
+        WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)),
+			((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
+    } else {
+        MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin));
+    }
+}
+
+static inline __attribute__((always_inline))
+void directModeOutput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
+{
+    if (SS_GPIO == GPIO_TYPE(pin)) {
+        WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)),
+			((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
+    } else {
+        MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin));
+    }
+}
+
+static inline __attribute__((always_inline))
+void directWriteLow(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
+{
+    if (SS_GPIO == GPIO_TYPE(pin)) {
+        WRITE_ARC_REG(READ_ARC_REG(base) & ~(0x01 << GPIO_ID(pin)), base);
+    } else {
+        MMIO_REG_VAL(base) &= ~(0x01 << GPIO_ID(pin));
+    }
+}
+
+static inline __attribute__((always_inline))
+void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
+{
+    if (SS_GPIO == GPIO_TYPE(pin)) {
+        WRITE_ARC_REG(READ_ARC_REG(base) | (0x01 << GPIO_ID(pin)), base);
+    } else {
+        MMIO_REG_VAL(base) |= (0x01 << GPIO_ID(pin));
+    }
+}
+
+#define DIRECT_READ(base, pin)		directRead(base, pin)
+#define DIRECT_MODE_INPUT(base, pin)	directModeInput(base, pin)
+#define DIRECT_MODE_OUTPUT(base, pin)	directModeOutput(base, pin)
+#define DIRECT_WRITE_LOW(base, pin)	directWriteLow(base, pin)
+#define DIRECT_WRITE_HIGH(base, pin)	directWriteHigh(base, pin)
+
+#elif defined(ARDUINO_AMEBA)
+
+#define PIN_TO_BASEREG(pin)             ((uint32_t *)digitalPinToPort(pin))
+#define PIN_TO_BITMASK(pin)             digitalPinToBitMask(pin)
+#define IO_REG_TYPE                     uint32_t
+#define IO_REG_ASM
+#define DIRECT_READ(port, mask)         ( (*portInputRegister((uint32_t)port) & mask) ? 1 : 0)
+#define DIRECT_WRITE_LOW(port, mask)    ( (*portOutputRegister((uint32_t)port)) &= ~mask )
+#define DIRECT_WRITE_HIGH(port, mask)   ( (*portOutputRegister((uint32_t)port)) |=  mask )
+#define DIRECT_MODE_INPUT(port, mask)   ( *( (volatile uint32_t *)( 0x40001004 + ((uint32_t)port) * 0x0C ) ) &= ~mask )
+#define DIRECT_MODE_OUTPUT(port, mask)  ( *( (volatile uint32_t *)( 0x40001004 + ((uint32_t)port) * 0x0C ) ) |=  mask )
+
+#else
+#define PIN_TO_BASEREG(pin)             (0)
+#define PIN_TO_BITMASK(pin)             (pin)
+#define IO_REG_TYPE unsigned int
+#define IO_REG_ASM
+#define DIRECT_READ(base, pin)          digitalRead(pin)
+#define DIRECT_WRITE_LOW(base, pin)     digitalWrite(pin, LOW)
+#define DIRECT_WRITE_HIGH(base, pin)    digitalWrite(pin, HIGH)
+#define DIRECT_MODE_INPUT(base, pin)    pinMode(pin,INPUT)
+#define DIRECT_MODE_OUTPUT(base, pin)   pinMode(pin,OUTPUT)
+#warning "OneWire. Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite. Operation of this library is not guaranteed on this architecture."
+
+#endif
+
+
+class OneWire
+{
+  private:
+    IO_REG_TYPE bitmask;
+    volatile IO_REG_TYPE *baseReg;
+
+#if ONEWIRE_SEARCH
+    // global search state
+    unsigned char ROM_NO[8];
+    uint8_t LastDiscrepancy;
+    uint8_t LastFamilyDiscrepancy;
+    uint8_t LastDeviceFlag;
+#endif
+
+  public:
+    OneWire( uint8_t pin);
+
+    // Perform a 1-Wire reset cycle. Returns 1 if a device responds
+    // with a presence pulse.  Returns 0 if there is no device or the
+    // bus is shorted or otherwise held low for more than 250uS
+    uint8_t reset(void);
+
+    // Issue a 1-Wire rom select command, you do the reset first.
+    void select(const uint8_t rom[8]);
+
+    // Issue a 1-Wire rom skip command, to address all on bus.
+    void skip(void);
+
+    // Write a byte. If 'power' is one then the wire is held high at
+    // the end for parasitically powered devices. You are responsible
+    // for eventually depowering it by calling depower() or doing
+    // another read or write.
+    void write(uint8_t v, uint8_t power = 0);
+
+    void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0);
+
+    // Read a byte.
+    uint8_t read(void);
+
+    void read_bytes(uint8_t *buf, uint16_t count);
+
+    // Write a bit. The bus is always left powered at the end, see
+    // note in write() about that.
+    void write_bit(uint8_t v);
+
+    // Read a bit.
+    uint8_t read_bit(void);
+
+    // Stop forcing power onto the bus. You only need to do this if
+    // you used the 'power' flag to write() or used a write_bit() call
+    // and aren't about to do another read or write. You would rather
+    // not leave this powered if you don't have to, just in case
+    // someone shorts your bus.
+    void depower(void);
+
+#if ONEWIRE_SEARCH
+    // Clear the search state so that if will start from the beginning again.
+    void reset_search();
+
+    // Setup the search to find the device type 'family_code' on the next call
+    // to search(*newAddr) if it is present.
+    void target_search(uint8_t family_code);
+
+    // Look for the next device. Returns 1 if a new address has been
+    // returned. A zero might mean that the bus is shorted, there are
+    // no devices, or you have already retrieved all of them.  It
+    // might be a good idea to check the CRC to make sure you didn't
+    // get garbage.  The order is deterministic. You will always get
+    // the same devices in the same order.
+    uint8_t search(uint8_t *newAddr, bool search_mode = true);
+#endif
+
+#if ONEWIRE_CRC
+    // Compute a Dallas Semiconductor 8 bit CRC, these are used in the
+    // ROM and scratchpad registers.
+    static uint8_t crc8(const uint8_t *addr, uint8_t len);
+
+#if ONEWIRE_CRC16
+    // Compute the 1-Wire CRC16 and compare it against the received CRC.
+    // Example usage (reading a DS2408):
+    //    // Put everything in a buffer so we can compute the CRC easily.
+    //    uint8_t buf[13];
+    //    buf[0] = 0xF0;    // Read PIO Registers
+    //    buf[1] = 0x88;    // LSB address
+    //    buf[2] = 0x00;    // MSB address
+    //    WriteBytes(net, buf, 3);    // Write 3 cmd bytes
+    //    ReadBytes(net, buf+3, 10);  // Read 6 data bytes, 2 0xFF, 2 CRC16
+    //    if (!CheckCRC16(buf, 11, &buf[11])) {
+    //        // Handle error.
+    //    }     
+    //          
+    // @param input - Array of bytes to checksum.
+    // @param len - How many bytes to use.
+    // @param inverted_crc - The two CRC16 bytes in the received data.
+    //                       This should just point into the received data,
+    //                       *not* at a 16-bit integer.
+    // @param crc - The crc starting value (optional)
+    // @return True, iff the CRC matches.
+    static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0);
+
+    // Compute a Dallas Semiconductor 16 bit CRC.  This is required to check
+    // the integrity of data received from many 1-Wire devices.  Note that the
+    // CRC computed here is *not* what you'll get from the 1-Wire network,
+    // for two reasons:
+    //   1) The CRC is transmitted bitwise inverted.
+    //   2) Depending on the endian-ness of your processor, the binary
+    //      representation of the two-byte return value may have a different
+    //      byte order than the two bytes you get from 1-Wire.
+    // @param input - Array of bytes to checksum.
+    // @param len - How many bytes to use.
+    // @param crc - The crc starting value (optional)
+    // @return The CRC16, as defined by Dallas Semiconductor.
+    static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0);
+#endif
+#endif
+};
+
+#endif

          
A => libraries/OneWire/examples/DS18x20_Temperature/DS18x20_Temperature.pde +112 -0
@@ 0,0 1,112 @@ 
+#include <OneWire.h>
+
+// OneWire DS18S20, DS18B20, DS1822 Temperature Example
+//
+// http://www.pjrc.com/teensy/td_libs_OneWire.html
+//
+// The DallasTemperature library can do all this work for you!
+// http://milesburton.com/Dallas_Temperature_Control_Library
+
+OneWire  ds(10);  // on pin 10 (a 4.7K resistor is necessary)
+
+void setup(void) {
+  Serial.begin(9600);
+}
+
+void loop(void) {
+  byte i;
+  byte present = 0;
+  byte type_s;
+  byte data[12];
+  byte addr[8];
+  float celsius, fahrenheit;
+  
+  if ( !ds.search(addr)) {
+    Serial.println("No more addresses.");
+    Serial.println();
+    ds.reset_search();
+    delay(250);
+    return;
+  }
+  
+  Serial.print("ROM =");
+  for( i = 0; i < 8; i++) {
+    Serial.write(' ');
+    Serial.print(addr[i], HEX);
+  }
+
+  if (OneWire::crc8(addr, 7) != addr[7]) {
+      Serial.println("CRC is not valid!");
+      return;
+  }
+  Serial.println();
+ 
+  // the first ROM byte indicates which chip
+  switch (addr[0]) {
+    case 0x10:
+      Serial.println("  Chip = DS18S20");  // or old DS1820
+      type_s = 1;
+      break;
+    case 0x28:
+      Serial.println("  Chip = DS18B20");
+      type_s = 0;
+      break;
+    case 0x22:
+      Serial.println("  Chip = DS1822");
+      type_s = 0;
+      break;
+    default:
+      Serial.println("Device is not a DS18x20 family device.");
+      return;
+  } 
+
+  ds.reset();
+  ds.select(addr);
+  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
+  
+  delay(1000);     // maybe 750ms is enough, maybe not
+  // we might do a ds.depower() here, but the reset will take care of it.
+  
+  present = ds.reset();
+  ds.select(addr);    
+  ds.write(0xBE);         // Read Scratchpad
+
+  Serial.print("  Data = ");
+  Serial.print(present, HEX);
+  Serial.print(" ");
+  for ( i = 0; i < 9; i++) {           // we need 9 bytes
+    data[i] = ds.read();
+    Serial.print(data[i], HEX);
+    Serial.print(" ");
+  }
+  Serial.print(" CRC=");
+  Serial.print(OneWire::crc8(data, 8), HEX);
+  Serial.println();
+
+  // Convert the data to actual temperature
+  // because the result is a 16 bit signed integer, it should
+  // be stored to an "int16_t" type, which is always 16 bits
+  // even when compiled on a 32 bit processor.
+  int16_t raw = (data[1] << 8) | data[0];
+  if (type_s) {
+    raw = raw << 3; // 9 bit resolution default
+    if (data[7] == 0x10) {
+      // "count remain" gives full 12 bit resolution
+      raw = (raw & 0xFFF0) + 12 - data[6];
+    }
+  } else {
+    byte cfg = (data[4] & 0x60);
+    // at lower res, the low bits are undefined, so let's zero them
+    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
+    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
+    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
+    //// default is 12 bit resolution, 750 ms conversion time
+  }
+  celsius = (float)raw / 16.0;
+  fahrenheit = celsius * 1.8 + 32.0;
+  Serial.print("  Temperature = ");
+  Serial.print(celsius);
+  Serial.print(" Celsius, ");
+  Serial.print(fahrenheit);
+  Serial.println(" Fahrenheit");
+}

          
A => libraries/OneWire/examples/DS2408_Switch/DS2408_Switch.pde +77 -0
@@ 0,0 1,77 @@ 
+#include <OneWire.h>
+
+/*
+ * DS2408 8-Channel Addressable Switch
+ *
+ * Writte by Glenn Trewitt, glenn at trewitt dot org
+ *
+ * Some notes about the DS2408:
+ *   - Unlike most input/output ports, the DS2408 doesn't have mode bits to
+ *       set whether the pins are input or output.  If you issue a read command,
+ *       they're inputs.  If you write to them, they're outputs.
+ *   - For reading from a switch, you should use 10K pull-up resisters.
+ */
+
+void PrintBytes(uint8_t* addr, uint8_t count, bool newline=0) {
+  for (uint8_t i = 0; i < count; i++) {
+    Serial.print(addr[i]>>4, HEX);
+    Serial.print(addr[i]&0x0f, HEX);
+  }
+  if (newline)
+    Serial.println();
+}
+
+void ReadAndReport(OneWire* net, uint8_t* addr) {
+  Serial.print("  Reading DS2408 ");
+  PrintBytes(addr, 8);
+  Serial.println();
+	
+  uint8_t buf[13];  // Put everything in the buffer so we can compute CRC easily.
+  buf[0] = 0xF0;    // Read PIO Registers
+  buf[1] = 0x88;    // LSB address
+  buf[2] = 0x00;    // MSB address
+  net->write_bytes(buf, 3);
+  net->read_bytes(buf+3, 10);     // 3 cmd bytes, 6 data bytes, 2 0xFF, 2 CRC16
+  net->reset();
+
+  if (!OneWire::check_crc16(buf, 11, &buf[11])) {
+    Serial.print("CRC failure in DS2408 at ");
+    PrintBytes(addr, 8, true);
+    return;
+  }
+  Serial.print("  DS2408 data = ");
+  // First 3 bytes contain command, register address.
+  Serial.println(buf[3], BIN);
+}
+
+OneWire net(10);  // on pin 10
+
+void setup(void) {
+  Serial.begin(9600);
+}
+
+void loop(void) {
+  byte i;
+  byte present = 0;
+  byte addr[8];
+  
+  if (!net.search(addr)) {
+    Serial.print("No more addresses.\n");
+    net.reset_search();
+    delay(1000);
+    return;
+  }
+  
+  if (OneWire::crc8(addr, 7) != addr[7]) {
+    Serial.print("CRC is not valid!\n");
+    return;
+  }
+  
+  if (addr[0] != 0x29) {
+    PrintBytes(addr, 8);
+    Serial.print(" is not a DS2408.\n");
+    return;
+  }
+
+  ReadAndReport(&net, addr);
+}

          
A => libraries/OneWire/examples/DS250x_PROM/DS250x_PROM.pde +90 -0
@@ 0,0 1,90 @@ 
+/*
+DS250x add-only programmable memory reader w/SKIP ROM.
+ 
+ The DS250x is a 512/1024bit add-only PROM(you can add data but cannot change the old one) that's used mainly for device identification purposes
+ like serial number, mfgr data, unique identifiers, etc. It uses the Maxim 1-wire bus.
+ 
+ This sketch will use the SKIP ROM function that skips the 1-Wire search phase since we only have one device connected in the bus on digital pin 6.
+ If more than one device is connected to the bus, it will fail.
+ Sketch will not verify if device connected is from the DS250x family since the skip rom function effectively skips the family-id byte readout.
+ thus it is possible to run this sketch with any Maxim OneWire device in which case the command CRC will most likely fail.
+ Sketch will only read the first page of memory(32bits) starting from the lower address(0000h), if more than 1 device is present, then use the sketch with search functions.
+ Remember to put a 4.7K pullup resistor between pin 6 and +Vcc
+ 
+ To change the range or ammount of data to read, simply change the data array size, LSB/MSB addresses and for loop iterations
+ 
+ This example code is in the public domain and is provided AS-IS.
+ 
+ Built with Arduino 0022 and PJRC OneWire 2.0 library http://www.pjrc.com/teensy/td_libs_OneWire.html
+ 
+ created by Guillermo Lovato <glovato@gmail.com>
+ march/2011
+ 
+ */
+
+#include <OneWire.h>
+OneWire ds(6);                    // OneWire bus on digital pin 6
+void setup() {
+  Serial.begin (9600);
+}
+
+void loop() {
+  byte i;                         // This is for the for loops
+  boolean present;                // device present var
+  byte data[32];                  // container for the data from device
+  byte leemem[3] = {              // array with the commands to initiate a read, DS250x devices expect 3 bytes to start a read: command,LSB&MSB adresses
+    0xF0 , 0x00 , 0x00   };       // 0xF0 is the Read Data command, followed by 00h 00h as starting address(the beginning, 0000h)
+  byte ccrc;                      // Variable to store the command CRC
+  byte ccrc_calc;
+
+  present = ds.reset();           // OneWire bus reset, always needed to start operation on the bus, returns a 1/TRUE if there's a device present.
+  ds.skip();                      // Skip ROM search
+
+  if (present == TRUE){           // We only try to read the data if there's a device present 
+    Serial.println("DS250x device present");
+    ds.write(leemem[0],1);        // Read data command, leave ghost power on
+    ds.write(leemem[1],1);        // LSB starting address, leave ghost power on
+    ds.write(leemem[2],1);        // MSB starting address, leave ghost power on
+
+    ccrc = ds.read();             // DS250x generates a CRC for the command we sent, we assign a read slot and store it's value
+    ccrc_calc = OneWire::crc8(leemem, 3);  // We calculate the CRC of the commands we sent using the library function and store it
+
+    if ( ccrc_calc != ccrc) {      // Then we compare it to the value the ds250x calculated, if it fails, we print debug messages and abort
+      Serial.println("Invalid command CRC!");
+      Serial.print("Calculated CRC:");
+      Serial.println(ccrc_calc,HEX);    // HEX makes it easier to observe and compare
+      Serial.print("DS250x readback CRC:");
+      Serial.println(ccrc,HEX);
+      return;                      // Since CRC failed, we abort the rest of the loop and start over
+    }
+    Serial.println("Data is: ");   // For the printout of the data 
+    for ( i = 0; i < 32; i++) {    // Now it's time to read the PROM data itself, each page is 32 bytes so we need 32 read commands
+      data[i] = ds.read();         // we store each read byte to a different position in the data array 
+      Serial.print(data[i]);       // printout in ASCII
+      Serial.print(" ");           // blank space 
+    }
+    Serial.println();
+    delay(5000);                    // Delay so we don't saturate the serial output
+  }
+  else {                           // Nothing is connected in the bus 
+    Serial.println("Nothing connected");
+    delay(3000);
+  }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

          
A => libraries/OneWire/keywords.txt +38 -0
@@ 0,0 1,38 @@ 
+#######################################
+# Syntax Coloring Map For OneWire
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+OneWire	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+reset	KEYWORD2
+write_bit	KEYWORD2
+read_bit	KEYWORD2
+write	KEYWORD2
+write_bytes	KEYWORD2
+read	KEYWORD2
+read_bytes	KEYWORD2
+select	KEYWORD2
+skip	KEYWORD2
+depower	KEYWORD2
+reset_search	KEYWORD2
+search	KEYWORD2
+crc8	KEYWORD2
+crc16	KEYWORD2
+check_crc16	KEYWORD2
+
+#######################################
+# Instances (KEYWORD2)
+#######################################
+
+
+#######################################
+# Constants (LITERAL1)
+#######################################

          
A => libraries/OneWire/library.json +58 -0
@@ 0,0 1,58 @@ 
+{
+"name": "OneWire",
+"frameworks": "Arduino",
+"keywords": "onewire, 1-wire, bus, sensor, temperature, ibutton",
+"description": "Control 1-Wire protocol (DS18S20, DS18B20, DS2408 and etc)",
+"authors":
+[
+    {
+        "name": "Paul Stoffregen",
+        "email": "paul@pjrc.com",
+        "url": "http://www.pjrc.com",
+        "maintainer": true
+    },
+    {
+        "name": "Jim Studt"
+    },
+    {
+        "name": "Tom Pollard",
+        "email": "pollard@alum.mit.edu"
+    },
+    {
+        "name": "Derek Yerger"
+    },
+    {
+        "name": "Josh Larios"
+    },
+    {
+        "name": "Robin James"
+    },
+    {
+        "name": "Glenn Trewitt"
+    },
+    {
+        "name": "Jason Dangel",
+        "email": "dangel.jason AT gmail.com"
+    },
+    {
+        "name": "Guillermo Lovato"
+    },
+    {
+        "name": "Ken Butcher"
+    },
+    {
+        "name": "Mark Tillotson"
+    },
+    {
+        "name": "Bertrik Sikken"
+    },
+    {
+        "name": "Scott Roberts"
+    }
+],
+"repository":
+{
+    "type": "git",
+    "url": "https://github.com/PaulStoffregen/OneWire"
+}
+}

          
A => libraries/OneWire/library.properties +10 -0
@@ 0,0 1,10 @@ 
+name=AmebaOneWire
+version=2.3.2
+author=Jim Studt, Tom Pollard, Robin James, Glenn Trewitt, Jason Dangel, Guillermo Lovato, Paul Stoffregen, Scott Roberts, Bertrik Sikken, Mark Tillotson, Ken Butcher, Roger Clark, Love Nystrom
+maintainer=Paul Stoffregen
+sentence=Access 1-wire temperature sensors, memory and other chips.
+paragraph=
+category=Communication
+url=http://www.pjrc.com/teensy/td_libs_OneWire.html
+architectures=*
+

          
A => libraries/README.md +3 -0
@@ 0,0 1,3 @@ 
+This folder contains libraries which are not follow Ameba main SDK version.
+These libraries should compatible with Ameba's each version if possible.
+Each library are tarble separately and put it under this folder.

          
A => libraries/RFID-RC522.zip +0 -0

        
A => libraries/Time-1.0.0.zip +0 -0

        
A => libraries/Time/DateStrings.cpp +97 -0
@@ 0,0 1,97 @@ 
+/* DateStrings.cpp
+ * Definitions for date strings for use with the Time library
+ *
+ * Updated for Arduino 1.5.7 18 July 2014
+ *
+ * No memory is consumed in the sketch if your code does not call any of the string methods
+ * You can change the text of the strings, make sure the short strings are each exactly 3 characters 
+ * the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in TimeLib.h
+ * 
+ */
+
+#if defined(__AVR__)
+#include <avr/pgmspace.h>
+#else
+// for compatiblity with Arduino Due and Teensy 3.0 and maybe others?
+#define PROGMEM
+#define PGM_P  const char *
+#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+#define pgm_read_word(addr) (*(const unsigned char **)(addr))
+#define strcpy_P(dest, src) strcpy((dest), (src))
+#endif
+#include <string.h> // for strcpy_P or strcpy
+#include "TimeLib.h"
+ 
+// the short strings for each day or month must be exactly dt_SHORT_STR_LEN
+#define dt_SHORT_STR_LEN  3 // the length of short strings
+
+static char buffer[dt_MAX_STRING_LEN+1];  // must be big enough for longest string and the terminating null
+
+const char monthStr0[] PROGMEM = "";
+const char monthStr1[] PROGMEM = "January";
+const char monthStr2[] PROGMEM = "February";
+const char monthStr3[] PROGMEM = "March";
+const char monthStr4[] PROGMEM = "April";
+const char monthStr5[] PROGMEM = "May";
+const char monthStr6[] PROGMEM = "June";
+const char monthStr7[] PROGMEM = "July";
+const char monthStr8[] PROGMEM = "August";
+const char monthStr9[] PROGMEM = "September";
+const char monthStr10[] PROGMEM = "October";
+const char monthStr11[] PROGMEM = "November";
+const char monthStr12[] PROGMEM = "December";
+
+const PROGMEM char * const PROGMEM monthNames_P[] =
+{
+    monthStr0,monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6,
+    monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12
+};
+
+const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec";
+
+const char dayStr0[] PROGMEM = "Err";
+const char dayStr1[] PROGMEM = "Sunday";
+const char dayStr2[] PROGMEM = "Monday";
+const char dayStr3[] PROGMEM = "Tuesday";
+const char dayStr4[] PROGMEM = "Wednesday";
+const char dayStr5[] PROGMEM = "Thursday";
+const char dayStr6[] PROGMEM = "Friday";
+const char dayStr7[] PROGMEM = "Saturday";
+
+const PROGMEM char * const PROGMEM dayNames_P[] =
+{
+   dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7
+};
+
+const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat";
+
+/* functions to return date strings */
+
+char* monthStr(uint8_t month)
+{
+    strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month])));
+    return buffer;
+}
+
+char* monthShortStr(uint8_t month)
+{
+   for (int i=0; i < dt_SHORT_STR_LEN; i++)      
+      buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)]));  
+   buffer[dt_SHORT_STR_LEN] = 0;
+   return buffer;
+}
+
+char* dayStr(uint8_t day) 
+{
+   strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day])));
+   return buffer;
+}
+
+char* dayShortStr(uint8_t day) 
+{
+   uint8_t index = day*dt_SHORT_STR_LEN;
+   for (int i=0; i < dt_SHORT_STR_LEN; i++)      
+      buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i]));  
+   buffer[dt_SHORT_STR_LEN] = 0; 
+   return buffer;
+}

          
A => libraries/Time/LICENSE.txt +1 -0
@@ 0,0 1,1 @@ 
+The Time library is forked from works at "http://www.pjrc.com/teensy/td_libs_Time.html". The source codes under folder "Time" inherit the license from any license claim of "http://www.pjrc.com/teensy/td_libs_Time.html".
  No newline at end of file

          
A => libraries/Time/Readme.txt +131 -0
@@ 0,0 1,131 @@ 
+Readme file for Arduino Time Library
+
+Time is a library that provides timekeeping functionality for Arduino.
+
+The code is derived from the Playground DateTime library but is updated
+to provide an API that is more flexable and easier to use.
+
+A primary goal was to enable date and time functionality that can be used with
+a variety of external time sources with minimum differences required in sketch logic.
+
+Example sketches illustrate how similar sketch code can be used with: a Real Time Clock,
+internet NTP time service, GPS time data, and Serial time messages from a computer
+for time synchronization.
+
+The functions available in the library include:
+
+hour();            // the hour now  (0-23)
+minute();          // the minute now (0-59)          
+second();          // the second now (0-59) 
+day();             // the day now (1-31)
+weekday();         // day of the week, Sunday is day 0 
+month();           // the month now (1-12)
+year();            // the full four digit year: (2009, 2010 etc) 
+
+there are also functions to return the hour in 12 hour format
+hourFormat12();    // the hour now in 12 hour format
+isAM();            // returns true if time now is AM 
+isPM();            // returns true if time now is PM
+
+now();             // returns the current time as seconds since Jan 1 1970 
+
+The time and date functions can take an optional parameter for the time. This prevents
+errors if the time rolls over between elements. For example, if a new minute begins
+between getting the minute and second, the values will be inconsistent. Using the 
+following functions eliminates this probglem 
+  time_t t = now(); // store the current time in time variable t 
+  hour(t);          // returns the hour for the given time t
+  minute(t);        // returns the minute for the given time t
+  second(t);        // returns the second for the given time t 
+  day(t);           // the day for the given time t 
+  weekday(t);       // day of the week for the given time t  
+  month(t);         // the month for the given time t 
+  year(t);          // the year for the given time t  
+  
+  
+Functions for managing the timer services are:  
+setTime(t);             // set the system time to the give time t
+setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr (2010 or 10 sets year to 2010)
+adjustTime(adjustment); // adjust system time by adding the adjustment value
+
+timeStatus();       // indicates if time has been set and recently synchronized
+                    // returns one of the following enumerations:
+    timeNotSet      // the time has never been set, the clock started at Jan 1 1970
+    timeNeedsSync   // the time had been set but a sync attempt did not succeed
+    timeSet         // the time is set and is synced
+Time and Date values are not valid if the status is timeNotSet. Otherwise values can be used but 
+the returned time may have drifted if the status is timeNeedsSync. 	
+
+setSyncProvider(getTimeFunction);  // set the external time provider
+setSyncInterval(interval);         // set the number of seconds between re-sync
+
+
+There are many convenience macros in the time.h file for time constants and conversion of time units.
+
+To use the library, copy the download to the Library directory.
+
+The Time directory contains the Time library and some example sketches
+illustrating how the library can be used with various time sources:
+
+- TimeSerial.pde shows Arduino as a clock without external hardware.
+  It is synchronized by time messages sent over the serial port.
+  A companion Processing sketch will automatically provide these messages
+  if it is running and connected to the Arduino serial port. 
+
+- TimeSerialDateStrings.pde adds day and month name strings to the sketch above
+  Short (3 character) and long strings are available to print the days of 
+  the week and names of the months. 
+  
+- TimeRTC uses a DS1307 real time clock to provide time synchronization.
+  A basic RTC library named DS1307RTC is included in the download.
+  To run this sketch the DS1307RTC library must be installed.
+
+- TimeRTCSet is similar to the above and adds the ability to set the Real Time Clock 
+
+- TimeRTCLog demonstrates how to calculate the difference between times. 
+  It is a vary simple logger application that monitors events on digtial pins
+  and prints (to the serial port) the time of an event and the time period since the previous event.
+  
+- TimeNTP uses the Arduino Ethernet shield to access time using the internet NTP time service.
+  The NTP protocol uses UDP and the UdpBytewise library is required, see:
+  http://bitbucket.org/bjoern/arduino_osc/src/14667490521f/libraries/Ethernet/
+
+- TimeGPS gets time from a GPS
+  This requires the TinyGPS library from Mikal Hart:
+  http://arduiniana.org/libraries/TinyGPS
+
+Differences between this code and the playground DateTime library
+although the Time library is based on the DateTime codebase, the API has changed.
+Changes in the Time library API:
+- time elements are functions returning int (they are variables in DateTime)
+- Years start from 1970 
+- days of the week and months start from 1 (they start from 0 in DateTime)
+- DateStrings do not require a seperate library
+- time elements can be accessed non-atomically (in DateTime they are always atomic)
+- function added to automatically sync time with extrnal source
+- localTime and maketime parameters changed, localTime renamed to breakTime
+ 
+Technical notes:
+
+Internal system time is based on the standard Unix time_t.
+The value is the number of seconds since Jan 1 1970.
+System time begins at zero when the sketch starts.
+  
+The internal time can be automatically synchronized at regular intervals to an external time source.
+This is enabled by calling the setSyncProvider(provider) function - the provider argument is
+the address of a function that returns the current time as a time_t.
+See the sketches in the examples directory for usage.
+
+The default interval for re-syncing the time is 5 minutes but can be changed by calling the 
+setSyncInterval( interval) method to set the number of seconds between re-sync attempts.
+
+The Time library defines a structure for holding time elements that is a compact version of the  C tm structure.
+All the members of the Arduino tm structure are bytes and the year is offset from 1970.
+Convenience macros provide conversion to and from the Arduino format.
+
+Low level functions to convert between system time and individual time elements are provided:                    
+  breakTime( time, &tm);  // break time_t into elements stored in tm struct
+  makeTime( &tm);  // return time_t  from elements stored in tm struct 
+
+The DS1307RTC library included in the download provides an example of how a time provider
+can use the low level functions to interface with the Time library.

          
A => libraries/Time/Time.cpp +321 -0
@@ 0,0 1,321 @@ 
+/*
+  time.c - low level time and date functions
+  Copyright (c) Michael Margolis 2009-2014
+
+  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; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  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
+  
+  1.0  6  Jan 2010 - initial release
+  1.1  12 Feb 2010 - fixed leap year calculation error
+  1.2  1  Nov 2010 - fixed setTime bug (thanks to Korman for this)
+  1.3  24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update
+                     status, updated examples for Arduino 1.0, fixed ARM
+                     compatibility issues, added TimeArduinoDue and TimeTeensy3
+                     examples, add error checking and messages to RTC examples,
+                     add examples to DS1307RTC library.
+  1.4  5  Sep 2014 - compatibility with Arduino 1.5.7
+*/
+
+#if ARDUINO >= 100
+#include <Arduino.h> 
+#else
+#include <WProgram.h> 
+#endif
+
+#include "TimeLib.h"
+
+static tmElements_t tm;          // a cache of time elements
+static time_t cacheTime;   // the time the cache was updated
+static uint32_t syncInterval = 300;  // time sync will be attempted after this many seconds
+
+void refreshCache(time_t t) {
+  if (t != cacheTime) {
+    breakTime(t, tm); 
+    cacheTime = t; 
+  }
+}
+
+int hour() { // the hour now 
+  return hour(now()); 
+}
+
+int hour(time_t t) { // the hour for the given time
+  refreshCache(t);
+  return tm.Hour;  
+}
+
+int hourFormat12() { // the hour now in 12 hour format
+  return hourFormat12(now()); 
+}
+
+int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
+  refreshCache(t);
+  if( tm.Hour == 0 )
+    return 12; // 12 midnight
+  else if( tm.Hour  > 12)
+    return tm.Hour - 12 ;
+  else
+    return tm.Hour ;
+}
+
+uint8_t isAM() { // returns true if time now is AM
+  return !isPM(now()); 
+}
+
+uint8_t isAM(time_t t) { // returns true if given time is AM
+  return !isPM(t);  
+}
+
+uint8_t isPM() { // returns true if PM
+  return isPM(now()); 
+}
+
+uint8_t isPM(time_t t) { // returns true if PM
+  return (hour(t) >= 12); 
+}
+
+int minute() {
+  return minute(now()); 
+}
+
+int minute(time_t t) { // the minute for the given time
+  refreshCache(t);
+  return tm.Minute;  
+}
+
+int second() {
+  return second(now()); 
+}
+
+int second(time_t t) {  // the second for the given time
+  refreshCache(t);
+  return tm.Second;
+}
+
+int day(){
+  return(day(now())); 
+}
+
+int day(time_t t) { // the day for the given time (0-6)
+  refreshCache(t);
+  return tm.Day;
+}
+
+int weekday() {   // Sunday is day 1
+  return  weekday(now()); 
+}
+
+int weekday(time_t t) {
+  refreshCache(t);
+  return tm.Wday;
+}
+   
+int month(){
+  return month(now()); 
+}
+
+int month(time_t t) {  // the month for the given time
+  refreshCache(t);
+  return tm.Month;
+}
+
+int year() {  // as in Processing, the full four digit year: (2009, 2010 etc) 
+  return year(now()); 
+}
+
+int year(time_t t) { // the year for the given time
+  refreshCache(t);
+  return tmYearToCalendar(tm.Year);
+}
+
+/*============================================================================*/	
+/* functions to convert to and from system time */
+/* These are for interfacing with time serivces and are not normally needed in a sketch */
+
+// leap year calulator expects year argument as years offset from 1970
+#define LEAP_YEAR(Y)     ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
+
+static  const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
+ 
+void breakTime(time_t timeInput, tmElements_t &tm){
+// break the given time_t into time components
+// this is a more compact version of the C library localtime function
+// note that year is offset from 1970 !!!
+
+  uint8_t year;
+  uint8_t month, monthLength;
+  uint32_t time;
+  unsigned long days;
+
+  time = (uint32_t)timeInput;
+  tm.Second = time % 60;
+  time /= 60; // now it is minutes
+  tm.Minute = time % 60;
+  time /= 60; // now it is hours
+  tm.Hour = time % 24;
+  time /= 24; // now it is days
+  tm.Wday = ((time + 4) % 7) + 1;  // Sunday is day 1 
+  
+  year = 0;  
+  days = 0;
+  while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
+    year++;
+  }
+  tm.Year = year; // year is offset from 1970 
+  
+  days -= LEAP_YEAR(year) ? 366 : 365;
+  time  -= days; // now it is days in this year, starting at 0
+  
+  days=0;
+  month=0;
+  monthLength=0;
+  for (month=0; month<12; month++) {
+    if (month==1) { // february
+      if (LEAP_YEAR(year)) {
+        monthLength=29;
+      } else {
+        monthLength=28;
+      }
+    } else {
+      monthLength = monthDays[month];
+    }
+    
+    if (time >= monthLength) {
+      time -= monthLength;
+    } else {
+        break;
+    }
+  }
+  tm.Month = month + 1;  // jan is month 1  
+  tm.Day = time + 1;     // day of month
+}
+
+time_t makeTime(tmElements_t &tm){   
+// assemble time elements into time_t 
+// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
+// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
+  
+  int i;
+  uint32_t seconds;
+
+  // seconds from 1970 till 1 jan 00:00:00 of the given year
+  seconds= tm.Year*(SECS_PER_DAY * 365);
+  for (i = 0; i < tm.Year; i++) {
+    if (LEAP_YEAR(i)) {
+      seconds +=  SECS_PER_DAY;   // add extra days for leap years
+    }
+  }
+  
+  // add days for this year, months start from 1
+  for (i = 1; i < tm.Month; i++) {
+    if ( (i == 2) && LEAP_YEAR(tm.Year)) { 
+      seconds += SECS_PER_DAY * 29;
+    } else {
+      seconds += SECS_PER_DAY * monthDays[i-1];  //monthDay array starts from 0
+    }
+  }
+  seconds+= (tm.Day-1) * SECS_PER_DAY;
+  seconds+= tm.Hour * SECS_PER_HOUR;
+  seconds+= tm.Minute * SECS_PER_MIN;
+  seconds+= tm.Second;
+  return (time_t)seconds; 
+}
+/*=====================================================*/	
+/* Low level system time functions  */
+
+static uint32_t sysTime = 0;
+static uint32_t prevMillis = 0;
+static uint32_t nextSyncTime = 0;
+static timeStatus_t Status = timeNotSet;
+
+getExternalTime getTimePtr;  // pointer to external sync function
+//setExternalTime setTimePtr; // not used in this version
+
+#ifdef TIME_DRIFT_INFO   // define this to get drift data
+time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync  
+#endif
+
+
+time_t now() {
+	// calculate number of seconds passed since last call to now()
+  while (millis() - prevMillis >= 1000) {
+		// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
+    sysTime++;
+    prevMillis += 1000;	
+#ifdef TIME_DRIFT_INFO
+    sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift     
+#endif
+  }
+  if (nextSyncTime <= sysTime) {
+    if (getTimePtr != 0) {
+      time_t t = getTimePtr();
+      if (t != 0) {
+        setTime(t);
+      } else {
+        nextSyncTime = sysTime + syncInterval;
+        Status = (Status == timeNotSet) ?  timeNotSet : timeNeedsSync;
+      }
+    }
+  }  
+  return (time_t)sysTime;
+}
+
+void setTime(time_t t) { 
+#ifdef TIME_DRIFT_INFO
+ if(sysUnsyncedTime == 0) 
+   sysUnsyncedTime = t;   // store the time of the first call to set a valid Time   
+#endif
+
+  sysTime = (uint32_t)t;  
+  nextSyncTime = (uint32_t)t + syncInterval;
+  Status = timeSet;
+  prevMillis = millis();  // restart counting from now (thanks to Korman for this fix)
+} 
+
+void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
+ // year can be given as full four digit year or two digts (2010 or 10 for 2010);  
+ //it is converted to years since 1970
+  if( yr > 99)
+      yr = yr - 1970;
+  else
+      yr += 30;  
+  tm.Year = yr;
+  tm.Month = mnth;
+  tm.Day = dy;
+  tm.Hour = hr;
+  tm.Minute = min;
+  tm.Second = sec;
+  setTime(makeTime(tm));
+}
+
+void adjustTime(long adjustment) {
+  sysTime += adjustment;
+}
+
+// indicates if time has been set and recently synchronized
+timeStatus_t timeStatus() {
+  now(); // required to actually update the status
+  return Status;
+}
+
+void setSyncProvider( getExternalTime getTimeFunction){
+  getTimePtr = getTimeFunction;  
+  nextSyncTime = sysTime;
+  now(); // this will sync the clock
+}
+
+void setSyncInterval(time_t interval){ // set the number of seconds between re-sync
+  syncInterval = (uint32_t)interval;
+  nextSyncTime = sysTime + syncInterval;
+}

          
A => libraries/Time/Time.h +1 -0
@@ 0,0 1,1 @@ 
+#include "TimeLib.h"

          
A => libraries/Time/TimeLib.h +144 -0
@@ 0,0 1,144 @@ 
+/*
+  time.h - low level time and date functions
+*/
+
+/*
+  July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this)
+              - fixed  daysToTime_t macro (thanks maniacbug)
+*/     
+
+#ifndef _Time_h
+#ifdef __cplusplus
+#define _Time_h
+
+#include <inttypes.h>
+#ifndef __AVR__
+#include <sys/types.h> // for __time_t_defined, but avr libc lacks sys/types.h
+#endif
+
+
+#if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc
+typedef unsigned long time_t;
+#endif
+
+
+// This ugly hack allows us to define C++ overloaded functions, when included
+// from within an extern "C", as newlib's sys/stat.h does.  Actually it is
+// intended to include "time.h" from the C library (on ARM, but AVR does not
+// have that file at all).  On Mac and Windows, the compiler will find this
+// "Time.h" instead of the C library "time.h", so we may cause other weird
+// and unpredictable effects by conflicting with the C library header "time.h",
+// but at least this hack lets us define C++ functions as intended.  Hopefully
+// nothing too terrible will result from overriding the C library header?!
+extern "C++" {
+typedef enum {timeNotSet, timeNeedsSync, timeSet
+}  timeStatus_t ;
+
+typedef enum {
+    dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
+} timeDayOfWeek_t;
+
+typedef enum {
+    tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields
+} tmByteFields;	   
+
+typedef struct  { 
+  uint8_t Second; 
+  uint8_t Minute; 
+  uint8_t Hour; 
+  uint8_t Wday;   // day of week, sunday is day 1
+  uint8_t Day;
+  uint8_t Month; 
+  uint8_t Year;   // offset from 1970; 
+} 	tmElements_t, TimeElements, *tmElementsPtr_t;
+
+//convenience macros to convert to and from tm years 
+#define  tmYearToCalendar(Y) ((Y) + 1970)  // full four digit year 
+#define  CalendarYrToTm(Y)   ((Y) - 1970)
+#define  tmYearToY2k(Y)      ((Y) - 30)    // offset is from 2000
+#define  y2kYearToTm(Y)      ((Y) + 30)   
+
+typedef time_t(*getExternalTime)();
+//typedef void  (*setExternalTime)(const time_t); // not used in this version
+
+
+/*==============================================================================*/
+/* Useful Constants */
+#define SECS_PER_MIN  (60UL)
+#define SECS_PER_HOUR (3600UL)
+#define SECS_PER_DAY  (SECS_PER_HOUR * 24UL)
+#define DAYS_PER_WEEK (7UL)
+#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
+#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
+#define SECS_YR_2000  (946684800UL) // the time at the start of y2k
+ 
+/* Useful Macros for getting elapsed time */
+#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)  
+#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 
+#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
+#define dayOfWeek(_time_)  ((( _time_ / SECS_PER_DAY + 4)  % DAYS_PER_WEEK)+1) // 1 = Sunday
+#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)  // this is number of days since Jan 1 1970
+#define elapsedSecsToday(_time_)  (_time_ % SECS_PER_DAY)   // the number of seconds since last midnight 
+// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971
+// Always set the correct time before settting alarms
+#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY)  // time at the start of the given day
+#define nextMidnight(_time_) ( previousMidnight(_time_)  + SECS_PER_DAY )   // time at the end of the given day 
+#define elapsedSecsThisWeek(_time_)  (elapsedSecsToday(_time_) +  ((dayOfWeek(_time_)-1) * SECS_PER_DAY) )   // note that week starts on day 1
+#define previousSunday(_time_)  (_time_ - elapsedSecsThisWeek(_time_))      // time at the start of the week for the given time
+#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK)          // time at the end of the week for the given time
+
+
+/* Useful Macros for converting elapsed time to a time_t */
+#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)  
+#define hoursToTime_t   ((H)) ( (H) * SECS_PER_HOUR)  
+#define daysToTime_t    ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011
+#define weeksToTime_t   ((W)) ( (W) * SECS_PER_WEEK)   
+
+/*============================================================================*/
+/*  time and date functions   */
+int     hour();            // the hour now 
+int     hour(time_t t);    // the hour for the given time
+int     hourFormat12();    // the hour now in 12 hour format
+int     hourFormat12(time_t t); // the hour for the given time in 12 hour format
+uint8_t isAM();            // returns true if time now is AM
+uint8_t isAM(time_t t);    // returns true the given time is AM
+uint8_t isPM();            // returns true if time now is PM
+uint8_t isPM(time_t t);    // returns true the given time is PM
+int     minute();          // the minute now 
+int     minute(time_t t);  // the minute for the given time
+int     second();          // the second now 
+int     second(time_t t);  // the second for the given time
+int     day();             // the day now 
+int     day(time_t t);     // the day for the given time
+int     weekday();         // the weekday now (Sunday is day 1) 
+int     weekday(time_t t); // the weekday for the given time 
+int     month();           // the month now  (Jan is month 1)
+int     month(time_t t);   // the month for the given time
+int     year();            // the full four digit year: (2009, 2010 etc) 
+int     year(time_t t);    // the year for the given time
+
+time_t now();              // return the current time as seconds since Jan 1 1970 
+void    setTime(time_t t);
+void    setTime(int hr,int min,int sec,int day, int month, int yr);
+void    adjustTime(long adjustment);
+
+/* date strings */ 
+#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
+char* monthStr(uint8_t month);
+char* dayStr(uint8_t day);
+char* monthShortStr(uint8_t month);
+char* dayShortStr(uint8_t day);
+	
+/* time sync functions	*/
+timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
+void    setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
+void    setSyncInterval(time_t interval); // set the number of seconds between re-sync
+
+/* low level functions to convert to and from system time                     */
+void breakTime(time_t time, tmElements_t &tm);  // break time_t into elements
+time_t makeTime(tmElements_t &tm);  // convert time elements into time_t
+
+} // extern "C++"
+#endif // __cplusplus
+#endif /* _Time_h */
+

          
A => libraries/Time/examples/TimeNTP/TimeNTP.ino +129 -0
@@ 0,0 1,129 @@ 
+/*
+ * Time_NTP.pde
+ * Example showing time sync to NTP time source
+ *
+ * 2014/04/25 refined by RTK
+ */
+ 
+#include <TimeLib.h>
+#include <WiFi.h>
+#include <WiFiUdp.h>
+
+int status = WL_IDLE_STATUS;
+char ssid[] = "yourNetwork";          // your network SSID (name)
+char pass[] = "secretPassword";       // your network password
+
+// NTP Servers:
+IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
+
+const int timeZone = 8;     // Beijing Time, Taipei Time
+//const int timeZone = 1;   // Central European Time
+//const int timeZone = -5;  // Eastern Standard Time (USA)
+//const int timeZone = -4;  // Eastern Daylight Time (USA)
+//const int timeZone = -8;  // Pacific Standard Time (USA)
+//const int timeZone = -7;  // Pacific Daylight Time (USA)
+
+// A UDP instance to let us send and receive packets over UDP
+WiFiUDP Udp;
+unsigned int localPort = 2390;  // local port to listen for UDP packets
+
+void setup() 
+{
+  Serial.println("TimeNTP Example");
+  while (status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
+    status = WiFi.begin(ssid, pass);
+    if (status == WL_CONNECTED) break;
+    // wait 1 seconds for retry
+    delay(1000);
+  }
+
+  Udp.begin(localPort);
+  Serial.println("waiting for sync");
+  setSyncProvider(getNtpTime);
+}
+
+time_t prevDisplay = 0; // when the digital clock was displayed
+
+void loop()
+{  
+  if (timeStatus() != timeNotSet) {
+    if (now() != prevDisplay) { //update the display only if time has changed
+      prevDisplay = now();
+      digitalClockDisplay();  
+    }
+  }
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits){
+  // utility for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
+/*-------- NTP code ----------*/
+
+const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
+byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
+
+time_t getNtpTime()
+{
+  Serial.println("Transmit NTP Request");
+  Udp.setRecvTimeout(1500);
+  sendNTPpacket(timeServer);
+  if ( Udp.read(packetBuffer, NTP_PACKET_SIZE) > 0 ) {
+      unsigned long secsSince1900;
+      // convert four bytes starting at location 40 to a long integer
+      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
+      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
+      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
+      secsSince1900 |= (unsigned long)packetBuffer[43];
+      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
+  } else {
+    Serial.println("No NTP Response :-(");
+    return 0; // return 0 if unable to get the time
+  }
+}
+
+// send an NTP request to the time server at the given address
+void sendNTPpacket(IPAddress &address)
+{
+  // set all bytes in the buffer to 0
+  memset(packetBuffer, 0, NTP_PACKET_SIZE);
+  // Initialize values needed to form NTP request
+  // (see URL above for details on the packets)
+  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
+  packetBuffer[1] = 0;     // Stratum, or type of clock
+  packetBuffer[2] = 6;     // Polling Interval
+  packetBuffer[3] = 0xEC;  // Peer Clock Precision
+  // 8 bytes of zero for Root Delay & Root Dispersion
+  packetBuffer[12]  = 49;
+  packetBuffer[13]  = 0x4E;
+  packetBuffer[14]  = 49;
+  packetBuffer[15]  = 52;
+  // all NTP fields have been given values, now
+  // you can send a packet requesting a timestamp:                 
+  Udp.beginPacket(address, 123); //NTP requests are to port 123
+  Udp.write(packetBuffer, NTP_PACKET_SIZE);
+  Udp.endPacket();
+}

          
A => libraries/Time/examples/TimeRTC/TimeRTC.ino +55 -0
@@ 0,0 1,55 @@ 
+/*
+ * TimeRTC.pde
+ * example code illustrating Time library with Real Time Clock.
+ * 
+ */
+
+#include <TimeLib.h>
+#include <Wire.h>
+#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
+
+void setup()  {
+  Serial.begin(9600);
+  while (!Serial) ; // wait until Arduino Serial Monitor opens
+  setSyncProvider(RTC.get);   // the function to get the time from the RTC
+  if(timeStatus()!= timeSet) 
+     Serial.println("Unable to sync with the RTC");
+  else
+     Serial.println("RTC has set the system time");      
+}
+
+void loop()
+{
+  if (timeStatus() == timeSet) {
+    digitalClockDisplay();
+  } else {
+    Serial.println("The time has not been set.  Please run the Time");
+    Serial.println("TimeRTCSet example, or DS1307RTC SetTime example.");
+    Serial.println();
+    delay(4000);
+  }
+  delay(1000);
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits){
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+

          
A => libraries/Time/keywords.txt +34 -0
@@ 0,0 1,34 @@ 
+#######################################
+# Syntax Coloring Map For Time
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+time_t	KEYWORD1
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+now	KEYWORD2
+second	KEYWORD2
+minute	KEYWORD2
+hour	KEYWORD2
+day	KEYWORD2
+month	KEYWORD2
+year	KEYWORD2
+isAM	KEYWORD2
+isPM	KEYWORD2
+weekday	KEYWORD2
+setTime	KEYWORD2
+adjustTime	KEYWORD2
+setSyncProvider	KEYWORD2
+setSyncInterval	KEYWORD2
+timeStatus	KEYWORD2
+TimeLib	KEYWORD2
+#######################################
+# Instances (KEYWORD2)
+#######################################
+
+#######################################
+# Constants (LITERAL1)
+#######################################

          
A => libraries/Time/library.json +22 -0
@@ 0,0 1,22 @@ 
+{
+"name": "Time",
+"frameworks": "Arduino",
+"keywords": "Time, date, hour, minute, second, day, week, month, year, RTC",
+"description": "Time keeping library",
+"url": "http://playground.arduino.cc/Code/Time",
+"authors":
+[
+{
+        "name": "Michael Margolis"
+},
+{
+        "name": "Paul Stoffregen"
+}
+],
+"repository":
+{
+    "type": "git",
+    "url": "https://github.com/PaulStoffregen/Time"
+}
+}
+

          
A => libraries/Time/library.properties +10 -0
@@ 0,0 1,10 @@ 
+name=AmebaTime
+version=1.5
+author=Realtek
+maintainer=Realtek <ameba.arduino@gmail.com>
+sentence=Timekeeping functionality for Arduino
+paragraph=Date and Time functions, with provisions to synchronize to external time sources like GPS and NTP (Internet).  This library is often used together with TimeAlarms and DS1307RTC.
+category=Timing
+url=http://playground.arduino.cc/code/time
+architectures=*
+