Introduction
With this control, I try to solve a problem common to all of us who do not have English as our native language. All led, lcd, seven segments controls I have seen so far do not support more than the 7 bit ASCII character set. So for all of us that would like to have our own specific character or symbol, I have added a character editor. I was inspired by another led control by Liu Xia A Fine-looking Segmented LED Control from where I also borrowed a few things like ISupportInitialize
.
Background
There are some quite nice, free led fonts on the Internet like Ozone by Andreas Nylin and AI pointe by Ritchie Ned Hansel. Al pointe uses a 5x7 matrix, a nice light font, and Ozone uses a bolder 6x8 matrix. Have a look and get some inspiration to create your own font. Feel free to use anything between 4x4 (never tried) and 8x8, 5x7, 6x10 or anything that multiplies to 64 or less.
The matrix is stored in a ulong
that holds up to 64 bits.
Using the Code
This control has just a few specific public properties, two to change the appearance of the font, and three for scrolling. Besides that, you can of course change ForeColor
and BackColor
, which also support transparency. The font you create is stored in a Dictionary key=char, value=bits
and saved to an XML file in your Documents directory using:
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\LedFont.xml";
This is because the file is needed in design mode as well as in run time, explained in Points of Interest. The file created contains the characters and their binary matrix as a ulong
, e.g. character char="A" bits="227873781661662"
which translates to “0000 0000 0000 0000 1100 1111 0011 1111 1111 1111 1100 1111 0011 1111 1101 1110”
in binary.
If you read from the right, you can match bit by bit in the editor starting from the upper left corner.
It is also possible to change the layout of the matrix, currently 6x8, by changing the constants HSEGMENTS
and VSEGMENTS
.
But be sure to make a backup of your previous font file before that and start with a new one, or else if the matrix doesn't comply with your font, it will be scrambled.
As the font isn't a bitmap, it's freely resizable and so far I have tested a 16 pixel high and up to 120 pixel high font. Of course the bigger it is, the higher will be the CPU load.
I have included a LedFont.xml with capital letters and numbers if you would like to get started.
Just remember to copy this file to your documents folder.
The editor
The binary matrix
Public Properties
SegmentSpace
- Space between segments in percent of segment size
CharSpace
- Space between characters in percent of character size
Scroll
- true
or false
, to scroll or not to scroll
ScrollDelay
- Delay in milliseconds between movements
ScrollStep
- Scroll by pixel or segment
Public Methods
-
public bool FindChar(char chr)
Checks if character exists in dictionary
-
public void AddChar(char chr, ulong bits)
Adds a character to dictionary, replaces old one if it exists
-
public void RemoveChar(char chr)
Removes character if it exists in dictionary
-
public ulong GetValue(char chr)
Gets value for a character
-
public ArrayList GetKeys()
Gets a list of characters in dictionary
-
public void DrawChar(ulong bits, Graphics g, Rectangle rect)
Draws a single char
to the graphics object within a specified rectangle
-
public void DrawString(string str, Graphics g, Rectangle rect)
Draws a string
to the graphics object within a specified rectangle
Normally you don't use these methods directly as that is done by the Text
property, which itself calls the DrawString
method. The DrawString
method is called when the Text
property is changed, size changed, colors changed and spaces changed. DrawChar
uses the ulong
bits by masking of a bit at a time and painting the led if not zero.
public void DrawString(string str, Graphics g, Rectangle rect)
{
ulong bits;
if (str != null && str.Length > 0)
{
int width = Convert.ToInt32(this.Height / VSEGMENTS * HSEGMENTS);
int pos = (int) (width + width * charSpace);
for (int i = 0; i < str.Length; i++)
{
segDict.TryGetValue(str[i], out bits);
rect = new Rectangle(pos * i, 0, width, rect.Height);
DrawChar(bits, g, rect);
}
}
}
public void DrawChar(ulong bits, Graphics g, Rectangle rect)
{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
float size = rect.Height / VSEGMENTS;
float space = (size * segmentSpace) / 2;
using (SolidBrush segmentBrush = new SolidBrush(ForeColor))
{
for (int y = 0; y < VSEGMENTS; y++)
{
for (int x = 0; x < HSEGMENTS; x++)
{
if ((bits & 1) != 0)
{
RectangleF segmentRect =
new RectangleF(rect.X + x * size,
y * size, size, size);
if ((space * 2) < size)
segmentRect.Inflate
(-space, -space);
g.FillEllipse(segmentBrush,
segmentRect);
}
bits = bits >> 1;
}
}
}
}
Points of Interest
I discovered one thing while using an XML file for storing character definitions. To use the file in design mode which I have to, I could not use the common Application.StartupPath
, as you usually do when storing data, because in design mode that path changed to Visual Studio's directory! The simplest solution was to use the documents folder to make it available in both modes, but feel free to change that. Another thing is that System.Timer.Timer
and Windows.Forms.Timer
is quite inaccurate. That makes timer ticks vary depending on what else the computer does. I also couldn't get a lower tick rate than 15 milliseconds on my mediocre computer.
History
- 17th September, 2008: Initial version