Design Watch Face With LVGL

by 陳亮 in Circuits > Arduino

8288 Views, 29 Favorites, 0 Comments

Design Watch Face With LVGL

13_watchface.gif

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:

https://github.com/moononournation/LVGL_Watchface.git

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.:

https://en.wikipedia.org/wiki/Vector_graphics

https://en.wikipedia.org/wiki/Raster_graphics

Design Flow

Watch Face Design Flow.png

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

螢幕截圖 2023-02-09 下午2.36.40.png

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

螢幕截圖 2023-02-09 下午3.16.33.png
螢幕截圖 2023-02-09 下午3.16.20.png

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:

Clock Arms

螢幕截圖 2023-02-13 下午9.58.16.png
螢幕截圖 2023-02-09 下午4.26.06.png

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

inkscape_extract_components.gif

If you selected using clipart on the web, some steps for extract the required parts:

  1. import the EPS or SVG file to Inkscape
  2. ungroup the object
  3. remove all unnecessary parts
  4. modify details, e.g. dimension, rotation, fill color, border size

Export PNG

螢幕截圖 2023-02-10 下午11.23.33.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:

  1. select the export object
  2. select "File" menu -> "Export..."
  3. adjust export size, my practice is set document as 100 DPI for easy adjust export size by inches
  4. ensure checked "Export Selected Only"
  5. export to SquareLine project assets sub-folder

Clock Arms Rotation

螢幕截圖 2023-02-12 上午10.24.14.png
螢幕截圖 2023-02-12 上午9.59.29.png

In SquareLine Studio, create a new image object for each clock arms and assign the corresponding image asset. Then:

  1. change X and Y value to align the clock arm to center axis
  2. fill different value in rotation to check the effect, the value is 10 times of degree
  3. adjust the Pivot X and Pivot Y value to align the clock arm to center axis in any rotation value
  4. 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:

  1. 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
  2. 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
  3. from NTP server: if you are using ESP32 family dev device, get the time from the Internet also a common way

First Run

01DSCN0849.gif

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

小2372356.jpeg.transform.vacfw.jpeg
Ulysse_Nardin_Marine_Tourbillon_wrist2.jpg

Imitate Movement

ezgif.com-optimize.gif

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

ezgif.com-optimize.gif

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

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:

https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_T-Watch_2021_animation

Ref.:

https://docs.lvgl.io/8.3/widgets/extra/animimg.html


Custom Font & Multi-language Support

螢幕截圖 2023-02-13 下午9.51.30.png

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

螢幕截圖 2023-02-12 下午12.37.45.png
螢幕截圖 2023-02-12 下午12.39.07.png

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

螢幕截圖 2023-02-11 下午6.07.09.png

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:

  1. find and create your own GIF
  2. resize and optimize GIF by your familiar tools, e.g. ezgif.com
  3. follow previous instructables steps to create an empty LVGL Arduino project
  4. use LVGL Online Image Converter convert GIF file to a C source file and copy to Arduino project folder
  5. add an empty image object in SquareLine Studio project and export to Arduino project folder
  6. modify the "ui.c" file, replace "ui_Image1 = lv_img_create(ui_Screen1);" to "ui_Image1 = lv_gif_create(ui_Screen1);"
  7. 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.:

https://docs.lvgl.io/8.3/libs/gif.html

LVGL GIF Decoder Example

03DSCN0847.gif

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.:

https://docs.lvgl.io/8.3/widgets/core/canvas.html

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

https://github.com/moononournation/LVGL_Watchface/tree/main/LVGL_Watchface_240_T-Watch_2021_GIF_simple

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:

https://www.bilibili.com/video/BV1RY411q7mA/

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!