Help choosing correct SLEEP mode w/ Asset Tracker v2

Greetings! I’m a newbie at this. I’ve done lots of searching, and I’m coming up blank. If it’s been answered elsewhere, then I’m happy to follow a link rather than re-create the wheel. (Most of the posts on the subject were c. 2016 which seemed to be a point when lots of the sleep stuff changed.)

My situation is a bit different than others. I’m making a tracker for my church’s tractor (TractorTracker!) which has been stolen a few times now. I have no problem spending lots of data but I since I’m hiding the tracker and won’t have access to recharging, I need to save as much power as possible (again, even if the cost is data usage).

I’ve been trying to follow the fancy asset tracker project but I’m not really getting it to work as expected. After some research it seems like it might be a v1 vs v2 thing(?).

I’ve decided to go back to the 3_WAKEONMOVE.ino example from the Asset Tracker Library. The problem(?) that I’m running into is the use of the SLEEP_NETWORK_STANDBY rather than deep sleep mode,

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

because it seems like it takes a bit more power to keep the network standby mode activated than the “deep” sleep mode (see this). Please correct me if I’m wrong, but again I’m trying to save as much energy as possible!

So here’s the question:
What would be the best way to put the tracker into a deep sleep mode? I want it to wake up on movement, and every x hours to check in, and (obviously) would make it stay awake long enough to get a GPS signal lock, etc. I don’t have a coin cell yet but realize that’ll help get the lock quicker.

Thanks much! I’ve been impressed at this community when reading other posts (and I’ve read lots).

SLEEP_NETWORK_STANDBY is just a flag that can be used with either Stop Mode sleep (the one you use) or deep sleep (aka Standby Mode).
And if you are not intending to wake every 23 minutes to keep the cloud connecction active, SLEEP_NETWORK_STANDBY won't really help your purpose.

If your prime target is to conserve energy and you are not chargning the device while asleep then you may rather want to use System.sleep(SLEEP_MODE_SOFTPOWEROFF, seconds)
https://docs.particle.io/reference/firmware/electron/#sleep-sleep-

BTW, AssetTrackerRK library is a better evolution of that library you may reather want to use.

1 Like

Hi,
one question I always had about this was the following:
if we use

System.sleep(SLEEP_MODE_SOFTPOWEROFF, seconds)

Is there a way so the Electron wakes up on move when using SLEEP_MODE_SOFTPOWEROFF?

Or in order to use wake up on move there is no other way but to sacrifice battery and use:

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

?

Thanks!
Gustavo.

As the docs state SLEEP_MODE_SOFTPOWEROFF is just a special mode of deep sleep and as such waking via WKP does still apply.

1 Like

Hey @ScruffR,

Oh I get it now, this mix will wake the Electron at "X seconds" and if it moves:

System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, SLEEP_MODE_SOFTPOWEROFF, long seconds);

So a practical example that would wake on move and every 6 hours could look like:

  System.sleep(WKP, RISING, SLEEP_MODE_SOFTPOWEROFF, 6*3600);

Thanks!
Gustavo.

@ScruffR
Seems like I still don't get it.

If I try:

  System.sleep(WKP, RISING, SLEEP_NETWORK_STANDBY, 60);

that compiles ok.

But what I'm after is something like this:

  System.sleep(WKP, RISING, SLEEP_MODE_SOFTPOWEROFF, 60);

And that does not compile:

3_WakeOnMove.ino: In function 'void loop()':
3_WakeOnMove.ino:214:56: error: call of overloaded 'sleep(int, InterruptMode, Spark_Sleep_TypeDef, int)' is ambiguous
   System.sleep(WKP, RISING, SLEEP_MODE_SOFTPOWEROFF, 60);
                                                        ^
3_WakeOnMove.ino:214:56: note: candidates are:
In file included from ../wiring/inc/spark_wiring.h:46:0,
                 from ./inc/application.h:40,
                 from ./inc/Particle.h:5,
                 from 3_WakeOnMove.cpp:4:
../wiring/inc/spark_wiring_system.h:126:17: note: static void SystemClass::sleep(uint16_t, InterruptMode, long int, SleepNetworkFlag)
     static void sleep(uint16_t wakeUpPin, InterruptMode edgeTriggerMode, long seconds=0, SleepNetworkFlag flag=SLEEP_NETWORK_OFF);
                 ^
In file included from ../wiring/inc/spark_wiring.h:46:0,
                 from ./inc/application.h:40,
                 from ./inc/Particle.h:5,
                 from 3_WakeOnMove.cpp:4:
