Always on seconds freeze after two minutes

Former Member
Former Member

Dear Garmin Developers,

I am working on a new watch face and I would like to implement always on seconds by using onPartialUpdate(). I implemented the code so far, but for some reason, the seconds freeze after two minutes. Can someone help? Heres the code:

using Toybox.WatchUi;
using Toybox.Graphics;
using Toybox.System;
using Toybox.Lang;
using Toybox.Application;
using Toybox.Graphics as Gfx;
using Toybox.System as Sys;

class OneHerzTestView extends WatchUi.WatchFace {

    function initialize() {
        WatchFace.initialize();
    }

    // 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 the current time and format it correctly
        var timeFormat = "$1$:$2$";
        var clockTime = System.getClockTime();
        var hours = clockTime.hour;
        if (!System.getDeviceSettings().is24Hour) {
            if (hours > 12) {
                hours = hours - 12;
            }
        } else {
            if (Application.getApp().getProperty("UseMilitaryFormat")) {
                timeFormat = "$1$$2$";
                hours = hours.format("%02d");
            }
        }
        var timeString = Lang.format(timeFormat, [hours, clockTime.min.format("%02d")]);

        // Update the view
        var view = View.findDrawableById("TimeLabel");
        view.setColor(Application.getApp().getProperty("ForegroundColor"));
        view.setText(timeString);
        
        // Call the parent onUpdate function to redraw the layout
        View.onUpdate(dc);
        
        DoSeconds(dc);
    }

    function onPartialUpdate(dc) {
		DoSeconds(dc);
    }
    function DoSeconds(dc) {
   		dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_TRANSPARENT);
    	dc.fillRectangle(160, 110,35,20);
    	dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_TRANSPARENT);
    	dc.drawText(160, 120,0,":"+Sys.getClockTime().sec.format("%02d"), 2 | 4);
    }

    // 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() {
    }

    // Terminate any active timers and prepare for slow updates.
    function onEnterSleep() {
    }

}

Thanks a lot.

Greetings,

GreanBean.

  • If your onPartialUpdate function takes too much time, then after a couple of minutes the always on seconds become disabled (onPartialUpdate stops being called). In order to keep running, onPartialUpdate needs to average less than 30ms per call, over the whole minute.

    This was the thread I got a lot of useful information from when I was learning about it: https://ciq.forums.garmin.com/developer/connect-iq/f/discussion/5156/1hz-watch-faces---q-a

    In the simulator if you turn on "View watchface diagnostics" it gives you the timings for onPartialUpdate so you can work out how long it is taking and which areas need optimizing etc.

    It doesn't look like you are drawing that large an area of the screen, but maybe you are just over the limit ... or maybe just adding a dc.setClip to match your rectangle size is enough to fix it.

  • Former Member
    Former Member in reply to markdotai

    Dear Mark,

    awesome, thanks alot for that short and precise hint. I wonder how much time you can save by replacing the dc.fillRectangle() through the dc.setClip() command. For me, this fixed my bug.

    Many thanks!

    GreenBean.

  • It looks like you're not doing the dc.setClip().  Because you're not, you're actually doing the full screen update, and exceeding the power limit after the first full minute.

    an easy fix here is add a 2nd paramerter to DoSeconds called "doFull".  Set it true in onUpdate, false in onParttialUpdate.

    Then in DoSeconds, if false, do a dc.SetClip() for the area you need if it's false for the same area as the fillRecatangle().  

    It can be a bit smarter in that you don't have to clear whats there if called from onUpdate().

    Now you must reset that too, so at the start of onUpdate(), do a dc.clearClip();

  • See below - if you don't do the dc.clearClip, what happens is only the seconds will update.  The minutes won't change as you'd clipped the size of how much of the display gets updated..

  • Another thing here, is with a watch face that uses onPartialUpdate, you can have a delegate so you get notified when you exceed the budget.

    What I do in mine is if I exceed the budget, I continue to run but in the same mode as I do on a watch that doesn't support 1hz seconds.  In the thread about 1hz, I posted a simple watchface that will show how to do this