Asset Tracker Accelerometer + GPS problem

Hey @rickkas7, I know this is an older thread but I’m still having a problem waking up the Electron using your AssetTrackRK library.

If I don’t turn off the GPS before going to sleep (like your wake on move example does by default), then the Electron wakes up every time.

However, if I uncomment the lines in your code to turn off the GPS before sleeping, the Electron wakes up from motion once, then never again. This behavior happens every time.

Here is a code snippet of my sleep state:

case SLEEP_STATE:
    // Wait for Electron to stop moving for 2 seconds so we can recalibrate the accelerometer
    accel.calibrateFilter(2000);

    // Is this necessary?
      digitalWrite(D6, HIGH);
      pinMode(D6, INPUT);

      Serial.println("going to sleep");
      delay(500);

    // Sleep
    System.sleep(WKP, RISING, TIME_PUBLISH_BATTERY_SEC, SLEEP_NETWORK_STANDBY);

    // This delay should not be necessary, but sometimes things don't seem to work right
    // immediately coming out of sleep.
    delay(500);

    awake = ((accel.clearInterrupt() & LIS3DH::INT1_SRC_IA) != 0);

    Serial.printlnf("awake=%d", awake);

    // Restart the GPS
    pinMode(D6, OUTPUT);
    digitalWrite(D6, LOW);

    startFix = millis();
    gettingFix = true;

    state = GPS_WAIT_STATE;
    stateTime = millis();
    break;

I have tried many variations of when to turn on and off the GPS, and when to clear the accel interrupt, but I haven’t had any luck.

I am using the AssetTrackerV2 and I have tried my code on system firmware versions 0.6.0 and 0.6.4.

Have you experienced this problem as well?

Thank you for finding a reproducible fail case for this. I was able to reproduce this and fix it when the GPS is powered down (D6 set HIGH before sleep). I suspect this change will also fix the randomly not being able to wake up from sleep even when the GPS is on.

Reinitializing the LIS3DH prior to calibrating the filter seems to solve the problem. Basically this, just before accel.calibrateFilter(2000).

			LIS3DHConfig config;
			config.setLowPowerWakeMode(16);
	
			accel.setup(config);

When unable to wake up from sleep, it’s definitely the LIS3DH not generating the WKP pulse, not an issue with the Electron not waking up. I put a logic analyzer on it and WKP doesn’t change.

There’s a new version of AssetTrackerRK 0.1.5, with this change. It also has a fix for isValid not actually being valid.

5 Likes

I tried that out and it works for me too! Thanks so much!

1 Like

Thank you both for the fix… I’m still in need of one. I’ll give it a try !
-jc

Thanks @rickkas7 - This works! Yay! (With one small exception.)

Here’s the scenario. Tap the electron to wake it up. Let it communicate with server/etc. Pause for 1500 ms then tap it again just before it goes to sleep. WKP stays high and the electron won’t wake back up again with motion, but it WILL wake via TIME_PUBLISH_BATTERY_SEC (which I’d like to set to 12ish hours).

You may say this is unlikely to happen, but this is going on a tractor. It might sit still for 2 seconds and then move again abruptly, causing the tracker to become unresponsive for 12 hours.

I took a video (WARNING: some electron frustration will be seen) to see if anyone can reproduce my findings. This is taken with an unedited 3-wakeonmove.ino from AssetTrackerRK 0.1.5 library. The blue light is tied with a jumper to WKP so blue light = WKP high.

I tried removing the delay(500); just before the sleep command, but that didn’t fix it. Also tried adding accel.clearInterrupt() just before the sleep command, but I clearly don’t know what I’m doing.

I made the following observation using the following code change:

    if( digitalRead(WKP) == LOW ) {
      Serial.println("WKP LOW");
      System.sleep(WKP, RISING, TIME_PUBLISH_BATTERY_SEC, SLEEP_NETWORK_STANDBY);
    } else {
      Serial.println("WKP HIGH");
      break;
    }

and my serial terminal says the following just before going to sleep (with WKP high):

resetting lastMovement int1_src=0x6a
recalibrating
resetting lastMovement int1_src=0x6a
recalibrating
WKP LOW

