Garmin RepaField
A few weeks ago there was a rainy weekend and I was not able to do the planned work in the garden, also the Vadlan Ultra Terep trail running competition was near, so I decided to create my own Garmin watch datafield.
My main plan was to display both the stamina and the remaining distance from the track in a comparible way (among other metrics I’m interested in, like heart rate, pace and such), but that failed since many metrics are not available through the SDK - stamina among them. But anyway, I’ve created my datafield, I really like it, and at the moment of writing 178 other runners have installed it, too.
You can download it from the Garmin Connect IQ store.
What’s included?⌗
Metrics shown:
- heart rate (color coded)
- current
- average
- max
- line histogram
- pace
- current
- average
- distance
- cadence (color coded)
- altitude
- current
- elevation gain
- elevation loss
- current time
- elapsed time
- done vs remaining distance of the current course (red if you’re off track)
Configuration:
- 3 theme colors
Plans:
- vertical speed, GAP
- pace/speed switch in settings
- hr display options (hr value, percentage of max, hr zone)
How?⌗
Garmin provides an SDK and tools to help creating apps and widgets for their wearables, for Windows, Mac and Linux as well.
The SDK is JVM based, but instead of Java it uses a custom language called Monkey C. It’s a bit like Java, but there are differences, like dynamic typing, lack of primitive types, reference counting GC, functions, etc. But if you have used some kind of modern high level programming language, than you won’t struggle too much.
The SDK is well documented, but as I said a lot of metrics are not exposed through it.
Command line tools are also provided with the SDK, but a more user friendly Visual Code extension is also maintained by Garmin.
Publishing⌗
The publish process is pretty straightforward, you export your app, upload it to the Connect IQ storewith some info and screenshots, they review it, and that’s it. You can even ask money for your apps, but that should be done via some kind of 3rd party, Garmin does not provide any help for that.
Challenges⌗
Many different devices⌗
Garmin has a lot of watches, and they can have very different capabilities. Round or square; classic LCD, MIPS or AMOLED (1, 8, 16, 64, or basically 24 bit colors); wide range of resolutions from low like 208x208, 240x240 … to high like 416x416 or 454x454. Processor power and memory size is also very different for each model.
The resource compiler helps with this, reduces bitmap colors for the target device for example, and you can use different layouts and resources for different device groups (like resources-round
, resources-round-240x240
or even resources-fenix7spro
).
Performance⌗
Well, to be honest, some of the watches are much more powerful than computers from 20 years ago, but they are nothing compared to recent ones, or mobile phones. They also have quite small batteries, that can be sucked dry by CPU heavy tasks - so writing performance optimized code is a key.
Garmin doesn’t invented Monkey C for just fun, they are probably doing a lot of optimizations during compilation, but there are areas where you should really be aware what you are doing, and how much it costs.
Memory usage is also something you need to check. For example my datafield currently uses about 31kbyes of memory - with the icons and code and everything. Sounds low compared to modern programs, but many of the older/lower end Garmin watches allow only 28k for a single datafield 😐
(I could probably take steps to go below 28k, but that would also block my plans for further development, so I decided not to support those watches. Newer/more high end watches provide 124k/252k for a datafield, which currently seems to be enough for a while.)
Lacking API⌗
As I mentioned above, the API is far from complete, lot of data available for native widgets aren’t accessible through it. You can create a thread in the developer forums asking Garmin to include this or that in the API, and with luck you might get it in years. Or not, that’s life.
Performance optimization example⌗
Image rotation
A long requested feature is the ability to rotate images or text - for example rotating hands are crucial part of any analog-like watchface. That is currently not easily possible with the API - you can do that pixel-by-pixel, but that will consume a lot of CPU, and will be ugly because of the lack of antialiasing.
Bitmap fonts to the rescue! Smart developers recognized that they could use the bitmap font support - that is available in the API - to store the pre-rotated versions of images, and use them later in an optimized way. Bitmap fonts are also support alpha channel, so anti-aliasing is an extra, and the resource compiler optimizes them for the actual product (reducing colors where they aren’t available, etc.) Only grayscale images are supported - but they can be used as colored text later, and with multiple “layers” you can do fancy stuff.
Somebody even wrote a tool that creates the bitmap font from images for you.
I’ve also started to create a watchface last weekend, and wrote an other small tool that rotates bmfont descriptors, to support text written vertically.