The goal was easy to read, large numbers and simple layout. Succeeded on at least one of those. Icons can sometimes be confusing. I added lots of features to see what I could fit in. My philosophy is to always "stand on the shoulders of giants" instead of "re-inventing the wheel." I use several available libraries but where I could not find one, I wrote the code. ezTime and OpenWeatherMap.org were easy choices. Writing code to properly text wrap and modifying fonts was, well, fun. I am putting most of the documentation within the code itself as to how things were done, and will but some here as well. I fully expect that if anyone gets anything out of my efforts they will be cut-and-pasting into their own design.

One other thing, I try to keep the code decipherable as much as possible but one person's decipherable is another's awful spaghetti code. I know many will trash my code, that is the nature of the game. I started in this world coding a PDP-8 in machine code and went on via Fortran, LSIP, ... many assemblers, Pascal, html, php, etc. 

Personally I find:

if (a > b) { result = x; } else { result = y; }

much easier to read and understand than

result = a > b ? x : y;

but that's just me, and printf drives me crazy.

On to the project at hand:

FONTS

To keep the fonts somewhat easy to use and modify and to not need too many added bitmaps I chose to modify a known font. I added symbols for degrees and precipitation (a raindrop). I also needed up and down arrows. By modifying the fonts I was able to replace characters that I knew would not be needed. Modifying the original TTF file (true type file, a font format often used by Windows, etc.) I was then able to produce different font sizes simply while testing out what I needed for the display.

FontForge is an open source (free) font editor I installed on my PC. I never used it before but it was pretty straight forward. I loaded the font (Roboto) and was able to copy and paste some glyphs (the individual characters) as needed. For the arrows I loaded a Windows symbol font and copied the arrows from there into what was Roboto. They overwrote some of the first printable characters (the project has a .png file that is the mapping). I created the rain drop and degree symbols as well. The Cyrillic letters (for the Russian version) were copied within the ttf file, in other words I changed their positions within the file to overwrite the Latin (i.e., English) letters. This was a bit tricky because some are linked to each other within the ttf file (who knew?) so that glyphs (letters) that look alike (a Latin "a" and a Cyrillic "a" are the same) are only in the file once, but there are links (pointers) where needed. I figured out how to break those links and copied all the Cyrillic letters over the Latin ones. The order in the file of the letters stayed the same as it maps to the UTF-8 order. Since there are a couple of more Cyrillic glyphs than Latin ones I did kludge a bit on the last two. But the end result is a ttf font file that has the Cyrillic characters where the Latin characters were and a direct mapping to the UTF-8 standard. And, with all the needed letters and symbols in the first part of the file, converting to the Adafruit GFX format was trivial. I output the new ttf file.

Next step was to convert the ttf file to Adafruit GFX format. I used the on-line converter truetype2gfx. You upload the ttf file, specify the font size, and get a header file read to go. Note that the font size may not be what you think so you will probably go through this cycle a few times to get it right. 

To touch up any of the font, once I had the right font size for this project, I found it easer to use the Adafruit GFX Pixel font customiser [sic] to touch things up. You copy the source in the .h file, hit "extract" and scroll through all the glyphs (like how I keep switching from "characters" to "glyphs"? Just to keep you awake.) Here you can do thinks like kerning (move where the next character would start). I set it for the degree character for the next character to start before the end of the degree symbol. This saved space since I knew that the next character would be a lower case letter (because of the overall design) and could start very close, if not under, the degree symbol. It was also in this editor where I made the Su and Th abbreviations for Sunday and Thursday in English to be single, highly kerned characters. That was because this project was originally on an Adafruit Feather Huzzah and smaller eInk display. Finally hitting "Process and create file" makes the new .h file that you copy and paste into your code. It takes a couple of iterations, but you end up with the font you need.

ICONS

I have a few icons in the menus since when the device first comes up I don't know if the person reads only English or Russian. And I wanted to try my hand at icons. I use "The Free & Open Source Image Editor" GIMP, similar to Photoshop, etc. but any image program would work. I set it to one bit mode (Image -> Mode -> Indexed ... Use black and white (1-bit) palette) and played around. Then exported as BMP. I did find lots of icons, etc. on the web and sometimes started with one of those and cleaned it up for my use.

SOUND

There are two types of sound used in the project, one is RTTTL and the other is LPCM. Ring Tone Text Transfer Language (RTTTL) is what old people like me used on our cell phones (which were only invented when we were older than you) before they could handle mp3 files or whatever. It is a very compressed form of monophonic (one tone at a time) music (?) ... ok, sounds. LPCM (or just PCM to be less accurate) is a method of sampling an analog sound at precise intervals and keeping the digital representation in a file. This is the core of a WAV file for instance. We don't need the headers and other stuff, just the raw data. These representations are stored in the program memory space on the MagTag since we don't have any other place for them. Hence, size IS an issue in this case. Hundreds of RTTTL sounds can be stored in the space of one LPCM but the quality is quite different. I tried to get the Westminster Quarterly chimes somewhat realistic, and the cuckoo, so that required PCM. The clock starts up with Reveille, a bugle call which sounds pretty good monophonic, and when the battery is very low, Taps is played. 

