MPU-6050 Accelerometer + Gyro

I am new to eimp. I am trying to use an MPU-6050 Accelerometer with the eimp but can’t cant seem to find any information. Would someone here be able to at least point me in the right direction?

Here is the MPU-6050 datasheet, which says the chip supports both I2C and SPI buses, so you’ll need to hook it up to the relevant imp pins. Take a look at the Pin Mux page to see the imp’s pin options, and the i2c and spi API pages for information on how to make use of them.

If the accelerometer is on a breakout board, it may be set to support only one of these buses. To find out more about them, see I2C Explained and SPI Explained.

Thanks for the info. I did find some very helpful information on understanding i2c communication. I need some help, but I am not sure is this is the right place to ask. I am a bit new new to this.

I spent the last few days trying to figure this out and I am not getting anywhere. From reading the datasheet and the Register Map document…along with the links you provided I am sure I have to use i2c at 400 kHZ. I also understand that the device address is 0x68 because I came across this:

"The MPU-60X0 always operates as a slave device when communicating to the system processor, which thus acts as the master. SDA and SCL lines typically need pull up resistors to VDD. The maximum bus speed is 400 kHz.

The slave address of the MPU - 60X0 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is determined by the logic level on pin AD0. This allows two MPU 60X0s to be connected to the same I2C bus. When used in this configuration, the address of the one of the devices should be b1101000 (pin AD0 is logic low) and the address of the other should be b1101001 (pin AD0 is logic high)."

I tried the following:

GND connects to GND on the eimp
VCC connects to 3.3v on the eimp
SDA connects to PIN9 on the eimp
SCL connects to PIN8 on the eimp

Note: I am using the imp Breakout from SparkFun.

hardware.configure(I2C_89);
i2c <- hardware.i2c89;
i2c.configure(CLOCK_SPEED_400_KHZ);

local alsAddr = (0x68<<1);

function readSensor() {
//i2c.write(alsAddr, “\0x00”);
//i2c.write(alsAddr, “\0x3B”);

i2c.write(alsAddr,"\0x3B");

imp.sleep(0.038);

//local result = i2c.read(alsAddr, “\0x3B”, 2);
//local result = i2c.read(alsAddr, “\0x3B”, 15);

local result = i2c.read(alsAddr,"\0x3B", 15);

if (result != null) {
server.log(result[7]);
local bits = (result[1]<<8)|result[0];
server.log(bits);
}
imp.wakeup(0.5, readSensor);
}

readSensor();

And here is the result I am getting.

2014-07-21 21:51:36 UTC-4 [Device] 5
2014-07-21 21:51:36 UTC-4 [Device] 64466
2014-07-21 21:51:36 UTC-4 [Device] 64466
2014-07-21 21:51:36 UTC-4 [Device] 5
2014-07-21 21:51:37 UTC-4 [Device] 5
2014-07-21 21:51:37 UTC-4 [Device] 64466
2014-07-21 21:51:38 UTC-4 [Device] 5
2014-07-21 21:51:38 UTC-4 [Device] 64466
2014-07-21 21:51:38 UTC-4 [Device] 5
2014-07-21 21:51:38 UTC-4 [Device] 64466

The values are not changing. I am pretty sure that I am close, however, my lack of knowledge is giving me a challenge. This is the first time I am looking at a Register Map. What do they mean by ACCEL_XOUT_H[15:8]…do they mean the length is 15 and my significant bit is 8? Also, do i first need to write to a register before reading it?

I would appreciate it if someone can offer some assistance. I am a C# developer so I have some programming knowledge. I am interested in the accelerometer on this device.

Here are some links I have been using for reference:

  1. The MPU 6050 device I am using

  2. The DataSheet

  3. The Register Map

  4. The MPU 6050 used on an Arduino

  5. Electric imp Forum Post 1

  6. Electric imp Forum Post 2

  7. Electric imp Forum Post 3

  8. Electric imp Forum Post 4

Thanks!

Quick, first glance, points: First, drop the ‘hardware.configure(I2C_89)’ - it’s no longer required, and the API call is deprecated.

Second, string handling. You write:

result = i2c.read(alsAddr, "\\0x3B", 2)

this should be:

result = i2c.read(alsAddr, "\\x3B", 2)

The \x causes Squirrel to treat the following two hexadecimal digits as a single 8-bit value; without it, Squirrel reads the string character by character, ie. five characters in your case.

The [15:8] simply means bits 15 through 8 of a 16-bit number, ie. the most significant byte. The name ‘ACCEL_XOUT_H’ (as opposed to ‘ACCEL_XOUT_L’) confirms this (H = high, L = low). XOUT_H’s address of 0x3B, XOUT_L’s is 0x3C, so the MSB comes first.

