Herold
Solutions
Blog
Preview for Build your own Garmin Watchface

Build your own Garmin Watchface

A introduction to watchface development for your Garmin device

August 22, 202411 min read

Today, we are able to customize watches to our own needs. I customize my Venu 2 Garmin watch with a VS Code-like watchface, to suit my own needs and my own style. You can take a look at it in the Garmin Connect IQ Store. The advantages of a custom personalized watchface are clear. It fits all your own needs, and it is always a nice show-off to say that you developed it on your own. This guide should give you a basic idea on how to start developing a watchface for your Garmin device.

Preparations

Before you can start to develop you need to install some things:

  • Code Editor: My recommendation is VSCode since there is a extension for the development
  • Connect IQ SDK Manager: This is needed to debug and simulate your watchface local. You can read more about it here
  • Additional software may be needed if you want to use custom fonts or graphics

You should also check out the compatible devices on the Garmin website, to check out wich API Level your watch supports and which screen technology it uses since that will impact which features and metrics you can use within your watchface.

To use the Connect IQ SDK Manager you need to follow the steps in the getting started guide from the Garmin documentation. At the end of that documentation you will generate a developer key. This key is very important and your apps will be signed with that key. If you loose this key you cant update your apps and watchface that are signed with that key anymore. Keep that in mind and backup that key very good.

First steps

When you installed the Monkey C extention in VSCode you can go ahead and select the Monkey C: New Project command. The command will ask you for the following inputs:

  • Project Name: The name of your new watchface
  • Project Type: We want to develop a watchface
  • Template: To understand the basics it is better to select simple
  • API Level: The minimum required API Level for your watchface. It should be the API level of your device or lower
  • Supported devices: Select one or multiple devices that you want to support

Now a default project watchface project has been created. The default watchface only displays the current time in the center. You can try it out by pressing F5 and starting the debug mode. That should open the simulator with your selected device. f you want to support multiple devices, I recommend setting up a .vscode/launch.json file with the following content. With this launch configuration, the debug mode will ask which device should be used to start your watchface:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "monkeyc",
            "request": "launch",
            "name": "Run App",
            "stopAtLaunch": false,
            "device": "${command:GetTargetDevice}"
        }
    ]
}

The Project Structure

Now lets have a look at the project structure of your default project to get an idea where we store wich parts of our code:

root
├── .vscode
├── bin
├── resources
│   ├── drawables
│   │   ├── drawables.xml
│   │   └── launcher_icon.png
│   ├── layouts
│   │   └── layout.xml
│   └── strings
│       └── strings.xml
├── source
│   ├── exampleApp.mc
│   └── exampleView.mc
├── manifext.xml
└── monkey.jungle
  • bin: This directory contains the build files and can be ignored. Also if you use git or other version control this directory should not be committed
  • resources: This directory contains all static defined used resources like used custom fonts, images, translations and everything that falls into categories like these
  • resources/drawables: This directory is specific for all used images. New images should be add to this folder and needs to be defined with a id in the drawables.xml like the launcher_icon.png already is.
  • resources/layouts: This directory is to define the layouts and designs of your watchface. By default, we already got a layout.xml which contains a label tag positioned in the center of our watchface. You most likely only need one layout for your watchface.
  • resources/strings: In the strings.xml file of this directory you can define all the used texts of your watchface. That enables you to make your watchface multilingual if you want to do that.
  • source: This directory should contain all your interactive code that will fill the actual watchface with metrics. You can define more .mc files in this folder to split different parts of your watchface and keep you code as clean as possible.
  • source/exampleApp.mc: This file is the general initialization of our watchface. Code that only needs to be called once for setup purpose can be executed here on the watchface startup. Also the view buildup is called from here.
  • source/exampleView.mc: This file includes the actual view of our watchface. We are able to control what should happen on different watch activities. The most important function in here is the onUpdate function. This will be called every second and can update our layouts and set the wanted metrics.
  • manifext.xml: Here we are able to configure our application. We are able to change the API level or add more devices to our supported list. We can also manage the permissions that are needed for our watchface since some features need approval from the user before the watchface can be installed.
  • monkey.jungle: This file is the build configuration file to include external imports and specific build options. You probably do not need to edit this file at all.

The default generated project already has very good commented code that explains when wich part of the code is automatically called. At the beginning you just have to focus on the onUpdate function to understand how to implement new metrics.

