How to get current Hearth Rate on Watch Faces?

Former Member
Former Member
I developed a quick Watch Face and want to display the current Hearth Rate exactly same way as Hearth Rate widget.

I used this code to achieve that:

var HRH=Act.getHeartRateHistory(1, true);
var HRS=HRH.next();
var HRSnow=HRS.heartRate;

if(HRSnow!= Act.INVALID_HR_SAMPLE){
heartRateText = HRSnow;
}

The problem with this is that it refreshes (only) each minute, so it's not accurate at all. Is there a way to get last Hearth Rate calculated by device and get it refreshed every second?

  • Info is a class. getActivityInfo() gets the data.
  • Do you have an idea why it works perfect on Fenix 5, but the HR does only update once/minute on Fenix5x? (info from a user)
    The WatchFace is on: https://apps.garmin.com/en-US/apps/89a9856a-ffd2-4c5d-bb55-6c52e51f49ca
  • See post 13 in this thread. There are HW differences on the f5x (and similar devices) and you'll not see the HR in Activity.Info.currentHeartrate. You only get the HR from history on those watches.
  • Former Member
    Former Member over 7 years ago
    Hello Seli17. Could you please share the final code? I've tried to check suggested "Activity.getActivityInfo().currentHeartRate" but looks like it was depreciated by now, so the result is always "000". I use SDK version 2.4.2, watchface for f5, 935

    Thanks a lot.
  • It's not depreciated, but it can be null (check for that). Are you seeing "0" or are you seeing null?

    var hr=Activity.getActivityInfo().currentHearRate;
    if(hr==null) {
    Sys.println("no hr available");
    } else {
    Sys.println("hr is "+hr.toString());
    }


    If you are just testing in the simulator, you need to be simulating data for it to be non-null (Simulation>FIT Data>Simulate Data for example)
  • Former Member
    Former Member over 7 years ago
    Hello Jim. Many thanks for your help.

    It is strange, that autocomplete feature does not work with "Activity.getActivityInfo().currentHearRate;". I see only Activity.getApp(). Ok, my be it is a bug. Sure, I know about simulating data in the simulator, but thanks for reminder.

    So my code is:
    //before initialize ()
    function GetHeartRate(dc, heartRateText, heartRate, currentHeartRate, heartRateHistory, heartRateSample) {
    if(currentHeartRate != null)
    {
    heartRateText = currentHeartRate;
    } else {
    heartRate = heartRateSample.heartRate();
    heartRateText = heartRate;
    }
    }

    // skip some code
    function initialize() {
    WatchFace.initialize();
    }

    function onLayout(dc)
    {

    }

    function onUpdate(dc) {
    var heartRateText = "000";
    var heartRate = -1;
    var currentHeartRate = Act.getActivityInfo().currentHeartRate;
    var heartRateHistory = AM.getHeartRateHistory(1, true); //newestFirst = true
    var heartRateSample = heartRateHistory.next();

    View.onUpdate(dc);

    // skip

    GetHeartRate(dc, heartRateText, heartRate, currentHeartRate, heartRateHistory,heartRateSample);
    if (currentHeartRate==null) {
    dc.drawText(HRx, HRy, Gfx.FONT_SYSTEM_XTINY, "HR null: "+heartRateText, Gfx.TEXT_JUSTIFY_CENTER);
    } else {
    dc.drawText(HRx, HRy, Gfx.FONT_SYSTEM_XTINY, "HR tostring: "+heartRateText.toString(), Gfx.TEXT_JUSTIFY_CENTER);
    }

    }



    Sorry for my code).

    At result I see at watchFace: HR tostring: 000, but expected some random HR...
  • Ok, in GetHeartRate, you pass in vales but you never return any. Nothing you pass in gets changed

    lt doesn't look like you actually have to pass anything as parameters, but rerun the result

    function GetHeartRate() {
    var ret="--';
    var hr=Act.getActivityInfo().currentHeartRate;
    if(hr!= null) {
    ret=hr.toString();
    } else {
    var hrI = AM.getHeartRateHistory(1, true);
    var hrs=hrI.next().heartRate;
    if(hrs!=null && hrs!=AM.INVALID_HR_SAMPLE) {ret=hrs.toString();}
    }
    return ret;
    }

    (there may be typos, as I just typed this in the post).
    It will return a string with the hr, or "--" if heart rate is not available
    If currentHeartRate isn't null, it gets returned if the newest reading in history is valid, return that heart rate.
    Notice the "return".

    dc.drawText(HRx, HRy, Gfx.FONT_SYSTEM_XTINY, "HR: "+GetHeartRate(), Gfx.TEXT_JUSTIFY_CENTER);


    unless you've checked before this, you want to make sure that getHeartrateHistory is available as it's only on watches with optical HR (use a "has" check)

    You don't need the 6 lines at the beginning of your onUpdate at all. or the "if" and those two dc.drawText() calls.
  • in your GetHeartRate() function, when you change one of the function parameters, that change is local, and the value in onUpdate won't change. You need to return the change.

  • Former Member
    Former Member over 7 years ago
    Jim, many thanks for your patience!

    As I'm a bad developer (honestly, I'm not a developer) , I could not realize why it does not work. I see error :
    Could not find symbol ret.
    Symbol Not Found Error
    in onUpdate (D:\My Documents\eclipse-workspace\ps\source\psView.mc:70)

    Please point me where I did a mistake.

    using Toybox.WatchUi as Ui;
    using Toybox.Graphics as Gfx;
    using Toybox.System as Sys;
    using Toybox.Lang as Lang;
    using Toybox.Activity as Act;
    using Toybox.ActivityMonitor as AM;

    class psView extends Ui.WatchFace {
    // sleep var
    var sleeping = false;

    // functions here
    //Heart Rate
    function GetHeartRate(dc) {
    var ret = "--";
    var hr = Act.getActivityInfo().currentHeartRate;
    if(hr != null) {ret = hr.toString();}
    else {
    var hrI = AM.getHeartRateHistory(1, true);
    var hrs = hrI.next().heartRate;
    if(hrs != null && hrs != AM.INVALID_HR_SAMPLE) {ret = hrs.toString();}
    }
    return ret;
    }
    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) {
    // Set variables
    var HRsupport = (AM has :getHeartRateHistory) ? true : false; //check HR support
    var ret = GetHeartRate(dc);

    // Get and show the current time
    var clockTime = Sys.getClockTime();
    var clock_hours = Lang.format("$1$", [clockTime.hour.format("%02d")]);
    var clock_minutes = Lang.format("$1$", [clockTime.min.format("%02d")]);
    //Redraw layout
    View.onUpdate(dc);
    // Normal mode (not sleep)
    if(sleeping == false)
    {
    dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
    dc.clear();
    //hours
    dc.setColor(Gfx.COLOR_BLUE, Gfx.COLOR_BLACK);
    dc.drawText(dc.getWidth() / 2 - 10, dc.getHeight() / 3, Gfx.FONT_NUMBER_THAI_HOT, clock_hours, Gfx.TEXT_JUSTIFY_RIGHT);

    //minutes
    dc.setColor(Gfx.COLOR_GREEN, Gfx.COLOR_BLACK);
    dc.drawText(dc.getWidth() / 2 + 10, dc.getHeight() / 3, Gfx.FONT_NUMBER_THAI_HOT, clock_minutes, Gfx.TEXT_JUSTIFY_LEFT);

    //Indicate mode
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.drawText(dc.getWidth() / 2, dc.getHeight() / 1.5, Gfx.FONT_SMALL, "normal mode", Gfx.TEXT_JUSTIFY_CENTER);

    // draw heart rate if supported
    if (HRsupport == true) {
    GetHeartRate(dc);
    dc.setColor(Gfx.COLOR_RED, Gfx.COLOR_BLACK);
    dc.drawText(dc.getWidth() / 2, dc.getHeight() / 1.25, Gfx.FONT_SMALL, ret, Gfx.TEXT_JUSTIFY_CENTER);
    }
    }
    else
    {
    //hours
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.drawText(dc.getWidth() / 2 - 10, dc.getHeight() / 3, Gfx.FONT_SMALL, clock_hours, Gfx.TEXT_JUSTIFY_RIGHT);

    //minutes
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.drawText(dc.getWidth() / 2 + 10, dc.getHeight() / 3, Gfx.FONT_SMALL, clock_minutes, Gfx.TEXT_JUSTIFY_LEFT);

    //Indicate mode
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.drawText(dc.getWidth() / 2, dc.getHeight() / 1.5, Gfx.FONT_SYSTEM_XTINY, "sleep mode", Gfx.TEXT_JUSTIFY_CENTER);
    }
    }

    function onHide() {
    }
    function onExitSleep() {
    sleeping = false;
    }
    function onEnterSleep() {
    sleeping = true;
    Ui.requestUpdate();
    }
    }

  • you need var ret=GetHeartrare(dc) in onupdate. or 2 lines later, in the drawText(), instead of ret, put GetHeartRate(dc) there.

    There's actually no need to pass dc to GetHeartRate(), as it's not used in that function.

    GetHeartRate() returns a value, but in onUpdate(), you're not saving that value. In onUpdate(), you want to use that value.