../wiring/inc/spark_wiring_system.h:127:24: note: static void SystemClass::sleep(uint16_t, InterruptMode, SleepNetworkFlag, long int)
     inline static void sleep(uint16_t wakeUpPin, InterruptMode edgeTriggerMode, SleepNetworkFlag flag, long seconds=0) {
                        ^
../build/module.mk:267: recipe for target '../build/target/user/platform-10-m3_WakeOnMove.o' failed
make[2]: *** [../build/target/user/platform-10-m3_WakeOnMove.o] Error 1
make[2]: Leaving directory '/firmware/user'
../../../build/recurse.mk:11: recipe for target 'user' failed
make[1]: Leaving directory '/firmware/modules/electron/user-part'
../build/recurse.mk:11: recipe for target 'modules/electron/user-part' failed
make[1]: *** [user] Error 2
make: *** [modules/electron/user-part] Error 2

Are you able to see what I got wrong?
Thanks,
Gustavo.

Why not just try my syntax (which I copied from the docs)?

There is no need to state the WKP pin as deep sleep (and all its derivatives) is "hard wired" to WKP as the wake pin.

Ah-ha! that is what I was missing in this puzzle.
one googol thanks!
Gustavo.

1 Like

Thanks @ScruffR (and @gusgonnet for jumping in!).

At your suggestion, I’m using System.sleep(SLEEP_MODE_SOFTPOWEROFF, TIME_PUBLISH_BATTERY_SEC); which seems to work mostly. At times it just won’t wake up though, unless I press the reset button. Seems to be related to this issue.

I have yet to try the AssetTrackerRK you mentioned earlier, but in your opinion, does it offer a better/more reliable “wake up from sleep on movement” experience? As is, if I cannot trust this to wake up when there’s motion, then I might as well throw it away.

@gusgonnet, do you have issues with yours waking on movement? (Or are you doing something else with yours?)

Yes, but I see the creator is about to answer :wink:

Actually, not necessarily. When your tracktor is stolen, it might not wake up on first move, but then your regular timed check might still do :wink:
And after that chances are that a subsequent move will do again too.

2 Likes

I wrote both implementations of wake-on-move. The one in AssetTrackerRK is way better than then one in the official AssetTracker library. I’ve been meaning to merge the change into the official version but I haven’t had a chance to.

3 Likes

You'd think so.. but in my limited testing since flashing it last night, it doesn't seem to wake up (at all - timed or motion-activated) if it goes to sleep while moving or (perhaps) if it wakes up and continues moving lots while waking up. I'm going to watch for messages on the console thru the day. Should know within 4 hours.

That’ll be my homework tonight. :slight_smile: If I can’t figure out how to get it working, I know where to come! Thanks both of you.

1 Like

@rickkas7 I’m afraid I might’ve encountered a bug. Wondering if you have any thoughts.

I loaded AsstTrackerRK example 3-wakeonmove, and just like the non-RK version, it still has problems if the device is moving when it falls asleep. Basically what I did was shake the device until it fell back asleep. Then if I smack it again later, it will not wake back up. I waited TIME_PUBLISH_BATTERY_SEC seconds but it never woke itself back up again. The device is dead to the world until I hit reset.

Here is the serial output (modified slightly to reduce repetition):

Opening serial monitor for com port: "/dev/cu.usbmodem1411"
waiting to come online
no location
<repeated>
resetting accelerometer
no location
<repeated>
failed to get GPS fix
resetting lastMovement int1_src=0x59
recalibrating
resetting lastMovement int1_src=0x69
recalibrating
resetting lastMovement int1_src=0x59
recalibrating
resetting lastMovement int1_src=0x69
recalibrating
going to sleep

I have pretty much no idea, but might it have something to do with this: accel.calibrateFilter(2000); just before sleeping? Would it maybe be smart to determine if the accelerometer is firing, and not go to sleep until it’s quiet?

I’ve searched around and this seems like it might be a known issue. I can’t tell if it’s been resolved. Is this how your devices respond as well?

EDIT: looks like this bug still. @rickkas7 were you ever able to replicate that bug? Because it certainly seems as though I am!

1 Like

This currently seems to be the best workaround (if you need the timed wake).
In order to minimise power consumption in case of "permanent" movement for a long time (e.g. during legitimate use of the tractor) you could send the device to sleep (e.g. 15 minutes) without a wake-pin set (0.8.0-rc.2 allows this for deep sleep, otherwise System.sleep(BTN, FALLING, 15*60) should work and gives you the option to wake the device via the SETUP/MODE button).

The underlying problem is in the Particle firmware issue #1262 that you already found.
The resolution for this issue is planned for 0.8.0 (providing it's not an unresolvable hardware bug of the STM32F2 µC).

Alternatively you could rely on the WKP pin only and drop the timed wake, as this issue should not apply for System.sleep(SLEEP_MODE_DEEP) (stated in #1262 comment).
And in that connection, you could implement an external wake-trigger - e.g. with a CD4541B (or other low power, long periode timer chip) that just pulls RESET when needed.

2 Likes

What ScruffR said. Plus:

Sleep while moving is problem inherent in how the LIS3DH works, and yes, you need to wait until you stop moving until you go to sleep with wake-on-move. Here’s why:

In order to do wake-on-move, the accelerometer has to compensate for the force of gravity. Higher-end inertial measurement units have gyroscopes that can determine the tilt of the device, but the LIS3DH does not.

The official AssetTracker library just ignores the Z axis to do this. The problem is that if you don’t set the AssetTracker down flat, it does not work.

The AssetTrackerRK uses the concept of calibration before sleep, so it can figure out which way the device is oriented and cancel out the effect of gravity in any axis (or a combination).

The issue is that this only works when you are not moving. If you calibrate while moving, you’ll get incorrect values.

3 Likes

So my next step: determine when I'm not moving.

Here's what hasn't worked:

bool readyForSleep = accel.calibrateFilter(2000);  // <<-- this doesn't work! need to figure out how to detect non-movement.
if (readyForSleep) { do stuff }

Giving it more time ala accel.calibrateFilter(2000, MORE_SECONDS); is more of the same, but takes longer. I was hoping the bool would be false if it wasn't able to calibrate b/c of movement, but it appears to always be true(?). Any chance this is a good fix but I'm implementing it wrong?

Also what doesn't seem to work is using something like this from 4_Position to figure out when the detector is not moving:

void loop() {
    
        LIS3DHConfig config;
    	config.setPositionInterrupt(16);
    	LIS3DHSample sample;
        
        if (accel.getSample(sample)) {
			Serial.printlnf("%d,%d,%d", sample.x, sample.y, sample.z);
		}

I was hoping I could watch on the serial monitor that once it's flat and not moving it would either show (0,0,0) or at the very least (stable, stable, stable) but it's sitting here, undisturbed on my couch and it's oscillating like crazy.. 256,0,512 -> -256,0,0 -> 0,256,-768 -> 0,0,0 -> -256,-256,768 -> etc and it happens very quickly despite no noticable movement!

Is there a solution here that I'm perhaps implementing incorrectly? Or something completely different? I'm struggling here.. :neutral_face:

Actually that is exactly the opposite. The second parameter doesn't allow for more time to calibrate but actually limits the max time the calibration process is allowed to take. If you omit this parameter (or set it as 0) the calibration will wait indefinetly for a periode of 2000ms "silence". With a value != 0 the process will be terminated even when no silence was detected and in that case you should see false returned.

This is the (current) implementation of the function

bool LIS3DH::calibrateFilter(unsigned long stationaryTime, unsigned long maxWaitTime) {
	bool ready = false;

	unsigned long start = millis();
	unsigned long lastMovement = start;
	unsigned long lastRecalibrate = start - RECALIBRATION_MOVEMENT_DELAY;

	while(maxWaitTime == 0 || millis() - start < maxWaitTime) {
		uint8_t int1_src = readRegister8(REG_INT1_SRC);
		if ((int1_src & INT1_SRC_IA) != 0) {
			Serial.printlnf("resetting lastMovement int1_src=0x%x", int1_src);
			lastMovement = lastRecalibrate = millis();
			clearInterrupt();
		}

		if (lastRecalibrate != 0 && millis() - lastRecalibrate >= RECALIBRATION_MOVEMENT_DELAY) {
			Serial.println("recalibrating");
			lastRecalibrate = 0;
			readRegister8(REG_REFERENCE);
			clearInterrupt();
		}

		if (millis() - lastMovement >= stationaryTime) {
			ready = true;
			break;
		}
	}

	return ready;
}
1 Like

Bummer. I feel like i was sold a false bill of goods!

Can you explain what you mean by “regular movement-interruption”? Thanks

@Vitesze, you share link is no longer valid so you must have deleted the app. I have a strong suspicion that your FSM is not working as you would expect.

@jschwalbe, not sure which “bill of goods” you feel cheated on. @Vitesze’s challenges with coding a solution is not a reflection of the Asset Tracker’s capabilities!

2 Likes