Design and Layout

Since we are now aware of the general code structure lets have a look how to change the layout and add a new metric and text into our watchface. Lets try to add a text above the Time saying CURRENT and implement a step metric below the time. To do this we need to place new labels into our layout.xml:

<layout id="WatchFace">
    <label text="@Strings.Current" x="center" y="25%" font="Graphics.FONT_XTINY" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_RED" />
    <label id="TimeLabel" x="center" y="center" font="Graphics.FONT_LARGE" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_BLUE" />
    <label id="StepCount" x="center" y="65%" font="Graphics.FONT_XTINY" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_YELLOW" />
</layout>

Here we added the first label with the text @Strings.Current and the last label with the ìd StepCount. The content of our step count will be set programmatically within the onUpdate function. The @Strings from the first label means that we want to load a string from the strings.xml configuration so we have to add this string to that file:

<strings>
    <string id="AppName">example</string>
    <string id="Current">CURRENT</string>
</strings>

Now the watchface will look into the strings and load the corresponding id from that file for our first label. At the moment we only display some labels but there are more possible tags to design and layout our watchface. These can be found in the official Garmin documentation;

Monkey C Development

We already added a label for the step count but we need to pass the actual current steps into our layout within the onUpdate function. So we need to update that function to something like this:

    // Update the view
    function onUpdate(dc as Dc) as Void {
        // Get and show the current time
        var clockTime = System.getClockTime();
        var timeString = Lang.format("$1$:$2$", [clockTime.hour, clockTime.min.format("%02d")]);
        var view = View.findDrawableById("TimeLabel") as Text;
        view.setText(timeString);

        // Get and show the current step count
        var stepCount = ActivityMonitor.getInfo().steps;
        System.println(stepCount);
        var displayedSteps = stepCount == null ? 0 : stepCount;
        var stepCountLabel = View.findDrawableById("StepCount") as Text;
        stepCountLabel.setText(displayedSteps.toString());


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

In addition we have to add the import for the ActivityMonitor at the top of our file with this line import Toybox.ActivityMonitor;.
Now we can access the current step count from the activities and set them as text inside of our step count label. Since the activity monitor could return a null value we will check for that and display 0 in that case. I added also a System.println(stepCount); to check what we get not only on the watchface but also in our debugger.

Testing and Debugging

It is time to check out all of the changes we made. We can start the watchface again with F5 and select a device. After the start we should already see that the text is correctly displayed as capitalized CURRENT just like we defined it in the strings.xml. But at the moment our step count is at zero. The simulator allows us to set custom data into our activity monitor and change the tracked values within Simulation -> Activity Monitoring -> Set Activity Monitor Info. In the now oped window we can define all possible activity infos and also the steps. After changing the steps and apply the changes on the bottom right a update will automatically render the correct new steps value into our watchface. And when we will look into our VSCode DEBUG CONSOLE we will also see that every second a new value is printed into this output.

Since everything works fine we could remove the System.println(stepCount); from our onUpdate function and we are ready to publish the watchface.

Publishing

To publish our watchface we have to export a build for the Connect IQ Store. For this we can use the Monkey C: Export Project command in VSCode. Select a directory and the command will automatically build a .iq file that can be uploaded to the store. This file contains the builds for all supported devices. When you select multiple devices in your manifest.xml it could take a moment to generate that .iq file.

After the build is finished you can head over to the Connect IQ Store and submit your app. Enter all the needed data, and now you are already able to download your watchface on your own device from the store. It could take a few days until it is available for all users since Garmin has to review and approve your watchface first, but you will get notified once it is approved.

Conclusion

These are the basics of writing and publishing a watchface for Garmin devices. Since this guide only covers how to start the development process, there are a lot of things that are missing. You could do many advanced things, but that would be too much to cover in this guide. You can read about all available APIs in the official api documentation.

If you have any questions regarding this guide or building watchfaces feel free to contact me on any platform you like. And also if you publish a watchface you can send me the link and I will have a look at it. I am very interested in your creativity and what you want to build for your watch and maybe mine as well. Don’t forget to share this blog with your friends and fellow watchface enthusiasts! Your support and feedback are greatly appreciated.

Selfie of Felix Herold

The article was helpful, or are you feeling a bit unsure about anything?