which makes me think that if the electron gets bumped AS the sleep command is being performed, it will not have a chance to reset the accel and thus WKP stays HIGH.

Not sure this can be classified as a bug, but it’s more of less behaving as it’s supposed to (bump = WKP HIGH until reset), right? If this is what’s happening, is there a workaround, since it doesn’t seem like detecting WKP works?

From my understanding, if the electron gets bumped while entering sleep mode, there isn’t much you can do about it.

However, I found that the most time consuming part of entering sleep mode is turning off the cellular modem. So my “work around” is to turn off cellular, then check the wake pin right before entering sleep mode.

Here is that part of my code:

    Cellular.off();
    delay(4000); //wait for cellular to actually turn off

    // check WKP to make sure it isn't high before sleeping
    if (digitalRead(WKP)==LOW) {
      // Sleep
      System.sleep(WKP, RISING, TIME_PUBLISH_BATTERY_SEC);

      // This delay should not be necessary, but sometimes things don't seem to work right
      // immediately coming out of sleep.
      delay(500);

      // Connect to network
      Particle.connect();

      // Clear interrupt and AND return value with the interrupt flag register
      awake = ((accel.clearInterrupt() & LIS3DH::INT1_SRC_IA) != 0);
      newMotion = 1;

      Serial.printlnf("awake=%d", awake);
    }else { //Motion caught before going to sleep

      // Connect to network
      Particle.connect();
      delay(500);

      accel.clearInterrupt();
      awake = 1;
      newMotion = 1;
    }

I find this greatly limits the time interval where the wake pin can go high without catching it.

4 Likes

That definitely does work. It makes the cell signal take about 15 sec to reconnect, but I think it’s worth the trade off to have a device that works. Thanks lots!

P.S. I’m using System.sleep(SLEEP_MODE_SOFTPOWEROFF, TIME_PUBLISH_BATTERY_SEC); as my sleep command. That might be part of why it takes longer to get cell signal back up, not sure.

@rickkas7’s solution involving resetting the accelerometer before going to sleep works for making waking up the electron, once the GPS is turned on again in the SLEEP_STATE, the accelerometer no longer generates an interrupt on the WKP pin. This is an issue if you want to detect continual movement before going back to sleep. This behavior tells me that powering on the GPS is what screws with the accelerometer.

Further, the accelerometer cannot be reset immediately after powering on the GPS or it won’t work right for some reason.

An alternate solution to the one proposed by Rick is instead of resetting the accelerometer before sleeping in the WakeOnMove example, is to instead go to the online wait state after waking up. This provides enough of a delay before going to the reset state where the accelerometer is reset. Doing so will allow the WKP interrupt to be set/reset many times before going back to sleep state.

1 Like

I was having this problem for a while but didn’t know where to start to troubleshoot it. Turning off cellular before going to sleep fixed it, thanks.

Now question regarding accel calibration, should I do it when I’m sure WKP is low (inside the if)? or leave it before? Reason I’m asking is because if it turns out it’s still moving, is it a good time to calibrate it?
Thanks.

I check WKP before and after calibrating to make sure there was no movement. If there is movement, I skip going to sleep. This seems to work well. I haven’t had any issues yet.

3 Likes

I believe I’ve tried all of these suggestions to prevent the sleep with motion issue from occurring, however I’ve noticed two things:

  1. I can still get it to go to sleep and not wake up via WKP. As the device falls asleep, the status light goes from light blue > green > dark blue. If I apply motion it just before the dark blue light goes out, it will get stuck.
  2. If I use a sleep mode other than SLEEP_NETWORK_STANDBY, and it has motion applied to it while going to sleep, it will not wake up with WKP for a timer. It fully crashes until a reset is applied.

