Creating a Simple Garmin Watch Face

So, I recently got into running and picked up a bargain Garmin watch. I looked on the marketplace for a simple watch face that had the attributes I wanted. I found nothing that suited my needs.

The apps from the app store were often too complex looking or lacked functionality.

This is where I started researching how to create a custom watch face and maybe even post it to the marketplace if possible. I learnt that Garmin uses Monkey C along with their SDK to create everything from watch faces to apps.

First things first, you need to setup the environment and download the SDK: https://developer.garmin.com/connect-iq/sdk/

Once setup, lets open Visual Studio Code and open the command palette using Ctrl+Alt+P. In this prompt, we want to search for the Monkey C: New Project option. It will then ask for the name of the project and the type. For this example, I will select a Simple Watch Face and set the API level to the lowest value.

This will build the project structure and create a base watch face to work from.

We now need to add supported products to the project. My watch is the Garmin Forerunner 735XT but you can use whichever watch you have. Use the command palette again and use the Monkey C: Edit Products option. In that dropdown, select all the watches you want to support and build for.

Now we can hit F5 and run the watch face in the virtual watch:

The prebuilt project from Garmin

In this post, I’m going to build a watch face similar to the below design:

Simple watch face design concept

First, lets edit the layout.xml to build the design of the watch face. This file is in Resources -> Layouts.

Remove the default label from the XML file and lets build the 4 new labels required for our battery percentage, steps, current time and todays date.

The layouts are fairly easy to follow, each label requires the following attributes:

AttributeExplanationPossible Values
idThe name of the label. This will be used in the code to update the value.Any string value starting with a capital letter. E.g. TestLabel
xThe horizontal positioning of the label. This can be a pixel value or the predefined values.left, center, right, pixel value, or a percentage
yThe vertical positioning of the label. This can be a pixel value or the predefined values.top, center, bottom, pixel value, or a percentage
fontThe font to use on the label. These are predefined.All options are documented in the API docs. We are only using values from API level 1.0.0 for now:

https://developer.garmin.com/connect-iq/api-docs/Toybox/Graphics.html
justificationHow the text should position itself in the space provided.TEXT_JUSTIFY_RIGHT,
TEXT_JUSTIFY_CENTER,
TEXT_JUSTIFY_LEFT,
TEXT_JUSTIFY_VCENTER
colorThe colour of the textAll options are documented in the API docs:

https://developer.garmin.com/connect-iq/api-docs/Toybox/Graphics.html

So my new layout.xml file will look like this:

<layout id="WatchFace">
<label id="TimeLabel" x="center" y="center" font="Graphics.FONT_NUMBER_THAI_HOT" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_WHITE" />
<label id="BatteryPercentageLabel" x="center" y="top" font="Graphics.FONT_MEDIUM" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_LT_GRAY"/>
<label id="StepsLabel" x="center" y="10%" font="Graphics.FONT_MEDIUM" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_LT_GRAY"/>
<label id="DateLabel" x="center" y="bottom" font="Graphics.FONT_MEDIUM" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_LT_GRAY"/>
</layout>

Now that the layout is built, we need to build the functionality in the backend to update the view. Open the watch face _View.mc file. This can be found in Source -> watchface_View.mc.

First, lets add a required using statement at the top. Add the following line:

using Toybox.Time.Gregorian as Calendar;

I will also create a new method for updating the view. This method will take in the string ID of the label as well as the new value:

//Update the display object text
function updateDisplayObject(updateObject, updateText){
var _viewObject = View.findDrawableById(updateObject) as Text;
_viewObject.setText(updateText);
}

We can now use the above method to update the view.

We will now head over to the onUpdate method to update the view.

Now, lets work on building the required data and formatting it before updating the view. You can see below how I work through each of the four required sections and use the above updateDisplayObject method to update the view:

// Get and show the current time
var clockTime = System.getClockTime();
var timeString = Lang.format("$1$:$2$", [clockTime.hour, clockTime.min.format("%02d")]);
updateDisplayObject("TimeLabel", timeString);
//Get and show the current battery percentage
var pwr = System.getSystemStats().battery;
var batStr = Lang.format( "$1$%", [ pwr.format( "%2d" ) ] );
updateDisplayObject("BatteryPercentageLabel", batStr);
//Get and show the steps data
var stepCountString = ActivityMonitor.getInfo().steps.toString();
updateDisplayObject("StepsLabel", stepCountString);
//Get and show the date
var now = Time.now();
var info = Calendar.info(now, Time.FORMAT_LONG);
var dateString = Lang.format("$1$ $2$ $3$", [info.day_of_week, info.day, info.month]);
updateDisplayObject("DateLabel", dateString);

Now, when we hit F5 to run the watch face, you can see how the display has changed and we have built the watch layout:

The built simple watch layout

Hope this helps someone out as the documentation is a little lacking. Enjoy! πŸŽ‰ πŸŽ‰

Additional Notes – You can also confirm the languages your watch face will support by using the Monkey C: Edit Languages option.


Leave a Reply