6 Text Ausgabe
Zifern und Buchstaben werden für unser Auge aus Dot-Anordnungen gebildet. Zu ihrer Darstellung definiert man in Variablen das Pixelabbild jedes gewünschten Zeichens. Über Textfunktionen liest man diese Variablen aus und setzt alle entsprechenden Dots auf dem Display. Im Beispiel wwFlipdot-clock-direct
verdeutliche ich, wie man das "zu Fuß" mit direkten Dotbefehlen realisieren kann.
4x7 Ziffern-Font - wwFont_4x7
Text-Fonts
Text-Fonts sind Sammlungen von Pixelabbildern einzelner Zeichen, die man im Programm komfortabel nutzen kann. Vom PC her sind wir in 2020 bezüglich Font-Vielfalt und Darstellungsqualität sehr verwöhnt. Wegen der kleinen Anzeigeaulösung bzw. -größe geht es auf unseren Flip-Dot-Modulen etwas spartanischer zu. Wir können aber auf einen reichen Erfahrungsschatz aus den Anfängen des Computer zurück greifen: 9-Nadel-Drucker oder 5x7 LED Anzeigen hatten früher das gleiche Problem.
Font-Strategie der aaFlipra
Meine Fonts sollten 1. für eine einfache Benutzung per ASCII Code ansprechbar sein und 2. aus Speicherplatzgründen nicht immer den vollen Zeichensatz enthalten müssen.
Dazu gibt es die Strategie des Jump Table bzw Look-Up-Tables = Nachschau-Tabelle. Bei dieser Struktur findet man am Anfang jeder Font-Datei eine Tabelle, in der für jedes ASCII Zeichen Anfangsposition und Byte-Anzahl notiert sind.
Gibt es für ein vom Benutzer verwendetes ASCII-Zeichen keine Dot-Daten, wird dies der aufrufenden Funktion über den Look-Up-Table mitgeteilt. Das beugt Fehlfunktionen im Programmablauf vor - man kann jeden ASCII Text an alle Textfunktionen senden, ohne das dadurch unzulässige Speicherzugriffe erfolgen würden.
Fonts der aaFlipra-Library
Um das Rad nicht neu erfinden zu müssen, habe ich mir verschiedene Font-Strukturen angeschaut. Mene Wahl fiel auf die ThingPulse ESP8266 OLED SSD1306 Library, da sie einen Look-Up-Table verwendet und sowohl einen Font Converter, als auch einen Font-Editor zur freien Verfügung stellt. Dazu mehr auf der nächsten Seite.
Die von mir erstellten und mit der Library ausgeliferten Fonts findet man im Unterordner Arduino\libraries\aaFlipra\src\fonts\
. Die Ziffern im Dateinamen geben Breite und Höhe eines Fonts an. wwFont_4x7_fix.h
ist somit ein 4 Dot breiter und 7 Dot hoher Font. fix
bedeutet, dass er eine feste Zeichenbreite aufweist. Dieser Font hat einen geringen Speicherbedarf, da er nur die Ziffern 0-9 und wenige Buchstaben enthält, mit denen man eine Uhr darstellen kann.
Speicherort aller Fonts der aaFlipra-Library
Der wwFont_Xx7.h
besitzt ebenfalls eine Zeichenhöhe von 7 Dots, ist aber in der breite variabel (X) und enthält einen vollen ASCII Zeichensatz incl. deutscher Umlaute. Er besitzt keine Ober- oder Unterlängen. Mit 7 Dot Höhe kann er auf jedem Flip-Dot-Modul ausgegeben werden. Auf 16 Dot hohen Displays ist 2-zeiligiger Text möglich. In deutschem Text unübliche ASCII Zeichen fehlen zur Speicherplatzersparnis. Fehlende Zeichen können ergänzt werden - siehe nächste Seite. Im Beispiel aaFlipra-01-lauftext
wird die Verwendung dieses Fonts demonstriert.
Ziffernsätze für Uhren
Zur Darstellung der Uhrzeit benötigt man mindestens 4 Ziffern, einen Doppelpunkt und 4 Zwischenräume zwischen den Zeichen. Bei einem 4 Dot breiten Schriftsatz ergibt das eine Mindestbreite von 21 Dot.
4x7 Font für Uhren ab 21x7 Display - wwFont_4x7
4x9 Font. Für zweizeiligen Text sind 19 Dot hohe Module nötig - wwFont_4x9
4x9 Font für Uhren. Einzeilig ab 21x9 Dot großen Modulen darstellbar,
also auch auf KRUEGER 24x16 Modulen
Befehle zur Verwendung von Fonts
Bevor man einen Font benutzen kann, muss man ihn am Anfang des Programms per #include
einbinden. Fonts werden im Programm Memory des Arduino gespeichert, belegen also nicht das RAM. Wenn der Speicherplatz ausreicht, kann man mehrere Fonts parallel einbinden und im Programm abwechselnd verwenden.
#include "fonts/wwFont_4x7_fix.h" //kopiert den Font in das Programm Memory
#include "fonts/wwFont_4x9_fix.h" //Fonts können parallel verwendet werden
Bevor man mit einem Font schreiben kann, muss er per mSetFont(wwFont_Xx7)
ausgewählt werden. Das kann einmal am Anfang des Prorgamms im Setup()
geschehen oder beliebig oft im Programmablauf. Alle Textfunktionen verwenden immer den zuletzt gesetzten Font zur Darstellung.
Da der Font zu Beginn des Programms 1x dauerhaft in das Programm Memory geladen wird, erhöht ein hin und her Schalten zwischen verschienden Fonts anschließen den Speicherbedarf nicht weiter.
mSetFont(wwFont_Xx7); //wählt einen Font für alle folgenden Textbefehle aus
// hier ist die Dateiendung .h nicht mit anzugeben
Zur Speicherplatzersparnis kann man in der Zeichensatzdefinitionen die Leerspalten zum Trennen der Zeichen entfernen und erst später programmtechinsch wieder hinzu fügen. Die aaFlipra erlaubt das Einstellen eines beliebigen Zeichenabstands, was auch für Hervorhebungseffekte verwendet werden kann. Der Zeichenabstand kann beliebig oft umgestellt werden.
void mSetCharSpace(uint8_t charSpace); // Setzt den Zeichenabstand (0-255)
// Default = 1 = ein Dot Abstand
Die Zeichenketten werden immer erst in die Matrix geschrieben und anschließend per mUpdate();
auf das Display gespielt - Erklärung dazu siehe eine Seite weiter vorne unter Grafikfunktionen. Alles zum Thema Koordinatenursprung (x/y) ist dort ebenfalls nachzulesen. Vor einem mUpdate();
können beliebig viele Text und Grafikfunktionen zur Erzeugung des Bildinhalts aufgerufen werden. Texte werden auf schwarzem Hintergrund dargestellt, darunterliegende Grafiken also gelöscht.
Der Textstart darf auch außerhalb des "sichtbaren" Flip-Dot-Displays liegen und positive wie negative Werte im Rahmen eines int16_t annehmen. Die außerhalb des Sichtfelds liegenden Dots eines jeden Zeichens werden ignoriert. So kann man sehr einfach Laufschriften erzeugen - siehe Beispiel unten.
Per mDrawDigit();
kann man eine Ziffer von 0-9 mit dem vorher gewählten Font ausgeben. Man übergibt die Zahl nicht als ASCII String, sondern direkt als Integer Wert bzw. per Variable im Bereich zwischen 0 und 9. Das spart z.B. bei einer Uhr Konvertierungsaufwand im eigenen Code.
void mDrawDigit(int16_t topLeftCornerX, int16_t topLeftCornerY, uint8_t digit);
//Schreibt die Ziffern 0-9. Übergabe als Integer. x/Y = linkes oberes Dot
Normalen Text übergibt man hingegen als String and die entsprechende Funktion. Es sind alle ASCII Zeichen erlaubt. Ist eins davon im aktuellen Zeichnesatz nicht definiert, wird es problemlos ausgelassen. Der übergebene Startpunkt x/y markiert die linke obere Position des ersten Dots im Text. Übergibt man z.B. für x negative Werte, wird der entsprechende Textanfang nicht mit ausgegeben.
void mDrawString(int16_t topLeftCornerX, int16_t topLeftCornerY, String text);
// Schreibt einen Text in die Matrix. x/y = linke obere Textecke
Wer bei Fonts mit variabler Zeichenlänge die Textbreite für eigene Berechnungen eines Zeilenumbruchs oder einer Scrolldauer benötigt, kann sie per getStringWidth();
abrufen. Der Text kann als String oder per Pointer und ASCII-String-Länge übergeben werden.
uint16_t getStringWidth(const char* text, uint16_t length);
uint16_t getStringWidth(String text);
// Rückgabe = Stringbreite in Dots bei Ausgabe mit aktuellem Font
Fallblattsimulation
Immer wieder hört man von Menschen: "Die Flipdots klingen ja wie am Flughafen". Das ist so aber häufig nicht richtig, da am Flughafen oder am Bahnhof meist Fallblattanzeigen motiert waren. Fallblattanzeigen bestehen aus einem "Block" von Ziffern und Buchstaben, die auf Karten = Fallblätter gedruckt sind und ähnlich einem alten Telefonregister drehbar um eine Achse angeordnet sind. Dreht man diese Achse, springt ein Fallblatt nach dem anderen zur Vorderseite hin sichtbar um, was ein charakteristisches Geräusch des Durchscrollens erzeugt.
Um diesen Effekt einfach simulieren zu können, habe ich die Funktion mScrollDigit();
entwickelt. Es sollten möglichst nur Fonts mit fester Breite verwendet werden - sonst kann es "komische" Effekte geben. Aktuell können die Ziffern 0-9 angezeigt werden. Die Übergabe des Werts 10 entspricht einem leeren Feld. Damit kann man eine Ziffer neu aufbauen oder löschen.
void mScrollDigit(uint8_t x0, uint8_t y0, uint8_t digitOld, uint8_t digitNew);
//Funktion zum Simulieren einer Fallblattanzeige
//Wechselt eine Ziffer (zwischen 0 und 9) digitOld in digitNew
//Übergabe als integer Wert; Wert 10 entspricht einem leeren Feld
Die Funktion mScrollDigit();
berücksichtigt einige Parameter zur Ausgabegestaltung, die man vor dem Funktionsaufruf nach eigenem Geschmack einstellen kann.
void mScrollSpace(uint8_t scrollSpace);
// Abstand zwischen einlaufendem und auslaufendem Zeichen in Dots
void mScrollDirection(uint8_t scrollDirection);
// Scrollrichtung hoch oder runtern: UP/DOWN
Um den Scrollefekt zu erzeugen, "blättert" die Funktion die Ziffern in der Matrix jeweils um ein Dot nach oben/unten, bevor sie ein mUpdate();
ausführt. Die Scrollgeschwindigkeit wird durch ein internes delay();
realisiert, dessen Länge man frei wählen kann.
void mScrollDelay(uint16_t scrollDelay);
// Verzögerung zwischen jedem einzelnen Scrollschritt in ms (1 Sekunde = 1000 ms)
Der folgende Code verwendet den Font wwFont_4x7_fix.h
. Die Ziffern 1 und 2 scrollen langsam von unten nach oben und schnell wieder zurück nach unten.
// ==========================================================================
#include "aaFlipra.h"
#include "fonts/wwFont_4x7_fix.h" // Enthält nur die Zeichen +-0123456789:C°
aaFlipra ww;
// ==========================================================================
void setup() {
ww.begin();
ww.dotPowerOn(); // schaltet die Flipspannung auf die Treiberbausteine
ww.setCoilFlipDuration(1000);
ww.resetAll(0); // lösche alle Dots so schnell es geht, also ohne Verzögerung
delay(500);
ww.mSetFont(wwFont_4x7_fix);
}
// ==========================================================================
void loop() {
uint8_t x0 = 3; //Linke obere Ecke der Ziffer
uint8_t y0 = 1;
ww.mScrollDirection(UP); //Voreinstellung der Scrollrichtung auf Up = nach oben
ww.mScrollDelay(200); //200 ms = 0,2 Sekunden Anzeigezeit pro Scrollschritt
ww.mScrollSpace(2); //2 Dot Abstand zwischen den scrollenden Ziffern
ww.mScrollDigit(x0, y0, 1, 2); //Ziffer 1 zu Ziffer 2 an Position x0/y0 wechseln
delay(2000); //2 Sekunden warten
ww.mScrollDirection(DOWN);
ww.mScrollDelay(80);
ww.mScrollSpace(4);
ww.mScrollDigit(x0, y0, 2, 1);
delay(1000);
}
// ===========================================================================