The ESP32 environment does not have a tune() function and I did not want to use one or two on found on the Internet because, well, I knew I could do better. I wanted to take advantage of the fact that the MagTag has an amplifier on the digital to analog converter that goes to the "speaker." Most implementations simply toggle the line to the piezo speaker at different rates, and that works but it only changes the frequency of the sound (the "note") whereas we can do better and change the frequency AND the amplitude (the volume);

I created a sine wave here. Actually, I created four, all 8-bit (by keeping the "Max Amplitude" under 255) and all 32 samples. I changed the amplitude for the four so I ended up with four sine waves, each a 32 byte array, but with different amplitudes. The idea is that I now have four different volumes to play at (and to end a sentence is a preposition). So how do I get different frequencies (tones, notes)? That depends on how fast the digital to analog converter gets the data. The faster we output the sinewave, the higher the note. The routine is totally BLOCKING, meaning that nothing else happens when a tune is playing. This could be re-written as an interrupt routine to allow it to run in the background, but that is left as an exercise to the student (you). This works fine, besides, there ain't nuttin' else to do during that time. So by choosing one of the four sine wave arrays, and choosing how quickly we shuffle the data to the digital-to-analog converter, we have a volume variable tune() function. That is in the RTTTL.h file in the code along with the RTTTL "interpreter" I found on the web and copied and pasted (with attributes). It actually works rather well.

...

I just re-worked some of the ringtone code. I did not like the way the device reproduces the sound of the Big Ben bell via LPCM - probably because the frequency response of the piezo does not match my Bose speakers ... so I tried the Westminster Quarters and the "bong" as ringtones. I modified the RTTTL ringtone code and the Tone() routine in two ways. First, I added a "fade" parameter to the Tone() routine (which I call myFancyTone()). If the fade parameter is true, the tone will first play for its duration, then it will play at the next lower volume for half its duration, then a lower volume for a quarter of the duration. This was a quick and dirty attempt at a fading bell sound. It kinda works, but needs more granularity in the diminishing volume since it goes in "steps" and not a smooth transition. It may need some real-time volume diminishing as opposed to look-up tables but that is left as an exercise for the student (i.e., I'm too lazy or busy at the moment). Second, in order to implement this I added to the RTTTL definition keeping entirely backwards compatible. In order to implement this fade, the RTTTL note definition has a "!" at the end of it. And finally, I expanded the range of tones possible. Originally Nokia (I believe the RTTTL spec is theirs) had four octaves and I added to the upper and lower ranges. I know basically nothing about music but I do know how to reproduce it. Sort of like I am with people. I wonder if we can make an effective doppler siren effect ...

TEXT JUSTIFICATION

This was fun. All my searching for how to justify text came up empty. I found lots of other people asking the same question. If text-wrap is turned on, you won't lose any characters off the end of the display but the text would wrap wherever it needed to, often seemingly randomly within a word, and of course no hyphenation, etc. How to properly word wrap?

I found a library called Regexp which is a "regular expression parser." It is a bit different than I expected as it is based on Lua but it definitely fit the bill. In short, I supply it with a coded string to match within a string. So I can tell it (in essence) find any occurrence of alpha-numeric substring that is surrounded by white space. Any substring of letters and or numbers that have a space, tab, line-feed, carriage return, etc. on either side is considered a word. The way to define what I am looking for is somewhat cryptic but powerful. 

The library used a call back. In convertAlert(), I send it the string to match, the string to search within and a pointer to a routine for it to use whenever it finds a match. In this code I give it the weather alert string that comes from some web site (Open Weather or Dark Sky). I also give it the match string which is (the quote marks are part of the parameter since it is a string) 

"([%w%p]+)"

which, yes, is cryptic. What it means is:  find either an alphanumeric (%w) or punctuation (%p) character (the [ ] signify that the search is a set that contains either or %w or %p) and do this repeatedly (+) until you find something that is NOT within the set defined (i.e., NOT an alphanumeric or punctuation character). I also send it the address of a function to call with each match, match_callback_covert().

The routine match_callback_convert() (what a dumb name for my use but so be it) gets called every time a new word is found in the alert string. I simply take the word, calculate how many pixels wide the word is using the getTextBounds() call in the GFX library, and determine if there is enough room on the display for the word to fit. If yes, I add it to the current string, if not, I start a new line. For this to work I need to know the width of the display area in pixels, have the font preset for the getTextBounds() to work properly, and keep track of how many pixels on the line previous words have used. This algorithm gives a left justified text output. If the word does not fit, one could calculate how many spaces would fit instead and then go back through that line of text and add that number of spaces between previous words hence making the text fully justified (i.e., first character of the line is all the way to the left margin, last character on line is at right margin) but this is left as an exercise to the student. If instead of spaces you even distribute individual pixels, it would be variable-width full justification which would be cool but it would take a different level of manipulation that is beyond this project for now. Center justification would be to divide the needed number of spaces in half and add to the beginning and end of the line. 

ALERT

I decided that when an alert comes in, it automatically takes over the display and shows a simple menu on the bottom. This allows scrolling the display exiting the alert. If the user exits the alert, the display will refresh with time, date and weather. At the next weather information fetch (set for every 15 minutes in the main loop(), the alert will show again if it is still active.

(more to come)