Arduino Guide Using MPU-6050 and nRF24L01: Digital Human Research Center
Arduino Guide Using MPU-6050 and nRF24L01: Digital Human Research Center
Tutorial
Author:
Daniel titello - Intern
Internship Mentor:
Mathew Schwartz
DHRC Director:
Jaeheung Park
http://dhrc.snu.ac.kr
2 Arduino 2
2.1 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.3 How to create a project . . . . . . . . . . . . . . . . . . . . . . . 2
4 IMU MPU-6050 5
4.1 Schematic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
4.2 Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
4.3 Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
4.3.1 Three-Axis MEMS Gyroscope . . . . . . . . . . . . . . . . 11
4.3.2 Three-Axis MEMS Accelerometer . . . . . . . . . . . . . 11
4.3.3 DMP function . . . . . . . . . . . . . . . . . . . . . . . . 12
4.4 Register Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.4.1 Register 25 - Sample Rate Divider - SMPRTDIV . . . . . 12
4.4.2 Register 35 - FIFO Enable . . . . . . . . . . . . . . . . . 12
4.4.3 Register 56 - Interrupt Enable . . . . . . . . . . . . . . . 13
4.4.4 Register 58 - Interrupt Status . . . . . . . . . . . . . . . . 15
4.4.5 Registers 59 to 64 - Accelerometer Measurements . . . . . 15
4.4.6 Registers 65 and 66 - Temperature Measurement . . . . . 16
4.4.7 Registers 67 to 72 - Gyroscope Measurements . . . . . . . 16
4.4.8 Registers 73 to 96 - External Sensor Data . . . . . . . . . 17
5 nRF24L01 18
5.1 Schematic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.2 Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.3 Multiceiver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6 Integration 26
7 References 28
1
1 Introduction
This tutorial will explain how to use the Arduino platform Since the creation of
a simple arduino sketch until the implementation of more complicate examples
using the sensor MPU-6050 and the wireless communication module nRF24L01.
All the details to make the sensor and the wireless communication work will be
explain step by step in this tutorial, including the library code too.
2 Arduino
2.1 History
Arduino is an open-source platform which allows people easily make project
mixing hardware and software. An Arduino board consists of a microcontroller
with complementary components that facilitate programming and incorporation
into other circuits. Standard connectors are responsible to make so easy the
connection between the main board and other auxiliary boards, called Shields.
Arduino is open-source, so everyone has complete access to the schematic
and layout boards or a hundred of online examples.
2.2 Software
Although, the Arduino integrated development environment(IDE) was written
in Java, the Arduino programs or Sketchs are written in C or C++ language.
For better working, it is really important keeping update the computer with the
last support version of the Arduino IDE.
2
Figure 1: The Arduino board is the most popular board.
The second step is uploading the sample code Blink which is an example
that will turn on and turn o the LED connect to pin 13 of any Arduino board.
If the code is uploaded without error and the LED is blinking, it means the
board is working properly and you can start your project.
3
Figure 3: Done uploading
3.2 Accelerometer
An accelerometer is an electromechanical device used to measure acceleration
forces. Such forces may be static, like the continuous force of gravity or, as is
the case with many mobile devices, dynamic to sense movement or vibrations.
Accelerometer allows us to know if objects are moving and since the acceler-
ation of an object is known, we also can determine speed and orientation. For
example, 1g is equal to 9.81 m/s2.
4
3.3 Gyroscope
A gyroscope is a device that uses Earths gravity to help determine orientation
and maintains this level of eectiveness by being able to measure the rate of ro-
tation around a particular axis. Gyroscope are strong used in altitude indicator
on typical aircrafts.
4 IMU MPU-6050
MPU-6050 is a sensor that contains MEMS accelerometer and a MEMS gyro-
scope in one chip. Both accelerometer and gyrosocope contains 3 axis that can
captures x,y and z with 16-bits analog to digital conversion hardware for each
channel. Mpu-6050 uses I2C for communication which is a multi-master,multi-
slave,single-ended,serial computer bus with low speed but very useful because
uses only two wires: SCL(clock) and SDA(data) lines.
If we search on Arduinos website about this sensor, there are some ex-
amples and we are going to use the library i2cdevlib which comes with two
examples: one getting raw values and another one using a Digital Motion Pro-
cessor(DMP).I am using the example that uses DMP because of the complexity
of the project.
The library Wire.h has the I2C commands and configuration. For exam-
ple, the library says that the pins SCL and SDA have to be connect to the pins
A5 and A4 respectively when the user uses Arduino Uno and Arduino ethernet.
The other includes are part of the library created that was downloaded.
4.1 Schematic
To make the sensor MPU-6050 work, it is necessary:
Arduino IDE;
Arduino board;
MPU-6050;
Breadboard;
Although the breadboard and the wires are optional items, the two pull-up
resistor are essential. The diagram below shows how to connect the sensor to a
Arduino Uno. It is also important connect all the sensor pins with the correct
arduino pins. The pull-up resistor will always keep a small amount of current
flowing between VCC and the pin, in other words, it will keep a valid logic level
if it is not flowing current in the pin.
5
Sensor VDD - Arduino 3.3v or 5v
Sensor GND - Arduino GND
Sensor INT - Arduino digital pin 2
Sensor SCL - Arduino SCL dedicated pin = A5
4.2 Code
The first thing to do is declare the libraries we are going to use. While the
libraries i2c and Wire are responsible for the i2c communication, the library
i2cdevlib is responsible for setting up all the registers and necessary configura-
tion to make the sensor work.
1 #include <I2Cdev.h>
2 #include <MPU6050_6Axis_MotionApps20.h>
3 #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
4 #include <Wire.h>
5 #endif
The line below is configuring the I2C address. The MPU-6050 has two I2C
addresses that makes possible to use two sensor at the same arduino board
without an I2C multiplexer. The default address is 0x68 which means that the
pin AD0 has to be connect to the ground.
1 MPU6050 mpu;
6
Those control variables are very important to make the MPU works and
each one has a dierent function.
After initialize the i2c, it is time to start the MPU and test if the device is
connect to the Arduino. The commands below are responsible for that:
1 mpu.initialize();
2 Serial.println(mpu.testConnection());
Note: The library used has a file called MPU6050.cpp which initializes
the sensor. The following code is located in this file.
1 void MPU6050::initialize()
2 {
7
3 setClockSource(MPU6050_CLOCK_PLL_XGYRO);
4 setFullScaleGyroRange(MPU6050_GYRO_FS_250);
5 setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
6 setSleepEnabled(false);
7 }
8 bool MPU6050::testConnection()
9 {
10 return getDeviceID() == 0x34;
11 }
The code above is very simple and it is just setting the clock, gyroscope and
accelerometer scales and disabling the sleep mode.
If the DMP initializes correctly, the devStatus will receive a status zero
and the variable dmpReady will be set in one and this means that the DMP
was initialized correctly and the program is ready the send data to the FIFO.
Otherwise, it will be printed an error message and the variable dmpReady will
continue false which makes impossible the executing of the code.
1 if (devStatus == 0)
2 {
3 mpu.setDMPEnabled(true);
4 attachInterrupt(0, dmpDataReady, RISING);
5 mpuIntStatus = mpu.getIntStatus();
6 dmpReady = true;
7 packetSize = mpu.dmpGetFIFOPacketSize();
8 }
9 else {
10 Serial.print(devStatus);
11 Serial.println("error");
12 }
Now, the code is being executed in the function loop(). The first line is a
conditional that tests if the DMP was initialize correctly. The examination is
made through the variable dmpReady.
1 if (!dmpReady) return;
Until this part, the code was making sure that the device is connected to
an Arduino and initializing the DMP. From now, the program will put the data
into the FIFO and the FIFO will put the data out through the serial monitor.
The sample rate is specified by the register 25 and it is the speed that the
sensor will send data to the FIFO. The sample is generated by dividing the
gyroscope output rate by SMPLRTDIV: Sample Rate = Gyroscope Output
Rate / (1 + SMPLRTDIV) where Gyroscope Output Rate = 8kHz when the
DLPF is disabled (DLPFCFG = 0 or 7), and 1kHz when the DLPF is enabled.
8
SMPLRTDIV is a 8-bit unsigned value.
As expected, the FIFO has a buer size and it is impossible knowing how
to deal with it. The line below is getting the the number of bytes stored in
the FIFO buer. This number is in turn the number of bytes that can be read
from the FIFO buer and it is directly proportional to the number of samples
available given the set of sensor data bound to be stored in the FIFO. The
program will return the FIFO buer size.
1 fifoCount = mpu.getFIFOCount()
1 uint16_t MPU6050::getFIFOCount()
2 {
3 I2Cdev::readBytes(devAddr, MPU6050_RA_FIFO_COUNTH, 2, buffer);
4 return (((uint16_t)buffer[0]) << 8) | buffer[1];
5 }
Since the moment the program knows the size of the FIFO, it is possible
making a conditional that will delete the oldest data in the FIFO if an overflow
happen. If not, the fifobuer will be read and the data will be available.
The FIFO R-W register is the register is used to read and write data from
the FIFO buer. 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 buer 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 buer has overflowed, the status bit FIFO-OFLOW-INT is au-
tomatically set to 1. This bit is located in INT-STATUS (Register 58). When
the FIFO buer has overflowed, the oldest data will be lost and new data will
be written to the FIFO.
If the FIFO buer 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 buer is not read when
empty.
9
5 else if (mpuIntStatus & 0x02)
6 {
7 while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
8 mpu.getFIFOBytes(fifoBuffer, packetSize);
9 fifoCount -= packetSize;
10
11 #ifdef OUTPUT_READABLE_YAWPITCHROLL
12 mpu.dmpGetQuaternion(&q, fifoBuffer);
13 mpu.dmpGetGravity(&gravity, &q);
14 mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
15 Serial.print("ypr\t");
16 Serial.print(ypr[0] * 180/M_PI);
17 Serial.print("\t");
18 Serial.print(ypr[1] * 180/M_PI);
19 Serial.print("\t");
20 Serial.println(ypr[2] * 180/M_PI);
21 #endif
22 }
4.3 Data
More important than get the data from the sensor, it is get a reliable data. The
code below is calibrating the sensor with the right oset of each axis: x,y and
z. Every sensor has dierent osets, so it is essential to find those values using
another example that can be MPU6050.raw.ino that comes in the library file
too.
1 mpu.setXGyroOffset(220);
2 mpu.setYGyroOffset(76);
3 mpu.setZGyroOffset(-85);
4 mpu.setZAccelOffset(1788);
10
5 #include<Wire.h>
6 const int MPU=0x68;
7 int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
8 void setup()
9 {
10 Wire.begin();
11 Wire.beginTransmission(MPU);
12 Wire.write(0x6B);
13 Wire.write(0);
14 Wire.endTransmission(true);
15 Serial.begin(9600);
16 }
17 void loop(){
18 Wire.beginTransmission(MPU);
19 Wire.write(0x3B);
20 Wire.endTransmission(false);
21 Wire.requestFrom(MPU,14,true);
22 AcX=Wire.read()<<8|Wire.read();
23 AcY=Wire.read()<<8|Wire.read();
24 AcZ=Wire.read()<<8|Wire.read();
25 Tmp=Wire.read()<<8|Wire.read();
26 GyX=Wire.read()<<8|Wire.read();
27 GyY=Wire.read()<<8|Wire.read();
28 GyZ=Wire.read()<<8|Wire.read();
29 Serial.print("AcX = "); Serial.print(AcX);
30 Serial.print(" | AcY = "); Serial.print(AcY);
31 Serial.print(" | AcZ = "); Serial.print(AcZ);
32 Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53);
33 Serial.print(" | GyX = "); Serial.print(GyX);
34 Serial.print(" | GyY = "); Serial.print(GyY);
35 Serial.print(" | GyZ = "); Serial.println(GyZ);
36 delay(333);
37 }
11
4.3.3 DMP function
According to the website Greek Mom projects, the MPU6050 IMU contains a
DMP (Digital Motion Processor) which combines the accelerometer and gyro-
scope data together to minimize the eects of errors. The result is computed
by the DMP in terms of quaternions and can convert the results to Euler angles
and perform other computations with the data as well. The DMP is better
than the complementary filter because is able to calculate Pitch, roll and yaw
which are known as X,Y and Z axis (Euler angles). These calculations were
limited by certain properties of both the accelerometer and gyroscope and one
way to avoid the problems is to use an alternate method of representing rotation
called quaternions. Quaternions describe rotation in three dimensions by using
four scalar values. Three of these scalars define an axis, and the fourth specifies
a rotation around that axis.
12
loaded into the FIFO buer if a sensor?s respective FIFO-EN bit is set to 1 in
this register.
When a sensor FIFO-EN bit is enabled in this register, data from the sensor
data registers will be loaded into the FIFO buer.
Parameters:
13
MOT-EN - When set to 1, this bit enables Motion detection to generate
an interrupt.
FIFO-OFLOW-EN - When set to 1, this bit enables a FIFO buer overflow
to generate an interrupt
I2C-MST-INT-EN - When set to 1, this bit enables any of the I2C Master
interrupt sources to generate an interrupt.
DATA-RDY-EN - When set to 1, this bit enables the Data Ready inter-
rupt, which occurs each time a write operation to all of the sensor registers
has been completed.
14
Figure 7: Interrupt enable - Source: Datasheet
15
Figure 9: Accelerometer Measurements - Source: Datasheet
16
Figure 11: Gyroscope Measurement - Source: Datasheet
17
5 nRF24L01
The nRF24L01 is a Radio/Wireless Transceiver module which is able to commu-
nicate two or more Arduinos over a distance and it is constantly used for remote
sensor, Robot control and monitoring from 50 feet to 2000 feet distances, but
this distance can change according to the environment because of walls and ma-
terials. There are modules that it is possible to buy and those modules such as,
Transmitters power amplifiers and Receivers preamplifiers, permit transmitting
in longer distances.
The nRF24L01 supports the high-speed Serial Peripheral Interface(SPI) and
it is still low power consumption. Sometimes the low current necessary can cause
some power problems, so it is recommended add a 0.1uF or 10uF capacitor
between the GND and 3.3V pin to guarantee current to module. The capacitor
recommended will serve as a source of energy, in other words, as a battery.
The nRF24L01 operates at 250KHz, 1Mhz and 2Mhz and it is suggested use
the lower speed to make sure if the data sent and received are reliable. It is also
important to consider those detailed specially because of the dierent suppliers.
Later on, we are going to talk about Multiceivers which allows 6 Arduinos
to talk to a Primary Arduino in an organized manner.
5.1 Schematic
To make the module nRF24L01 work, it is necessary:
Arduino IDE;
Arduino board;
nRF24L01;
One 0.1uF or 10uF capacitor;
Wires;
18
Figure 14: Bottom view. Source: Arduino Info
The images below will show how to set the modules nRF24L01 up.
The picture above shows that is necessary 8 pins to connect the module to
a Arduino. The place of connection of those pins is dierent from one Arduino
to another. The pins CE and CSN can change by programming.
In our example, we are using the following configuration.
19
Module CE - Arduino CE dedicated pin = 9
Module CSN - Arduino CSN dedicated pin = 10
Module SCK - Arduino SCK dedicated pin = 13
Module MOSI - Arduino MOSI dedicated pin = 11
Module MISO - Arduino MISO dedicated pin = 12
Note: The pinout of the transmitter module and receiver module are the
same.
5.2 Code
The first thing to take a look in this part is about the library that we are going
to use. The Arduino website talks about two libraries: RF24 and Mirf. In this
code we will use the first library which has the same configuration above.
Remember to declare the libraries used at the first part of the code right in
the top. The following libraries are necessary to communicate with the nRF2401
and with the SPI.
1 #include <SPI.h>
2 #include <nRF24L01.h>
3 #include <RF24.h>
After the declaration of the libraries, we have the opportunity of modify the
pin numbers of CE and CSN which will be used later to initialize the module or
radio. According to the datasheet, the pin CE is used to active or standby the
mode. The pin CSN is used to tell the nRF24 whether the SPI communication
is a command or message to send out as claimed by the same datasheet.
1 #define CE_PIN 9
2 #define CSN_PIN 10
Until here, this part is the same for both modes: the transmitter and receiver.
Now, we will explain how to write a transmitter code. This Wireless module
send and receive data through pipes, in other words, a pipe is an transceiver
address. The lines below has to be programmed in both modules.
The first dierence starts in the function setup() which will start the commu-
nication with the command radio.begin() and open the only pipe for writing
because we are programming the transmitter part.
20
1 radio.begin();
2 radio.openWritingPipe(pipe);
Then, the next function is the loop() which will be very simple too. We just
have to write the data we want to send to the receiver. In this example, we are
sending an array of three elements.
1 int test[3];
2 test[0] = 10;
3 test[1] = 20;
4 test[2] = 30;
5 radio.write( test, sizeof(test));
Now, we will se how to program the function setup() of the receiver mode.
1 radio.openReadingPipe(1,pipe);
2 radio.startListening();;
1 int text[3];
2 if ( radio.available() )
3 {
4 bool done = false;
5 while (!done)
6 {
7 done = radio.read( text, sizeof(text) );
8 Serial.print("Number: ");
9 Serial.print(text[0]);
10 Serial.print("Number: ");
11 Serial.print(text[1]);
12 Serial.print("Number: ");
13 Serial.print(text[2]);
14 }
15 }
16 else
17 {
18 Serial.println("No radio available");
19 }
20 }
21
The entire Transmitter code is
1 #include <SPI.h>
2 #include <nRF24L01.h>
3 #include <RF24.h>
4
5 #define CE_PIN 9
6 #define CSN_PIN 10
7
18 void loop()
19 {
20 test[0] = 10;
21 test[1] = 20;
22 test[2] = 30;
23 radio.write( test, sizeof(test));
24 }
1 #include <SPI.h>
2 #include <nRF24L01.h>
3 #include <RF24.h>
4
5 #define CE_PIN 9
6 #define CSN_PIN 10
7
18 void loop()
22
19 {
20 if ( radio.available() )
21 {
22 bool done = false;
23 while (!done)
24 {
25 done = radio.read( text, sizeof(text) );
26 Serial.print("Number: ");
27 Serial.print(text[0]);
28 Serial.print("Number: ");
29 Serial.print(text[1]);
30 Serial.print("Number: ");
31 Serial.print(text[2]);
32 }
33 }
34 else
35 {
36 Serial.println("No radio available");
37 }
38 }
5.3 Multiceiver
MultiCeiver is a feature used in RX mode that contains a set of six parallel
data pipes with unique address. A data pipe is a logical channel in the phys-
ical RF channel. Each data pipe has its own physical address decoding in the
NRF24L01+.
Both modes have dierents codes from the first and second example one
showed. ***************************Arduino Code for Receiver*******************************
23
1 include <SPI.h>
2 include <nRF24L01.h>
3 include <RF24.h>
4
19 void loop()
20 {
21 byte pipe = 0;
22
23 while(radio.available(&pipe))
24 {
25 radio.read( &number, sizeof(number));
26 Serial.print("Transmitter number ");
27 Serial.println(pipe);
28 Serial.print("Number: ");
29 Serial.println(number);
30 Serial.println();
31 }
32 }
1 #include <SPI.h>
2 #include <nRF24L01.h>
3 #include <RF24.h>
4
24
13 {
14 Serial.begin(57600);
15 radio.begin();
16 radio.openWritingPipe(wAddress);
17 radio.stopListening();
18 }
19
20
21 void loop()
22 {
23 if(!done)
24 {
25 if (!radio.write( &number, sizeof(number) ))
26 {
27 Serial.println("Sending failed");
28 }
29 else
30 {
31 Serial.print("Success sending: ");
32 Serial.println(number);
33 }
34 }
35 }
1 #include <SPI.h>
2 #include <nRF24L01.h>
3 #include <RF24.h>
4
20
21 void loop()
25
22 {
23 if(!radio.write( &number, sizeof(number) ))
24 {
25 Serial.println("Sending failed");
26 }
27 else
28 {
29 Serial.print("Success sending ");
30 Serial.println(number);
31 }
32
33 }
It is possible to use more four transmitter together. The only thing to care is
the addresses and the data that the new transmitter would send. All addresses
must be dierent and the RX mode has to receive the same data that the
TX mode are sending. If we want more than two transmitter we can use the
same code, but instead of the same address we have to modify for the following
addresses below.
6 Integration
The aim of this tutorial is learn about two devices: MPU-6050 and nRF24L01.
After understanding that, we chose to integrate both, getting the data from
the sensor and sending through the Wireless module. We had some problems
specially because the sensor gets information using the DMP that only works
without delay, but the library used has a small delay in one of the functions.
After a lot of tests, we know that the devices are working well separately, through
they dont work reliably together.
The ready - functions from the RF24 library were analysed too since we
thought the problem could be there. Some of the functions inside the library
have delay. Those delays are responsible for sending and receiving data, which
can overflow the MPU - 6050 FIFO because the data output is becoming slower
than the data input.
One of the possible solutions was decrease the non - crucial delays to see the
response. Sadly, after some minutes the program crashed again. We also tried
to run two loops that we could initialize the MPU-6050 and nRF24L01 after
26
the counter achieves some number. The answer was good, however, the data
got was not reliable because every time the sensor was initiazed, the sensor was
calibrating for a few seconds.
27
7 References
[1] Arduino Official.
[2] MPU-6050 Datasheet.
[3] Gyroscopes and Accelerometers.
[8] GithubRF24.
[9] Multiceiver.
28