Skip to content

Canvas Clockface

Jonathas Barbosa edited this page Jul 17, 2023 · 8 revisions

Clockface 0x07 is a special type of theme. Unlike the previous ones that are of a specific theme like Mario or Time in Words, this is a blank canvas that is capable of rendering different themes that are described in a JSON file, that's why it is called Canvas .

How does it work?

Clockface 0x07 adds two extra parameters to Clockwise's Settings page, the server address and the name of the file describing the theme:

  • [Canvas] Server Address: by default it will point to the Clock-Club repository where the themes are located, but this parameter can be used to point to your machine, so it is possible to develop your themes locally. Default value is raw.githubusercontent.com
  • [Canvas] Description file: is the name of the JSON file without extension that describes how the theme will be, more details on how this file is created can be seen below. For example, if the file is called my-theme.json, just enter my-theme.

Once the parameters are filled in correctly, starting Clockwise with the "Canvas" clockface will download the JSON file and render it on the display automatically.

I invite you to open a Pull Request to Clock-Club and share your clockface with other users!

Limitations

As you can imagine, all graphic elements such as images, including the JSON file, are stored in the ESP32's RAM memory. This means that the space is super limited to create a complex theme. It's up to you to better organize and optimize the resources described in the JSON file. Bare in mind that is always better creating a clockface from C++ code where you have more control of the resources, follow the instructions here. Even so, here are some good practices to create a theme using Canvas:

  • For simple designs, prefer to use geometric shapes instead of images
  • Avoid using large images (like full size 64x64) or a high number of sprites
  • When exporting PNG, remove metadata which are optional like exif data, gamma, comments, thumbnail, creation time and so on.

JSON Structure

Root object

{
    "name": "My Theme",   // String. Name of the theme
    "version": 1,         // Number. Version of the theme   
    "author": "@jnthas",  // String. Name, handle or email of the author
    "bgColor": 0,         // Number. Background color of the display. In decimal format.
    "delay": 300,         // Number. Delay in milliseconds 
    "setup": [],          // Obj.Array. The 'setup' array is executed once as the clockface starts up 
    "sprites": [],        // Obj.Array. The 'sprites' array contains the images to be rendered and updated in the clock
    "loop": []            // Obj.Array. The 'loop' array contain elements that will be rendered in every loop respecting the delay param
}

Setup array

All the elements of the Setup array are rendered once right after the initialization. The name setup was intentional to be compared with the setup method of the Arduino, it's the same idea. Inside this array, it's possible to add the following elements.

Date/Time element

Show date and/or time in the clock

{
    "type": "datetime",     // String. The type of element, must be `datetime`
    "content": "H:i",       // String. The format od date/time. Use any formatting string from [ezTime](https://github.com/ropg/ezTime#getting-date-and-time)
    "font": "",             // String. The font name to be used. Available values are picopixel, square, big, medium or leave it empty to set the default font.
    "fgColor": 65535,       // Number. Foreground color. Use in decimal format.     
    "bgColor": 396,         // Number. Background color. Use in decimal format.
    "x": 34,                // Number. Horizontal position of the element. 0-64.
    "y": 45                 // Number. Vertical position of the element. 0-64.
},

Line element

Draws a line in the clock

{
    "type": "line",         // String. The type of element, must be `line`
    "color": 29582,         // Number. Line color. Use in decimal format.    
    "x": 10,                // Number. Initial position of the line element. 0-64.
    "y": 41,                // Number. Initial position of the line element. 0-64.
    "x1": 54,               // Number. Final position of the line element. 0-64.
    "y1": 41                // Number. Final position of the line element. 0-64.
},

Image element

Renders a PNG image in the clock

{
    "type": "image",        // String. The type of element, must be `image`
    "x": 3,                 // Number. Horizontal position of the element. 0-64.
    "y": 0,                 // Number. Vertical position of the element. 0-64.
    "image": "iVBORw0K"     // String. It's a PNG in base64 format. More details below.
},

Text element

Renders a text in the clock

{
    "type": "text",                 // String. The type of element, must be `text`
    "content": "enjoy your day!",   // String. The text to be displayed
    "font": "picopixel",            // String. The font name to be used. Available values are picopixel, square, big, medium or leave it empty to set the default font.
    "fgColor": 26592,               // Number. Foreground color. Use in decimal format.     
    "bgColor": 396,                 // Number. Background color. Use in decimal format.
    "x": 6,                         // Number. Horizontal position of the element. 0-64.
    "y": 60                         // Number. Vertical position of the element. 0-64.
},

Rect element

Draws a rect in the clock

{
    "type": "rect",         // String. The type of element, must be `rect`
    "color": 29582,         // Number. Rect color. Use in decimal format.    
    "x": 10,                // Number. Horizontal position of the element. 0-64.
    "y": 41,                // Number. Vertical position of the element. 0-64.
    "width": 10,            // Number. Width of the element. 0-64.
    "height": 10            // Number. Height of the element. 0-64.
},

Fillrect element

Draws a fullfilled rect in the clock

{
    "type": "fillrect",     // String. The type of element, must be `fillrect`
    "color": 29582,         // Number. Rect color. Use in decimal format.    
    "x": 10,                // Number. Horizontal position of the element. 0-64.
    "y": 41,                // Number. Vertical position of the element. 0-64.
    "width": 10,            // Number. Width of the element. 0-64.
    "height": 10            // Number. Height of the element. 0-64.
}

Sprites array

The sprites array is a property to hold images that will be rendered often, like in GIF, it contains one or more frames that will be referenced by the loop property.

"sprites": [  // This example contains 2 sprites, with index 0 and 1
    
    [   // This is a sequence of the first sprite, index = 0
        {
            "image": "iVBORw0..."  // String. It's a PNG in base64 format. More details below.
        },
        {
            "image": "iVBORw0..."
        }
    ],
    [   // This is a sequence of the second sprite, index = 1
        {
            "image": "iVBORw0..."
        },
        {
            "image": "iVBORw0..."
        },
        {
            "image": "iVBORw0..."
        }
    ]
]

Loop array

It's the array of objects that describes the sprites. It's executed in a loop every time the value defined in delay expires in milliseconds.

"loop": [
    {
        "type": "sprite",   // String. The type of element, must be `sprite`
        "movement": 0,      // Number. Not implemented yet.
        "sprite": 0,        // Number. Index of the sprite array in `sprite` property
        "x": 0,             // Number. Horizontal position of the element. 0-64.
        "y": 5              // Number. Vertical position of the element. 0-64.
    },
    {
        "type": "sprite",
        "movement": 0,
        "sprite": 1,
        "x": 58,
        "y": 3
    },
]

Next steps

A visual editor to generate the JSON will be a nice tool.

Common errors

When local server is not running, you will see the following error in the log:

[HTTP] GET request to '192.168.3.19/hello-world.json' on port 4443
[  4176][E][ssl_client.cpp:129] start_ssl_client(): socket error on fd 49, errno: 104, "Connection reset by peer"
[  4177][E][WiFiClientSecure.cpp:144] connect(): start_ssl_client: -1
Connection failed
deserializeJson() failed: EmptyInput