Project - Skin Clock

This  demo video  shows how to install the Skin Clock on a 'virgin' device, then how to quickly load and use the Skin Clock, but there is much more useful information included below.


This version of the Digiclock allows loading of a personalised 'skin' style from an optional  settings.ini  file (name can be changed if wished).

filename$ = "/program/settings.ini"      'path and filename of optional settings file (root-relative path is required)

The program reads the specified file if found, then parses the contents for any recognised settings to over-ride any corresponding defaults.
Here is the Load subroutine to explain what is happening:
load:
a$ = ""
if FILE.EXISTS(filename$) > 0 then a$ = FILE.READ$(filename$)
gosub getpars
gosub makestyle
refresh
gosub paint
return


The settings file contents are read into a$, then the getpars subroutine is called to parse a$ for any required 'commands=data" pairs.
Checks are made to ensure the embedded default values are only overwritten with valid new data, eg:
if WORD.GETPARAM$(a$,"fontsize$") <> ""  then fontsize$ = WORD.GETPARAM$(a$,"fontsize$")

The makestyle subroutine is called to add any new style data into the 'mystyle$' css var, then the screen is repainted with the new loaded styles.

Here is the Skin Clock script, save it as  /program/skinclock.bas
Basic:
'  Skin Clock - V1.a
filename$ = "/program/skinclock.ini"  'path and filename of optional settings file
fonts$ = "default,dig7monoitalic.ttf,dig7mono.ttf,scoreboard.ttf"  'list of available fonts
fontpath$   = "/font/"                 'path to optional font file
'fontfile$   = "dig7mono.ttf"           'filename.ext of optional font file
fontfile$   = ""                       'filename.ext of optional font file
fontsize$   = "4"                      'font size (in em)
fontcol$    = "#000000"                'font colour
fontbak$    = "#ffffff"                'font background colour
fontgrad$   = "#888888"                '2nd value for gradient if present - else use "" for solid fontbak$ background colour
fontstyles$ = "normal,italic"          'list of available fontstyles
fontstyle$  = "normal"                 'font style - normal,italic,oblique
fontweights$ = "normal,bold"           'list of available fontweights
fontweight$ = "normal"                 'font weight - normal,bold
fontpad$    = "2"                      'font padding (in em)
fontpad = val(fontpad$)
lineheight$ = "1"
bordsize$   = "2"                      'border thickness (in em)
bordsize = val(bordsize$)
bordrad$    = "0"                      'border corner radius (in em - dependent on border thickness
bordcol$    = "#000000"                'border colour
bakcol$     = "#ffffff"                'screen background colour
voffset$    = "200"                    'div top vertical offset in px
menu = 0
grad = 0
vpos = 0
gosub makestyle
gosub load
onhtmlchange htmlchanged
timer0 1000, clock                     'jumps to refresh every second
wait

