Communications optimize battery usage

Hi.

I am trying to develop a companion app that will get data (historic HR) from the Garmin Vivofit HR and show historic calories graph on the phone app. I also want to send back the calorie values to the vivofit (including the intake calories logged by the user, to be shown on the vivofit display as a graph).

On my current prof of concept, the battery power usage is about 4x than a watchface and I believe the issue is with the communications (here is the code: https://github.com/DailyWeightFatControl/garmin_watchface/tree/watchapp1). Can someone please help me with suggestions for optimizations?

I plan to have the phone app asking at any time the Garmim device app for the historic HR and send back the users logged intake calories. Maybe I can define define some specific frequency and not have the communications always on? would that be possible?

NOTE: I am an ex Pebble user and I were starting to develop this app when they shutdown and were bought by Fitbit :-( :-(
  • If you're doing a widget, they have a short running life (about a minute), so it would be: send the HR history, get the calorie data, display it, and be done.

    If you're doing an app, it could be the same, but maybe only do the comm every 15-60 minutes after the first exchange. But you could also draw the HR history graph every minute during those times (on the va-hr, the history is updated about every 2 minutes)

    I assume you're getting the HR from getHeartRateHistory() and not starting up the HRM and building your own history, as that alone will impact the battery and you have no data unless your app is running. Using the 24/7 history (from the last 4 hrs) handles some of the times your app isn't running. You get the same HR graph as in the native HR widget if you look at all 148 available samples.

    In an app you can also save battery by controlling how often the screen updates. If the data is only changing every couple minutes, there's no need to update the screen every second, for example.
  • I think you're tossing half of your data away. Every call to next() advances the iterator, so you are getting the time from one sample and the data from the next one.

    var HRSensorHistoryIterator = SensorHistory.getHeartRateHistory({
    :period => duration,
    :order => SensorHistory.ORDER_NEWEST_FIRST
    });
    var when = HRSensorHistoryIterator.next().when(); // sample 1
    var hrValue = HRSensorHistoryIterator.next().data(); // sample 2

    while (hrValue != null) {
    dataArray.add(when);
    dataArray.add(hrValue);

    var when = HRSensorHistoryIterator.next().when(); // samples 3, 5, 7, ...
    var hrValue = HRSensorHistoryIterator.next().data(); // samples 4, 6, 8, ...
    }


    You are also breaking the loop at the most recent failure to acquire heart rate, not when you've run out of data. This will cause the graph to be cut short in some cases (user took watch off for a few minutes). To do it right...

    var historyIterator = SensorHistory.getHeartRateHistory({
    :period => duration,
    :order => SensorHistory.ORDER_NEWEST_FIRST
    });

    var historySample = historyIterator.next();
    while (historySample != null) {
    var data = historySample.data();

    // if you really want to stop at the first bad sample
    //if (data == null) {
    // break;
    //}

    var when = historySample.when();

    dataArray.add(data);
    dataArray.add(when);

    historySample = historyIterator.next();
    }


    Additionally, you extend Ui.WatchFace and use the onEnterSleep and onExitSleep methods that are only called for watch faces, but you are using the Communications module, which is not available to watch faces. You also use Timers in low power mode, which is not allowed.

    It seems you want to extend Ui.View, and roll your own low power code. I posted some code a while back that tries to fake a watch face like That. You can find that code here. That said, it seems we both have a bug with the low power handling. In some cases, you might not see the displayed minute value change for more than one minute.. Imagine that at ten seconds past 1:21 the timer is switched being called every minute. The screen would be immediately refreshed to show 1:21, one minute would elapse, and finally the screen would update to 1:22... after showing 1:21 for one minute and 50 seconds. The fix is pretty easy, just making sure you see the potential problem.

    Travis
  • New code, just date incorrect

    Please see here my latest code after review with your suggestions: https://github.com/DailyWeightFatControl/garmin_watchface/blob/watchapp-test_comm1/source/DFC_garmin_watchfaceView.mc

    function sendHR() {
    Comm.setMailboxListener(mailMethod);
    var listener = new CommListener();

    var HRSensorHistoryIterator = SensorHistory.getHeartRateHistory(
    {
    :period => 10,
    :order => SensorHistory.ORDER_NEWEST_FIRST
    });

    var HRSample = HRSensorHistoryIterator.next();
    var dataArray = []; // array size will be increased as needed using .add()

    // Starting building the command response
    dataArray.add(HISTORIC_HR_COMMAND);
    dataArray.add(HISTORIC_HR_COMMAND);
    while (HRSample != null) {

    dataArray.add(HRSample.when.value());
    dataArray.add(HRSample.data);

    HRSample = HRSensorHistoryIterator.next();
    }

    System.println("timeNow " + Time.now().value());
    System.println("dataArray " + dataArray);

    // Transmit command response
    Comm.transmit(dataArray, null, listener);
    }


    So now I can see the data (UTC seconds) and HR values ok on the simulator. On the Vivoactive HR, the HR values are ok but the date values are really wrong :-( :-(

    Please not that I am using "HRSample.when.value()" and not "HRSample.when" as suggested - but just like this it works.

    Data values on simulator:
    Copying file.... 94% complete
    Copying file.... 97% complete
    Copying file.... 99% complete
    Copying file.... 100% complete
    File pushed successfully
    Connection Finished
    Closing shell and port
    Found Transport: tcp
    Connecting...
    Connecting to device...
    Device Version 0.1.0
    Device id 1 name "A garmin device"
    Shell Version 0.1.0
    timeNow 1482242743
    dataArray [-24576, -24576, 1482242743, 80, 1482242676, null, 1482242609, 84, 1482242542, 81, 1482242475, 76, 1482242408, 75, 1482242341, 79, 1482242274, 83, 1482242207, 85, 1482242140, 82]
    send er 1482242747
    timeNow 1482242748
    dataArray [-24576, -24576, 1482242743, 80, 1482242676, null, 1482242609, 84, 1482242542, 81, 1482242475, 76, 1482242408, 75, 1482242341, 79, 1482242274, 83, 1482242207, 85, 1482242140, 82]
    send er 1482242749
    timeNow 1482242753
    dataArray [-24576, -24576, 1482242743, 80, 1482242676, null, 1482242609, 84, 1482242542, 81, 1482242475, 76, 1482242408, 75, 1482242341, 79, 1482242274, 83, 1482242207, 85, 1482242140, 82]
    send er 1482242755
    Complete
    Connection Finished
    Closing shell and port


    Data values that are sent to the Android app:

  • Wow Travis - I can't believe you found that thread from March 2015! I didn't even try to search for it :)
  • Thank you guys for the very helpful answers!!!

    My issue now is more about how to securely save the HR history of at least a full day. For what I can understand, then just the last 4h are saved by the system while I were expecting at least 24h (on Pebble were 1 full week!!). I am now looking at Android Wear devices to find if they store a larger period like at least 24h - but I would prefer to keep Garmin if I found a reliable way to keep the history.

    Can you guys please help me to understand a possible solution to store the most large period of HR historic data and have that available for the companion app??

    - The apps can't save and read data from persistent storage, right?
    - The user can exit an app and not run it again, and so the history will be loose, right?
    - Is there a way to run an app on background and record the HR historic data to persistent storage?

    Thank you in advance.
  • - The apps can't save and read data from persistent storage, right?

    Apps have an object store that can hold up to 8k. Data saved there is available anytime the app runs again.
    - The user can exit an app and not run it again, and so the history will be loose, right?

    if you use the Object store, it won't be lost, just not updated.
    - Is there a way to run an app on background and record the HR historic data to persistent storage?

    There's no way to do any background processing on the watch. The app has to be running in the foreground.
  • My app now get the date and HR values from Garmin Vivoactive HR. With this values, the app calculates the calories I spent and write them to a database (also do sum for daily calories) and next step I want to show the daily calories on the app graph.

    I am running the watchapp on the Garmin full time and seeing battery going down at about 2%/hour or sometimes even higher... I guess like this the battery will only last for 1day and half :-( :-(

    Pictures of the date, HR and calorie values on the app database:





    Code is OpenSource, here: https://github.com/DailyFatControl/
  • And today finally I have 2 graphs, one showing active calories for the day only and the other showing total calories for the day.

    I am using 1 minute resolution and doing approximations since HR period on my Garmin is each 95 seconds while I am using the 60 seconds resolution. The code is here: https://github.com/DailyFatControl/DailyFatControl-AndroidApp

    I would love if someone can review the code/formulas for the calories calculation here: https://github.com/DailyFatControl/DailyFatControl-AndroidApp/blob/master/app/src/main/java/comdailyweightfatcontrol/httpsgithub/dailyfatcontrol_androidapp/Calories.java
    And here: https://github.com/DailyFatControl/DailyFatControl-AndroidApp/blob/master/app/src/main/java/comdailyweightfatcontrol/httpsgithub/dailyfatcontrol_androidapp/GraphData.java

  • residents calle

    So, I am now using my app in full - it now synchronizes automatically with the phone app and shows the graphs as I wanted. And I am getting results!! my weight get's lower when I do sports and ate like less 500 cals on that day. I can clearly see that on some days when I ate more then I spent, my weight increases. This app is being helping me a lot!!

    So, now I would like to optimize battery usage, which currently is about 1.6% every hour. When I had communications all the time enable I had about 1.8% per hour but now, every 2 minutes I sync with the app and I just keep the communications 10 seconds enable on that every 2 minutes. I am disabling the communications like this:

    Comm.setMailboxListener(null);
    mailMethod = null;


    Can someone please help me to lower the battery usage? I am not sure I am correctly disabling the communications...
    The code is OpenSource and is here: https://github.com/DailyFatControl/garmin_watchface/tree/watchapp-test_comm1/source





  • every 2 minutes I sync with the app and I just keep the communications 10 seconds enable on that every 2 minutes.

    Do you really need to transmit the data every 2 minutes? Could you just collect data as frequently as you need and do the transmit every 10/15/30 minutes? It seems that would make a big difference.

    Can someone please help me to lower the battery usage? I am not sure I am correctly disabling the communications.

    I've not implemented an app to talk to a mobile device application, but it seems like what you're doing would work if you want to prevent incoming messages (from the mobile device to the garmin). If you are just transmitting, you don't really need to do that at all. I'd think that you'd set the listener just once and leave it alone. As long as the mobile application isn't sending data, the garmin shouldn't pay a penalty... Again, I have no evidence to support these claims, so some testing would be in order to verify.

    The code is OpenSource and is here: https://github.com/DailyFatControl/garmin_watchface/tree/watchapp-test_comm1/source

    I'll take a peek at the code after work this evening. I'm not sure if I'll come up with something or not, but I'll take a peek.

    Travis