Open kanimaru opened 2 years ago
Hi Kanimaru,
I am also looking to get tap detection working. The MPU6050 does support it according to the spec but this library doesn't have that support yet. Did you manage to find any alternatives?
Was looking for other modules but didn't find any and currently I have no time for that anymore. Sorry.
You know, although this library is good, it's not the only one, which supports MPU6050. The official driver available almost for 10 years now. You can download it yourself from manufacture's website: https://invensense.tdk.com/developers/software-downloads/#mpu-en
Also, there is several repos you can check as an example:
The SparkFun library already has tap example: https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/examples/MPU9250_DMP_Tap/MPU9250_DMP_Tap.ino. You only need to define the correct sensor version (#define MPU6050
)
If for some reasons, you highly coupled with this library, it is possible to reproduce the same behavior here (mix MPU6050_DMP6_using_DMP_V6.12 with SpurkFun example).
const int16_t mpuAddress = 0x68; //It can be 0x68 or 0x69
MPU6050 mpu(mpuAddress);
bool dmpReady = false; // set true if DMP init was successful
uint8_t fifoBuffer[64]; // FIFO storage buffer
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void enableMpuInterrupts() {
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
}
void mpuWriteMemory(unsigned short memAddress, unsigned short length, unsigned char *data) {
mpu.writeMemoryBlock(data, length, memAddress >> 8, memAddress & 0xFF);
}
void setTwoByteValue(int value, unsigned char *data) {
data[0] = value >> 8;
data[1] = value & 0xFF;
}
void configureTaps() {
// Array to store register values
unsigned char tmp[4];
// Add taps to FIFO buffer (i.e. DMP output)
// see dmp_enable_feature method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L1005
tmp[0] = 0x20;
mpuWriteMemory(2742, 1, tmp); // CFG_27
// Enable taps
// see dmp_enable_feature method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L1030
tmp[0] = 0xF8;
mpuWriteMemory(2224, 1, tmp); // CFG_20
// set FIFO rate
// see dmp_set_fifo_rate method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L660
setTwoByteValue((DMP_SAMPLE_RATE / 10 - 1), tmp);
mpuWriteMemory(534, 2, tmp); // D_0_22
const unsigned char regs_end[12] = { 0xfe, 0xf2, 0xab, 0xc4, 0xaa, 0xf1, 0xdf, 0xdf, 0xBB, 0xAF, 0xdf, 0xdf };
mpuWriteMemory(1106, 12, (unsigned char *)regs_end); // CFG_6
// Set threshold (0-1600) for each axis, for example 30
// see dmp_set_tap_thresh method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L727
// Divide threshold by sample rate (200)
// And then multuply by current accelerometer LSB (16384, 8192, 4096, 2048)
// For some reason also save 0.75 value (12288, 6144, 3072, 1536)
// X-Axis
setTwoByteValue((30 * 16384 / DMP_SAMPLE_RATE), tmp);
mpuWriteMemory(468, 2, tmp); // DMP_TAP_THX
setTwoByteValue((30 * 12288 / DMP_SAMPLE_RATE), tmp);
mpuWriteMemory(292, 2, tmp); // D_1_36
// Y-Axis
setTwoByteValue((30 * 16384 / DMP_SAMPLE_RATE), tmp);
mpuWriteMemory(472, 2, tmp); // DMP_TAP_THY
setTwoByteValue((30 * 12288 / DMP_SAMPLE_RATE), tmp);
mpuWriteMemory(296, 2, tmp); // D_1_40
// Z-Axis
setTwoByteValue((30 * 16384 / DMP_SAMPLE_RATE), tmp);
mpuWriteMemory(476, 2, tmp); // DMP_TAP_THZ
setTwoByteValue((30 * 12288 / DMP_SAMPLE_RATE), tmp);
mpuWriteMemory(300, 2, tmp); // D_1_44
// Set for which axes detect taps X | Y | Z
// see dmp_set_tap_axes method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L762
tmp[0] = 0x30 | 0x0C | 0x03;
mpuWriteMemory(328, 1, tmp); // D_1_72
// Set minimal tap count (1-4), for example 1
// see dmp_set_tap_count method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L780
tmp[0] = 0; // (add -1 to the value)
mpuWriteMemory(335, 1, tmp); // D_1_79
// Set length between valid taps, for example 100 ms
// see dmp_set_tap_time method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L796
setTwoByteValue((100 * DMP_SAMPLE_RATE / 1000), tmp);
mpuWriteMemory(478, 2, tmp); // DMP_TAPW_MIN
// Set max time between taps to register as a multi-tap, for example 800 ms
// see dmp_set_tap_time_multi method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L812
setTwoByteValue((800 * DMP_SAMPLE_RATE / 1000), tmp);
mpuWriteMemory(474, 2, tmp); // D_1_218
// Set shake rejection threshold. If the DMP detects a gyro sample larger than @e thresh, taps are rejected, for example 190
// see dmp_set_shake_reject_thresh method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L830
long thresh = 190 * 46850825LL * 200 / DMP_SAMPLE_RATE / 1000;
tmp[0] = (unsigned char)(((long)thresh >> 24) & 0xFF);
tmp[1] = (unsigned char)(((long)thresh >> 16) & 0xFF);
tmp[2] = (unsigned char)(((long)thresh >> 8) & 0xFF);
tmp[3] = (unsigned char)((long)thresh & 0xFF);
mpuWriteMemory(348, 4, tmp); // D_1_92
// Set shake rejection time, for example 40 ms
// see dmp_set_shake_reject_time method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L848
setTwoByteValue((40 / (1000 / DMP_SAMPLE_RATE)), tmp);
mpuWriteMemory(346, 2, tmp); // D_1_90
// Set shake rejection timeout. For example 10 ms
// see dmp_set_shake_reject_timeout method
// https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L866
setTwoByteValue((10 / (1000 / DMP_SAMPLE_RATE)), tmp);
mpuWriteMemory(344, 2, tmp); // D_1_88
}
void setup() {
Serial.begin(115200);
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
mpu.setInterruptMode(true);
mpu.setIntMotionEnabled(true);
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// wait for ready
Serial.println(F("\nSend any character to begin DMP programming and demo: "));
while (Serial.available() && Serial.read())
; // empty buffer
while (!Serial.available())
; // wait for data
while (Serial.available() && Serial.read())
; // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
int8_t devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
// *** here ***
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
mpu.CalibrateAccel(6);
mpu.CalibrateGyro(6);
Serial.println();
mpu.PrintActiveOffsets();
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
configureTaps();
enableMpuInterrupts();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
} else {
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
Serial.println(F("Setup finished"));
}
void loop() {
// if programming failed, don't try to do anything
if (!dmpReady) return;
// read a packet from FIFO
if (mpuInterrupt) {
if (mpu.dmpPacketAvailable() && mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
if (fifoBuffer[29] & 0x01) {
unsigned char tap = 0x3F & fifoBuffer[31];
unsigned char direction, count;
direction = tap >> 3;
count = (tap % 8) + 1;
switch (direction) {
case TAP_X_UP:
Serial.print("Tap X+ ");
break;
case TAP_X_DOWN:
Serial.print("Tap X- ");
break;
case TAP_Y_UP:
Serial.print("Tap Y+ ");
break;
case TAP_Y_DOWN:
Serial.print("Tap Y- ");
break;
case TAP_Z_UP:
Serial.print("Tap Z+ ");
break;
case TAP_Z_DOWN:
Serial.print("Tap Z- ");
break;
}
Serial.println(count);
}
}
mpuInterrupt = false;
}
}
Remember, that you also need to update FIFO size in library (MPU6050_6Axis_MotionApps612.cpp
):
/*
dmpPacketSize += 16;//DMP_FEATURE_6X_LP_QUAT
dmpPacketSize += 6;//DMP_FEATURE_SEND_RAW_ACCEL
dmpPacketSize += 6;//DMP_FEATURE_SEND_RAW_GYRO
dmpPacketSize += 4;//DMP_FEATURE_TAP
*/
dmpPacketSize = 32;
It's a bit low-level, but works just fine.
You also can try to modify dmpMemory
:
#define DMP_SAMPLE_RATE (200) // https://github.com/sparkfun/SparkFun_MPU-9250-DMP_Arduino_Library/blob/master/src/util/inv_mpu_dmp_motion_driver.c#L443
#define MPU6050_DMP_CODE_SIZE 3062 // dmpMemory[]
unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] = {
// copy content form the library
}
void printDmpMemory() {
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
Serial.print(F("const unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] PROGMEM = {"));
for (int i = 0; i <= MPU6050_DMP_CODE_SIZE - 1; i++) {
if (i % 16 == 0) {
Serial.println(F(""));
} else {
Serial.print(F(" "));
}
if (i % 256 == 0) {
Serial.print(F("/* bank # "));
Serial.print(i / 256);
Serial.println(F(" */"));
}
Serial.print(F("0x"));
Serial.print(hex[dmpMemory[i] >> 4]);
Serial.print(hex[dmpMemory[i] & 0x0F]);
if (i < MPU6050_DMP_CODE_SIZE - 1) {
Serial.print(F(","));
}
}
Serial.println(F(""));
Serial.println(F("};"));
}
void mpuWriteMemory(unsigned short memAddress, unsigned short length, unsigned char *data) {
// mpu.writeMemoryBlock(data, length, memAddress >> 8, memAddress & 0xFF);
// instead of actual write to the sensor - just update the array
for (int i = 0; i <= length; i++) {
dmpMemory[i + memAddress] = data[i];
}
}
void configureTaps() {
// see above
}
void setup() {
Serial.begin(115200);
configureTaps();
printDmpMemory();
}
void loop() {
// do nothing
}
But I didn't test it. You can produce the dmpMemory
output on simulator (), for example https://wokwi.com/projects/new/esp32 (esp32 just for memory requirements)
Hi guys what have to be done to get the Tap feature that the datasheet is promoting? Probably another version of dmpMemory but where you get it in the first place? And then I'm confused what to do next? Shouldn't be there a documentation how the DMP communicate these information?
My use case: I have an ESP32 connected to GY512 as a portable device. To save power I just want to activate the device when it get tapped. If there is a better Idea it would be also OK otherwise how the heck this works :).
Much thanks in advance.