After several weeks of getting to know my new Arduino Mega kit, I have completed a reasonably functional prototype, written in Arduino C++, functionally similar to the 68HC11 font manager I described a few months ago on this forum. Being new, Font Pal assuredly contains bugs, although I am aware of only one, described below, which I need help troubleshooting. I would greatly appreciate any feedback or suggestions on improving what could become a helpful piece of software for embedded Arduino projects using the HD44780 LCD controller. Its 68HC11 predecessor was used in a family of home theater audio processors for many years, driven by a state machine that took input from the incoming audio bitstream and the user's remote control commands.
While Font Pal may be a little large, requiring Arduino Mega or above, trimming the font bitmap tables for a particular application is a simple matter. Running Font Pal on my Arduino Mega R3, the practical maximum dynamic memory is about 67%. For this reason, you can include only about half of the 600 or so lines of Font Pal test cases (which occupy the last third of the main code) in a single run and still run properly (the heap begins to make Strings go wonky), so I recommend that you comment out at least half of the tests for any particular run.
Please try out Font Pal and have some fun with it. I invite your comments and will try to respond to all questions.
---ooOOOoo---
I have included many test screens and 'scene builds' to show what Font Pal can quickly achieve. Others are present mainly to stress its capabilities and also test the detection of error conditions (e.g., every time you request a custom character (CC) when eight are already in use, the display should show a "#", signifying CGRAM overflow. Some test screens may look unnecessary, but each has a role in testing, such as verifying the automatic replacement of a font-flagged character if it appears more than once in the display. The "@" error flag should signify a font error (you requested a non-existent font) or a CC font-flagged character that does not exist in the font). It does, but this prototype version is also sometimes indicating such errors when it shouldn't, viz., in one or more particular circumstances, something is changing either the fontChar or the fontFlag, producing the (correct) "@" indication because there is no such character/font combination (the variable in question is CC_id = fontChar + fontFlag). The altered font_id issue occurs mainly with strings like "SsS|SSS" (which produced "Σ@@" on-screen), or "B AB|B BB" --> "B @@" (where the B is bold), but not with "BBbB|BBbB" --> "BBbB" (where the resulting BBbB is bold).
I have verified that the getBitmap() function (which accesses the font table to retrieve a bitmap that gets loaded into the CGRAM in the LCD) is not the culprit. Rather, it occurs consistently with certain fontChar/fontFlag combinations, where each character exists in the font tables, which you can prove by changing the lcdString by adding extra spaces so the problem is not triggered. Moreover, you can deliberately make the getBitmap() function return -1 (to give a "@" on the display), and some of the test cases do just this. In all cases, if fed wrong data, getBitmap() replies with -1, which translates to "@" on-screen. So, in a way, getBitmap() extends the "@" status to include a 'software bug' in addition to a char/font error. ![]()
Before anyone says, "Strings!" I got this working quite well in a week with the darned String things. Also, I believe the problem is minor (or will be) once I understand the root cause and subsequent chain of events that writes the extra "@" on the display. Maybe some of you with more experience in Arduino can see what I cannot. It seems that CC_ids LL, RL, RL, RL become LL, RL, R? ?L at line 98 in LCDfontManager.ino (I'm using ? because I can't easily show the non-printing-char 'box' or a space character. The ? in R? is typically 0x08, which looks like a CGRAM address (obtained from RL), but that should not happen before the call to getBitmap(). The ? in ?L is typically a space (0x20) but should be an R.
Further, on the subject of Strings, the speed of this code (with no Serial.print debugging lines running) is comparable to the 2 MHz 68HC11 ancestor of this program, which I previously described in this forum.
One feature of the String type that is very well suited for this project is mystring.replace(), which automatically replaces all instances of a string, which is precisely the behavior I wanted to take advantage of the property of HD44780 LCD controllers that once a CC is in the CGRAM, Font Pal can display it multiple times on-screen, without penalty. Some of my examples demonstrate this.
Main code:
// #############################################################################
// ################## A r d u i n o - F o n t - P a l ##################
// ################## ( F o n t P a l o o z a ) ##################
// ################## F O R H I T A C H I H D 4 4 7 8 0 ##################
// ################## L C D C O N T R O L L E R S ##################
// #############################################################################
//
// Copyright (c) 2024 Alastair Roxburgh
// (Font Pal recreates the functionality of an original 68HC11 stack-frame
// assembly language program named SCREENS, that was devised in 1997 by Alastair
// Roxburgh & Sunil Rawal for the EOS series of TheaterMaster digital home theater
// audio processors. Written in Arduino C++, Font Pal makes heavy use of the String
// class and arrays of struct to create a custom font manager for 2x20 LCD displays
// based on the Hitachi HD44780 chip.)
//
// Note:
// While Font Pal, which was developed expressly for the Arduino Mega platform,
// reproduces most of the functionality of its 1977 68HC11 predecessor its
// internal algorithms are only remotely similar.
//
// Font Pal is free software: you can redistribute it and/or modify it
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// Font Pal is distributed in the hope that it will be useful, but WITHOUT
// WITHOUT ANY WARRANTY; without even the implied warranty of MECHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along
// with Font Pal. If not, see <http://www.gnu.org/licenses/>.
//
// See the license.txt file for further licensing & copyright details.
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
// History
//
// 2024.09.04 Alastair Roxburgh (kiwiengr) - initial creation
//
//
//
/*
---To Do---
Once everything is working, add more error chekcing:
a) Check that the length of lcdString <=40.
b) If there is one '|' in lcdString, check that lcdString.length() - 2 * marker_idx = 1.
c) If there are two '|' in lcdString, check that lcdString.length() - 4 * marker_idx = 2.
d) Consider how to select LCD size: 16x1, 20x1, 16x2, or 20x4.
Is there a sensible way to configure Font Pal as an Arduino library?
Font Pal is a font manager for Arduino and Hitachi HD44780-based LCDs, so-named
as a more easily pronounced version of Font-Palooza, a handle inspired by a more
challenging to pronounce, Lollapalooza, the famous recurring music festival first
held in 1991. Since then, the word "palooza" has been used so often that it has
entered the mainstream language as a stand-alone word, a neologism. Indeed,
following in Lollapalooza's footsteps, "palooza" is now defined as an event or
thing that aspires to be "an extraordinary or unusual thing, person, or event;
an exceptional example or instance" (dictionary.com). Some of my favorite examples
are Hula Palooza, Datapalooza, law-LA-palooza, St. Lou-a-palooza, and MINE-a-palooza
(sometimes hyphenated, sometimes not; sometimes preceded by the "a" from Lolla).
OED Online traces Lollapalooza in its various spellings back to 1904 but, strangely,
makes no mention of Lala Palooza, the name of the female protagonist in Pulitzer
prize winner Rube Goldberg's 1936-1939 comic strip of the same name.
Hence, with apologies to Douglas Adams, Font Pal hopes it, too, will be
seen as impressive and wholly remarkable, even if not as wildly popular
and in-your-face as the asteroid named Arthurdent (asteroid 25924).
Written in Arduino C++ and making heavy use of the String type and arrays of
struct, Font Pal recreates the functionality of an original 68HC11 stack-frame
assembly language program that was devised in 1997 by Alastair Roxburgh & Sunil
Rawal for the ground-breaking EOS series of TheaterMaster digital home theater
audio processors.
Although this new implementation mirrors many of the capabilities and structure
of its 68HC11-based predecessor, apart from using a similar character bitmap
database, many of the algorithmic details are different. Interestingly, whereas
the 68HV11E1 microcontroller used in EOS TheaterMasters is an 8-/16-bit 2 MHz
CISC-based design, the Arduino's 16 MHz 8-bit RISC-based ATmega2560 runs at a
similar speed overall, the bottleneck being the modest speed of the Hitachi
HD44780 controller chip in typical LCDs, not to mention the relatively slow
response of typical Okaya STN Transflective LCDs (which can take 150 ms to go
from clear to full black when a typical character is displayed and 300 ms to
fade to clear when a space is displayed.)
INTRODUCTION:
Font Pal for the Arduino Mega platform allows the C++ app designer to bypass much
of the frequently complex chore of custom character (CC) and font management for
typical Hitachi HD44780 character-based LCDs. In its essentials, Font Pal manages
CGRAM, a scarce memory resource that stores up to eight bit-mapped CCs in the
HD44780 LCD Controller CGRAM. Font Pal reduces the barrier to programming complex
and varied sequences of CCs, working behind the scenes to automate the loading and
unloading of CC bitmaps according to the application's graphical design needs. By
separating font management from the application code, the application programmer
has more time to pursue application code improvements, not bogged down by the
tedious task of manually managing CCs.
Consider how much of an all-around improvement Font Pal can produce:
1) Reduced application development time,
2) Improved application quality,
3) Nicer-looking and more legible displays,
4) Better LCD utilization through optimized CC automation.
The improvements gained with Font Pal are not necessarily subtle and
may defer indefinitely the need to move to a bit-mapped display and
faster processor.
EXAMPLE (1):
Font Pal can quickly turn a 2x20 LCD into a 2- to 10-channel VU meter,
simultaneously displaying up to 16 levels per channel at 6 dB per step.
If we reserve the bottom bar for zero signal and ignore the 6 dB gap
between the two LCD rows, we obtain a total range on each channel of
15*6 dB = 90 dB, which can be updated several times a second. Indeed,
by using the two LCD rows separately, we can display even 20 channels
with half the vertical resolution. In these extreme cases of power
through repetition of bitmaps, eight static bitmaps stored in the
CGRAM are stretched to as many as twenty simultaneously displayed
locations on the LCD, as in the following examples of symmetrical
VU mater layouts:
xxxxxxx xxxxxxx 2 channels
xxxxxx xxxxxx xxxxxx 3 channels
xxxx xxxx xxxx xxxx 4 channels
xxx xxx xxxx xxx xxx 5 channels
xx xx xxx xxx xx xx 6 channels
xx xx xx xx xx xx xx 8 channels
x x x x xx x x x x 9 channels
x x x x x x x x x x 10 channels
where 'x' represents a single bar-font character. The following diagram
shows a VU meter for ten audio channels constructed using a 2x20 char LCD.
For 6 dB steps, converting from linear amplitude to logarithmic (dB)
amplitude is straightforward: the audio DSP averages the observed position
of the highest 1-bit in arithmetically positive audio samples (typically
16- to 24-bit words). It does not matter if the number representation is
integer or fractional. If the VU meter processes five values per second,
an appropriate averaging period is about 200 ms or 10,000 audio samples.
If you require a peak-reading VU meter, display the position of the highest
bit observed during the same period rather than the average. You can also
slide the dB scale up or down in 6 dB steps to set the 0 dB wherever you
want. The diagram (below) shows amplitudes of 0, 0, 60 dB, 66 dB, 18 dB,
78 dB, 30 dB, 36 dB, 96 dB, and 0, where '0' (not defined on a logarithmic
scale) represents zero signal. This example illustrates how you can stretch
the eight CGRAM locations to create a VU meter for up to 10 channels on a
2x20 LCD:
+--------------------+
| 2 3 5 8 |
| 1 1 8 8 4 8 6 7 8 1|
+--------------------+
where the numeral signifies the height in pixels of a block-shaped CC.
A '1' alone in a channel (i.e., the bottom row of pixels only) represents
zero signal, leaving a range of 16 x 6 dB = 96 dB (including the 1-pixel-
height inter-row gap of 6 dB). You can find an example of this type of bar
graph in the optional Font Pal splash screen. Also included in Font Pal is
a utility function, printFonts(), which can help you verify the correctness
of the font bitmap tables after you've made changes.
In the vertical bargraphs just described, the entire resource of eight CCs
is dedicated to this one use. Nevertheless, we are free to choose the number
of channels and bar widths because these take advantage of the freedom to
repeat CCs to build bars of any suitable widths. This way, we can create
displays that compel the eye of the beholder, creating the impression that
there are more than eight individually programmable CCs. I need to point
out that the Font Pal LCD manager avoids the method of tricking the eye
through CC animation, which can lead to less-than-satisfactory results due
to the relatively slow response time of STN (super-twisted nematic) trans-
flective LCDs. Font Pal's bar graph characters have a central gap to hide
the distracting presence of the inter-character gap when the bar width is
greater than one character. This gap also allows for flexibility in setting
the width of bars and using mixed bar widths while maintaining the same look
of the bars. For example, if we construct three bars having widths of one,
two, and three characters, respectively, with heights of 1, 3, and 2 pixels,
they will look something like:
XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX
rather than:
XXXXX XXXXX XXXXX
XXXXX XXXXX XXXXX XXXXX XXXXX
XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX
The split bar design of the top example makes the bars look more uniform.
EXAMPLE (2):
A home theater processor's LCD screen might look like this:
+--------------------+
|vOv LR Digital 5.1 |
|^Q^ D1:Blu X -15 dB|
+--------------------+
where the eight unique CCs are marked as vO^QLRXg, for a total of ten CCs
on screen. The vOv and ^Q^ represent a "channel daisy" that indicates which
channels are in use with the current signal source, requiring four unique
CCs. The "LR" is two CCs forming a Dolby "Double-Dee" logo; the "g" is an
improved lowercase g with a true descender, and the "X" is an equalization
indicator, a single CC in the shape of 'E/Q'.
The Channel ("C") font produces 6.1 channel layouts similar to the following:
* C C = Center front
* * LF RF LF = Left Front
* correspond channels: SUB RF = Right Front
* * LS RS LS = Left Surround
* ES RS = Right Surround
SUB = Subwoofer
EXAMPLE (3):
A micro-space-invaders game layout might look like this:
+--------------------+
| x x x x x 000|
| /\ /\ ! /\ /\ _!!|
+--------------------+
where the five on screen CCs are marked x/\!, representing an invader (repeated
five times), the "/\" is a pair of CCs that has the shape of a shield, while "!"
is a CC in the shape of a laser. The invaders use ten different CCs (five with
legs in and 5 with legs out) that load in sequence as the waves of aliens descend
ever closer. Arduino C++ code for constructing the above screen:
startPos = 0;
lcdString = "a a a a 000|"
"I I I I "
" LR LR 3 LR LR _11|"
" ss ss P ss ss PPP";
LCDfontManager();
setupRow2();
LCDfontManager();
EXAMPLE (4):
Another screen example, one that might look at home in a home theater
audio processor, is:
+--------------------+
|LF:O CR:ox LR:O |
|LS:ox SB:O RS:ox |
+--------------------+
where the LF, CR, LR, LS, SB, and RS use a mixture of lowercase ('c' and 's')
and small-caps CCs (L,F,R,B) to render a larger set of small-caps using only four
CCs. Together with speaker-shaped CCs (large and small, denoted here as 'O' and
'o'), and a regular lowercase 'x' indicating the cross-over of low frequencies
from channels with small speakers, this screen simplifies speaker and base
management setup for a home theater processor. In total it uses six CCs, but
it gives an impression that there are more than that.
LCD DISPLAY STRING FORMAT:
This routine parses font-encoded Strings, N characters at a time, loading the
relevant custom characters before sending the processed String to a Hitachi
HD44780-compatible LCD module for display.
Example String (where "_" represents an ASCII space 0x20, but you would type
an actual space character):
clearTheStage();
startPos = 0;
lcdString = "Large LCD Display|"
" l BBB l l";
LCDfontManager();
There can be up to 40 display characters before the '|' marker character,
with the corresponding font flags in the following row, which is convenient
when constructing a display String on the fly. The VU meter capability is
discussed later. In the meantime, here is a more normal string that fills
all 40 locations on a 2x20 LCD:
clearTheStage();
startPos = 0
lcdString = "This long String fills two lines exactly|" // 2 CCs, 2 unique.
" l l l";
LCDfontManager();
The number of characters after the "|" must equal the number before it.
Counting is zero-relative, so the marker character is at position 40. Thus,
the number of fontChars before the "|" is 40, and there are 40 fontFlags
after the "|". A space in the second line indicates that the LCD's default
ROM-based characters will be used. The total number of characters in this
string is 81, of which only the first 40 get displayed (after CC substitutions).
Important Note: The Font Pal LCD font manager expects ALL font-formatted
Strings to have the second part marked by "|" and containing the fontFlags.
DISPLAYING PLAIN TEXT:
Fontg Pal also supports un-font-formatted plain-text Strings, e.g., "Plain Text".
Plain text displays more quickly than font-formatted text, making it useful
for elements of an LCD 'scene' for which plain text suffices.
Refer to the examples below for more guidance on using this feature.
EXAMPLES:
(a) An example of how to display a plain text String at position 25 on the
LCD, euivalent to placing the text starting at position 5 in the second row.
Note: If our intention is to add a new String to an an existing LCD 'scene',
we would omit clearTheStage().
clearTheStage(); // This line may not be needed (see note)
startPos = 25; // Allowable range is 0-39
lcdString = "D2:DVD"; // Plaintext example (no font encoding)
LCDfontManager();
The variable named startPos is used to select the position on the LCD where
you want to display the text. Strings that are positioned so that they extend
beyond the end of row 0 of the LCD, wrap around onto row 1. Strings that extend
past 40 characters (i.e., the size of the screen) are truncated. The variable
startPos has a default value of 0, and can go as high as 39.
(b) The best way to blank out only part of the LCD is to write a blank plain-
text String:
// Write six spaces to the LCD, starting at position 8:
startPos = 8;
lcdString = " "; // <-- Note: no '|' marker.
LCDfontManager();
(c) An example of displaying a String at position 4 that has mixed font-formatted
text and plain text:
clearTheStage();
startPos = 4;
lcdString = "LR Dolby digital|" // <-- Note the '|' marker, and no ';'
"LL l l "; // Need the ';' here, though!
LCDfontManager();
(d) It is often a simple matter to pad an item with spaces, to make sure that
the next item completely overwrites the previous one. The extra space in the
bold-formatted "ON" String in the following example illustrates this:
"ON |BB "; and "OFF|BBB";
(e) Whenever beginning a new LCD 'scene', the best way to clear both the
LCD and CCs stored in the CGRAM,of the LCD is to 'clear the stage':
clearTheStage(); // Do this before building a new 'scene'
if we just need to clear all or part of the LCD (without clearing CGRAM),
we can write some spaces. The following example write 8 plain-text spaces
beginning at screen position 10:
startPos = 10;
lcdString = " "; // 8 plain-text spaces beginning at 10.
This is simpler and takes much less time than the following functionally
equivalent, but not-recommended alternatives:
startPos = 10;
lcdString = " |" // 8 plaintext spaces beginning at 10.
" ";
or:
startPos = 10;
lcdString = " | "; // 8 plaintext spaces beginning at 10.
(f) Most useful for shapes that take up two lines (e.g., the channel 'daisy'
CC patterns defined in the fint library) is the ability to break up the
pattern into two short Strings of equal length, rather than overwrite the
rest of the display with spaces. These lcdStrings differ from the standard
lcdString in the use of two "|" markers, one for each row:
startPos = 0;
lcdString = "232|CCC"
"1e1|CCC";
If you prefer, this may be written in the following alternative (equivalent)
form:
startPos = 0;
lcdString = "232|" // Make sure all four Strings have the same length.
"CCC"
"1e1|"
"CCC";
However, to make either of these double-row examples work, you must use two
calls to LCDfontManager(). I have omitted the optional call to clearTheStage()
because if the LCD already displayed a different 'daisy', this new one would
rewrite it, leaving any other text on the LCD unchanged. It is important to
note that the 'daisy' examples all use four unique CCs, leaving only four
for any other lcsStrings that are part of this 'scene'.
Either:
// Recommended for formatting double row CC graphics:
clearTheStage();
startPos = 0;
lcdString = "232|CCC"
"1e1|CCC";
LCDfontManager();
setupRow2();
LCDfontManager();
Or:
// Recommended for formatting double row CC graphics:
clearTheStage();
startPos = 0;
lcdString = "232|"
"CCC"
"1e1|"
"CCC";
LCDfontManager();
setupRow2();
LCDfontManager();
Without the setupRow2() function and the second call to LCDfontManager(),
the second row ("1e1|CCC") would be ignored and not get printed.
THE FOLLOWING METHOD IS NOT RECOMMENDED FOR DOUBLE ROW CC GRAPHICS:
The method of combining both rows of characters into a single string that
wraps onto the second row of the LCD fails to be as useful because not only
does it fail to mimic the LCD layout, but it cannot avoid overwriting other
items on the LCD. Although it bypasses the need for the setupRow2() helper
function and a second call to the LCD font manager, it leaves you with the
need to pack out lcdString with enough spaces to push the second row characters
("1e1|CCC" in this example) past the end of the first row and onto the second
row, which (depending on the value of startPos) may write over parts of LCD
screen you wish to keep. However, unless that is your intention, the two "|"
marker format shown above largely avoids the use of space characters to
correctly format double-row graphics, and is the preferred method.
// The following is not recommended for double-row CC graphics:
startPos = 0;
lcdString = "232 1e1|CCC CCC";
LCDfontManager();
ERROR REPORTING:
Various error conditions are indicated by the appearance of the following special
characters on the LCD (which the controller displays in place of every requested,
but unavailable custom character):
"#" --- CGRAM overflow (more than 8 unique custom characters)
"@" --- Bad font-char or bad font-flag
HITACHI HD44780-BASED LCD DISPLAYS:
The LCD is used write-only, with all screen editing taking place in the screenImage
RAM buffer that is copied to the LCD display by calls to the LCDfontManager() function.
A major part of any digital controller design that uses a Hitachi HD44780-based LCD display
is consideration of the font-formatting design; juggling the timing of *what* is shown on
the LCD, and *when*, given the scarcity of CGRAM locations in the HD44780. This controller
chip can display only eight unique custom characters (CCs), mitigated by the ease with which
any CC can be repeated on-screen up to the limit of 40 characters in a 20x2 LCD display.
With due care during the design phase of a project (such as the EAD EOS TheaterMasters), the
limitation on the number of unique CCs can be made to look like many more. Esay to overlook,
but useful in this regard are the following points:
(i) Lowercase ROM characters scouvwxz are sufficiently shape-similar to the custom smallcaps
font in this package, that they can do the work of up to eight more CCs.
(ii) By carefully choosing your onscreen words and names, Font Pal can aid you in minimizing
your use of custom characters versus the use of built-in ROM characters, especially in
the all-important 'splash screen' that uniquely identifies a product or system:
Splash screen example 1: TheaterMaster SIGNATURE (8 unique CCs, 9 unique ROM chars)
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster|BbbbbbbBbbbbb"; // Bold and lowercase bold
LCDfontManager(); startPos = 25;
lcdString = "SIGNATURE"; // Plain text
LCDfontManager();
delay(3000);
or
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster SIGNATURE|"
"BbbbbbbBbbbbb ";
LCDfontManager();
delay(3000);
Splash screen example 2: TheaterMaster Ovation (7 unique CCs, 8 unique ROM chars)
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster Ovation|"
" Bbbbbbb";
LCDfontManager();
delay(3000);
Splash screen example 3: TheaterMaster Encore (6 unique CCs, 8 unique ROM chars)
clearTheStage(); startPos = 3;
lcdString = "TheaterMaster Encore|"
" Bbbbbb";
LCDfontManager();
delay(3000);
Splash screen example 4: 8800Pro (8 unique bignum CCs, 3 unique ROM characters)
clearTheStage(); startPos = 5;
lcdString = "ABABABAB |88880000 " // Double-row Strings must
"CDCDCDCDPro|88880000 "; // be of equal length!
LCDfontManager(); setupRow2(); LCDfontManager();
delay(3000);
Splash/Feature screen example 5: (6 unique CCs, 9 unique ROM characters)
clearTheStage(); startPos = 1;
lcdString = "Reference CinemaTM & Cinema 7.1TM|"
"B B SS B B BSS";
LCDfontManager();
delay(3000);
(iii) Letters in-common between font-enhanced words come at no additional CGRAM cost.
(iv) Although CGRAM limits unique custom characters (CCs) to eight, they can be repeated
up to the size of the display (a 2x20 LCD can display as many as 40 CCs).
(v) Eight CCs is sufficient to code multi-channel vertical bargraphs that have 16 steps,
and horizontal bargraphs that have up to 100 steps.
(vi) Sometimes it's feasible to design multi-character CCs in a way that hides the one-pixel
gap between LCD characters and LCD rows (with reference to Okaya and similar 2x20 LCDs).
Examples of this are provided in the bargraph font.
(vii) Use the "#" and "@" error flags to help you design screen layouts free from CGRAM
overflow and font errors.
NOTES:
During this development, I discovered that Arduino variables of type String, and built-in
functions that process them, often have a problem with bytes or characters with the value
0x00, i.e., byte(0). This is a carry-over from C (and C++'s) use of the \0 null character
(0x00) the end-of-string marker in traditional C-strings, and even though the relatively
new String type does not use such markers, my attempts to print Strings containing 0x00
sometimes had problems. Perhaps the Hitachi HD44780 designers already anticipated this
problem, because, to their credit, they arranged for a set of shadow addresses, allowing
us to access to those same eight CC bitmap locations in the chip's CGRAM at 0x00-0x07 or
0x08-0x0f (BTW, that OR is an inclusive-OR). Therefore, where it matters in this program,
we number the HD44780 LCD controller's CGRAM locations from 0x08 to 0x0f (8 to 15).
However, because no such duplicate addressing exists for the various arrays, all of which
start at index 0, make sure to add 8 to all zero-relative custom character (CC) CGRAM
pointer values when assembling a 'scene' (a name used here to describe an LCD display
that is made up of two or more small Strings that are assembled into the screenImage
String being written to the LCD.
Example of Arduino C++ code for displaying two aligned 20-character Strings:
clearTheStage();
startPos = 0;
lcdString = "Hi-Sccrers: AJR SCR|" // Begins at startPos on the LCD.
"BB BBOBBBB "
" AR JSH|" // Begins at startPos + 20 on LCD.
" ";
LCDfontManager();
setupRow2();
LCDfontManager();
Note that all such two-row strings have two '|' font markers. While the same
display may be constructed using a single String, the lack of inherent screen
layout makes verification more difficult, despite fewer function calls:
clearTheStage();
startPos = 0;
lcdString = "Hi-Scorers: AJR SCR AR JSH|"
"Bb Bb bbbb ";
LCDfontManager();
Behind the scenes, after custom font processing, lcdString overwrites screenImage,
which, in turn, is written to the LCD. Whichever way you edit your Strings, the
resulting LCD screen layout will be similar to the following:
+--------------------+
|Hi-Scorers: AJR SCR|
| AR JSH|
+--------------------+
*/
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
/*
-----SUMMARY OF FONT PAL FORMATS AND COMMANDS-----
-----SUMMARY OF FONT PAL FORMATS AND COMMANDS-----
-----SUMMARY OF FONT PAL FORMATS AND COMMANDS-----
1) Plain text (1-40 chars) starting at position 0-39. Example:
startPos = 5; lcdString = "Hello World!"; LCDfontManager();
2) Font-formatted text and CCs (1-40 chars) starting at position 0-39.
Formatted text must end with '|'. The fontChar and fontFlag parts of
lcdString must match. Using two lines as shown allows you to visually
check the font formatting. Example:
startPos = 24; lcdString = "Nice day for it!|"
"B l ";
LCDfontManager();
3) Optional command to clear font formatting prior to overwriting
the LCD with a new font-formatted string. This is useful when creating
an LCD 'scene' from multiple lcdStrings:
clearTheStage();
4) Font-formatting of double-height aligned blocks of text and CCs.
This is particularly useful when creating 'big nums' or other CC groupings.
Every double-height lcdString must have exactly two '|' marker characters.
The string lengths must match. Due to the HD44780 limit of eight CCs,
repetition of CCs is your friend, because, for example, a single CC can
be repeated 40 times to fill the display, at no cost. Bar graphs, in
particular can take full advantage of this. Example:
clearTheStage();
startPos = 0;
lcdString = "232|"
"CCC"
"1e1|"
"CCC";
LCDfontManager();
setupRow2();
LCDfontManager();
5) If your LCD is the STN (super-twisted nematic) transflective type, and
your display data changes several times a second, you may need to add delays
to ensure full visibility. Although providing excellent sunlight visibility
at low cost, STN LCDs can take 150 ms to go from clear to full black, and
300 ms to fade to clear. To make your display easy to read under all light
conditions, you may need to wait out these times by adding delays of 100 ms
or more. Experimentation is your best guide.
6) Dynamic displays such as bar graphs require lcdString to be constructed
on the fly,. e.g. using C++ built-in String functions.
*/
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
#include <Arduino.h>
#include <LiquidCrystal.h>
// Currently compatible only with 2x20 LCDs.
// Initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 8, 9, 10, 11, 12); // RS, RW, E, D0, D1, D2, D3. 4-bit transfer with R/W
// %%%%%%%%%%% GLOBALS:
String final_screenImage; // Buffers the final LCD-formatted version of screen_image for mapping to the LCD's two rows.
String screenImage = " "; // 40 fontChars + 40 fontFlags, 0-79, merged.
String lcdString = "";
String merged_lcdString = "";
int8_t marker1_idx; // index of 1st '|' marker in lcdString. Not found = -1 --> fontEncoded = false (i.e., plain text).
int16_t startPos = 0; // Position in screenImage that will be overwritten with a new font-formatted String.
String current_fontChar; // e.g., "d"
String current_fontFlag; // e.g., "L"
String current_CC_id; // e.g., "dL" ( = "d" + "L")
int8_t fontChar_num;
uint8_t* gBitmap; // Pointer into the CC bitmap array of structs'
String CC_id_table[8]; // e.g., {"gL","","dL","",...}
int8_t CGRAM_ptr; // 0-7, but flags written to the LCD screen must be 8-f !
uint8_t scrn_ptr;
byte blankBitMap[8] = {0,0,0,0,0,0,0,0};
/*----------------------------------------------*/
/* A Collection of Useful CC Strings */
/*----------------------------------------------*/
String daisy[] = { "242|CCC"
"1d1|CCC", // 3/2/0 daisy 0 (daisies 0-6 are without LFE channel)
"2c2|CCC"
"a5a|CCC", // 2/P/0 daisy 1
"2c2|CCC"
"ada|CCC", // 2/0/0 daisy 2
"242|CCC"
"a5a|CCC", // 3/1/0 daisy 3
"242|CCC"
"ada|CCC", // 3/0/0 daisy 4
"2c2|CCC"
"1d1|CCC", // 2/2/0 daisy 5
"b4b|CCC"
"ada|CCC", // 1/0/0 daisy 6
"bcb|CCC"
"ada|CCC", // 0/0/0 daisy 7 (no lock pattern)
"232|CCC"
"1e1|CCC", // 3/2/.1 daisy 8 (daisy 8-14 are with LFE channel)
"2f2|CCC"
"a6a|CCC", // 2/P/.1 daisy 9
"2f2|CCC"
"aea|CCC", // 2/0/.1 daisy 10
"232|CCC"
"a6a|CCC", // 3/1/.1 daisy 11
"232|CCC"
"a6a|CCC", // 3/0/.1 daisy 12
"2f2|CCC"
"1e1|CCC", // 2/2/.1 daisy 13
"b3b|CCC"
"aea|CCC", // 1/0/.1 daisy 14
"242|CCC" // 3/3/0 daisy 15
"151|CCC",
"232|CCC" // 3/3/.1 daisy 16
"161|CCC",
" | " // 0/0/0 daisy xx (for no lock mute)
" | "
};
String normalSpeakers[] = {
"LF|cc", // 0. E.g., Display a selectable menu of speakers (all are in small caps): LF CR RF
"RF|cc", // 1. LS SB RS
"Ls|c ", // 2.
"Rs|c ", // 3.
"cR| c", // 4.
"sB| c" // 5.
};
String revSpeakers[ ] = {
"LF|rr", // 0. Example of use: indicate a speaker selection for adjustment (all are in reverse small caps).
"RF|rr", // 1.
"Ls|rr", // 2.
"Rs|rr", // 3.
"cR|rr", // 4.
"sB|rr" // 5.
};
String HorizontalSPLmeter = {"SPL: Ref! . dB|" // Boiler plate for horizontal SPL meter. Ref "!|G" (down arrow) marks a reference level.
" G "}; // However, the actual bar graph is constructed on the bottom LCD row (not shown)
//String analogDaisy[] = ;
//analogDaisy = ;
String invArray[][2] = {
{ "a a a a a|I I I I I", "a a a a a|O O O O O" }, // 0
{ "b b b b b|I I I I I", "b b b b b|O O O O O" }, // 1
{ "c c c c c|I I I I I", "c c c c c|O O O O O" }, // 2
{ "d d d d d|I I I I I", "d d d d d|O O O O O" } // 3
};
/*----------------------------------------------*/
/* Custom Character Fonts */
/*----------------------------------------------*/
// @@@@@@@@@@ G - BARGRAPH FONT @@@@@@@@@@
byte bargraphfont[][8] { // "0abcLR!87654321"
// Horizontal Bar Graph:
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '0' no bars
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, // 'a' one bar
{ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 }, // 'b' two bars
{ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15 }, // 'c' three bars
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }, // 'L' L-bar
{ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, // 'R' R-bar
{ 0x00, 0x1c, 0x04, 0x04, 0x04, 0x15, 0x0e, 0x04 }, // '!' bent down-arrow
// Multi-Channel VU Meter Bar Graph (6 dB per step):
{ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '8'
{ 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '7'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '6'
{ 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }, // '5'
{ 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b }, // '4'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b }, // '3'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b }, // '2'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b } // '1'
};
// @@@@@@@@@@ 0 to 9 - BIGNUMS FONTS @@@@@@@@@@
byte big0font[][8] { // "ABCD"
// '0' = 4*0 + 0,1,2,3
{ 0x03, 0x07, 0x0f, 0x0e, 0x1c, 0x1c, 0x18, 0x18 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x0e, 0x07, 0x07, 0x03, 0x03 }, // 'B'
{ 0x18, 0x18, 0x1c, 0x1c, 0x0e, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x03, 0x03, 0x07, 0x07, 0x0e, 0x1e, 0x1c, 0x18 } // 'D'
};
byte big1font[][8] { // "ABCD"
// '1' = 4*1 + 0,1,2,3
{ 0x01, 0x07, 0x0f, 0x0f, 0x01, 0x01, 0x01, 0x01 }, // 'A'
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, // 'B'
{ 0x01, 0x01, 0x01, 0x01, 0x01, 0x0f, 0x0f, 0x0f }, // 'C'
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x1e, 0x1e } // 'D'
};
byte big2font[][8] { // "ABCD"
// '2' = 4*2 + 1
{ 0x01, 0x07, 0x0f, 0x1e, 0x18, 0x00, 0x00, 0x00 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x07, 0x03, 0x03, 0x07, 0x0e }, // 'B'
{ 0x00, 0x01, 0x03, 0x07, 0x0e, 0x1f, 0x1f, 0x1f }, // 'C'
{ 0x1c, 0x18, 0x10, 0x00, 0x03, 0x1f, 0x1f, 0x1f } // 'D'
};
byte big3font[][8] { // "ABCD"
// '3'
{ 0x03, 0x0f, 0x1f, 0x18, 0x00, 0x00, 0x00, 0x03 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x06, 0x06, 0x06, 0x0e, 0x1c }, // 'B'
{ 0x03, 0x00, 0x00, 0x10, 0x18, 0x1f, 0x0f, 0x07 }, // 'C'
{ 0x1e, 0x0f, 0x07, 0x07, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
byte big4font[][8] { // "ABCD"
// '4'
{ 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x0e }, // 'A'
{ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x0c, 0x0c, 0x0c }, // 'B'
{ 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'C'
{ 0x1f, 0x1f, 0x1f, 0x0c, 0x0c, 0x1e, 0x1e, 0x1e } // 'D'
};
byte big5font[][8] { // "ABCD"
// '5'
{ 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f }, // 'A'
{ 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x1c, 0x1e }, // 'B'
{ 0x00, 0x00, 0x00, 0x08, 0x0c, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x07, 0x03, 0x03, 0x03, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
byte big6font[][8] { // "ABCD"
// '6'
{ 0x03, 0x07, 0x0e, 0x1c, 0x18, 0x18, 0x1b, 0x1f }, // 'A'
{ 0x1c, 0x1e, 0x07, 0x03, 0x01, 0x00, 0x1c, 0x1e }, // 'B'
{ 0x1c, 0x18, 0x18, 0x1c, 0x0e, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x07, 0x03, 0x03, 0x03, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
byte big7font[][8] { // "ABCD"
// '7'
{ 0x0f, 0x0f, 0x0f, 0x0c, 0x00, 0x00, 0x00, 0x00 }, // 'A'
{ 0x1f, 0x1f, 0x1f, 0x07, 0x07, 0x0e, 0x0e, 0x0e }, // 'B'
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01 }, // 'C'
{ 0x1c, 0x18, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10 } // 'D'
};
byte big8font[][8] { // "ABCD"
// '8'
{ 0x03, 0x07, 0x0f, 0x1c, 0x1c, 0x1c, 0x0e, 0x07 }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x07, 0x07, 0x07, 0x0e, 0x1c }, // 'B'
{ 0x0f, 0x1c, 0x1c, 0x1c, 0x1c, 0x0f, 0x07, 0x03 }, // 'C'
{ 0x1e, 0x07, 0x07, 0x07, 0x07, 0x1e, 0x1c, 0x18 } // 'D'
};
byte big9font[][8] { // "ABCD"
// '9'
{ 0x03, 0x07, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0x1c }, // 'A'
{ 0x18, 0x1c, 0x1e, 0x0e, 0x07, 0x03, 0x03, 0x07 }, // 'B'
{ 0x0f, 0x07, 0x00, 0x10, 0x18, 0x1c, 0x0f, 0x07 }, // 'C'
{ 0x1f, 0x1b, 0x03, 0x03, 0x07, 0x0e, 0x1c, 0x18 } // 'D'
};
// @@@@@@@@@@ S - SYMBOL FONT @@@@@@@@@@
byte symbolfont[][8] { // "sDSTMH><^vud_1234KkLQnNbCF"
{ 0x00, 0x00, 0x00, 0x0e, 0x10, 0x0c, 0x02, 0x1c }, // 's' Subscript 's'
{ 0x04, 0x04, 0x0a, 0x0a, 0x11, 0x11, 0x1f, 0x00 }, // 'D' Greek Delta
{ 0x1f, 0x10, 0x08, 0x04, 0x08, 0x10, 0x1f, 0x00 }, // 'S' Greek Sigma
{ 0x1f, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00 }, // 'T' Trademark symbol, use "TM|SS"
{ 0x11, 0x1b, 0x15, 0x11, 0x00, 0x00, 0x00, 0x00 }, // 'M'
{ 0x1b, 0x1f, 0x1b, 0x00, 0x1f, 0x18, 0x1e, 0x18 }, // 'H' single HF character symbol, use "H|S"
{ 0x10, 0x18, 0x1c, 0x1e, 0x1c, 0x18, 0x10, 0x00 }, // '>' Right Arrow symbol
{ 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01, 0x00 }, // '<' Left Arrow symbol
{ 0x04, 0x04, 0x0e, 0x0e, 0x1f, 0x1f, 0x00, 0x00 }, // '^' Up Arrow symbol
{ 0x00, 0x00, 0x1f, 0x1f, 0x0e, 0x0e, 0x04, 0x04 }, // 'v' Down Arrow symbol
{ 0x04, 0x0e, 0x1f, 0x00, 0x04, 0x0e, 0x1f, 0x00 }, // 'u' Up double arrow
{ 0x1f, 0x0e, 0x04, 0x00, 0x1f, 0x0e, 0x04, 0x00 }, // 'd' Down double arrow
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f }, // '_' underline char (in the cursor row)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, // '1' Centered 4-dot screen-saver pattern:
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 }, // '2'
{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '3'
{ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '4'
{ 0x00, 0x01, 0x03, 0x1f, 0x1f, 0x1f, 0x03, 0x01 }, // 'K' Big Speaker (Kicker)
{ 0x00, 0x00, 0x01, 0x03, 0x0f, 0x03, 0x01, 0x00 }, // 'k' small speaker (small-k kicker)
{ 0x10, 0x18, 0x1f, 0x1f, 0x1f, 0x18, 0x10, 0x00 }, // 'L' Big Speaker reversed
{ 0x06, 0x08, 0x08, 0x1c, 0x08, 0x09, 0x16, 0x00 }, // 'Q' Pound Sterling £ "Quid" (ALT-163)
{ 0x04, 0x06, 0x05, 0x05, 0x0c, 0x1c, 0x18, 0x00 }, // 'n' Musical note
{ 0x06, 0x05, 0x07, 0x05, 0x05, 0x1d, 0x1b, 0x03 }, // 'N' Two musical notes
{ 0x04, 0x0e, 0x0e, 0x0e, 0x1f, 0x04, 0x00, 0x00 }, // 'b' Bell
{ 0x18, 0x18, 0x00, 0x07, 0x08, 0x08, 0x08, 0x07 }, // 'C' Celsius degrees
{ 0x18, 0x18, 0x00, 0x0f, 0x08, 0x0e, 0x08, 0x08 } // 'F' Fahrenheit degrees
};
// @@@@@@@@@@ B - BOLD Uppercase FONT @@@@@@@@@@
byte Boldfont[][8]{ // "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
{ 0x0e, 0x19, 0x19, 0x19, 0x1f, 0x19, 0x19, 0x00 }, // 'A'
{ 0x1e, 0x19, 0x19, 0x1e, 0x19, 0x19, 0x1e, 0x00 }, // 'B'
{ 0x0e, 0x19, 0x18, 0x18, 0x18, 0x19, 0x0e, 0x00 }, // 'C'
{ 0x1c, 0x1a, 0x19, 0x19, 0x19, 0x1a, 0x1c, 0x00 }, // 'D'
{ 0x1f, 0x18, 0x18, 0x1e, 0x18, 0x18, 0x1f, 0x00 }, // 'E'
{ 0x1f, 0x18, 0x18, 0x1e, 0x18, 0x18, 0x18, 0x00 }, // 'F'
{ 0x0e, 0x19, 0x18, 0x1b, 0x19, 0x19, 0x0f, 0x00 }, // 'G'
{ 0x19, 0x19, 0x19, 0x1f, 0x19, 0x19, 0x19, 0x00 }, // 'H'
{ 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00 }, // 'I'
{ 0x0f, 0x06, 0x06, 0x06, 0x06, 0x16, 0x0c, 0x00 }, // 'J'
{ 0x1b, 0x1a, 0x1c, 0x18, 0x1c, 0x1a, 0x1b, 0x00 }, // 'K'
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00 }, // 'L'
{ 0x11, 0x1b, 0x1f, 0x1f, 0x1b, 0x1b, 0x1b, 0x00 }, // 'M'
{ 0x19, 0x19, 0x1d, 0x1f, 0x1b, 0x19, 0x19, 0x00 }, // 'N'
{ 0x0e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x00 }, // 'O'
{ 0x1e, 0x19, 0x19, 0x19, 0x1e, 0x18, 0x18, 0x00 }, // 'P'
{ 0x0e, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x0d, 0x00 }, // 'Q'
{ 0x1e, 0x13, 0x13, 0x1e, 0x1c, 0x16, 0x13, 0x00 }, // 'R' rhs bold
{ 0x0e, 0x19, 0x1c, 0x0e, 0x07, 0x13, 0x0e, 0x00 }, // 'S'
{ 0x0f, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00 }, // 'T'
{ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x00 }, // 'U'
{ 0x19, 0x19, 0x19, 0x19, 0x19, 0x0a, 0x04, 0x00 }, // 'V'
{ 0x11, 0x11, 0x11, 0x15, 0x1f, 0x1f, 0x0a, 0x00 }, // 'W'
{ 0x19, 0x19, 0x0e, 0x04, 0x0e, 0x13, 0x13, 0x00 }, // 'X'
{ 0x19, 0x19, 0x19, 0x0e, 0x04, 0x04, 0x0e, 0x00 }, // 'Y'
{ 0x1f, 0x03, 0x06, 0x0c, 0x18, 0x10, 0x1f, 0x00 }, // 'Z'
{ 0x0e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x00 }, // '0'
{ 0x06, 0x0e, 0x16, 0x06, 0x06, 0x06, 0x1f, 0x00 }, // '1'
{ 0x0e, 0x13, 0x03, 0x06, 0x0c, 0x18, 0x1f, 0x00 }, // '2'
{ 0x1f, 0x03, 0x06, 0x0e, 0x03, 0x13, 0x0e, 0x00 }, // '3'
{ 0x06, 0x0e, 0x16, 0x16, 0x1f, 0x06, 0x06, 0x00 }, // '4'
{ 0x1f, 0x18, 0x18, 0x1e, 0x01, 0x11, 0x1e, 0x00 }, // '5'
{ 0x07, 0x0c, 0x18, 0x1e, 0x19, 0x19, 0x0e, 0x00 }, // '6'
{ 0x1f, 0x13, 0x03, 0x06, 0x0c, 0x0c, 0x0c, 0x00 }, // '7'
{ 0x0e, 0x19, 0x19, 0x0e, 0x19, 0x19, 0x0e, 0x00 }, // '8'
{ 0x0e, 0x19, 0x19, 0x0f, 0x01, 0x02, 0x0c, 0x00 } // '9'
};
// @@@@@@@@@@ b - bold Lowercase Font @@@@@@@@@@
byte boldlcfont[][8]{ // "abcdefghijklmnopqrstuvwxyz"
{ 0x00, 0x00, 0x0e, 0x03, 0x0f, 0x1b, 0x0f, 0x00 }, // 'a'
{ 0x18, 0x18, 0x1e, 0x1b, 0x1b, 0x1b, 0x1e, 0x00 }, // 'b'
{ 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x1b, 0x0e, 0x00 }, // 'c'
{ 0x03, 0x03, 0x0f, 0x1b, 0x1b, 0x1b, 0x0f, 0x00 }, // 'd'
{ 0x00, 0x00, 0x0e, 0x1b, 0x1f, 0x18, 0x0e, 0x00 }, // 'e'
{ 0x06, 0x0d, 0x0c, 0x1e, 0x0c, 0x0c, 0x0c, 0x00 }, // 'f'
{ 0x00, 0x00, 0x0f, 0x1b, 0x1b, 0x0f, 0x03, 0x0e }, // 'g'
{ 0x18, 0x18, 0x1e, 0x1b, 0x1b, 0x1b, 0x1b, 0x00 }, // 'h'
{ 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x0f, 0x00 }, // 'i'
{ 0x03, 0x00, 0x07, 0x03, 0x03, 0x03, 0x13, 0x0e }, // 'j'
{ 0x18, 0x18, 0x1b, 0x1e, 0x1c, 0x1a, 0x1b, 0x00 }, // 'k'
{ 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00 }, // 'l'
{ 0x00, 0x00, 0x1a, 0x1f, 0x15, 0x15, 0x15, 0x00 }, // 'm'
{ 0x00, 0x00, 0x16, 0x1b, 0x1b, 0x1b, 0x1b, 0x00 }, // 'n'
{ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x0e, 0x00 }, // 'o'
{ 0x00, 0x00, 0x1e, 0x1b, 0x1b, 0x1e, 0x18, 0x18 }, // 'p'
{ 0x00, 0x00, 0x0d, 0x13, 0x13, 0x0f, 0x03, 0x03 }, // 'q'
{ 0x00, 0x00, 0x16, 0x19, 0x18, 0x18, 0x18, 0x00 }, // 'r'
{ 0x00, 0x00, 0x0f, 0x1c, 0x0e, 0x07, 0x1e, 0x00 }, // 's'
{ 0x0c, 0x0c, 0x1e, 0x0c, 0x0c, 0x0d, 0x06, 0x00 }, // 't'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x0d, 0x00 }, // 'u'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x0a, 0x04, 0x00 }, // 'v'
{ 0x00, 0x00, 0x11, 0x15, 0x1f, 0x1b, 0x11, 0x00 }, // 'w'
{ 0x00, 0x00, 0x11, 0x1b, 0x0e, 0x1b, 0x11, 0x00 }, // 'x'
{ 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x0f, 0x03, 0x0e }, // 'y'
{ 0x00, 0x00, 0x1f, 0x03, 0x06, 0x0c, 0x1f, 0x00 } // 'z'
};
// %%%%%%%%%% l - LOWERCASE FONT %%%%%%%%%%
byte lowercasefont[][8] = { // "gjpqym"
{ 0x00, 0x00, 0x0f, 0x11, 0x11, 0x0f, 0x01, 0x0e }, // 'g'
{ 0x02, 0x00, 0x06, 0x02, 0x02, 0x02, 0x12, 0x0c }, // 'j'
{ 0x00, 0x00, 0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10 }, // 'p'
{ 0x00, 0x00, 0x0e, 0x12, 0x12, 0x0e, 0x02, 0x03 }, // 'q'
{ 0x00, 0x00, 0x11, 0x11, 0x11, 0x0f, 0x01, 0x0e }, // 'y'
{ 0x00, 0x00, 0x1a, 0x15, 0x15, 0x15, 0x15, 0x00 } // 'm' An alternate to the ROM 'm'
};
// @@@@@@@@@@ c - SMALL CAPS FONT @@@@@@@@@@
byte smallcapsfont[][8] { // "ABDEFGHIJKLMNPQRTUY"
{ 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x11, 0x11, 0x00 }, // 'A'
{ 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x11, 0x1e, 0x00 }, // 'B'
// Use c
{ 0x00, 0x00, 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x00 }, // 'D'
{ 0x00, 0x00, 0x1f, 0x10, 0x1c, 0x10, 0x1f, 0x00 }, // 'E'
{ 0x00, 0x00, 0x1f, 0x10, 0x1c, 0x10, 0x10, 0x00 }, // 'F'
{ 0x00, 0x00, 0x0f, 0x10, 0x17, 0x11, 0x0f, 0x00 }, // 'G'
{ 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x00 }, // 'H'
{ 0x00, 0x00, 0x0e, 0x04, 0x04, 0x04, 0x0e, 0x00 }, // 'I'
{ 0x00, 0x00, 0x01, 0x01, 0x01, 0x11, 0x0e, 0x00 }, // 'J'
{ 0x00, 0x00, 0x12, 0x14, 0x18, 0x14, 0x12, 0x00 }, // 'K'
{ 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x1f, 0x00 }, // 'L'
{ 0x00, 0x00, 0x11, 0x1b, 0x15, 0x11, 0x11, 0x00 }, // 'M'
{ 0x00, 0x00, 0x11, 0x19, 0x15, 0x13, 0x11, 0x00 }, // 'N'
// Use o
{ 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x10, 0x10, 0x00 }, // 'P'
{ 0x00, 0x00, 0x0e, 0x11, 0x11, 0x15, 0x0e, 0x01 }, // 'Q'
{ 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x14, 0x12, 0x00 }, // 'R'
// Use s
{ 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x00 }, // 'T'
{ 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00 }, // 'U'
// Use v
// Use w
// Use x
{ 0x00, 0x00, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x00 } // 'Y'
// Use z
};
// @@@@@@@@@@ F - SMALL FREQUENCY FONT @@@@@@@@@@
byte smlfrequfont[][8] { // "kHz"
{ 0x00, 0x00, 0x08, 0x0a, 0x0c, 0x0a, 0x09, 0x00 }, // 'k' as in kHz
{ 0x00, 0x00, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x00 }, // 'H' as in Hz
{ 0x00, 0x00, 0x00, 0x1e, 0x04, 0x08, 0x1e, 0x00 } // 'z' as in Hz
};
// @@@@@@@@@@ r - SMALL REVERSE CAPS FONT @@@@@@@@@@
byte smlrevcapsfont[][8] { // "BCcFLSsTR"
{ 0x1f, 0x1f, 0x13, 0x15, 0x13, 0x15, 0x13, 0x1f }, // 'B'
{ 0x1f, 0x1f, 0x19, 0x17, 0x17, 0x17, 0x19, 0x1f }, // 'C'
{ 0x1f, 0x1f, 0x19, 0x17, 0x17, 0x17, 0x19, 0x1f }, // 'c'
{ 0x1f, 0x1f, 0x11, 0x17, 0x11, 0x17, 0x17, 0x1f }, // 'F'
{ 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x11, 0x1f }, // 'L'
{ 0x1f, 0x1f, 0x19, 0x17, 0x1b, 0x1d, 0x13, 0x1f }, // 'S'
{ 0x1f, 0x1f, 0x19, 0x17, 0x1b, 0x1d, 0x13, 0x1f }, // 's'
{ 0x1f, 0x1f, 0x11, 0x1b, 0x1b, 0x1b, 0x1b, 0x1f }, // 'T'
{ 0x1f, 0x1f, 0x13, 0x15, 0x13, 0x15, 0x15, 0x1f } // 'R'
};
// @@@@@@@@@@ R - REVERSE CAPS FONT @@@@@@@@@@
byte revcapsfont[][8] { // "ABCDEFGHIKLOPRSTUVXYZ0123456789=:"
{ 0x1f, 0x1b, 0x15, 0x15, 0x11, 0x15, 0x15, 0x1f }, // 'A'
{ 0x1f, 0x13, 0x15, 0x13, 0x15, 0x15, 0x13, 0x1f }, // 'B'
{ 0x1f, 0x19, 0x17, 0x17, 0x17, 0x17, 0x19, 0x1f }, // 'C'
{ 0x1f, 0x13, 0x15, 0x15, 0x15, 0x15, 0x13, 0x1f }, // 'D'
{ 0x1f, 0x11, 0x17, 0x11, 0x17, 0x17, 0x11, 0x1f }, // 'E'
{ 0x1f, 0x11, 0x17, 0x13, 0x17, 0x17, 0x17, 0x1f }, // 'F'
{ 0x1f, 0x11, 0x17, 0x17, 0x15, 0x15, 0x11, 0x1f }, // 'G'
{ 0x1f, 0x15, 0x15, 0x11, 0x15, 0x15, 0x15, 0x1f }, // 'H'
{ 0x1f, 0x11, 0x1b, 0x1b, 0x1b, 0x1b, 0x11, 0x1f }, // 'I'
// No J
{ 0x1f, 0x15, 0x13, 0x17, 0x13, 0x15, 0x15, 0x1f }, // 'K'
{ 0x1f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x11, 0x1f }, // 'L'
// No M,N
{ 0x1f, 0x11, 0x15, 0x15, 0x15, 0x15, 0x11, 0x1f }, // 'O'
{ 0x1f, 0x13, 0x15, 0x15, 0x13, 0x17, 0x17, 0x1f }, // 'P'
// No Q
{ 0x1f, 0x11, 0x15, 0x11, 0x13, 0x15, 0x15, 0x1f }, // 'R'
{ 0x1f, 0x19, 0x17, 0x1b, 0x1d, 0x1d, 0x13, 0x1f }, // 'S'
{ 0x1f, 0x11, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1f }, // 'T'
{ 0x1f, 0x15, 0x15, 0x15, 0x15, 0x15, 0x11, 0x1f }, // 'U'
{ 0x1f, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1b, 0x1f }, // 'V'
// No W
{ 0x1f, 0x15, 0x15, 0x1b, 0x1b, 0x15, 0x15, 0x1f }, // 'X'
{ 0x1f, 0x15, 0x15, 0x1b, 0x1b, 0x1b, 0x1b, 0x1f }, // 'Y'
{ 0x1f, 0x11, 0x1d, 0x1b, 0x1b, 0x17, 0x11, 0x1f }, // 'Z'
{ 0x1f, 0x1b, 0x15, 0x15, 0x15, 0x15, 0x1b, 0x1f }, // '0'
{ 0x1f, 0x1b, 0x13, 0x1b, 0x1b, 0x1b, 0x11, 0x1f }, // '1'
{ 0x1f, 0x1b, 0x15, 0x1d, 0x1b, 0x17, 0x11, 0x1f }, // '2'
{ 0x1f, 0x11, 0x1d, 0x1b, 0x1d, 0x15, 0x1b, 0x1f }, // '3'
{ 0x1f, 0x17, 0x15, 0x15, 0x11, 0x1d, 0x1d, 0x1f }, // '4'
{ 0x1f, 0x11, 0x17, 0x1b, 0x1d, 0x15, 0x1b, 0x1f }, // '5'
{ 0x1f, 0x1b, 0x15, 0x17, 0x13, 0x15, 0x1b, 0x1f }, // '6'
{ 0x1f, 0x11, 0x15, 0x1d, 0x1b, 0x17, 0x17, 0x1f }, // '7'
{ 0x1f, 0x11, 0x15, 0x1b, 0x15, 0x15, 0x11, 0x1f }, // '8' thin waist, 1b is rounded top and bottom
{ 0x1f, 0x1b, 0x15, 0x19, 0x1d, 0x15, 0x1b, 0x1f }, // '9'
{ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f }, // '=' space (5x8 black pixels)
{ 0x1f, 0x1f, 0x1f, 0x1b, 0x1f, 0x1b, 0x1f, 0x1f } // ':' colon
};
// @@@@@@@@@@ C - CHANNEL-DAISY FONT @@@@@@@@@@
byte channelfont[][8] { // "241dca5b3ef6xyzuvwjkl"
{ 0x00, 0x00, 0x00, 0x0e, 0x1f, 0x1f, 0x1f, 0x0e }, // '2'
{ 0x0e, 0x1f, 0x1f, 0x1f, 0x0e, 0x00, 0x0e, 0x11 }, // '4'
{ 0x0e, 0x1f, 0x1f, 0x1f, 0x0e, 0x00, 0x00, 0x00 }, // '1'
{ 0x11, 0x0e, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e }, // 'd'
//=================================================
{ 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x11 }, // 'c'
{ 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x00, 0x00 }, // 'a'
{ 0x11, 0x0e, 0x00, 0x0e, 0x1f, 0x1f, 0x1f, 0x0e }, // '5'
//=================================================
{ 0x00, 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e }, // 'b'
//=================================================
{ 0x0e, 0x1f, 0x1f, 0x1f, 0x0e, 0x00, 0x0e, 0x1f }, // '3'
{ 0x1f, 0x0e, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e }, // 'e'
//=================================================
{ 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x1f }, // 'f'
{ 0x1f, 0x0e, 0x00, 0x0e, 0x1f, 0x1f, 0x1f, 0x0e }, // '6'
//=================================================
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x00 }, // 'x' Analog pass-thru lower-half of daisy (vertical triangle) "xyz|CCC"
{ 0x04, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x00 }, // 'y'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x14, 0x00 }, // 'z'
//=================================================
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00 }, // 'u' Analog pass-thru 'Mute' daisy (hor. triangle) "uvw|CCC" (alternate with xyz|CCC)
{ 0x04, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00 }, // 'v'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1c, 0x00 }, // 'w'
//=================================================
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00 }, // 'j' Solid triangle symbol (same shape as the vert,. and hor. triangles) "jkl|CCC"
{ 0x04, 0x0e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x00 }, // 'k'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x1c, 0x00 } // 'l'
};
// @@@@@@@@@@ L - LOGOS FONT @@@@@@@@@@
byte logofont[][8]{ // "LRdtsHDC0123456 789EQ"
{ 0x1f, 0x13, 0x11, 0x11, 0x11, 0x13, 0x1f, 0x00 }, // 'L' (Dolby Labs logo, use "LR|LL")
{ 0x1f, 0x19, 0x11, 0x11, 0x11, 0x19, 0x1f, 0x00 }, // 'R'
//
{ 0x03, 0x03, 0x03, 0x0f, 0x1b, 0x1b, 0x0f, 0x00 }, // 'd' (Digital Theater Systems logo. use "dts|LLL")
{ 0x04, 0x0c, 0x1f, 0x0c, 0x0c, 0x0c, 0x07, 0x00 }, // 't'
{ 0x00, 0x00, 0x0f, 0x1c, 0x0e, 0x07, 0x1c, 0x00 }, // 's'
//
{ 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x00 }, // 'H' ('HDCD' small caps logo, use "HDCD|LLLL")
{ 0x00, 0x00, 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x00 }, // 'D'
{ 0x00, 0x00, 0x0f, 0x10, 0x10, 0x10, 0x0f, 0x00 }, // 'C'
//
// Standard version of CinEQ: Use "0123456"
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00 }, // '0' _ ('-cinEQ--' EAD CinEQ logo, use "0123456|LLLLLLL")
{ 0x03, 0x04, 0x04, 0x03, 0x00, 0x1f, 0x00, 0x00 }, // '1' C
{ 0x0e, 0x04, 0x04, 0x0e, 0x00, 0x1f, 0x00, 0x00 }, // '2' I
{ 0x0c, 0x12, 0x12, 0x12, 0x02, 0x12, 0x08, 0x07 }, // '3' N
{ 0x1e, 0x10, 0x10, 0x1e, 0x10, 0x1e, 0x00, 0x1f }, // '4' E
{ 0x0e, 0x11, 0x11, 0x11, 0x15, 0x0e, 0x04, 0x1f }, // '5' Q
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e } // '6' + (underbar)
};
// @@@@@@@@@@ O,I,P,E,s,U INVADERS FONTS @@@@@@@@@@
// O - Outvader (Invader legs out)
byte outvaderfont[][8] { // "abcd"
{ 0x0e, 0x15, 0x0e, 0x11, 0x00, 0x00, 0x00, 0x00 }, // 'a' /oo\ row 1
{ 0x00, 0x0e, 0x15, 0x0e, 0x11, 0x00, 0x00, 0x00 }, // 'b' /oo\ row 2
{ 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x11, 0x00, 0x00 }, // 'c' /oo\ row 3
{ 0x00, 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x11, 0x00 } // 'd' /oo\ row 4
};
// I - Invader (Invader legs in)
byte invaderfont[][8] { // "abcd"
{ 0x0e, 0x15, 0x0e, 0x0a, 0x00, 0x00, 0x00, 0x00 }, // 'a' \oo/ row 1
{ 0x00, 0x0e, 0x15, 0x0e, 0x0a, 0x00, 0x00, 0x00 }, // 'b' \oo/ row 2
{ 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x0a, 0x00, 0x00 }, // 'c' \oo/ row 3
{ 0x00, 0x00, 0x00, 0x0e, 0x15, 0x0e, 0x0a, 0x00 } // 'd' \oo/ row 4
};
// P - Laser
byte laserfont[][8] { // "12345_"
{ 0x00, 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '1' frame 1
{ 0x00, 0x00, 0x04, 0x04, 0x04, 0x0e, 0x1f, 0x1f }, // '2' frame 2
{ 0x04, 0x04, 0x04, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '3' frame 3
{ 0x04, 0x04, 0x00, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '4' frame 4
{ 0x04, 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1f, 0x1f }, // '5' frame 5
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f } // '_' Empty laser storage
};
// E - Explosion
byte kapowfont[][8] { // "12345"
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x0e, 0x00 }, // '1' frame 1
{ 0x00, 0x00, 0x00, 0x00, 0x15, 0x0e, 0x0e, 0x15 }, // '2' frame 2
{ 0x00, 0x00, 0x00, 0x00, 0x11, 0x04, 0x04, 0x11 }, // '3' frame 3
{ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11 }, // '4' frame 4
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // '5' frame 5
};
// s - Shield
byte shieldfont[][8] { // "LR"
{ 0x0f, 0xff, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'L' LH half
{ 0x1e, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 } // 'R' RH half
};
// U - Mothership/UFO
// Six frames of 4-char animation. Gameplay stops and
// the mothership flies across the LCD screen.
byte UFOfont[][8] { // "ABCDIJKLQRSTabcdijklqrst"
{ 0x00, 0x03, 0x07, 0x0d, 0x1f, 0x07, 0x02, 0x00 }, // 'A'
{ 0x1e, 0x1f, 0x1f, 0x0d, 0x1f, 0x0c, 0x00, 0x00 }, // 'B'
{ 0x00, 0x10, 0x18, 0x0c, 0x1e, 0x18, 0x10, 0x00 }, // 'C'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'D'
//
{ 0x00, 0x01, 0x03, 0x05, 0x0f, 0x03, 0x01, 0x00 }, // 'I'
{ 0x0f, 0x1f, 0x1f, 0x0d, 0x1f, 0x06, 0x00, 0x00 }, // 'J'
{ 0x00, 0x18, 0x1c, 0x0e, 0x1f, 0x1c, 0x08, 0x00 }, // 'K'
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'L'
//
{ 0x00, 0x00, 0x01, 0x03, 0x07, 0x01, 0x00, 0x00 }, // 'Q'
{ 0x07, 0x1f, 0x1f, 0x0d, 0x1f, 0x13, 0x00, 0x00 }, // 'R'
{ 0x00, 0x1c, 0x1e, 0x0d, 0x1f, 0x0e, 0x04, 0x00 }, // 'S'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }, // 'T'
//
{ 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00 }, // 'a'
{ 0x03, 0x0f, 0x1f, 0x0d, 0x1f, 0x19, 0x10, 0x00 }, // 'b'
{ 0x10, 0x1e, 0x1f, 0x0d, 0x1f, 0x07, 0x02, 0x00 }, // 'c'
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }, // 'd'
//
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }, // 'i'
{ 0x01, 0x0f, 0x1f, 0x0d, 0x1f, 0x1c, 0x08, 0x00 }, // 'j'
{ 0x18, 0x1f, 0x1f, 0x0d, 0x1f, 0x13, 0x01, 0x00 }, // 'k'
{ 0x00, 0x00, 0x00, 0x10, 0x18, 0x00, 0x00, 0x00 }, // 'l'
//
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 'q'
{ 0x00, 0x07, 0x0f, 0x1d, 0x1f, 0x0e, 0x04, 0x00 }, // 'r'
{ 0x1c, 0x1f, 0x1f, 0x0d, 0x1f, 0x19, 0x00, 0x00 }, // 's'
{ 0x00, 0x00, 0x10, 0x18, 0x1c, 0x10, 0x00, 0x00 } // 't'
};
/*----------------------------------------------*/
/* Define the CustomFont struct using typedef */
/*----------------------------------------------*/
typedef struct {
char fontFlag;
byte (*fontBits)[8]; // Pointer to the CC bitmap arrays.
String fontChars;
} CustomFont;
/*----------------------------------------------*/
/* Create instances of the CustomFont struct, */
/* and assemble into a CustomFont array of */
/* struct, one array per font */
/*----------------------------------------------*/
// Order of fields: fontFlag, fontBits, fontChars
CustomFont font0 = { 'G', bargraphfont, "0abcLR!87654321" }; // 15
CustomFont font1 = { '0', big0font, "ABCD" }; // 4 Location of the parts forming two bignums on-screen:
CustomFont font2 = { '1', big1font, "ABCD" }; // 4 AB A'B'
CustomFont font3 = { '2', big2font, "ABCD" }; // 4 CD C'D'
CustomFont font4 = { '3', big3font, "ABCD" }; // 4 A maximum of two bignums digits!
CustomFont font5 = { '4', big4font, "ABCD" }; // 4
CustomFont font6 = { '5', big5font, "ABCD" }; // 4
CustomFont font7 = { '6', big6font, "ABCD" }; // 4
CustomFont font8 = { '7', big7font, "ABCD" }; // 4
CustomFont font9 = { '8', big8font, "ABCD" }; // 4
CustomFont font10 = { '9', big9font, "ABCD" }; // 4
CustomFont font11 = { 'S', symbolfont, "sDSTMH><^vud_1234KkLQnNbCF" }; // 26
CustomFont font12 = { 'B', Boldfont, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" }; // 36
CustomFont font13 = { 'b', boldlcfont, "abcdefghijklmnopqrstuvwxyz" }; // 26
CustomFont font14 = { 'l', lowercasefont, "gjpqym" }; // 6
CustomFont font15 = { 'c', smallcapsfont, "ABDEFGHIJKLMNPQRTUY" }; // 19
CustomFont font16 = { 'F', smlfrequfont, "kHz" }; // 3
CustomFont font17 = { 'r', smlrevcapsfont, "BCcFLSsTR" }; // 9
CustomFont font18 = { 'R', revcapsfont, "ABCDEFGHIKLOPRSTUVXYZ0123456789=:" }; // 33
CustomFont font19 = { 'C', channelfont, "241dca5b3ef6xyzuvwjkl" }; // 21
CustomFont font20 = { 'L', logofont, "LRdtsHDC0123456" }; // 15
CustomFont font21 = { 'O', outvaderfont, "abcd" }; // 4
CustomFont font22 = { 'I', invaderfont, "abcd" }; // 4
CustomFont font23 = { 'P', laserfont, "12345_" }; // 6
CustomFont font24 = { 'E', kapowfont, "12345" }; // 5
CustomFont font25 = { 's', shieldfont, "LR" }; // 2
CustomFont font26 = { 'U', UFOfont, "ABCDIJKLQRSTabcdijklqrst" }; // 24
CustomFont fonts[] = { font0, font1, font2, font3, font4, font5, font6,
font7, font8, font9, font10, font11, font12, font13,
font14, font15, font16, font17, font18, font19, font20,
font21, font22, font23, font24, font25, font26 };
/*----------------------------------------------*/
/* getBitmap() - FETCH CUSTOM CHARACTER BITMAPS */
/*----------------------------------------------*/
// Function returns the bitmap for fontFlag, fontChar pair.
// NOTES: The foreach loop (a.k.a "range-based for loop") was introduced with C++11.
// This type of for loop eases the traversal over an iterable data set. It does this
// by eliminating the initialization process and traversing over each and every element
// rather than an iterator.
uint8_t* getBitmap(char myfontChar, char myfontFlag) {
for (CustomFont& font : fonts) {
if (font.fontFlag == myfontFlag) {
int charIndex = font.fontChars.indexOf(myfontChar);
if (charIndex != -1) return font.fontBits[charIndex];
}
}
return nullptr; // Font or Character not found
}
/***********************************************************/
/* setupRow2() - Set up Row 2 for Double-row CC graphics */
/***********************************************************/
void setupRow2() {
int8_t startPos2 = startPos + 20;
startPos = startPos2; // Update startPos for row2
lcdString = lcdString.substring((lcdString.indexOf('|') << 1) +1 );
return;
}
/*----------------------------------------------------*/
/* clearTheStage() - Clears the LCD 'stage', making */
/* it ready for a new LCD 'scene' composed of one or */
/* several Strings. */
/* It clears the LCD and various relevant variables: */
/* the CC_id_table[], the LCD CGRAM, and screenImage. */
/*----------------------------------------------------*/
void clearTheStage()
{
for (CGRAM_ptr = 0; CGRAM_ptr < 8; CGRAM_ptr++) {
CC_id_table[CGRAM_ptr]=""; // Clears out CC_id_table [0 to 7].
lcd.createChar(CGRAM_ptr, blankBitMap); delay(1);// Fill all 8 CGRAM locations with the bitmap of a blank character.
//Serial.print("\n =");Serial.print(CGRAM_ptr); \\DEBUG
}
screenImage = " "; // 80 spaces
lcd.setCursor(0,0); lcd.print(" "); // 20 spaces
lcd.setCursor(0,1); lcd.print(" ");
// Which is the equivalent of: startPos = 0; lcdString = " 40 spaces | 40 spaces "; LCDfontManager();
return;
}
/*---------------------------------*/
/* LCDsplash() - LCD SIGN-ON */
/* (Does not use the font manager) */
/*---------------------------------*/
// Uses CGRAM locations 8,9,10,11,12.
void LCDsplash() {
byte my_p[8] = { 0x00, 0x00, 0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10 }; // p with descender
byte myBar8[8] = { 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }; // Bar graph = 8
byte myBar6[8] = { 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b }; // Bar graph = 6
byte myBar4[8] = { 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1b, 0x1b, 0x1b }; // Bar graph = 4
byte my_star[8] = { 0x04, 0x15, 0x0e, 0x1f, 0x0e, 0x15, 0x04, 0x00 }; // custom '*' char 1
//byte my_star[8] = { 0x00, 0x15, 0x0e, 0x1b, 0x0e, 0x15, 0x00, 0x00 }; // custom '*' char 2
lcd.createChar(8,my_p);
lcd.createChar(9,myBar4);
lcd.createChar(10,myBar6);
lcd.createChar(11,myBar8);
lcd.createChar(12,my_star);
lcd.setCursor(0,0);
// Very important: DO NOT embed byte(0) in Strings, and don't try to print it!!! (it's equivalent to \0, the end-of-c-string
// null character). The issue arises because Hitachi HD44870 LCD controllers use byte(0) as a CGRAM address. Fortunately, the
// CGRAM 0x00-0x07 addresses are mirrored from 0x08-0x0f. Those all embed in Strings without problems! Use them instead.
lcd.write(byte(12));lcd.print("-----Font-Pal!");
lcd.setCursor(0,1);
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(9)); //4
lcd.print("1");
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(10)); //6
lcd.print(".");
for(uint8_t i = 0; i < 4; i++) lcd.write(byte(11)); //8
lcd.print("1");
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(10)); //6
lcd.print("9");
for(uint8_t i = 0; i < 3; i++) lcd.write(byte(9)); //4
delay(1000);
}
void printStringHex(const String& myString)
{
String Interp = "";
Serial.print(" ");
for (size_t i = 0; i < myString.length(); ++i)
{
char c = myString.charAt(i);
Interp = Interp + String(c); // + " ";
if (c > 15) Serial.print("0x");
else Serial.print("0x0");
Serial.print(c, HEX); // Print the character as a hexadecimal value
Serial.print(" "); // Add a space between characters
}
Serial.print(Interp);
//Serial.println(); // Print a newline at the end
}
/*---------------------------------------------*/
/* ++++++++++++++++ S E T U P ++++++++++++++++ */
/* ++++++++++++++++ S E T U P ++++++++++++++++ */
/* ++++++++++++++++ S E T U P ++++++++++++++++ */
/*---------------------------------------------*/
void setup() {
lcd.begin(20,2);
lcd.clear();
//Serial.begin(9600);
//Serial.print("\n");myFontsPrinter(); // FOR DEBUG & MAINTENANCE ONLY
LCDsplash();
delay(1000);
}
/*---------------------------------------------*/
/* ++++++++++++++++ L O O P ++++++++++++++++ */
/* ++++++++++++++++ L O O P ++++++++++++++++ */
/* ++++++++++++++++ L O O P ++++++++++++++++ */
/*---------------------------------------------*/
void loop() {
// Examples of font-formatted 'blurb' Strings:
/*
clearTheStage();
startPos = 5;
lcdString = "ABABABAB |88880000 " // Double-row Strings must
"CDCDCDCDPro|88880000 "; // be of equal length!
LCDfontManager();
setupRow2();
LCDfontManager();
delay(3000);
//while(true){};
clearTheStage();
startPos = 0;
lcdString = "SsS sS Ss s ss sss S SS SSS|" // The only entries in CC_id_table should be: SS sS
"SSS SS SS S SS SSS S SS SSS";
LCDfontManager();
delay(3000);
//while(true){};
clearTheStage();
startPos = 0;
lcdString = "LR RL Hey Baby Bluejay LLLLLLLLLL RRRRR|" // RL --> @@ caused by bad code
"LL LL B l B l Bbbbbbb LLLLLLLLLL LLLLL";
LCDfontManager();
delay(3000);
//while(true){};
clearTheStage();
startPos = 0;
lcdString = "SDS B AB S DS v|" // Get @@ errors not caused by bad font/char, but by bad code.
"SSS B BB S SS S"
"SDS B AB S DS ^|"
"SSS B BB S SS S";
LCDfontManager();
setupRow2();
LCDfontManager();
delay(2000);
//while(true){};
// Extreme CC and font tests:
clearTheStage();
startPos = 0; // 1st row works if it ends in AA bold, but we get 2 font/CC errors AA...A@@ if it ends with AB,
// and AA...@@A if it ends with BA. The second row behaves perfectly.
lcdString = "SsS SsypgjB BBB AAA|" // Get bogus @@ at end of 1st row.
"SSS SSllllB BBB RRR" // Fails with two bogus font/CC errors at the end of 1st row.
"QnNbCFE CCCC CCCCCC|"
" SS SSSSSS ";
LCDfontManager(); // If I change the top line to "T sS..." it does not fail.
setupRow2();
LCDfontManager();
delay(5000);
//while(true){}
clearTheStage();
startPos = 0;
lcdString = "All Bold A A A A A A A A A A A A A A A B|" // OK. Example with exactly 40 screen characters. Tests 1 Bold char repeated.
" B B B B B B B B B B B B B B B B B";
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 0;
lcdString = "ABABABABABABABABABABABABABABABABABABABAB|" // OK. Example with exactly 40 bold characters, A and B each repeated 20 times
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 0;
lcdString = "All Bold AAAAAAAAABA|" // Example with exactly 40 screen characters in two rows. Tests 1 Bold char repeated.
" B BBBBBBBBBBB" // Fails with two font/CC errors at the end of 1st row. It gives All Bold AAA...AAA@@A instead of ...AAAAABA
"AAAAAAAAAAAAAAAAAAAB|" // NOTE: The one-row approach (see below) works perfectly!
"BBBBBBBBBBBBBBBBBBBB";
LCDfontManager();
setupRow2();
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 0; // 1st row works if it ends in AA bold, but we get 2 font/CC errors AA...A@@ if it ends with AB,
// and AA...@@A if it ends with BA. The second row behaves perfectly.
// lcdString = "WWW C WWWWWWWWWWWWWC|" // Works perfectly!
lcdString = "WWW b wwwwwwwwwwwwwb|" // Get @@ at end of 1st row.
" b bbbbbbbbbbbbbb" // Fails with two font/CC errors at the end of 1st row. It gives All Bold AAA...AAA## instead of ...AAAAB
"AAAAAAAAAAAAAAAAAAAA|" // NOTE: The one-row approach (see below) works perfectly!
"BBBBBBBBBBBBBBBBBBBB"; // Get @@ at end if fontChar == fontFlag
LCDfontManager();
setupRow2();
LCDfontManager();
delay(3000);
//while(true){}
clearTheStage();
startPos = 0;
lcdString = "SSSSSSSSSSSSSSSSSSSTSSSSSSSSSSSSSSSSSSST|" // Works perfectly?
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
LCDfontManager();
delay(3000);
//while(true){}
// ---------------------------------------------------------------------------
// TEST THE CONSTRUCTION OF AN APPLICATION SCREEN FROM SEVERAL SMALLER STRINGS:
// NOTE: clearTheStage() is used once only, at the start of the scene.
// setupRow2() sorts out the 2nd row of a 2-row CC pattern.
clearTheStage(); // Clear LCD image and CCs for an 'LCD Scene'
startPos = 0;
lcdString = "232|" // 4 CCs
"CCC"
"1e1|"
"CCC";
LCDfontManager();
setupRow2();
LCDfontManager();
delay(200);
startPos = 4;
lcdString = "LR Digital 5.1 |" // 3 CCs
"LL l ";
LCDfontManager();
delay(200);
startPos = 24;
lcdString = "D1:Blu"; // 0 CCs An example of a non font-encoded string.
LCDfontManager();
delay(200);
startPos = 32;
lcdString = "-24.5 dB"; // 0 CCs
LCDfontManager();
delay(200);
startPos = 4;
lcdString = " |" // 4 CCs. Padded out with spaces to match the length of the field.
" ";
LCDfontManager();
delay(200);
startPos = 4;
lcdString = "dts Digital 5.1|" // 4 CCs. Padded out with spaces to match the length of the field.
"LLL l ";
LCDfontManager();
delay(2000);
//while(true){};
//----------------------------------------------------------------------------------------
// INVADER SPLASH SCREEN (7 CCs, 7 unique)
clearTheStage();
delay(500);
startPos = 0;
lcdString = " Space Invaders |"
" Bl B "
" b- ABC- |"
" I UUU ";
LCDfontManager();
setupRow2();
LCDfontManager();
delay(800);
startPos = 25;
lcdString = "2"; // Add points.
LCDfontManager();
delay(200);
startPos = 26;
lcdString = "5";
LCDfontManager();
delay(400);
startPos = 34;
lcdString = "1";
LCDfontManager();
delay(200);
startPos = 35;
lcdString = "0";
LCDfontManager();
delay(200);
startPos = 36;
lcdString = "0";
LCDfontManager();
delay(1500);
// INVADER GET READY! SCREEN (3 CCs, 2 unique)
clearTheStage();
startPos = 0;
lcdString = " GET 000"
" READY! ";
LCDfontManager();
delay(1000);
startPos = 37;
lcdString = "111|PPP"; // Stock up on lasers.
LCDfontManager();
delay(1000);
startPos + 37;
lcdString = "_|P"; // Show one laser used/in use: '_'
LCDfontManager();
delay(500);
//while(true) {}
// INVADER GAME SCREEN (cannot work properly until section clear wipes out CCs from CC_id_table)
clearTheStage();
startPos = 0;
lcdString = " 000|"
" "
" LR LR 3 LR LR _11|"
" ss ss P ss ss PPP";
LCDfontManager();
setupRow2();
LCDfontManager();
for (int8_t row=0; row<4; row++){ // 0 to 3
for (int8_t col=0; col<7; col++){ // 0 to 8
startPos = 0;
lcdString = " "; // 16 " ". Update to section clear later
LCDfontManager();
if (col % 2) lcdString = invArray[row][0]; else lcdString = invArray[row][1];
if (row % 2) startPos = 7 - col; else startPos = 1 + col;
LCDfontManager();
delay(600);
}
}
delay(2000);
// MOTHERSHIP FLY-BY
// INVADER OUTRO SCREEN (9 CCs, 8 unique, but with animation we have 10 CCs, 9 unique)
clearTheStage();
startPos = 0;
lcdString = "Hi-Scdrers: AJR SCR|"
"Bb BbObbbb "
" AR JSH|"
" ";
LCDfontManager();
setupRow2();
LCDfontManager();
// Now continue with a brief animation of one of the CCs:
startPos = 5;
for (uint8_t k=0; k < 8; k++) {
lcdString = "d|I"; // Space Invaders alien, legs in.
LCDfontManager();
delay(500);
lcdString = "d|O"; // Space Invaders alien, legs out.
LCDfontManager();
delay(500);
}
*/
clearTheStage();
startPos = 0;
lcdString = "Space is big.|" // 15 CCs, 12 unique, which should trigger CGRAM overflow --> # # # #
"Bbbbb bb bbb "
" Really big.|"
" B l bbb ";
LCDfontManager();
setupRow2();
LCDfontManager();
delay(3000);
startPos = 0;
clearTheStage();
lcdString = "You just won't believe how vastly,|" // 8 CCs, 8 unique. However, currently there is no w|b, l|b --> @ @
" l l bbbbbl ";
LCDfontManager();
delay(3000);
clearTheStage();
lcdString = "hugely, mind-bogglingly big it is.|" // 13 CCs, 8 unique. However, there is currently no m|b --> @
" l l bbbb ll l l bbb ";
LCDfontManager();
delay(3000);
clearTheStage(); // One line of font-formatted text, one line of plain text.
lcdString = "THE UNIVERSE IS HUGE|" // 17 CCs, 10 unique, which should trigger CGRAM overflow --> # # #
"BBB BBBBBBBB BB BBBB"
"(Douglas Adams HHGG)";
LCDfontManager();
setupRow2();
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 0;
lcdString = "A bright LCD displayis very easy to read|" // Example with exactly 40 screen characters. Requires 82 bytes of buffer memory.
" l BBB l l l l ";
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 8;
lcdString = "Hello World!"; // Example of plain text (i.e., no CCs)
LCDfontManager();
delay(3000);
//while(true){};
clearTheStage();
startPos = 0;
lcdString = " TheaterMaster SIGNATURE |"
" BbbbbbbBbbbbb ";
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 0;
lcdString = " TheaterMaster Ovation |"
" Bbbbbbb ";
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 0;
lcdString = " TheaterMaster Encore |"
" Bbbbbb ";
LCDfontManager();
delay(3000);
//while(true) {};
clearTheStage();
startPos = 0;
lcdString = "CPU:6.9o Z1:00302004DIP:FF-3 Z2:00302004|" // 10 CCs, 8 unique.
"RRR RR RRR RR ";
LCDfontManager();
delay(3000);
clearTheStage();
startPos = 0;
lcdString = " LF cR RF Ls sB Rs |"
" cc c cc c c c "
" 55 77 55 77 55 77 |" // The small caps channel labels use 4 CCs. These bars need 1 CC.
" GG GG GG GG GG GG "; // This will always work.
LCDfontManager();
setupRow2();
LCDfontManager();
delay(3000);
// Test many selection variations of a 5.1-channel surround sound speaker setup.
// Note the use of "s" and "c" lowercase characters as stand-ins for small caps.
// Without this work-around most of the following screen would be impossible.
// The best way to avoid a messy substitution (of "C" for "c" and "S" for "s")
// would be to extend the Reverse Caps font with "c" and "s", using the same
// bitmaps as the ordinary Reverse Caps (Update: Already implmented).
// Another approach would be to have reversed version of the big and small
// speakers, and to indicate selection with those.
clearTheStage();
startPos = 0;
lcdString = " LF:K cR:kx RF:K Ls:kx sB:K Rs:kx|" // 14 CCs, 6 unique. 3 speakers full range, 3 limited range.
" rr S c S cc S c S c S c S "; // The 'x' indicates that a particular small speaker has a cross-over.
LCDfontManager();
delay(1000);
clearTheStage();
startPos = 0;
lcdString = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF|" // 40 CCs comprising 2 unique.
"BRBRBRBRBRBRBRBRBRBRBRBRBRBRBRBRBRBRBRBR";
LCDfontManager();
delay(1000);
//while(true){};
// Test of channel daisies in a home theater controller:
// WHEN IT GET THE CC_id_table CLEARING WORKING, THE WHOLE OF THIS MUST BE in ONE LOOP:
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[0];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/2/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();startPos = 0;
lcdString = daisy[1];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/P/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[2];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/0/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[3];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/1/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[4];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/0/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[5];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/2/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[6];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "1/0/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "No Lock"; // input PLL unable to lock (caused by input signal fault)
LCDfontManager();
startPos = 0;
lcdString = daisy[7];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "0/0/0 "; // Plain text
LCDfontManager();
delay(1000);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[8];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/2/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[9];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/P/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[10];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/0/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[11];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/1/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[12];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/0/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[13];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "2/2/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "LR Digital |" // 3 CCs
"LL l ";
LCDfontManager();
startPos = 0;
lcdString = daisy[14];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "1/0/0.1"; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "dts ES |" // 3 CCs
"LLL ";
LCDfontManager();
startPos = 0;
lcdString = daisy[15];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/3/0 "; // Plain text
LCDfontManager();
delay(500);
clearTheStage();
startPos = 4;
lcdString = "dts ES |" // 3 CCs
"LLL ";
LCDfontManager();
startPos = 0;
lcdString = daisy[16];
LCDfontManager();
setupRow2();
LCDfontManager();
startPos = 24;
lcdString = "3/3/0.1"; // Plain text
LCDfontManager();
delay(500);
//----------------------------------------------------------------------------------------
}
// ------END------
Other tabs:
/*-------------------------------------------------------*/
/* LCDfontManager() - Liquid Crstal Display Font Manager */
/*-------------------------------------------------------*/
void LCDfontManager()
{
Serial.begin(9600);
bool fontEncoded = true;
// %%%%%%%%%% BEGIN FONT PROCESSING...
//(1) Scan lcdString to detect '|' markers...
int8_t marker_idx = lcdString.indexOf('|');
if (marker_idx == -1) fontEncoded = false; // lcdString is plain text, so print it more-or-less directly...
//(2a) Perform an interleave merge of fontChar & fontFlag sections of lcdString, excluding the '|' marker
// (e.g., "LR DoobyoD|ll BBB LBB" --> "LlRl DBoBoBb yLoBDB"), and also processes the top row of double-row
// symbols (e.g., "Test|B CASE|BBBB|" --> "TBe s t "):
if (fontEncoded == true)
{
merged_lcdString = "";
for (uint8_t i = 0; i < marker_idx; i++)
{
merged_lcdString = merged_lcdString + lcdString.charAt(i);
merged_lcdString = merged_lcdString + lcdString.charAt(marker_idx + 1 + i);
}
}
else // if lcdString isn't fontEncoded, treat as plain text...
{
merged_lcdString = "";
for (uint8_t i = 0; i < lcdString.length(); i++) merged_lcdString = merged_lcdString + lcdString.charAt(i) + " ";
fontEncoded = true;
}
//Serial.print("\n merged_lcdString = [" + merged_lcdString + "]"); //DEBUG
//(3) We are nearly ready to copy the merged lcdString into screenImage starting at startPos, but first
// we need to clean up the CC_id_table, removing any entries that will be overwritten by the current
// lcdString, necessary to maximize the tiny eight CC CGRAM resource.
//
// DETAILS: Parse the even characters in merged_screenImage for CC codes that will be overwritten by a new
// merged lcdString, beginning at startPos, and ending at startPos + lcdString.length() -1. As we proceed to
// discover these CCs in the relevant section of screenImage, we will delete them from the CC_id_table. And
// because CCs in screenImage are codes in the range 0x08-0x0f, that is the range we will look for. The tricky
// part of this parsing is to take into account that just like merged_lcsString, screenImage also uses merged
// format of screenImage, wherein the relevent fontchars are at even locations, 0, 2, 4, 6, ... We can obtain
// even addresses by using a simple arithmetic left-shift (<< 1). However, we don't do this for CC_id_table
// (a table that contains a list of the CCs loaded into CGRAM of the LCD's controller chip). Given that C++
// arrays are 0-relative, the CC_id_table addressing is 0x00 to 0x07, but because 0x00 (\nul) has special
// use in C and C++ for marking the end of c-char character strings, we cannot cannot use 0x00 as a CC code
// value in String function and char processing. Luckily, the HD44780 LCD controller provides a set of shadow
// addresses for the CGRAM: 0x08-0x0f, obtained by adding 8 to the 0x00-0x07 addresses. This avoids having to
// treat the 0x00 CC code value as a special case. However, since as mentioned above, C and C++ arrays start
// at 0 and proceed up from there, this program makes use of both ranges, 0x00-0x07 and 0x08-0x0f as and when
// necessary. Typcal contents of CC_id_table may look like this:
//
// CC_id_table[0] = "2C"; // ==> CC code = 0x08 = 0 + 8 = 8 Table Format: "2C" = fonchar,fontflag
// CC_id_table[1] = "3C"; // ==> CC code = 0x09 = 1 + 8 = 9
// CC_id_table[2] = "1C"; // ==> CC code = 0x0a = 2 + 8 = 10
// CC_id_table[3] = "eC"; // ==> CC code = 0x0b = 3 + 8 = 11
// CC_id_table[4] = "dL"; // ==> CC code = 0x0c = 4 + 8 = 12
// CC_id_table[5] = "tL"; // ==> CC code = 0x0d = 5 + 8 = 13
// CC_id_table[6] = "gl"; // ==> CC code = 0x0e = 6 + 8 = 14 lowercase g with true descender.
// CC_id_table[7] = "sL"; // ==> CC code = 0x0f = 7 + 8 = 15
//
// If our parse finds, e.g., a CC code of 6 in the merged_screenImage overwrite section, this is a reference
// to the gl entry in the table, currently stored at CC_id_table[6]. We delete it from the table with
//
// CC_id_table[6] = "";
//
// Then, after lcdString is overwritten on at its startPos in the merged_screenImage, any new CCs in lcdString are
// saved into free locations in CC_id_table. A condition called CGRAM overflow can occur if there are more than
// eight unique custom characters (CCs). This cindition is signalled with one or more "#" flags on the LCD.
//
// %%%%% Continuing, we now parse the relevant section of merged_screenImage for CCs:
int16_t mystartPos;
mystartPos = startPos << 1; // = startPos * 2 (previous code came to grief here because << 1 != *2 when applied to uint)
scrn_ptr = mystartPos;
while (scrn_ptr < (mystartPos + merged_lcdString.length()))
{
fontChar_num = int(screenImage.charAt(scrn_ptr));
scrn_ptr += 2;
if ((fontChar_num > 7) && (fontChar_num < 16)) CC_id_table[fontChar_num - 8] = ""; // Delete any found CCs from the CC_id_table
}
screenImage = screenImage.substring(0, mystartPos) + merged_lcdString + screenImage.substring((mystartPos) + merged_lcdString.length());
//Serial.print("\nmerged screenImage = [" + screenImage + "]"); //DEBUG
//(4) Now parse screenImage for CC flags, load the CC bitmaps to the LCD chip, and place CGRAM_ptrs in
// screenImage, in place of fontChars. Use CC_id_table to keep track of the limit of only 8 CCs!
// %%%%%%%%%%%%%% BEGIN PROCESSING THE MERGED LCDSTRING INTO SCREENIMAGE AT STARTPOS, INSERTING CCs AND ERROR FLAGS AS NEEDED...
scrn_ptr = startPos;
while (scrn_ptr < 40)
{
int16_t even_scrn_ptr = scrn_ptr << 1;
int16_t odd_scrn_ptr = even_scrn_ptr + 1;
current_fontChar = screenImage.charAt(even_scrn_ptr); // e.g., "q" font char IF FONTCHAR IS 8-15, ignore
current_fontFlag = screenImage.charAt(odd_scrn_ptr); // e.g., "l" bold caps font-flag
// %%%%%%%%%% IF CURRENT MERGED PRINTABLE CHAR HAS A NON-BLANK FONTFLAG...
if ((current_fontFlag != " ") && (current_fontChar >= " ")) // i.e., IF non-blank font flag AND fontChar is printable...
{
gBitmap = getBitmap(current_fontChar.c_str()[0], current_fontFlag.c_str()[0]); // Example: Get the 'g' bitmap from the 'L' font. [0] ensures 1 byte
current_CC_id = current_fontChar + current_fontFlag; // e.g., current_CC_id = "ql".
// %%%%%%%%%%%%% IF current_CC_id BITMAP FOUND...
if (gBitmap)
{
// %%%%% IF current_CC_id IS ALREADY IN TABLE,
// REPLACE ALL INSTANCES OF current_CC_id IN screenImage WITH A POINTER (0x08-0x0f + " ") TO THE TABLE...
bool present = false;
for (CGRAM_ptr = 0; CGRAM_ptr < 8; CGRAM_ptr++)
if (CC_id_table[CGRAM_ptr] == current_CC_id) // E.g., if CC_id_table[0-7] == "ql",
{
present = true; // Set 'present in table' == true, and then replace all instances of current_CC_id in...
screenImage.replace(current_CC_id, String(char(CGRAM_ptr + 8)) + " "); // ...screenImage with (CGRAM_ptr + 8) + " "
}
// %%%%% IF current_CC_id NOT IN TABLE, SAVE IT IN A FREE LOCATION IN THE TABLE, AND
// REPLACE ALL INSTANCES OF current_CC_id IN screenImage WITH A POINTER (0x08-0x0f + " ") TO THE TABLE...
if (present == false) // If not already present in CC_id_table,
{
CGRAM_ptr = 0;
while ((CGRAM_ptr < 8) && (CC_id_table[CGRAM_ptr] != "")) CGRAM_ptr++; // Stops at 1st free location in CC_id_table
if (CGRAM_ptr == 8) screenImage.replace(current_CC_id, "# "); // Replace all instances of CC_id in screenImage with "# ", signaling CGRAM overflow.
else
{
// Add current_CC_id to table and all instances of current_CC_id in screenImage with (CGRAM_ptr + 8) + " "...
CC_id_table[CGRAM_ptr] = current_CC_id; // Record the current custom character ID, e.g., "ql", in the CC_id_table.
lcd.createChar((CGRAM_ptr + 8), gBitmap); // Load bitmap into the LCD chip's CGRAM
present = true; // Set 'present in table' == true, and then replace all instances of current_CC_id in...
screenImage.replace(current_CC_id, String(char(CGRAM_ptr + 8)) + " "); // ...screenImage with (CGRAM_ptr + 8) + " "
}
}
}
else screenImage.replace(current_CC_id, "@ "); // Replace all instances of current_CC_id in screenImage with "@ ", to signal font/char error.
}
else
{
scrn_ptr += 1;
//if (current_fontChar == String(15,HEX)) scrn_ptr += 1; // skip
}
}
// (5) Now print all EVEN characters, 0,2,4,6,..., in screen_image to the LCD display...
lcd.setCursor(0,0);
final_screenImage = "";
for (scrn_ptr = 0; scrn_ptr < screenImage.length(); scrn_ptr += 2) final_screenImage = final_screenImage + screenImage.charAt(scrn_ptr);
lcd.setCursor(0,0); // row 0
lcd.print(final_screenImage.substring(0, 20));
lcd.setCursor(0,1); // row 1
lcd.print(final_screenImage.substring(20, 40));
// Note: STN transflective LCDs typically transition relatively slowly (150 ms transparent to 90% black, and 300 ms black
// to 90% transparent), requiring a delay of about 200 ms before the next LCD screen write, a time best determined by experiment.
}
/*------------------------------------------------------------------------*/
/* myFontsPrinter() - PRINT ALL FONT TABLES (USE FOR DEBUG PURPOSES ONLY) */
/*------------------------------------------------------------------------*/
void myFontsPrinter() {
Serial.begin(9600);
Serial.print("\n");Serial.print("----- FONT TABLES -----\n");
for (const CustomFont &font : fonts) { // Use a foreach statement for a compact printout of the structs.
Serial.print("\nFont Flag: ");
Serial.print(font.fontFlag);
//Serial.print("\nFont Name: ");
//Serial.print(font.fontName);
Serial.print("\nFont Characters: ");
Serial.print(font.fontChars);
Serial.print("\nFont Bitmaps for Each Custom Character:");
for (uint8_t i = 0; i < font.fontChars.length(); i++) {
Serial.print("\nCharacter '");
Serial.print(font.fontChars[i]);
Serial.print("':");
for (int j = 0; j < 8; j++) {
Serial.print("0x");
if (font.fontBits[i][j] < 0x10) Serial.print("0");
Serial.print(font.fontBits[i][j], HEX);
Serial.print(" ");
}
Serial.print("\n");
}
Serial.print("\n");
}
return;
}