Design Watch Face With LVGL
This intructables show how to design a watch face for Arduino project with LVGL and SquareLine Studio.
You may find more LVGL and SquareLine Studio details at my previous instructables:
https://www.instructables.com/Design-a-Fancy-GUI-for-Your-Project/
Note:
Watch face have many possibilities, this instructables only focus on analog watch face. But it will not limited your creativity, you can use similar method design non-analog watch face.
Supplies
Any Arduino supported dev device with color display should be ok. Here are some hardwares I used in this project:
Preparation
Please read my previous instructables and follow the software preparation if not yet:
https://www.instructables.com/Design-a-Fancy-GUI-for-Your-Project/
This project using Arduino_GFX as color display library, you may find more details at my previous instructables:
https://www.instructables.com/ArduinoGFX/
In addition, I am using follow softwares for the graphic design and conversion works:
All the source code in this project can be found at GitHub:
Design Tools
The graphic design material source is better in vector format. It can add or remove components easily, change color, fill color gradients, adjust object border size and change dimension losslessly. Most free vector graphics can be found on the Internet are EPS or SVG format, both format can import to Inkscape easily.
In contrast, vector graphics is too complicate for a MCU. So MCU program mainly using bitmap image as image source. In various bitmap image format, PNG format handle alpha channel better for the irregular shape object, e.g. clock arms in this project. So SquareLine Studio dedicated use PNG image as assets image format. All the Inkscape designed objects in this project are exported to PNG format for SquareLine Studio. This project mainly using Inkscape for image design, you can use your familiar tools instead.
In addition, some source come from video, e.g. the anchor escapement. It need ffmpeg extract and crop the frames and then use GIMP trim the corner to make it as a round PNG file.
Ref.:
Design Flow
Design is a long journey and it must start from an idea spark.
The main graphic design tools is Inkscape, or something similar. Then export the design to SquareLine Studio in PNG format as assets. Design the LVGL screen layout and export UI files to Arduino project. Coding the clock logic and then upload to the dev device. look and feel the result.
Then REPEAT and REPEAT the above steps.
Color Theme
Many real watch faces are light background. And since LCD is using white backlight, light theme should be more "energy efficient" logically.
In contrast, many LCD UI designs are dark theme. One of the main reason is it can hide the black border and make the screen looks better.
Select what color theme is subjective, but it is the option while creating SquareLine Studio new project and cannot change in the middle. So it is better make this decision at the beginning or you may need recreate a new project.
Clock Mark
The clock mark is easy to draw, every minute (6 degrees) draw a minor mark and every 5 minutes (30 degrees) draw a major mark. And may be add some numbers.
However, lazy like me would like to use existing design on the Internet. here are some sources I am using in this project:
- Image by macrovector on Freepik
- Image by macrovector on Freepik
Clock Arms
Clock arms also can draw from scratch or find on the web. If you have an idea of a fandom stuff, you can find some related clip art for the clock arm. E.g. for a stormtrooper theme, you can use some of their weapon as clock arms. I selected lightsaber as arm of second, DLT-19X as arm of minute and a short blaster as arm of hour:
- https://en.m.wikipedia.org/wiki/File:Dueling_lightsabers.svg
- https://battlefront.fandom.com/wiki/DLT-19X
- https://www.onlinewebfonts.com/icon/555311
Extract Clipart
If you selected using clipart on the web, some steps for extract the required parts:
- import the EPS or SVG file to Inkscape
- ungroup the object
- remove all unnecessary parts
- modify details, e.g. dimension, rotation, fill color, border size
Export PNG
The major SquareLine Studio required assets for an analog watch face are the base image and the clock arm images. Base image contains icon, clock marks and numbers, you can group it together for easier export.
Then export to PNG:
- select the export object
- select "File" menu -> "Export..."
- adjust export size, my practice is set document as 100 DPI for easy adjust export size by inches
- ensure checked "Export Selected Only"
- export to SquareLine project assets sub-folder
Clock Arms Rotation
In SquareLine Studio, create a new image object for each clock arms and assign the corresponding image asset. Then:
- change X and Y value to align the clock arm to center axis
- fill different value in rotation to check the effect, the value is 10 times of degree
- adjust the Pivot X and Pivot Y value to align the clock arm to center axis in any rotation value
- export UI files to Arduino project folder
In Arduino project, you need few lines of code keep clock arms in the right angle over time:
int16_t angle = (clock_ms % ONE_MINUTE_MS) * 3600 / ONE_MINUTE_MS;
lv_img_set_angle(ui_ImageArmSecond, angle);
angle = (angle + (minute * 3600)) / 60;
lv_img_set_angle(ui_ImageArmMinute, angle);
angle = (angle + (hour * 3600)) / 12;
lv_img_set_angle(ui_ImageArmHour, angle);
Ref.:
https://docs.lvgl.io/8.3/widgets/core/img.html#transformations
Get Time
How to get time correct in MCU is a little bit outside the design scope, I just list out 3 simple ways how to get the time:
- pass the compile time into the code: this is the simplest way without any extra hardware but the time is no longer valid if the MCU reset
- keep time by RTC module: this is the most practical way to keep time and this is most easy way if the dev device have built-in RTC
- from NTP server: if you are using ESP32 family dev device, get the time from the Internet also a common way
First Run
Discuss so far, there already have enough material to build the first example. Here are the source of the first simple example for your reference:
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240
You may need to modify the Arduino_GFX class declaration code for your dev device:
#include <Arduino_GFX_Library.h>
#define GFX_BL 25
/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_RPiPicoSPI(8 /* DC */, 9 /* CS */, 10 /* SCK */, 11 /* MOSI */, 12 /* MISO */, spi1 /* spi */);
/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_GC9A01(bus, 12, 0 /* rotation */, true /* IPS */);
More Moving Parts
Some watch faces have a hole let you peek the mechanical movements underneath. I really like to peek this movements, so I have collect some video trying to imitate it:
- Ulysse Nardin In-House Made & Designed Silicon Anchor Escapement | aBlogtoWatch
- How a Mechanical Watch Works | Explained in 5 Minutes
- Ulysse Nardin Ulysse Anchor Tourbillon
Ref.:
Vacheron Constantin Traditionnelle luxury watches
Ulysse Nardin Marine Tourbillon | ESCAPEMENT MAGAZINE | WATCH REVIEWS
Imitate Movement
The most straightforward way to imitate a real watch mechanical movement is take a 24 hours video from a real watch, then replay it on the screen. But playing 24 hours video in a MCU is impossible, it need simplifier the movement. The above example actually only contains 10 images.
Extract Images
Here are the scripts extracting 10 images from a video using ffmpeg:
ffmpeg -y -i watch.mp4 -ss 1:30.4 -t 0.37 -vf "crop=320:320:173:17" out.mp4
ffmpeg -y -i out.mp4 -vf "scale=160:160:flags=lanczos" 'out%02d.png'
Trim Corner
For each frame, use GIMP follow the above steps to trim out the corner (make it transparent). Then import to SquareLine Studio. Then in SquareLine Studio, create image object for each frame.
Show Hide Routine
In the program, repeat the show and hide routine for playing this. For every loop, I rotate the images 2.4 degrees, so it looks like a continuous movement:
lv_img_set_angle(anchors[next_anchor_idx], curr_anchor_angle);
lv_obj_clear_flag(anchors[next_anchor_idx], LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(anchors[curr_anchor_idx], LV_OBJ_FLAG_HIDDEN);
Anchor Escapement Examples
Here are the examples using the above method imitate anchor escapement movement:
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_RP2040
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_T-Watch
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_T-Watch_2021
LVGL Animation Image
Besides repeat the show and hide routine coding, LVGL also support Animation Image (lv_animing). Here are the example using lv_animing:
Ref.:
https://docs.lvgl.io/8.3/widgets/extra/animimg.html
Custom Font & Multi-language Support
SquareLine Studio can generate a small range of characters from a custom font file to a binary data for text display. The custom font file can be in TTF or OTF formats. Convert all characters from a unicode font file to binary data is size too big and cannot fit into a MCU flash. So you need select the only characters you need to use and paste into the Symbols list.
Note:
Higher BPP value have better anti-alias display result, but may not show preview correctly in SquareLine Studio.
Multi-language Example
Here are the example that can display weekday in Chinese language:
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_480
GIF Animation
Besides imitate mechanical movement, GIF animation gives much more further possibilities. There are many interesting Animated GIF can be found on the web.
It is better select the GIF that can loop seamlessly, e.g. this stormtrooper one:
https://giphy.com/gifs/illustration-vector-xTiTnJK44NXT46iKYM
LVGL GIF Decoder
LVGL have a built-in GIF decoder, but it requires a certain memory. In my experience, LVGL GIF decoder in RP2040 can support 96 x48 GIF but OOM (out of memory) for a 128x64 GIF.
In "lv_conf.h", turn on built-in GIF decoder by finding "LV_USE_GIF" and set to 1:
#define LV_USE_GIF 1
Here are the steps using animated GIF:
- find and create your own GIF
- resize and optimize GIF by your familiar tools, e.g. ezgif.com
- follow previous instructables steps to create an empty LVGL Arduino project
- use LVGL Online Image Converter convert GIF file to a C source file and copy to Arduino project folder
- add an empty image object in SquareLine Studio project and export to Arduino project folder
- modify the "ui.c" file, replace "ui_Image1 = lv_img_create(ui_Screen1);" to "ui_Image1 = lv_gif_create(ui_Screen1);"
- then add 2 lines after ui_init():
LV_IMG_DECLARE(star_wars_stormtrooper_48_gif);
lv_gif_set_src(ui_Image1, &star_wars_stormtrooper_48_gif);
Ref.:
LVGL GIF Decoder Example
Here are the example using LVGL GIF decoder:
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_Stormtrooper
External Image Decoder
Besides GIF decoder, LVGL also support canvas object. You can implement any image decoder and draw the output to the canvas. So you can use any external image decoder for playing animation, e.g. Motion JPEG, Animated GIF and even AVI.
External GIF decoder can decode larger dimension, e.g. it can decode 160x80 GIF in RP2040.
If you would like using external GIF decoder, remember turn off built-in GIF decoder in "lv_conf.h":
#define LV_USE_GIF 0
External Image Decoder also more flexible on the source of image data, the image files can be direct loaded from flash partition or even SD card.
Ref.:
Canvas Examples
Here are the example using canvas external image decoder:
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_GIF
https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_T-Watch_2021_GIF
Note:
You can simply replace your resized (to 240x240) GIF file in data folder then assign it in the code:
#define GIF_FILENAME "/my_favour_animation.gif"
Enjoy!
It's time to show off your designed watch face!
You may find all the above full HD demo video at bilibili:
What's Next?
All the above designs are analog watch faces. Those are just a very small portion of watch face design possibilities. It's time to design and visualize your idea spark!