Watch face too high power consumption

Hi,

I have three issues with my Watch Face, all related to power consumption. The Watch Face I programmed consumes way too much power: I think its battery is drained in two to three days.

1/ The watch rarely goes in low power mode.
I programmed a counter. When the watch face launches, it initially has a good % of low power time (like 60 to 80%) and I witness the watch being on low power mode. Over time, this percentage decreases to about 5% and I witness the watch being in normal power mode (as opposed to low power mode) most of the time.


2/ Low power mode onEnterSleep() function never runs.

//! The user has just looked at their watch. Timers and animations may be started here.
function onExitSleep() {
awake = true;
}


//! Terminate any active timers and prepare for slow updates.
function onEnterSleep() {
awake = false;
Ui.requestUpdate();

}


The rest of the program is designed to remove the second hand display (else it would be stuck at some random position) when awake is false.

I assume the onEnterSleep function will run once when low power mode becomes active.
On my watch, this function never triggers. I've made some tests, the onEnterSleep() function is just never triggered, and instead I have a second hand that is stuck where it was last updated.

This did not happen on the simulator. Simulator worked well, with second hand being removed when entering low power mode.


3/ Redraw all vs. selective
When programming the Watch face, one can (1) redraw everything each second (clear screen, then draw all), or (2) just redraw incrementally (if the step number changed, erase the just existing step number and redraw it).

On my watch, the execution time for (1) is 250ms, for (2) it's 60ms.
To me, that explains why the battery is drained so fast.

I tried to do incremental redraws. It works.
But once you have a notification and return from it, the screen should be refreshed but it's not, and so you have a weird superimposition of the previous notification with the newly selectively updated data. Not acceptable for a watch face.
Also, when coming back from widgets, the screen is simply white and not refreshed.
Same happens when exploring the settings menu if I recall correctly.

When returning from choosing an activity (Swim, Run, etc..) however, the screen is refreshed (presumably because the watch face is relaunched).

The problem is that there is no trigger when the watch comes back from notification or widget or menu, so I can't ask the screen to refresh/redraw everything.

Question/

Do you have experience with this? Same issue or just me? Any idea to solve these issues?