So to read ACCEL_X you want something like:

local result = i2c.read(alsAddr, "\\x3B", 2) local xaccel = (result[0] * 256) + result[1]

After several days of trying I was able to get RAW data from the device with your explanation above. Finally! Initially I was getting all zeros and after days of looking I realize that I have to take it out of sleep mode before reading data.

From your explanation above, what’s the difference between the ACCEL_XOUT_H and ACCEL_XOUT_L? Do I need to read the value of that register and then add it to:

local xaccel = (result[0] * 256) + result[1]

Currently I am getting the values below for ACCEL_XOUT_H

s - 2356354cead3dbee Clear Log
2014-07-27 16:45:07 UTC-4 [Device] 65304
2014-07-27 16:45:07 UTC-4 [Device] 65272
2014-07-27 16:45:08 UTC-4 [Device] 65280
2014-07-27 16:45:08 UTC-4 [Device] 65316
2014-07-27 16:45:09 UTC-4 [Device] 65364
2014-07-27 16:45:09 UTC-4 [Device] 65296
2014-07-27 16:45:10 UTC-4 [Device] 248
2014-07-27 16:45:10 UTC-4 [Device] 584
2014-07-27 16:45:11 UTC-4 [Device] 1408
2014-07-27 16:45:11 UTC-4 [Device] 704
2014-07-27 16:45:12 UTC-4 [Device] 760
2014-07-27 16:45:13 UTC-4 [Device] 1156
2014-07-27 16:45:13 UTC-4 [Device] 1596
2014-07-27 16:45:13 UTC-4 [Device] 1388
2014-07-27 16:45:14 UTC-4 [Device] 1456
2014-07-27 16:45:15 UTC-4 [Device] 92
2014-07-27 16:45:15 UTC-4 [Device] 65452
2014-07-27 16:45:16 UTC-4 [Device] 65240

Shouldn’t it read 0 (zero) without any movement?

Thanks!

Without movement you would expect a value that stays fairly static, but it won’t be zero (gravity is 1G at all times when you’re at rest).

It looks like here you are reading a 16 bit signed value, which means values <0 appear as >32767. For example, minus 1 = 65535.

The numbers you are showing up there hence range from 65272 (-264) to 1596. I don’t know what range you are set to, but if you are set to +/- 2G, then this is:

-264 = -264/32768 * 2g = 0.016g in the X axis
1596 = 1596/32767 * 2g = 0.097g in the X axis

If the device isn’t completely flat then you may well see this level of gravity acting on an X/Y axis. The main component, assuming Z is generally aligned with gravity the way the board is oriented, will be on the Z axis.

Thank you for responding.

I was looking at how the MPU 6050 was used here and saw how the values of ACCEL_XOUT_H and ACCEL_XOUT_L was read.

ACCEL_XOUT = ((ACCEL_XOUT_H<<8)|ACCEL_XOUT_L); ACCEL_YOUT = ((ACCEL_YOUT_H<<8)|ACCEL_YOUT_L); ACCEL_ZOUT = ((ACCEL_ZOUT_H<<8)|ACCEL_ZOUT_L);

When I try reading the RAW value of ACCEL_XOUT_L in the eimp IDE I am getting a string:

2014-07-28 21:50:09 UTC-4 [Device] 1AFsPfD1sA==
2014-07-28 21:50:10 UTC-4 [Device] EAF4PkD1oA==
2014-07-28 21:50:11 UTC-4 [Device] iAFIPnD1oA==
2014-07-28 21:50:12 UTC-4 [Device] AAFEPpT1sA==
2014-07-28 21:50:13 UTC-4 [Device] lAEYPhD10A==
2014-07-28 21:50:14 UTC-4 [Device] fAG0Pjj1sA==
2014-07-28 21:50:15 UTC-4 [Device] 9AE8PcT1oA==
2014-07-28 21:50:16 UTC-4 [Device] 4AGsPoj1oA==

so obviously I cannot perform a similar bitwise operation…or am I misunderstanding something? I am still trying to figure out if I have to use both the HIGH and LOW values for each axis.

You can do the bitwise operation, it’s just you obviously can’t log binary - it’s trying to encode it base64 there.

eg this should work:

local xaccel = ((result[0]<<24)>>16) | result[1];

…as that should sign-extend the high byte.

I tried this:

local xLowResult = i2c.read(alsAddr, "\\x3C", 7); local xHighResult = i2c.read(alsAddr, "\\x3B", 2); local x = ((((xHighResult[0] * 256) + xHighResult[1])<<24)>>16) | xLowResult

but I am getting the error at boot:

