EDC Lab 5
EDC Lab 5
Background
Component Introduction
The InvenSense GY-521 sensor contains a MEMS accelerometer and a MEMS gyro in a
single chip. It is very accurate, as it contains 16-bits analog to digital conversion hardwarefor
each channel. Therefore, it captures the x, y, and z channel at the same time. The sensor
uses the I2C-bus to interface with the Arduino.
IMU sensors are one of the most inevitable type of sensors used today in all kinds of
electronic gadgets. They are seen in smart phones, wearables, game controllers, etc. IMU
sensors help us in getting the attitude of an object, attached to the sensor in three-
dimensional space. These values usually in angles, thus help us to determine its attitude.
Thus, they are used in smart phones to detect its orientation. And also, in wearable gadgets
like the Nike fuel band or fit bit, which use IMU sensors to trackmovement. How does it
work?
The Gyroscope:
The gyroscope sensor within the MEMS is tiny (between 1 to 100 micrometers, the size of a
human hair). When the gyro is rotated, a small resonating mass is shifted as the angular
velocity changes. This movement is converted into very low-current electrical signals that
can be amplified and read by a host microcontroller.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 1 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
The Accelerometer:
An accelerometer works on the principle of piezo electric effect. Here, imagine acuboidal
box, having a small ball inside it, like in the picture above. The walls of this box are made
with piezo electric crystals. Whenever you tilt the box, the ball is forced to move in the
direction of the inclination, due to gravity. The wall with which the ball collides, creates tiny
piezo electric currents. There are totally, three pairs of opposite walls in a cuboid. Each pair
corresponds to an axis in 3D space: X, Y and Z axes. Depending on the current produced
from the piezo electric walls, we can determine the direction of inclination and its magnitude
for more information check this
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 2 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
Schematic Diagram
The wiring for this project is shown below, we will make a few minor adjustments as we go,
but this is where we start.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 3 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
Code
To start out, we will connect AD0 to 3.3V which will set the address to 0x69. We’ll be
including the Wire.h I²C communication library. The code below attempts to communicate
with the MPU.The ESP in configured by default to have the SDA line on pin D2 and the SCL
line on pin D1.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 4 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
void setup() {
Wire.begin();
Serial.begin(115200);
}
void loop() {
byte error;
// We are using the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(MPU_addr);
error = Wire.endTransmission();
if (error == 0)
{
Serial.println("Device Found");
}
else
{
Serial.println("No Device Found");
}
delay(5000); // Wait 5 seconds and scan again
}
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 5 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
When the MPU is powered on it initially is in sleep mode. To take our measurements we will
need to wake up the MPU. To wake up the MPU we need to write to the PWR_MGMT_1
(0x6B) register. This register configures both the power mode and the clock source. It also
can disable the temperature sensor and reset the entire device. Initially we will set this entire
register to 0. This will wake up the MPU, keep the Temperature sensor on and use it’s
internal clock.
Now we should be able to read the accelerometer, gyro and temperature signals. The way
that we do this is by starting a transmission, then writing the starting register, then
requesting how many bytes we want to read, then reading them. All of the measurements we
are interested in are 16 bit numbers, meaning they take up two registers and we need to
combine them when we read the values.
In [ ]: #include "Wire.h"
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
void setup() {
Wire.begin();
Serial.begin(115200);
Wire.beginTransmission(MPU_addr);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
}
void loop() {
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers
AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT
AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT
AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT
Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L
GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L
GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L
GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L
Serial.print("AcX = "); Serial.print(AcX);
Serial.print(" | AcY = "); Serial.print(AcY);
Serial.print(" | AcZ = "); Serial.print(AcZ);
Serial.print(" | Tmp = "); Serial.print(Tmp);
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 6 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
if (error == 0)
{
Serial.print(" Device Found at 0x");
Serial.println(addr,HEX);
}
else
{
Serial.print(" No Device Found at 0x");
Serial.println(addr,HEX);
}
return error;
}
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 7 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
The output that I produced is shown below, in this instance my MPU is mounted such that
the X axis is vertical. I did move the board around, to make sure the values changed, but
these are definitely not in very easy to use units. We’ll fix this soon.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 8 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
So, we’ve got a big list of numbers and we can make them change, but it would be much
more useful to have real numbers that are easy to use in the real world. Lets see if we can
make that happen.
I’ve started by cleaning up our previous code — I put all of our 16-bit readings into a single
struct and wrote functions for initializing the MPU and for taking the raw data values. I also
have another struct for our float readings (the real world values). I’ve also noticed, after a
read of the register map, that the gyro and accelerometer readings are 16 bti 2’s compliment,
which means they should be regular ints, not unsigned ints!
In [ ]: #include "Wire.h"
struct rawdata {
int16_t AcX;
int16_t AcY;
int16_t AcZ;
int16_t Tmp;
int16_t GyX;
int16_t GyY;
int16_t GyZ;
};
struct scaleddata{
float AcX;
float AcY;
float AcZ;
float Tmp;
float GyX;
float GyY;
float GyZ;
};
void setup() {
Wire.begin();
Serial.begin(115200);
mpu6050Begin(MPU_addr);
}
void loop() {
mpu6050Read(MPU_addr, true);
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 9 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
mpu6050Read(MPU_addr, true);
if (Wire.endTransmission() == 0)
{
Serial.print(" Device Found at 0x");
Serial.println(addr,HEX);
return true;
}
else
{
Serial.print(" No Device Found at 0x");
Serial.println(addr,HEX);
return false;
}
}
rawdata values;
Wire.beginTransmission(addr);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(addr,14,true); // request a total of 14 registers
values.AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (
values.AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 10 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
if(Debug){
Serial.print(" GyX = "); Serial.print(values.GyX);
Serial.print(" | GyY = "); Serial.print(values.GyY);
Serial.print(" | GyZ = "); Serial.print(values.GyZ);
Serial.print(" | Tmp = "); Serial.print(values.Tmp);
Serial.print(" | AcX = "); Serial.print(values.AcX);
Serial.print(" | AcY = "); Serial.print(values.AcY);
Serial.print(" | AcZ = "); Serial.println(values.AcZ);
}
return values;
}
Now that we’ve successfully duplicated our previous code, lets look at sorting the real world
values. The gyroscope has four possible full-scale ranges [±250, ±500, ±1000, ±2000 °/s]
and the accelerometer also has four possible full-scale ranges [±2, ±4, ±8, ±16 g]. The
Register map gives us all the appropriate scaling factors and the equation to convert the
temperature.
I’ve added in some functionality to set and read the the registers for the full-scale range
settings, as well as including the function to convert the raw values to scaled values.
In [ ]: #include "Wire.h"
struct rawdata {
int16_t AcX;
int16_t AcY;
int16_t AcZ;
int16_t Tmp;
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 11 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
int16_t Tmp;
int16_t GyX;
int16_t GyY;
int16_t GyZ;
};
struct scaleddata{
float AcX;
float AcY;
float AcZ;
float Tmp;
float GyX;
float GyY;
float GyZ;
};
void setup() {
Wire.begin();
Serial.begin(115200);
mpu6050Begin(MPU_addr);
}
void loop() {
rawdata next_sample;
setMPU6050scales(MPU_addr,0b00000000,0b00010000);
next_sample = mpu6050Read(MPU_addr, true);
convertRawToScaled(MPU_addr, next_sample,true);
if (Wire.endTransmission() == 0)
{
Serial.print(" Device Found at 0x");
Serial.println(addr,HEX);
return true;
}
else
{
Serial.print(" No Device Found at 0x");
Serial.println(addr,HEX);
return false;
}
}
rawdata values;
Wire.beginTransmission(addr);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(addr,14,true); // request a total of 14 registers
values.AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (
values.AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (
values.AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (
values.Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_
values.GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO
values.GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO
values.GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO
if(Debug){
Serial.print(" GyX = "); Serial.print(values.GyX);
Serial.print(" | GyY = "); Serial.print(values.GyY);
Serial.print(" | GyZ = "); Serial.print(values.GyZ);
Serial.print(" | Tmp = "); Serial.print(values.Tmp);
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 13 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
return values;
}
scaleddata values;
float scale_value = 0.0;
byte Gyro, Accl;
if(Debug){
Serial.print("Gyro Full-Scale = ");
}
switch (Gyro){
case 0:
scale_value = MPU_GYRO_250_SCALE;
if(Debug){
Serial.println("±250 °/s");
}
break;
case 1:
scale_value = MPU_GYRO_500_SCALE;
if(Debug){
Serial.println("±500 °/s");
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 14 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
Serial.println("±500 °/s");
}
break;
case 2:
scale_value = MPU_GYRO_1000_SCALE;
if(Debug){
Serial.println("±1000 °/s");
}
break;
case 3:
scale_value = MPU_GYRO_2000_SCALE;
if(Debug){
Serial.println("±2000 °/s");
}
break;
default:
break;
}
scale_value = 0.0;
if(Debug){
Serial.print("Accl Full-Scale = ");
}
switch (Accl){
case 0:
scale_value = MPU_ACCL_2_SCALE;
if(Debug){
Serial.println("±2 g");
}
break;
case 1:
scale_value = MPU_ACCL_4_SCALE;
if(Debug){
Serial.println("±4 g");
}
break;
case 2:
scale_value = MPU_ACCL_8_SCALE;
if(Debug){
Serial.println("±8 g");
}
break;
case 3:
scale_value = MPU_ACCL_16_SCALE;
if(Debug){
Serial.println("±16 g");
}
break;
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 15 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
break;
default:
break;
}
values.AcX = (float) data_in.AcX / scale_value;
values.AcY = (float) data_in.AcY / scale_value;
values.AcZ = (float) data_in.AcZ / scale_value;
if(Debug){
Serial.print(" GyX = "); Serial.print(values.GyX);
Serial.print(" °/s| GyY = "); Serial.print(values.GyY);
Serial.print(" °/s| GyZ = "); Serial.print(values.GyZ);
Serial.print(" °/s| Tmp = "); Serial.print(values.Tmp);
Serial.print(" °C| AcX = "); Serial.print(values.AcX);
Serial.print(" g| AcY = "); Serial.print(values.AcY);
Serial.print(" g| AcZ = "); Serial.print(values.AcZ);Serial.println(" g"
}
return values;
}
There are several libraries available to control the OLED display with the ESP8266. In this
tutorial we’ll use two Adafruit libraries: Adafruit_SSD1306 library and Adafruit_GFX library.
1. Open your Arduino IDE and go to Sketch > Include Library > Manage Libraries. The
Library Manager should open.
2. Type “SSD1306” in the search box and install the SSD1306 library from Adafruit.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 16 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
3. After installing the SSD1306 library from Adafruit, type “GFX” in the search box and
install the library.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 17 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
Check that the OLED display is properly wired to the ESP8266 Double-check the OLED
display I2C address: with the OLED connected to the ESP8266, upload this code and check
the I2C address in the Serial Monitor You should change the OLED address in the following
line, if necessary. In our case, the address is 0x3C.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 18 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
The Code:
We will add the following lines of code to the top of our file. These lines will include the
libraries for the OLED and initallize its connection
In [ ]: #include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Now within the setup of the code we will add a few lines to ensure that the OLED is
connected and to make sure the OLED is refreshed before any code is ran.
In [ ]: void setup() {
Wire.begin();
Serial.begin(115200);
mpu6050Begin(MPU_addr);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000);
display.clearDisplay();
display.setTextColor(WHITE);
}
Now we will add the code that will actually display the code. At the end of the file within the
last "if(Debug)" conditional we will display the acceleration mesurments in the the X,Y,and Z
direction.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 19 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
First we will set a delay for the OLED to prepare then we clear anything on the screen to get
rid of the previous measurment and define the text color.
In [ ]: delay(100);
display.clearDisplay();
display.setTextColor(WHITE);
Then we set the TextSize and intiatlize where the text should begin in the (x,y) format. After
this we reset our cursor to the right of the label and display the associated value.
In [ ]: delay(100);
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.print("g|AcX = ");
display.setCursor(70,0);
display.print(values.AcX);
We repeat this for the other two measurements and arrive to the following if conditional at
the end of the code.
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 20 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
In [ ]: if(Debug){
Serial.print(" °/s| GyX = "); Serial.print(values.GyX);
Serial.print(" °/s| GyY = "); Serial.print(values.GyY);
Serial.print(" °/s| GyZ = "); Serial.print(values.GyZ);
Serial.print(" g| AcX = "); Serial.print(values.AcX);
Serial.print(" g| AcY = "); Serial.print(values.AcY);
Serial.print(" g| AcZ = "); Serial.print(values.AcZ);Serial.println(" g"
delay(100);
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.print("g|AcX = ");
display.setCursor(70,0);
display.print(values.AcX);
display.setTextSize(1);
display.setCursor(0,20);
display.print("g|AcY = ");
display.setCursor(70,20);
display.print(values.AcY);
display.setTextSize(1);
display.setCursor(0,40);
display.print("g|AcZ = ");
display.setCursor(70,40);
display.print(values.AcZ);
display.display();
}
Final Product
If done correctly the final outcome will look as follows:
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 21 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
In [ ]:
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 22 of 23
EDC Lab 5 - Jupyter Notebook 1/16/20, 10:05 AM
http://localhost:8889/notebooks/Downloads/EDC%20Lab%205.ipynb Page 23 of 23