Can You Sense? - Part 1
This is part one in our series “All kind of sensors with Arduino and Raspberry Pi”. Here we will write about GY-521 sensor module. In future instructable posts, we will cover more sensors, like sensors for 16 in 1 sensors kit, or 35 in 1 sensors kit and much more.
So where can we use this sensor module? It can be used as sensor for self balancing quadcopters or drones, or self balancing robots, in mobile phones for screen orientation, etc. We will create example sketch for screen orientation with our 1.77’’ TFT display.
Parts list:
- GY-521 module
- Uno R3
- 1.77’’ TFT display
- Breadboard or prototyping shield
- few jumper wires
- battery to DC power 2.1mm jack (optional, only if you you doing stuff like we do)
- 9V battery (any you like, optional, only if you you doing stuff like we do)
An accelerometer is a device that measure proper acceleration, an acceleration relative to gravitational acceleration, but not gravitational acceleration. What is an acceleration? It is change of velocity in time, or rate of velocity change. If we move stationary object from stop state, to new stop state, its velocity changes from zero to some value, and then to zero. Whenever there is any change in velocity, we have acceleration. If velocity changes from some value to some higher value we have positive acceleration, and if velocity changes from some value to some lower value we have negative acceleration.
What is 3 axis accelerometer? If we use coordinate system to express dimensions in space, like on image, there are 3 axis that are perpendicular, so every point in space has three coordinates, X, Y and Z. If we move objects in one direction, for example X direction, we get acceleration in X direction. So for 3 axis we get 3 accelerations. This sensor chip gives some raw data a integer number that we should change into something meanable but we won’t cover this in this instructable post. If we move sensor with some speed and acceleration in positive X direction (like on image from datasheet) we get some positive integer number as raw data. If we move sensor with same speed and acceleration in negative X direction (opposite direction) we will get same integer number for raw data, but it will be negative (number will be slightly off because of imperfections in sensors, sensor noise and some errors in measurement).
A gyroscope is a device that is used to measure or maintain orientation and angular velocity. Angular velocity is velocity of object rotation relative to some point. For example if you stand up and start spinning, your arms will have some angular velocity which can be measured by gyroscope. Like in 3 axis accelerometer, we have 3 axis gyroscope which can measure angular velocity of free object in 3D space. Any free object in 3D space can rotate around some axis. If we use coordinate system like on image to express this, so any free object can rotate around some of three axis. Any other complex rotation can be expressed with these three rotations.
So if we take this sensor and rotate it around X axis in clockwise direction , we will get some positive integer number for raw gyroscope data. If we rotate in counter clockwise direction we will get some negative integer number for raw gyroscope data. Same is for Y and Z directions.
X and Y directions are printed on GY-521 board and Z direction is from board to you (if we look on this image)
On this module there is MPU-6050 sensor chip which has 3 axis accelerometer, 3 axis gyroscope, embedded temperature sensor and digital motion processor (DMP). Accelerometer measures non gravitational accelerations in all 3 axises, and gyroscope uses earth's gravity to determine orientation in 3 directions. DMP can be used to process complex algorithms directly on the board, usually we use it to process raw sensor data into usable informations. MPU-6050 uses I2C serial bus to communicate with master devices (SDA and SCL pins), but it also can act as master device to gather data from other sensors (XCA and XCL pins) which uses I2C serial bus, without intervention of other master devices. AD0 pin is used to setup one of two I2C addresses. If we drive it LOW, I2C address will be 0x68, and if we drive it HIGH, I2C address is 0x69. INT pin is used for interrupts, and we won’t cover it in this instructable.
Libraries
We won’t use any new library for GY-521. There are several libraries (for example this one) that you can use for advanced projects, but for purpose of this Instructable post we don’t need them. We will use Wire.h and Adafruit libraries for TFT displays.
Reading Raw Data
Connect everything like on diagram. We used prototyping shield for connecting everything. We thought that is easier to move everything with shield. You can use breadboard if you wish. We also created cable to connect 9V battery to DC power 2.1mm jack on arduino board. Here is how to connect it.
#include "Wire.h" #include // <Adafruit_GFX.h> #include // <Adafruit_ST7735.h> #include // <SPI.h> #define TFT_CS 10 #define TFT_RST 8 // Or set to -1 and connect to Arduino RESET pin #define TFT_DC 9 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); const int MPU_ADDR = 0x68; // I2C address of the MPU-6050. // If AD0 pin is set to HIGH, the I2C address will be 0x69. int16_t accel_x, accel_y, accel_z; // variables for accelerometer raw data int16_t gyro_x, gyro_y, gyro_z; // variables for gyro raw data int16_t temp; // variable for temperature data char tmp_str[7]; // temporary variable used in convert function char* convert_int16_to_str(int16_t i) { // converts int16 to string. Moreover, // resulting strings will have the same length sprintf(tmp_str, "%6d", i); return tmp_str; } void setup() { Wire.begin(); Wire.beginTransmission(MPU_ADDR); // Begins a transmission to the I2C slave (GY-521 board) Wire.write(0x6B); // PWR_MGMT_1 register Wire.write(0); // set to zero (wakes up the MPU-6050) Wire.endTransmission(true); tft.initR(INITR_GREENTAB); tft.fillScreen(ST77XX_BLACK); delay(500); } void loop() { Wire.beginTransmission(MPU_ADDR); Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) // [MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2, p.40] Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. // As a result, the connection is kept active. Wire.requestFrom(MPU_ADDR, 14, true); // request a total of 14 registers // "Wire.read()<<8 | Wire.read();" means two registers are read and stored in the same variable accel_x = Wire.read()<<8 | Wire.read(); // reading registers: 0x3B (ACCEL_XOUT_H) and 0x3C (ACCEL_XOUT_L) accel_y = Wire.read()<<8 | Wire.read(); // reading registers: 0x3D (ACCEL_YOUT_H) and 0x3E (ACCEL_YOUT_L) accel_z = Wire.read()<<8 | Wire.read(); // reading registers: 0x3F (ACCEL_ZOUT_H) and 0x40 (ACCEL_ZOUT_L) temp = Wire.read()<<8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L) gyro_x = Wire.read()<<8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L) gyro_y = Wire.read()<<8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L) gyro_z = Wire.read()<<8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)</p><p> tft.fillScreen(ST77XX_BLACK); //screenOrientation(); this we will create and use in next step // print data on TFT display printText(30, 40, "Ax = ", ST77XX_RED); printText(60, 40, convert_int16_to_str(accel_x), ST77XX_WHITE); printText(30, 50, "Ay = ", ST77XX_GREEN); printText(60, 50, convert_int16_to_str(accel_y), ST77XX_WHITE); printText(30, 60, "Az = ", ST77XX_BLUE); printText(60, 60, convert_int16_to_str(accel_z), ST77XX_WHITE); printText(30, 70, "T = ", ST77XX_ORANGE); tft.setCursor(65, 70); tft.setTextSize(1); tft.setTextColor(ST77XX_WHITE); // the following equation was taken from the documentation [MPU-6000/MPU-6050 Register Map and Description, p.30] tft.print((float)temp/340.00+36.53, 2); printText(100, 70, "C", ST77XX_WHITE); printText(30, 80, "Gx = ", ST77XX_RED); printText(60, 80, convert_int16_to_str(gyro_x), ST77XX_WHITE); printText(30, 90, "Gy = ", ST77XX_GREEN); printText(60, 90, convert_int16_to_str(gyro_y), ST77XX_WHITE); printText(30, 100, "Gz = ", ST77XX_BLUE); printText(60, 100, convert_int16_to_str(gyro_z), ST77XX_WHITE); delay(1000); } void printText(int x, int y, char *text, uint16_t color) { tft.setCursor(x, y); tft.setTextColor(color); tft.setTextWrap(true); tft.print(text); }
This sketch is two sketches mixed together. First if Test sketch for 1.77’’ TFT display that we created in our previous instructable post, and the other sketch is from this site.
We modified the end of that sketch to print data on TFT display instead to Serial Monitor. So we won’t cover the TFT part of the sketch, just GY-521 part.
We will read seven different values from sensor chip. First three are from 3 axis accelerometer, fourth is from temperature sensor, and last three are from 3 axis gyroscope.
At the beginning we define I2C address, which is 0x68 (AD0 pin is derived LOW). Then we define seven variables where we will store raw data from sensor chip. After this we create temporary variables which are used to convert number variables into char arrays, which are used for printing data.
In setup() function we setup I2C communication protocol. First we use Wire.begin(); to start I2C communication. Then we use Wire.beginTransmission(MPU_ADDR) to connect with sensor chip. Than we send 0x6B, this select PWR_MGMT_1 register in MPU-6050 and with Wire.write(0) we wake up MPU-6050 or we turn it on. With.endTransmission(true) we end this communication message, and stop communication
In loop() function we again start communication by connecting to MPU-6050 with address stored in MPU_ADDR. Then with Wire.write(0x3B) we set beginning register that we will read. With Wire.requestFrom(MPU_ADDR, 14, true) we request data from 14 registers, starting from 0x3B. Values stored in this 14 registers are accelerometer, gyroscope and temperature sensors raw data, stored in two registers, one for high 8 bits, and one for low 8 bits, actually data is 16 bit data. So for seven values we have 14 register.
Next seven lines are for reading data from this 14 registers. We wirst read high 8 bits for accel_x data, and we logically OR it with low 8 bits. And this is repeated for all seven variables for storing raw data. After these seven code lines, we read all raw data, and after this we print it on TFT display. In next step, we will create function that will change screen orientation but checking this accelerometer raw data.
Screen Orientation Part
After reading raw data, there is one comment:
screenOrientation();
Uncomment it, and add this code after loop() function:
void screenOrientation() { if(accel_x > 15000 && accel_x < 19000 && accel_y > -1000 && accel_y < 1000) { tft.setRotation(0); } else if (accel_x > 0 && accel_x < 2000 && accel_y > -18000 && accel_y < -13000) { tft.setRotation(1); } else if (accel_x > 0 && accel_x < 2000 && accel_y > 13000 && accel_y < 18000) { tft.setRotation(3); } }
First we watched what is shown on display, when we rotate it in some direction, and we used these values to create this function. When you upload this to your arduino, and rotate screen, you will get screen content oriented in direction you rotated it.
Future Projects
We will use this sensor module to control servo motors, or robot arms with servo motors, or RoboCar, or create two wheeled self balancing robot. Hit follow button if you want to stay up to date with our future projects.
This is all for this Instructable.
If you have any questions, feel free to ask.
You can reach us under info@az-delivery.com at any time and we will be glad to help you, or visit us under www.az-delivery.com