Thanks.
  • Are you extending Ui.View or Ui.WatchFace?
  • I extend Ui.WatchFace

    class WafaApp extends App.AppBase {

    //! onStart() is called on application start up
    function onStart() {
    }

    //! onStop() is called when your application is exiting
    function onStop() {
    }

    //! Return the initial view of your application here
    function getInitialView() {
    return [ new WafaView() ];
    }

    }

    class WafaView extends Ui.WatchFace {
  • Can you please share code on how you do selective redrawing?

    I am curious as it is something that I haven't figured out.

    How do you erase just a step counter? Do you draw a fillrectangle over it before writing the new step counter?

    Or do you have a drawable instance that you clear?
  • Former Member
    Former Member over 9 years ago
    I had the same issue where I display the seconds in high power mode and then the last drawn seconds value would remain when it entered low power mode until the next update. I couldn't get a UI update to get triggered, so in the end what ended up working for me is grabbing a handle to the drawing context early on and then using it when onEnterSleep is called to overlay a rectangle over the seconds like so:

    class MyWatchFaceView extends Ui.WatchFace {

    var dcHandle;

    function initialize() {
    ...
    drawSeconds = false;
    }

    function onLayout(dc) {
    ...
    dcHandle = dc;
    }

    function onUpdate(dc) {

    ...
    View.onUpdate(dc);
    ...

    // Draw seconds or black rectangle at correct location
    if (drawSeconds) {
    // second (hand) drawing code goes here
    } else {
    dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
    dc.fillRectangle([rectangle parameters]);
    }
    }

    function hideSeconds() {
    // need to check drawing context handle is set because onEnterSleep could be called before onLayout
    if (dcHandle) {
    dcHandle.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
    dcHandle.fillRectangle([rectangle parameters]);
    }
    }

    //! The user has just looked at their watch. Timers and animations may be started here.
    function onExitSleep() {
    drawSeconds = true;
    }

    //! Terminate any active timers and prepare for slow updates.
    function onEnterSleep() {
    drawSeconds = false;
    hideSeconds();
    }

    }

  • I had the same issue where I display the seconds in high power mode and then the last drawn seconds value would remain when it entered low power mode until the next update. I couldn't get a UI update to get triggered, so in the end what ended up working for me is grabbing a handle to the drawing context early on and then using it when onEnterSleep is called to overlay a rectangle over the seconds like so:

    class MyWatchFaceView extends Ui.WatchFace {

    var dcHandle;

    function initialize() {
    ...
    drawSeconds = false;
    }

    function onLayout(dc) {
    ...
    dcHandle = dc;
    }

    function onUpdate(dc) {

    ...
    View.onUpdate(dc);
    ...

    // Draw seconds or black rectangle at correct location
    if (drawSeconds) {
    // second (hand) drawing code goes here
    } else {
    dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
    dc.fillRectangle([rectangle parameters]);
    }
    }

    function hideSeconds() {
    // need to check drawing context handle is set because onEnterSleep could be called before onLayout
    if (dcHandle) {
    dcHandle.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
    dcHandle.fillRectangle([rectangle parameters]);
    }
    }

    //! The user has just looked at their watch. Timers and animations may be started here.
    function onExitSleep() {
    drawSeconds = true;
    }

    //! Terminate any active timers and prepare for slow updates.
    function onEnterSleep() {
    drawSeconds = false;
    hideSeconds();
    }

    }




    Actually, saving off the dc may not be safe. The easy way to do this is to call Ui.requestUpdate() at end of onEnterSleep() and onExitSleep(). That way onUpdate() will be called when it goes in and out of low power mode. Then in onUpdate(), do a dc.clear() and based on your drawSeconds boolean, you either draw the seconds or don't.

    Here's the default "new project/watchface", but I added seconds to the display when not in LP mode to show what I mean:

    class clockView extends Ui.WatchFace {

    var showSeconds=false;

    //! Load your resources here
    function onLayout(dc) {
    setLayout(Rez.Layouts.WatchFace(dc));
    }

    //! Called when this View is brought to the foreground. Restore
    //! the state of this View and prepare it to be shown. This includes
    //! loading resources into memory.
    function onShow() {
    }

    //! Update the view
    function onUpdate(dc) {
    // Get and show the current time
    var clockTime = Sys.getClockTime();
    var timeString;
    if(showSeconds) {
    timeString = Lang.format("$1$:$2$:$3$", [clockTime.hour, clockTime.min.format("%.2d"),clockTime.sec.format("%02d")]);
    } else {
    timeString = Lang.format("$1$:$2$", [clockTime.hour, clockTime.min.format("%.2d")]);
    }
    var view = View.findDrawableById("TimeLabel");
    view.setText(timeString);

    // Call the parent onUpdate function to redraw the layout
    View.onUpdate(dc);
    }

    //! Called when this View is removed from the screen. Save the
    //! state of this View here. This includes freeing resources from
    //! memory.
    function onHide() {
    }

    //! The user has just looked at their watch. Timers and animations may be started here.
    function onExitSleep() {
    showSeconds=true;
    Ui.requestUpdate();
    }

    //! Terminate any active timers and prepare for slow updates.
    function onEnterSleep() {
    showSeconds=false;
    Ui.requestUpdate();
    }

    }
  • I replied 2 days ago but seems the message never got posted... Anyways.

    My problem is that the watch face never enters onEnterSleep(). I tested this by displaying a text in onEnterSleep() but nothing gets displayed. Any idea how to let the watch enter this function before going to sleep?

    That's frustrating :/ I'll test some more and come back here with results.



    To answer Hermot's question, I would just draw a shape over what I want to erase. In some cases, I can just write a text with text background matching the watch face background, so that you save the operation of drawing a shape.
  • I tested this by displaying a text in onEnterSleep() but nothing gets displayed. Any idea how to let the watch enter this function before going to sleep?


    You can't display anything from onEnterSleep(). The best you can do is set a flag to change what is displayed by onUpdate(). For example...

    hidden var _sleepState;

    function initialize() {
    _sleepState = "Unknown";
    }

    function onEnterSleep() {
    _sleepState = "Sleeping";
    Ui.requestUpdate();
    }

    function onExitSleep() {
    _sleepState = "Awake";
    Ui.requestUpdate();
    }

    function onUpdate(dc) {
    var cx = dc.getWidth() / 2;
    var cy = dc.getHeight() / 2;
    var font = Gfx.FONT_MEDIUM;

    dc.drawText(cx, cy, font, _sleepState, Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
    }


    If you build a watch face with this code in it and it doesn't indicate the device is in sleep mode, then you might want to look at the firmware on your device. Is it up to date?

    Travis
  • Former Member
    Former Member over 9 years ago
    Actually, saving off the dc may not be safe. The easy way to do this is to call Ui.requestUpdate() at end of onEnterSleep() and onExitSleep(). That way onUpdate() will be called when it goes in and out of low power mode. Then in onUpdate(), do a dc.clear() and based on your drawSeconds boolean, you either draw the seconds or don't.


    Thanks for the heads up. I was unaware of the Ui.requestUpdate() method before this thread. I'll go back and revise my code. Thanks!
  • Travis,

    I tried your code but my watch doesn't work.

    Here is the video to show what happens, both in the simulator and the watch.

    www.youtube.com/watch

    Thanks,
    Julien
  • Former Member
    Former Member over 9 years ago
    Travis,

    I tried your code but my watch doesn't work.

    Here is the video to show what happens, both in the simulator and the watch.

    Thanks,
    Julien


    Hey Julien,

    This looks like a device bug. I will get it reported to the Fenix team.