clock:
refresh
jscall "_$('clock').innerHTML = """ + time$ + """"        ' updates the digital display
return

htmlchanged:
wlog "HTMLchanged"
fontsize$ = str$(fontsize)
bordrad$ = str$(bordrad)
bordsize$ = str$(bordsize)
fontpad$ = str$(fontpad)
voffset$ = str$(voffset)
lineheight$ = str$(lineheight / 10)
refresh
gosub makestyle
gosub paint
return

makestyle:
mystyle$ = "font-family: myfont;"
vpos$ = str$(val(voffset$) - val(fontsize$)*8)
mystyle$ = mystyle$ + "position:relative; top:" + vpos$ + "px;"  'clock vertical centre
face$ = fontbak$
if grad = 1 then face$ = "linear-gradient(" + fontbak$ + "," + fontgrad$ + ");"  'may need different syntax for your browser
mystyle$ = mystyle$ + "font-size:" + fontsize$ + "em;"
mystyle$ = mystyle$ + "font-style:" + fontstyle$ + ";"
mystyle$ = mystyle$ + "font-weight:" + fontweight$ + ";"
mystyle$ = mystyle$ + "color:" + fontcol$ + ";"
mystyle$ = mystyle$ + "background:" + face$ + ";"
mystyle$ = mystyle$ + "padding-left:." + fontpad$ + "em;"
mystyle$ = mystyle$ + "padding-right:." + fontpad$ + "em;"
mystyle$ = mystyle$ + "line-height:" + lineheight$ + ";"
if fontstyle$ = "italic" then mystyle$ = mystyle$ + "padding-right:." + str$(fontpad + 1) + "em;"
mystyle$ = mystyle$ + "border:" + bordsize$ + "px solid " + bordcol$ + ";"
mystyle$ = mystyle$ + "border-radius:." + bordrad$ + "em;"
return

paint:
cls
a$ = |<style> @font-face { font-family: myfont; src: url('| + fontpath$ + fontfile$ + |');} |
a$ = a$ + "body { background-color: " + bakcol$ + ";}"
a$ = a$ + "</style>"
a$ = a$ + |<div id='clock'  data-var='clicked' onclick='cmdButton(this)' style='display: table; margin-right:auto;margin-left:auto;text-align:center;|
a$ = a$ + mystyle$      'this applies all of the style setting to the clock div
a$ = a$ + |'>|
a$ = a$ +  time$  
a$ = a$ + |<br>|
a$ = a$ + |</div>|
a$ = a$ + |<br><b><br><br>|
a$ = a$ + |<div id="controls" style='display: table; margin-right:auto;margin-left:auto;text-align:center;|
a$ = a$ + |position:relative; top: | + str$(val(vpos$)-50) + |px;|
if menu = 0 then a$ = a$ + |visibility:hidden;|
a$ = a$ + |'>|
a$ = a$ + |show controls|
a$ = a$ + checkbox$(menu) + |<br>|
refresh
voffset = val(voffset$)
a$ = a$ + slider$(voffset,1,500) + |<br>|
a$ = a$ + |vertical offset<br>|
a$ = a$ + textbox$(filename$,"ftb") + |<br>|
a$ = a$ + cssid$("ftb","text-align:center;color:darkblue;background:lightcyan;")
a$ = a$ + button$("Save settings",save) + |<br><br>|
fontsize = val(fontsize$)
a$ = a$ + slider$(fontsize,1,18,1)
a$ = a$ + |<br>|
a$ = a$ + |font size<br>|
a$ = a$ + |<input title="Font colour" type="color" data-var="fontcol$" onchange="cmdChange(event)" value="| + fontcol$ + |"><br>|
a$ = a$ + |font colour<br>|
a$ = a$ + |<input title="Background colour"type="color" data-var="fontbak$" onchange="cmdChange(event)" value="| + fontbak$ + |"><br>|
a$ = a$ + |face colour<br>|
a$ = a$ + checkbox$(grad) + |<br>|
a$ = a$ + |<input title="Gradient colour"type="color" data-var="fontgrad$" onchange="cmdChange(event)" value="| + fontgrad$ + |"><br>|
a$ = a$ + |gradient colour<br>|
a$ = a$ + listbox$(fontfile$,fonts$) + |<br>|
a$ = a$ + |font file<br>|
a$ = a$ + listbox$(fontstyle$,fontstyles$) + |<br>|
a$ = a$ + |font style<br>|
a$ = a$ + listbox$(fontweight$,fontweights$) + |<br>|
a$ = a$ + |font weight<br>|
fontpad = val(fontpad$)
a$ = a$ + slider$(fontpad,1,8) + |<br>|
a$ = a$ + |horizontal padding<br>|
lineheight = val(lineheight$) * 10
a$ = a$ + slider$(lineheight,7,25) + |<br>|
a$ = a$ + |vertical padding<br>|
bordrad = val(bordrad$)
a$ = a$ + slider$(bordrad,0,9) + |<br>|
a$ = a$ + |border radius<br>|
a$ = a$ + |<input type="color" data-var="bordcol$" onchange="cmdChange(event)" value="| + bordcol$ + |"><br>|
a$ = a$ + |border colour<br>|
bordsize = val(bordsize$)
a$ = a$ + slider$(bordsize,0,80) + |<br>|
a$ = a$ + |border size<br><br>|
a$ = a$ + |<input title="Screen background colour"type="color" data-var="bakcol$" onchange="cmdChange(event)" value="| + bakcol$ + |"><br>|
a$ = a$ + |screen colour<br><br>|
a$ = a$ + |</div>|
html a$
a$ = ""
return

clicked:
if menu = 0 then menu = 1 else menu = 0
gosub paint
return

getpars:
if WORD.GETPARAM$(a$,"fontpath$") <> ""  then fontpath$ = WORD.GETPARAM$(a$,"fontpath$")
if WORD.GETPARAM$(a$,"fontfile$") <> ""  then fontfile$ = WORD.GETPARAM$(a$,"fontfile$")
if WORD.GETPARAM$(a$,"fontsize$") <> ""  then fontsize$ = WORD.GETPARAM$(a$,"fontsize$")
if WORD.GETPARAM$(a$,"fontcol$") <> ""  then fontcol$ = WORD.GETPARAM$(a$,"fontcol$")
if WORD.GETPARAM$(a$,"fontbak$") <> ""  then fontbak$ = WORD.GETPARAM$(a$,"fontbak$")
if WORD.GETPARAM$(a$,"fontstyle$") <> ""  then fontstyle$ = WORD.GETPARAM$(a$,"fontstyle$")
if WORD.GETPARAM$(a$,"fontweight$") <> ""  then fontweight$ = WORD.GETPARAM$(a$,"fontweight$")
if WORD.GETPARAM$(a$,"fontgrad$") <> ""  then fontgrad$ = WORD.GETPARAM$(a$,"fontgrad$")
if WORD.GETPARAM$(a$,"fontpad$") <> ""  then fontpad$ = WORD.GETPARAM$(a$,"fontpad$")
if WORD.GETPARAM$(a$,"lineheight$") <> ""  then lineheight$ = WORD.GETPARAM$(a$,"lineheight$")
if WORD.GETPARAM$(a$,"bordsize$") <> ""  then bordsize$ = WORD.GETPARAM$(a$,"bordsize$")
if WORD.GETPARAM$(a$,"bordrad$") <> ""  then bordrad$ = WORD.GETPARAM$(a$,"bordrad$")
if WORD.GETPARAM$(a$,"bordcol$") <> ""  then bordcol$ = WORD.GETPARAM$(a$,"bordcol$")
if WORD.GETPARAM$(a$,"bakcol$") <> ""  then bakcol$ = WORD.GETPARAM$(a$,"bakcol$")
if WORD.GETPARAM$(a$,"voffset$") <> ""  then voffset$ = WORD.GETPARAM$(a$,"voffset$")
if WORD.GETPARAM$(a$,"grad") <> ""  then grad = val(WORD.GETPARAM$(a$,"grad"))
if grad = 1 then fontbakcol$ = "linear-gradient(" + fontbak$ + "," + fontgrad$ + ")" else fontbakcol$ = fontbak$
return

setpars:
WORD.SETPARAM  a$, "fontpath$", fontpath$
WORD.SETPARAM  a$, "fontfile$", fontfile$
WORD.SETPARAM  a$, "fontsize$", fontsize$
WORD.SETPARAM  a$, "fontstyle$", fontstyle$
WORD.SETPARAM  a$, "fontweight$", fontweight$
WORD.SETPARAM  a$, "fontcol$", fontcol$
WORD.SETPARAM  a$, "fontbak$", fontbak$
WORD.SETPARAM  a$, "fontgrad$", fontgrad$
WORD.SETPARAM  a$, "fontpad$", fontpad$
WORD.SETPARAM  a$, "lineheight$", lineheight$
WORD.SETPARAM  a$, "bordsize$", bordsize$
WORD.SETPARAM  a$, "bordrad$", bordrad$
WORD.SETPARAM  a$, "bordcol$", bordcol$
WORD.SETPARAM  a$, "bakcol$", bakcol$
WORD.SETPARAM  a$, "voffset$", voffset$
WORD.SETPARAM  a$, "grad", str$(grad)
return

save:
' Saves settings to file
a$ = ""
if FILE.EXISTS(filename$) > 0 then a$ = FILE.READ$(filename$)
gosub setpars
FILE.SAVE filename$, a$
gosub paint
return

load:
' Loads settings from file
a$ = ""
if FILE.EXISTS(filename$) > 0 then a$ = FILE.READ$(filename$)
gosub getpars
gosub makestyle
refresh
gosub paint
return
'---------------------- END -----------------------



Run the script by itself to see what the embedded defaults look like when displayed in the Output window.
Then click on the Advanced checkbox in the editor
We are going to create a settings file, so change the normally hidden File to Edit name to  '/program/settings.ini' in readiness to save.
Select All in the editor ready to be overwritten, then copy and paste the following settings from below, and click Save.
Because Advanced is ticked, the clock script should still be loaded in the background, so switch to the Output window and click Run.
The clock script should load the style settings from '/program/settings.ini' and display this 'Moonlight' skin. 

fontsize$=8
fontcol$=#ffffe6
fontbak$=#0000ff
bordsize$=10
bordrad$=4
bordcol$=#666666
grad=1


Now let's give it hell...
Copy and paste the following settings over the previous editor contents and click Save, then go to the Output tab, click Stop, then Run.

fontsize$   = "14"    
fontcol$    = "#ffff00" 
fontbak$    = "#ff0000"   
bordsize$   = "25" 
bordrad$    = "0"
bordcol$    = "#c60000"   
bakcol$     = "
#000000"   
grad=1

Welcome to Hell.
Click Stop, switch back to Editor, change bakcol$ from "black" to "white" then Save it, switch to Output and Run it.
You can keep changing settings in the Editor tab and viewing the results in the Output tab.
When done playing with settings, untick the Advanced checkbox and open  /program/skinclock.bas  into the editor again.


The ability to manually save settings to a file for reading back later can be useful, but it would be better if the program could save its current settings, and ideally there might be a more convenient way to change settings rather than use a text editor. Easier said than done of course, and would be great if it all appeared like magic with a click of the fingers or a mouse button - and ABRACADABRA... it actually does !

Click on the clock and all the controls appear, click on the clock again (or untick the 'show controls' checkbox) and they disappear.
The controls allow you to change any of the settings, then Save them to the displayed filename using the 'Save settings' button.
If you change the settings filename showing in the window you need to press Enter to register the name change, then click Save settings button.
All the controls are labelled underneath, so you should be able to find your way around (the 'vertical offset' slider moves the clock up or down).
A word of warning - once you have saved a settings file, remember that those saved settings will always over-ride the embedded defaults.


A stylish digital clock deserves a stylish digital display... so it has been given a facility to add embedded fonts of your own choosing.
The same 'embedded font' facility could obviously offer tailored fonts for any of your other Annex-Wifi Basic projects if you wish.


NOTE:  Fonts
The CSS3 @font-face Rule allows you to create your own font family name pointing to a font file that has been downloaded from the web and uploaded to the ESP device. The font style declaration takes the form of:
<style>            @font-face {
                    font-family: myfont;                     src: url('/path/fontname.ext')
             }
</style>

This can be reduced down to a single line by removing the unnecessary white space:
<style>@font-face {font-family: myfont; src: url('/path/filename.ext');}</style>

Substitute the name 'myfont' for your own preferred name if you wish.
Substitute '/path/' for the 'root-relative' path to your font(s). eg: '/font/'
Substitute 'filename.ext' for the saved font filename and its extension (eg:ttf)
Or preferably use a pre-defined variable in place of the literal 'filename.ext'
 
NOTE:  the default system font will be used if the specified font file is not found, as can be demonstrated by the following example.
myfontfile$ = "dig7mono.ttf"    
html |<style>@font-face {font-family: myfont; src: url('/font/| + myfontfile$ + |');}</style>|
html |<p style="font-family: myfont;">This line is just to show an example of what my font looks like.</p>|

TIP:  A handy tip to remember... you don't need to lose your existing script contents to quickly try something else such as the example above, simply add END above your existing code, then add your new temporary code above END so Annex-Wifi Basic will run the temporary code then terminate without bothering about anything below. When done, just remove the temporary test code and END and you are back as you were.


The following website offers a top 10 list of free font download sites:

Top of that list is  https://www.dafont.com/  which offers many LED and 7-segment display fonts in the 'Techno' category - here are links to 2 fonts and their free for personal use licences - the 3 renamed font files used in these demos are include at the bottom of this page (upload them to /font/).

TIP: Annex Toolkit now has a ZIP facility, so zip contents can be uploaded into the appropriate ESP folders (a zip is attached at bottom of page).

FEATURE: The firmware has the ability to load a compressed .gz version of a file, as explained here by Ciccio...
...you should refer to the gz file with its regular name, omitting the final .gz.
Example: if the original file is named dig7monoitalic.ttf the compressed gz version should be nameddig7monoitalic.ttf.gz  BUT  it must always be called in the program using its original name without the .gz extension.
If the original file is present it will be loaded, but if the original filename is not found then the server will automatically try to send the corresponding compressed .gz version ... so in practice you can
replace any file with the correspondeing compressed .gz version without any change in the code.

TIP:  Search for "7-zip portable" for a free archive utility which does not need to be installed and can even be run from a USB flash drive.

NOTE:
  (Wiki quote:)  "A monospaced font, also called a fixed-pitch, fixed-width, or non-proportional font, is a font whose letters and characters each occupy the same amount of horizontal space. This contrasts with variable-width fonts, where the letters and spacings have different widths."
This is an important consideration for a digital clock display, because you don't want the clock width to keep changing according to the differing widths of the changing numbers, eg: 11 is narrower than 14 is narrower than 08 - but all digits of a Monospaced font occupy the same space.

NOTE:  Don't forget about the SPIFFS 31 character limit - a 6 chr path called '/font/' leaves 25 chrs max for the filename.ext, but fonts may have longer names and include characters that could give SPIFFS indigestion, so rename fonts to something reasonable that won't cause problems.

NOTE:  The bigger the font, the longer to load... all the fonts I used were smaller than 50K (one is only 22K).

TIP:  Search for 'portable font viewer' if you need a free font-viewing app which doesn't require installation.

HINT: 
Create a list of  favourites skins - rename favourite settings files and add them to a list of skins, ie:
skins$ = "moonlight, hell, steel, sleek"
Then add a listbox which will allow you to select your choice of skin from the list, ie:
html  listbox$(choice$,skins$)
You'll need to add path and .ext before loading the file... but the script offers an almost identical usage of selecting a font file from a list.
Use the same principle of selecting different sets of parameters to add configuration options to any of your Annex-Wifi Basic projects.



Margaret Baker,
Dec 24, 2017, 2:26 AM
v.1
Margaret Baker,
Dec 24, 2017, 2:26 AM
v.1
Margaret Baker,
Dec 24, 2017, 2:26 AM
v.1
Margaret Baker,
Dec 31, 2017, 8:09 AM
v.1