MPU 6050 help!!
# help
d
I am trying to write code to get the acceleration values from MPU 6050, the code in arduino for this is:
Copy code
#include<Wire.h>
const int MPU = 0x68;
float AccX, AccY, AccZ;

void setup() {
  Serial.begin(9600);

  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);


  Wire.beginTransmission(MPU);
  Wire.write(0x1C);
  Wire.write(0b00011000);  //scale factor +/-16g
  Wire.endTransmission();
}
loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 14, true); // Solicita os dados ao sensor

 AccX = Wire.read() << 8 | Wire.read(); //0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
 AccY = Wire.read() << 8 | Wire.read(); //0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
 AccZ = Wire.read() << 8 | Wire.read(); //0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)

 Serial.print(AccX / 2048);
 Serial.print(" ");
 Serial.print(AccY / 2048);
 Serial.print(" ");
 Serial.println(AccZ / 2048);
}
i have already started making the porting to toit, and i am stuck in this:
Copy code
import gpio
import i2c

MPU9250_ADDRESS := 0x68
ACCEL_CONFIG_REG := 0x1C


main:
    bus := i2c.Bus
        --sda=gpio.Pin 21
        --scl=gpio.Pin 22

    device := bus.device MPU9250_ADDRESS
can any one help me to finish this code in toit?
f
Off-topic: prefer to indent your code with two spaces (the
bus :=
and
device :=
). This will make the syntax highlighter happier. The
--sda=
can then be intended by 4. Small warning: I haven't tested any of the code I posted here. Let me know if something doesn't seem correct or doesn't work. The first thing the code does is to set the 0x1C register. If you look at the register map (which, for some reason, is not in the data sheet) you can see that 0x1C is the ACCEL_CONFIG register (as you correctly stated with your constant): https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf (Side-note: for constants, like
MPU9250_ADDRESS
and
ACCEL_CONFIG_REG
, prefer
::=
instead of
:=
. That just means that the variables are final and can't be changed). It sets the value
0b00011000
. According to the register map, this means that AFS_SEL is set to 0b11. On page 15 (Register 28), it explains that this means that the accelerometer reads values in the whole 16g range. This means that it might be a bit less precise, but can handle stronger accelerations. You could change it to
0b00000000
for more precise measurements. In that case you will need to change the constant to go from raw value to acceleration value later in the code, though. To write to an i2c register you would use the following code:
Copy code
registers := device.registers
  registers.write_u8 ACCEL_CONFIG_REG 0b00011000
For reading the actual values you have 2 options: - read all 6 bytes (2 for each axis) in one go, or - read two bytes each. The first is a bit more efficient, but the latter is easier to write. Here are the constants you need:
Copy code
ACCEL_XOUT_H ::= 0x3B
ACCEL_XOUT_L ::= 0x3C
ACCEL_YOUT_H ::= 0x3D
ACCEL_YOUT_L ::= 0x3E
ACCEL_ZOUT_H ::= 0x3F
ACCEL_ZOUT_L ::= 0x40
Copy code
// Read 6 bytes in one go:
  accel_data := registers.read_bytes ACCEL_XOUT_H 6
  accel_x := accel_data[0] << 8 | accel_data[1]
  accel_y := accel_data[2] << 8 | accel_data[3]
  accel_z := accel_data[4] << 8 | accel_data[5]
or
Copy code
// Read each value independently:
  accel_x := registers.read_i16_be ACCEL_XOUT_H_
  accel_y := registers.read_i16_be ACCEL_YOUT_H_
  accel_z := registers.read_i16_be ACCEL_ZOUT_H_
Note that we use
read_i16_be
which basically means: "read an integer with 16 bits, in big-endian format". And "big-endian" means that the big part (that is, most significant, or high part) is first. If you look at the description of these registers (page 29) you can see that the obtained value's meaning depends ot the AFS_SEL that we set in the configuration. With the highest precision (when setting the config-register to 0), the value must be divided by 16384. With the lowest accuracy (but highest range), it must be divided by 2048 (as you do in your code). You can then start a loop:
Copy code
while true:
    accel_x := registers.read_i16_be ACCEL_XOUT_H_
    accel_y := registers.read_i16_be ACCEL_YOUT_H_
    accel_z := registers.read_i16_be ACCEL_ZOUT_H_
    print "x: $(accel_x / 2048)"
    print "y: $(accel_y / 2048)"
    print "z: $(accel_z / 2048)"
    sleep --ms=1_000
Also of interest: there is already a package for the mpu6886: https://pkg.toit.io/package/github.com%2Fimliubo%2Fmpu6886-toit@v1.0.0
Even the
I2C_ADDRESS
seems to be the same.
I wouldn't be surprised if that package works directly with your MPU 6050.
d
Thank you very much for the detailed reply. I appreciate your advice and I will definitely test the code as well as the mpu6886 lib. If everything goes smoothly, I will reply here
everything work good, thanks a lot for your support, i opted to use the package of the mpu6886, with a little change i have make work on mpu 6050, for thus who will use MPU6050, the little change was:
Copy code
constructor dev/serial.Device:

    reg_ = dev.registers

    tries := 5
    while whoami != 0x71: // Here was the change, on MPU6886 was expected 0x19, but on MPU6050 is 0x71 
      tries--
      print whoami
      if tries == 0: throw "INVALID_CHIP"
      sleep --ms=1
f
nice. We should probably make an
mpu6xxx
package that knows about these differences.
d
I'll later posted on my github and linkedin, the project that i'm made using Toit for my thesis defense, that is a fall detection project: detecting object falls in real-time using mpu6050. really thanks for your help.
f
Thanks!
Fyi: one of the lsm303 drivers we have has free-fall detection.
Actually. It was the LIS3DH one.
Couldn't resist and had a look at the datasheet. The one I linked earlier doesn't mention free-fall detection. However, this page mentions that bit two bits in the interrupt register are reserved for free-fall detection: https://community.element14.com/technologies/sensor-technology/b/blog/posts/one-step-closer-to-create-own-fall-detector-with-mpu6050
d
It appears to me that the implementation at the register level is quite similar to the one I made. To determine if the sensor is falling or not, you just need to calculate the total force it's experiencing using the formula sqrt(ax2 + ay2 +ax**2). When the sensor is falling, the total force is almost zero.
So, the sensitivity that you can adjust is simply how close to zero you want the condition to be.
f
The advantage of using the sensor is that you can just wait for the interrupt. No need to continuously poll
d
sure
4 Views