[SOLVED] Determine if a device has been moving for more than a period of time

I got a MPU-6050 to sense acceleration. I managed to get it to work and put in a sensitivity so it prints when the chip has been moved (i.e. does not print the raw data, just the statement “MOVED” when there has been movement. Code is below (most if from the example, just showing how I determined if it moved).

However, I would like to do is build something that can tell for how long there has been movement, and only if there is movement for say 5 seconds does it trigger the “MOVED”. The difficult part is that there may be moments in there where there is no movement, e.g. for a half second it may stop but then start moving again, and I don’t want that to ‘reset’ the movement time counter.

The motivation is that I am building a movement alarm for my bag: if it moves for less than 5 seconds, then it is all good (may just have been bumped) but if it is moving for longer than 5 seconds then it has been stolen!

I am a little stumped on the approach to handle this part of the project (determining if movement has been over 5 seconds). Any advice on approach?

// MPU-6050 test for movement
//
// mostly taken from:
// MPU-6050 Short Example Sketch
// By Arduino User JohnChi
// August 17, 2014
// Public Domain

#include<Wire.h>
const int MPU=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

int OldAcX,OldAcY,OldAcZ,OldTmp,OldGyX,OldGyY,OldGyZ;

int AcSensitivity = 500;
boolean moved = false;

void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
  
}
void loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

/*  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53);  //equation for temperature in degrees C from datasheet
  Serial.print(" | GyX = "); Serial.print(GyX);
  Serial.print(" | GyY = "); Serial.print(GyY);
  Serial.print(" | GyZ = "); Serial.println(GyZ);
*/

  if (abs(OldAcX - AcX) > AcSensitivity) {
    moved = true;
  }

  if (abs(OldAcY - AcY) > AcSensitivity) {
    moved = true;
  }

  if (abs(OldAcY - AcY) > AcSensitivity) {
    moved = true;
  }

  if (moved == true) {
    Serial.println("MOVED");
  }

  OldAcX = AcX;
  OldAcY = AcY;
  OldAcZ = AcZ;
  moved = false;  
 
  delay(333);
}

A quick and dirty way could possibly be to use counters. Add 1 to time moved or time not moved (depending on situation) and then multiply by the time for a loop (crude estimate would be the 333 ms). Can use a large if statement to see if it has exceeded the movement time and then alert or continue monitoring.

You can use millis() to manage the time. See the demo several things at a time.

Save the value of millis() in every iteration of loop() when it does NOT move. Then if it moves for X seconds the start time won't be updated, the time will expire and the alarm can be sounded. Something like

void loop() {
  curMillis = millis();
  checkSensor();
  if (moved == false) {
     startMillis = curMillis;
  }
  if (curMills - startMillis >= safetyInterval) {
    // sound Alarm
  }
}

...R

Robin2: Save the value of millis() in every iteration of loop() when it does NOT move.

That does not address his actual concern.


You need a more concise formulation of the algorithm.

You have to decide just how much "not movement" will cancel the movement. One obvious way would be to increment for movement, and decrement for "not movement" so that it trips when a total of five seconds of "movement minus not movement" is reached. So it moves for four seconds, stops for two seconds (tally now two) then moves for two seconds, stops for one (tally now three) and moves for another two seconds reaching the threshold.

Exactly equal times of movement and non-movement would never reach the criterion, but it could instead decrement half as much as it increments which would then reach the criterion if it was moving more than one third of the time..

Paul__B: That does not address his actual concern.


I thought it would address this (or come close)

only if there is movement for say 5 seconds does it trigger the "MOVED".

...R

Thanks!

Robin2, I think the basic structure that you have is good, but Paul__B was right, doesn't address the concern I have, which is that there may be a few "not moved" sensor readings in there that reset the movement counter, but I don't want it to. If it really was binary: moved / not moved and once movement started it says moving, then I could use that approach but I suspect not. I will need to some more testing on it.

After Paul__B's comment about deciding how much "not movement" will cancel movement, I got thinking about how to build in a threshold.

Here is what I thought of as a possible approach:

1) I would store the ''moved" value in an array, shifting the new one in each time I take a reading. Since I have the sensor reading at 1/3 of a second right now, to store the previous 5 seconds of "moved" would take 15 elements. Each time I read a sensor value, I drop the oldest one, and add the newest one to the array.

2) I would then look to see if there is a more than 5 second period between two moments of movement (i.e. it has been potentially been moving for more than 5 seconds). I may need to have another criteria: the number of seconds I am reviewing (e.g. may need to store the previous 10 seconds of data, and look at any more than 5 second period in that) but would need to think through if this is actually necessary given the second criteria.

3) Then, I would loop through each element of the array and count how many are "moved" and how many are "not moved" between those two moments of movement, to see if it exceeds a threshold amount (say I allow 20% not movement).

This way, [u]there would be two tests[/u]: - Does the time between the oldest "moved" and the newest "moved" exceed 5 seconds? (i.e. it has potentially been moving for more that 5 seconds) - If so, does "moved" exceed the threshold value for that time period? (i.e. it has actually been moving)

I can thus [u]exclude the two events I want to exclude[/u]: - Two blips more than 5 seconds apart, but with little movement in between (below the threshold). - Movement (even 100% of the time) of say 4 seconds, and then stop.

I would [u]only include it as "stolen" if[/u]: - There are two blips of movement more than 5 second apart (with some maximum time period between moments of movement) - AND there has been movement above the threshold in between those two blips (e.g. 80% of the time it has been moving)

Any thought on this general approach?

Hello all,

Thanks again for the help! I took the general approach above, and I managed to create something that works pretty well for what I want to use it for.

Details on the solution here: MakeSelf blog "is it stolen?" algorithm

MakerSelf: Robin2, I think the basic structure that you have is good, but Paul__B was right, doesn't address the concern I have, which is that there may be a few "not moved" sensor readings in there that reset the movement counter, but I don't want it to.

Don't know why I didn't see this when you posted it.

My solution would be to start a separate millis() timer to ignore "not-moved" signals that occur within the period when you want them to be ignored. This would operate within the longer time period.

...R