Here is my sleep code:

        case SLEEP_STATE:
        digitalWrite(D6, HIGH);
		{
			LIS3DHConfig config;
			config.setLowPowerWakeMode(WKP_THRESHOLD);
			accel.setup(config);
		}
		accel.calibrateFilter(2000);
        Cellular.off();
        delay(4000);
        if (digitalRead(WKP)==LOW) {
            //THIS CAUSES FATAL CRASHES
      		//System.sleep(SLEEP_MODE_SOFTPOWEROFF, TIME_PUBLISH_BATTERY_SEC);
      		//THIS CAUSES FATAL CRASHES
      		//System.sleep(SLEEP_MODE_DEEP, TIME_PUBLISH_BATTERY_SEC);
      		//THIS DOESNT SEEM TO ACTUALLY GO TO SLEEP
      		//System.sleep(TIME_PUBLISH_BATTERY_SEC);
      		//THIS WORKS OK, BUT STILL CAN BE STUCK IF MOTION IS TIMED CORRECTLY.  IT AT LEAST ALLOWS TO WAKE BACK UP ON THE TIMER.
      		System.sleep(WKP, RISING, TIME_PUBLISH_BATTERY_SEC, SLEEP_NETWORK_STANDBY);
      		delay(500);
      		Particle.connect();
      		awake = ((accel.clearInterrupt() & LIS3DH::INT1_SRC_IA) != 0);
      		Serial.printlnf("awake=%d", awake);
    	}else {
  			Serial.println("MOTION CAUGHT BEFORE SLEEP");
    		delay(500);
     		Particle.connect();
     		delay(500);
      		awake = ((accel.clearInterrupt() & LIS3DH::INT1_SRC_IA) != 0);
    	}  
        digitalWrite(D6, LOW);
        startFix = millis();
        gettingFix = true;
        Serial.println("ONLINE_WAIT_STATE");
        state = ONLINE_WAIT_STATE;
        stateTime = millis();
        break;

I can still get it to go to sleep and not wake up via WKP. As the device falls asleep, the status light goes from light blue > green > dark blue. If I apply motion it just before the dark blue light goes out, it will get stuck.

If you apply motion in this time, the WKP pin is set before the Electron actually goes to sleep, so it can't generate an interrupt to wake it up. I haven't figured out a solution to this beyond using some external component to check for this condition. At least it will wake up after the specified amount of time and reset everything.

If I use a sleep mode other than SLEEP_NETWORK_STANDBY, and it has motion applied to it while going to sleep, it will not wake up with WKP for a timer. It fully crashes until a reset is applied.

There is a bug involving SLEEP_MODE_DEEP and SLEEP_NETWORK_STANDBY that locks up the Electron if the WKP pin is high when sleeping. As far as I know, this has not been resolved. My workout was to just not use these sleep methods :grinning:

Do you mind sharing how you accomplished that? What kind of external component?

May I ask why you are clearing the interrupt? Do you have the LIS3DH configured with latching interrupt?

I have been running two Electrons as GPS trackers for a couple of weeks now, testing it in my car, walking, etc, I do not use latching, so there is no need to clear the interrupt. So far I have had no problems with it hanging or failing to wake up. It is very reliable.

FWIW, I do not use the AssetTracker library at all, I had too many problems with it. I use only the Adafruit_GPS, Adafruit_LIS3DH, and Adafruit_DHT (for a temp/humidity sensor). If it helps I can post the code I use to initialize the LIS3DH.

Your idea sounds better than mine. Can you still detect if the Electron woke from motion opposed to the timer?

Yes, it would be helpful if you could post your initialization code. Thanks!

The only way I can think of to reliably detect if motion woke it up is to check the time once the GPS has a good fix and see if it woke up early. Latching seems to break it no matter what - with latching set it will frequently never wake up from sleep again, neither for motion or the time interval.

For what I am doing, I only care if it is still moving, which is easily detected. The cause of why it woke up does not really matter. As long as there is more motion, I keep sending location data once a minute. If the motion stops for over a minute, I put it to sleep for 15 minutes. If a single bump wakes it up, it will wait 30 seconds for the GPS to acquire accurate data, send the location data, and go back to sleep again. With latching off this works very reliably.

I took a quick look at the LIS3DH datasheet, and reading the IA bit from the INT1_SRC register will tell you if you if an interrupt occurred. I haven’t tried it yet, but you should be able to use this to detect if you woke from motion, assuming you clear this register before going sleep.