2014-07-29 00:36:49 UTC-4 [Status] Device booting; 3.76% program storage used
2014-07-29 00:36:50 UTC-4 [Device] ERROR: bitwise op between ‘integer’ and 'string’
2014-07-29 00:36:50 UTC-4 [Device] ERROR: bitwise op between ‘integer’ and 'string’
2014-07-29 00:36:50 UTC-4 [Device] ERROR: at readSensor:67
2014-07-29 00:36:50 UTC-4 [Device] ERROR: at readSensor:67
2014-07-29 00:36:50 UTC-4 [Device] ERROR: from main:128
2014-07-29 00:36:50 UTC-4 [Device] ERROR: from main:128

xLowResult should be xLowResult[0] (or, whatever offset in the 7 bytes you’re reading that the low result is at).

xLowResult is, otherwise, a 7 byte string which is hard to logic-or with a number.

After many weeks I got this to work! Thanks for your assistance…em…guidance.

@skhan Can you please share the code as a whole? As a beginner its hard for me to do the modify through the whole conversation…

@JosephWang I’ve been working on this also from the info above and lots of reading from different sources, porting from available python code. This is what I’ve come up with. Not looking at the gyro data yet, although it’s available. Learned much in the process! The code below returns an output every two seconds as such:

2014-11-05 13:59:56 UTC-7 [Device] x rotation -1.34
2014-11-05 13:59:56 UTC-7 [Device] y rotation -1.34
2014-11-05 13:59:56 UTC-7 [Device] --------------------------------
2014-11-05 13:59:58 UTC-7 [Device] x rotation -2.24
2014-11-05 13:59:58 UTC-7 [Device] y rotation -0.89
2014-11-05 13:59:58 UTC-7 [Device] --------------------------------
2014-11-05 14:00:00 UTC-7 [Device] x rotation -1.79
2014-11-05 14:00:00 UTC-7 [Device] y rotation -1.34
2014-11-05 14:00:00 UTC-7 [Device] --------------------------------

I’ll do my best to answer any questions about the bits & pieces of it…

`i2c <- hardware.i2c89;
i2c.configure(CLOCK_SPEED_400_KHZ);
local address = (0x68<<1);

function round(val, decimalPoints) {
local f = math.pow(10, decimalPoints) * 1.0;
local newVal = val * f;
newVal = math.floor(newVal + 0.5);
newVal = (newVal * 1.0) / f;
return newVal;
}

function corr(d) {
local val = (d);
if (val >= 0x8000){
return -((65535 - val) + 1)}
else {
return val;
}
}

function dist(a,b){
return math.sqrt((aa)+(bb));
}

function get_x_rotation(x,y,z){
local radians = math.atan(x / dist(y,z));
local degrees = (radians * (180/PI));
return degrees;
}

function get_y_rotation(x,y,z){
local radians = math.atan(y / dist(x,z));
local degrees = (radians * (180/PI));
return degrees;
}

function get_z_rotation(x,y,z){
local radians = math.atan(z / dist(x,y));
local degrees = (radians * (180/PI));
return degrees;
}

function readSensor() {
i2c.write(address, “\x6B\x00”);
local result = i2c.read(address,"\x3b", 14);
local xaccel = (corr((result[0]<<8) | result[1]) / 131);
local yaccel = (corr((result[2]<<8) | result[3]) / 131);
local zaccel = (corr((result[4]<<8) | result[5]) / 131);
local xgyro = (corr((result[8]<<8) | result[9]) / 16384.0);
local ygyro = (corr((result[10]<<8) | result[11]) / 16384.0);
local zgyro = ((corr(result[12]<<8) | result[13]) / 16384.0);
local temp = result[7];
local tempC = (temp / 340.0) + 36.53;
local tempF = tempC * 1.8 + 32;
//server.log(round(tempF, 1));
server.log("x rotation " + round(get_x_rotation(xaccel, yaccel, zaccel), 2));
server.log(“y rotation " + round(get_y_rotation(xaccel, yaccel, zaccel), 2));
// server.log(“z rotation " + (get_z_rotation(xaccel, yaccel, zaccel)));
server.log(”--------------------------------”);
imp.wakeup(2.0, readSensor);
}

readSensor();`

Thank you that was a great help.
A couple of small correction. You have the scaling factors backwards for the gyro and the accelerometer (the gyro factor should be 131).
(see http://www6.in.tum.de/pub/Main/TeachingWs2015SeminarAuonomousFahren/RM-MPU-6000A.pdf pages 30-32)

Luckily for your rotation computation the factors are irrelevant since they cancel in the division computation.

Also, you should combine the two temperature bytes (same as the others), since the register values are usually negative.

Thanks again.