Edge 1000 Generic ANT Quits Working After Sleep

I'm seeing a problem with the Generic ANT channel after the Edge 1000 wakes up from sleep.

To reproduce the problem:
1) Install the Moxy Data Field on a Data page. Leave the Sensor number set to 0 for wildcard search.
2) Open a Ride but don't start it.
3) Go to the data page with the Moxy Data Field on it.
4) Wait for the Edge to pair to a Moxy Sensor (SimulANT works fine too)
5) Let the Edge go to sleep or make it sleep by doing a short press on the power button.
6) Wait 30 seconds.
7) Wake up the Edge with a short press on the power button.

The Moxy Data field will be frozen on the values it was displaying before the Edge went to sleep.

The only way to recover from this is to reinstall the data field or to shut down the Edge completely.

I made a work around to fix this, but it's pretty ugly. I had to null out the Generic ANT channel and recreate it. Just closing it and reopening it would not allow it to start working again.

There are no messages coming in to the Generic ANT channel so I had to make a timer to detect how long it's been since a message was received.

Here's the code snippets I came up with for a work around.

In the onMessage function in the Generic Ant channel, I record the time of the last message.

function onMessage(msg) {
var payload = msg.getPayload();
msgTime = Sys.getTimer()/1000;


In onUpdate, I check how long it's been since the last message was received. If it's been too long, then I null and recreate the ANT channel. This will also get tripped if the DataField simply doesn't find a sensor in 10 seconds. This is pretty ugly because the Moxy Data field is frozen on the old readings for 10 seconds after waking up. Maybe there's a better way to detect that the Generic ANT channel is dead???

if (now - sensor.msgTime > 10) {
App.getApp().resetSensor();
}


Then in resetSensor, I null and recreate the Generic Channel.

function resetSensor() {
var sensorId = 0;
var sensorLx = getProperty("SensorLx").toNumber();
if (mSensor != null) {
var sensorId = mSensor.getId(); //If we've already paired to a sensor, remember the sensor number
}
mSensor = null;//null the dead Generic Ant Channel
try {
mSensor = new MoxySensor(sensorId, sensorLx);//create a new Generic Ant Channel
mSensor.open(); //Open the new channel
}
catch (exception) {
mSensor = null;
}
if (mView != null) {
mView.setSensor(mSensor);//pass the new channel into the View
}
}


It doesn't seem like this should be the intended functionality for a Generic ANT channel after waking up from sleep. A native ANT channel like cadence picks right up with new readings immediately upon reawakening.

Is this a bug in the Edge or should I keep working on a more elegant way to work around this issue?
  • It seems that the right thing to do would be to shut it all down when entering sleep and then build it all back up when returning from sleep. Unfortunately, I don't see anything that lets you know the device is going in and out of low power mode. I don't think the AppBase methods onStop/onStart() should be getting called in this case, so it seems that there may be a need for some new callback. Perhaps adding something like onEnterSleep/onExitSleep() but on the application object instead of the derived view.

    It does seem that if you're keeping track of the last time that an ANT+ message was received, you should be able to tell if the device has gone to sleep; there should be no ANT+ messages coming in while in sleep mode, and the first thing that the device should need to do after exiting sleep is refresh the display. If this is the case, it seems that your code _should_ reset the sensor immediately after the first redraw.
  • I suppose it is possible that the ANT+ callback gets invoked before draw callback, possibly with some stale data, and this is what is causing some of the trouble. It seems hacky, but you might be able to handle this all in your onMessage() callback...

    hidden var previousMessageTime;

    function onMessage(payload)
    {
    var currentMessageTime = Sys.getTimer();
    if (self.previousMessageTime == null) {
    self.previousMessageTime = currentMessageTime;
    }

    var elapsedTimeBetweenMessages = (currentMessageTime - self.previousMessageTime);
    self.previousMessageTime = currentMessageTime;

    if (elapsedTimeBetweenMessages > 1000) {
    // we didn't get a message in more than a second. assume the worst and re-connect to the sensor
    }
    else {
    // process the message
    }
    }


    Travis
  • It looks like I might be better off using Time.now() instead of Sys.getTimer(). The getTimer() timer seems to go to sleep too so it takes a while for my check to time out for not getting any new messages after waking up.

    I made a printout of the Sys.getTimer() in the first column and the Time.now() in the second column. I post processed the numbers so they are both in seconds and both start at 0 at the top of the list.

    The Edge goes to sleep at timenow of 12 seconds (no more onUpdate calls) but the onMessage continues writing until timenow of 28 seconds. When it wakes up, the getTimer has only advanced to 30.977, but timenow is at 161 seconds. (Timenow is correct since I had let it sit asleep for about 2 minutes).

    I have my timeout set to 10 seconds so it needs to wait until 38 seconds before recreating the channel.

    I don't think I can do this all inside of onMessage because I need to null out the Generic Channel and recreate a new one in order to get things back to life. Closing and re-opening is insufficient to get it going again.

    I checked and onShow is not called on waking up either so a timer is the only way I've come up with to revive the ANT channel. The timer will also get tripped if you simply don't pair to a sensor before the time out so this is a bit of a hacky solution.

    onMessage: 0 timenow 0
    onMessage: 0.005 timenow 0
    onMessage: 0.006 timenow 0
    onMessage: 0.007 timenow 0
    onMessage: 0.007 timenow 0
    onMessage: 0.008 timenow 0
    onMessage: 0.009 timenow 0
    onMessage: 0.01 timenow 0
    onMessage: 0.175 timenow 0
    onMessage: 0.425 timenow 0
    onMessage: 0.675 timenow 0
    onMessage: 0.925 timenow 1
    onMessage: 1.175 timenow 1
    onMessage: 1.425 timenow 1
    onMessage: 1.675 timenow 1
    onMessage: 1.925 timenow 2
    onMessage: 2.175 timenow 2
    onMessage: 2.425 timenow 2
    onMessage: 2.675 timenow 2
    onMessage: 2.925 timenow 3
    onMessage: 3.175 timenow 3
    onMessage: 3.425 timenow 3
    onMessage: 3.675 timenow 3
    onMessage: 3.925 timenow 4
    onMessage: 4.175 timenow 4
    onMessage: 4.425 timenow 4
    onMessage: 4.675 timenow 4
    onMessage: 4.925 timenow 5
    onMessage: 5.175 timenow 5
    onMessage: 5.425 timenow 5
    onMessage: 5.675 timenow 5
    onMessage: 5.925 timenow 6
    onMessage: 6.175 timenow 6
    onMessage: 6.425 timenow 6
    onMessage: 6.675 timenow 6
    onMessage: 6.925 timenow 7
    onMessage: 7.175 timenow 7
    onUpdate: 7.608 timenow 7
    onMessage: 7.817 timenow 7
    onUpdate: 7.829 timenow 7
    onMessage: 7.877 timenow 8
    onMessage: 7.925 timenow 8
    onMessage: 8.175 timenow 8
    onMessage: 8.425 timenow 8
    onUpdate: 8.524 timenow 8
    onMessage: 8.675 timenow 8
    onMessage: 8.925 timenow 9
    onMessage: 9.175 timenow 9
    onMessage: 9.425 timenow 9
    onUpdate: 9.525 timenow 9
    onMessage: 9.675 timenow 9
    onMessage: 9.925 timenow 10
    onMessage: 10.175 timenow 10
    onMessage: 10.425 timenow 10
    onUpdate: 10.524 timenow 10
    onMessage: 10.675 timenow 10
    onMessage: 10.925 timenow 11
    onMessage: 11.175 timenow 11
    onMessage: 11.425 timenow 11
    onUpdate: 11.525 timenow 11
    onMessage: 11.675 timenow 11
    onMessage: 11.925 timenow 12
    onMessage: 12.175 timenow 12
    onUpdate: 12.428 timenow 12
    onMessage: 12.469 timenow 12
    onUpdate: 12.524 timenow 12
    onMessage: 12.675 timenow 12
    onMessage: 12.925 timenow 13
    onMessage: 13.175 timenow 13
    onMessage: 13.425 timenow 13
    onMessage: 13.675 timenow 13
    onMessage: 13.925 timenow 14
    onMessage: 14.175 timenow 14
    onMessage: 14.425 timenow 14
    onMessage: 14.675 timenow 14
    onMessage: 14.925 timenow 15
    onMessage: 15.175 timenow 15
    onMessage: 15.425 timenow 15
    onMessage: 15.675 timenow 15
    onMessage: 15.925 timenow 16
    onMessage: 16.175 timenow 16
    onMessage: 16.425 timenow 16
    onMessage: 16.675 timenow 16
    onMessage: 16.925 timenow 17
    onMessage: 17.175 timenow 17
    onMessage: 17.425 timenow 17
    onMessage: 17.675 timenow 17
    onMessage: 17.925 timenow 18
    onMessage: 18.175 timenow 18
    onMessage: 18.424 timenow 18
    onMessage: 18.674 timenow 18
    onMessage: 18.924 timenow 19
    onMessage: 19.174 timenow 19
    onMessage: 19.424 timenow 19
    onMessage: 19.674 timenow 19
    onMessage: 19.924 timenow 20
    onMessage: 20.174 timenow 20
    onMessage: 20.424 timenow 20
    onMessage: 20.674 timenow 20
    onMessage: 20.924 timenow 21
    onMessage: 21.174 timenow 21
    onMessage: 21.424 timenow 21
    onMessage: 21.674 timenow 21
    onMessage: 21.924 timenow 22
    onMessage: 22.174 timenow 22
    onMessage: 22.424 timenow 22
    onMessage: 22.674 timenow 22
    onMessage: 22.924 timenow 23
    onMessage: 23.174 timenow 23
    onMessage: 23.424 timenow 23
    onMessage: 23.674 timenow 23
    onMessage: 23.924 timenow 24
    onMessage: 24.174 timenow 24
    onMessage: 24.424 timenow 24
    onMessage: 24.674 timenow 24
    onMessage: 24.924 timenow 25
    onMessage: 25.174 timenow 25
    onMessage: 25.424 timenow 25
    onMessage: 25.674 timenow 25
    onMessage: 25.924 timenow 26
    onMessage: 26.174 timenow 26
    onMessage: 26.424 timenow 26
    onMessage: 26.674 timenow 26
    onMessage: 26.924 timenow 27
    onMessage: 27.174 timenow 27
    onMessage: 27.424 timenow 27
    onMessage: 27.674 timenow 27
    onMessage: 27.924 timenow 28
    onUpdate: 30.977 timenow 161
    onUpdate: 31.475 timenow 162
    onUpdate: 32.475 timenow 163
    onUpdate: 33.475 timenow 164
    onUpdate: 34.475 timenow 165
    onUpdate: 35.475 timenow 166
    onUpdate: 36.486 timenow 167
    onUpdate: 37.475 timenow 168
    onUpdate: 38.475 timenow 169
    reopening
    onMessage: 38.567 timenow 169
    onMessage: 38.568 timenow 169
    onMessage: 38.569 timenow 169
    onMessage: 38.57 timenow 169
    onMessage: 38.571 timenow 169
    onMessage: 38.572 timenow 169
    onMessage: 38.573 timenow 169
    onMessage: 38.579 timenow 169
    onMessage: 38.606 timenow 169
    onMessage: 38.856 timenow 169
    onMessage: 39.106 timenow 170
    onMessage: 39.356 timenow 170
    onUpdate: 39.475 timenow 170
    onMessage: 39.606 timenow 170
    onMessage: 39.856 timenow 170
    onMessage: 40.106 timenow 171
    onMessage: 40.355 timenow 171
    onUpdate: 40.482 timenow 171
    onMessage: 40.605 timenow 171
    onMessage: 40.855 timenow 171
    onMessage: 41.105 timenow 172
    onMessage: 41.355 timenow 172
    onUpdate: 41.481 timenow 172
  • I'm going to file a couple of JIRAs about this. There must be some king of bug behind the initial problem reported that's causing the freeze. But the conversation here has highlighted the need for onEnterSleep/onExitSleep outside the context of a watch face.

    I'll let you know if we need more info.
  • We had the device team and ANT team look into this, and here's some of the comments I received:

    ...the issue is that when the Edge enters low power, we power down the ANT module and forget the state of all running channels (we also reset the state of the ANT radio stack). Then when the Edge exits low power mode the data field expects its channel to still be open. Calling open/close after exiting low power wouldn't work in this case as the original channel would have been reset.

    Ideally, a data field would have some method to know when the device is entering/exiting low power mode to work around this. Another solution could be to have TVM and ANT remember the state of the Generic Channels so we can auto-reopen them again, however I'm not very comfortable doing this as it could get messy.

    Until we get a fix in for this an app developer will need to set up a watchdog for their channel where if they don't receive a message for X seconds (with X being larger than their search timeout), the developer will reinitialize their channel.


    You're already implementing the short-term solution, and we have a ticket open to provide a way for a non-watchface app to know when the device is entering or exiting low power mode, so I think we have out bases covered at this point.
  • still there:  https://forums.garmin.com/developer/connect-iq/f/discussion/251444/datafield-and-device-sleep

    Would be nice to have an event for when the device wakes from sleep (guessing going to sleep may be hard)

  • You can detect this in your own code.  Even if a data field isn't visible, compute() gets called every second.  If the time between calls is greater than say 2 seconds, the device has been sleeping.

  • hmm should have thought about that.  It works but exposes a new bug.  Say I have two data fields connected to two different moxy's.  One data field I fix by detecting the length of time since the last compute and if greater then  2 seconds release the GenericChannel and then create a new one.  This works fine for that one data field.  But the other data field that doesn't have this fix?  That one acts like it is paired to the same sensor even though the deviceNumber it is paired to is completely different. 

    Also seems like there are other reasons the lag time is over 2 seconds so working on that...  (so a method to know when coming out of sleep would still be good)

  • Does searchTimeoutLowPriority even matter?  You'll get a MSG_CODE_EVENT_RX_SEARCH_TIMEOUT event but if you don't do anything based on it the search continues.  So the better timing to see if active is:
    - if in search mode check if the last message received was within that timeout (if the timeout is 25 seconds you'll at a minimum get a MSG_CODE_EVENT_RX_SEARCH_TIMEOUT every 25 seconds)
    - if not in search mode the messages should come based on messagePeriod but to be safe for missing data could do a longer window like 2 seconds

    Then no dependency on compute being called every second