I also read this today and had the same thought. I just tried it. When it wakes up because of motion, the value I get from INT1_SRC is always 255, the same value I get when it wakes up because of the timer. I even created an early wakeup service routine to see if I could catch it then.

If you want to try my code, the first thing you will need to do is either modify LIS3DH.h to expose the readRegister8() and writeRegister8() functions, or copy them from LIS3DH.cpp and put them in your program. I modified LIS3DH.h by moving the “private” statement down 2 lines.

This is what I use to initialize the LIS3DH:

int accelThreshold = 8;  // Threshold 125 mg (1 LSB equels xx mg. 2G = 16, 4G = 32, 8G = 262, 16G = 186)
void initAccelMotion() {  // set up for wake on motion instead of tap
  accel.begin(LIS3DH_DEFAULT_ADDRESS);
  // set control registers manually
  accel.writeRegister8(LIS3DH_REG_CTRL1, 0x5F);   // Write 0x5F - enable LP, x, y, z axis with ODR of 100 Hz
  accel.writeRegister8(LIS3DH_REG_CTRL1, 0x5F);   // Write 0x5F - enable LP, x, y, z axis with ODR of 100 Hz
  accel.writeRegister8(LIS3DH_REG_CTRL2, 0x09);   // High-pass filter (HPF) enabled
//  accel.writeRegister8(LIS3DH_REG_CTRL2, 0xC9);   // Autoreset, High-pass filter (HPF) enabled
  accel.writeRegister8(LIS3DH_REG_CTRL3, 0x40);   // ACC AOI1 interrupt signal is routed to INT1 pin. (I1_IA1)
  accel.writeRegister8(LIS3DH_REG_CTRL4, 0x00);   // Full Scale = +/-2G (2G=0x00, 4G=0x10, 8G=0x20, 16G=0x30)
//  accel.writeRegister8(LIS3DH_REG_CTRL5, 0x08);   // latching, means interrupt must be cleared
  accel.writeRegister8(LIS3DH_REG_CTRL5, 0x00);   // not latching, no need to clear interrupt
  accel.writeRegister8(LIS3DH_REG_INT1THS, accelThreshold);  // set acceleration threshold
  accel.writeRegister8(LIS3DH_REG_INT1DUR, 0x00);  // Event(!) duration - can be set to avoid false detections (e.g. to prevent false freefall detections). Duration = 1LSBs * (1/10Hz) = 0.1s.
  accel.writeRegister8(LIS3DH_REG_INT1CFG, 0x2A);  // Enable all 3 axes, Zh, YH, XH
  delay(250);
}

This is the code I use to put it to sleep:

  // if nothing's happened for a while, publish battery state and go to sleep
  // don't sleep if external battery is connected and well charged
  if (sleepMode && (extBatt<12.0) && (now - lastMotionTime) > STATIC_IDLE_SLEEP_DELAY) {
    String $batt = String::format("Sleep %.2fv,%.1f%%", batt.getVCell(), batt.getSoC());
    Serial.println($batt);
    if (Particle.connected() == true) {
      Particle.publish("B", $batt, 60, PRIVATE);
      delay(10*1000);  // give it time to publish the last message
    }
    digitalWrite(D6, HIGH); // Power down the GPS
    Particle.disconnect(); // Disconnect from the cloud
    Cellular.off(); // shut down cell modem
    batt.sleep(); // stop battery monitoring (~200uA)
    delay(10*1000); // settle down before sleep
// uncomment next 2 lines if interrupt is latched
//    lastINT1SRC = accel.readRegister8(LIS3DH_REG_INT1SRC);  // read INT1_SRC and clear interrupt
//    accel.writeRegister8(LIS3DH_REG_INT1CFG, 0x2A);  // Enable all 3 axes, Zh, YH, XH
    System.sleep(SLEEP_MODE_DEEP, SLEEP_DURATION_SECONDS);  // deep sleep for lowest power consumption
  // note: rising edge signal on WKP will also wake us up (LIS3DH motion will do this)
  // In Deep sleep, when the device wakes up, Setup() runs just like on a reset
  }