Reine Rendering-Bibliothek für OSSM-Bildschirme – Textseiten, Zeichenketten, Symbole und visuelle Tests
Überblick
Die lib/ui-Bibliothek ist eine reine Rendering-Ebene, die OSSM-Bildschirme in einen U8g2-Puffer zeichnet. Sie hat keine Hardwareabhängigkeiten, kein FreeRTOS und keine Mutexe – sie benötigt lediglich einen u8g2_t*-Zeiger und zeichnet Pixel. Thread-Sicherheit und Anzeige-E/A werden vom Anzeigedienst verwaltet.
Diese Trennung bedeutet, dass die Bibliothek nativ (x86) für automatisierte visuelle Tests ohne ESP32 ausgeführt werden kann.
Abhängigkeiten: U8g2, QRCode
Quelle: Software/lib/ui/src/
TextPage
TextPage ist die primäre Struktur für inhaltsgesteuerte Bildschirme. Sie umfasst Hilfeseiten, Fehlerbildschirme, Aktualisierungsstatus, WiFi-Einrichtung, Kopplung – alles, was hauptsächlich aus Text mit optionalen QR-Codes besteht.
struct TextPage {
const char* title = nullptr;
const char* subtitle = nullptr;
const char* body = nullptr;
const char* bottomText = nullptr;
const char* qrUrl = nullptr;
uint8_t qrVersion = 3;
int qrScale = 2;
bool centerBody = false;
int scrollPercent = -1;
};Alle Felder sind optional. Stellen Sie nur das ein, was Sie brauchen.
| Feld | Typ | Zweck |
|---|---|---|
title | const char* | Fetter Text oben, mit einer Trennlinie unten |
subtitle | const char* | Mittlerer oder fetter Text unter dem Titel; automatische Aufteilung, wenn zu breit |
body | const char* | Text mit automatischem Zeilenumbruch; unterstützt \n als Zeilenumbruch |
bottomText | const char* | Am unteren Bildschirmrand angeheftet (y=62) |
qrUrl | const char* | Unten rechts gerenderter QR-Code; schränkt die Textbreite ein |
qrVersion | uint8_t | QR-Version (Standard 3) |
qrScale | int | QR-Pixelskala (Standard 2) |
centerBody | bool | Textkörper zentrieren |
scrollPercent | int | Bildlaufanzeige anzeigen (0–100); -1 zum Ausblenden |
Rendering-Reihenfolge
drawTextPage() rendert in dieser Reihenfolge:
- Gesamten Bildschirm löschen (Kopfzeile + Seite + Fußzeile)
- QR-Code – untere rechte Ecke, reduziert die verfügbare Textbreite
- Titel – Fettschrift, gefolgt von einer horizontalen Trennlinie
- Untertitel – versucht zuerst die Medium-Schriftart; fällt auf Fettdruck zurück; wird auf zwei Zeilen aufgeteilt, wenn er immer noch zu breit ist
- Körper – mit
drawWrappedText()umbrochen, wenn ein Titel vorhanden ist; zentrierter Titelstil (drawStr::title), wenn der Text das einzige Feld ist - Unterer Text – auf y=62 fixiert
- Bildlaufanzeige – Bildlaufleiste am rechten Rand, wenn
scrollPercent >= 0
Verwendung
// Use a predefined page
ui::drawTextPage(&u8g2, ui::pages::helpPage);
// Or build one inline
ui::TextPage page;
page.title = "Custom Title";
page.body = "Some content\nwith line breaks";
page.scrollPercent = 50;
ui::drawTextPage(&u8g2, page);Vordefinierte Seiten
TextPages.h definiert statische TextPage-Instanzen im ui::pages-Namespace. Diese referenzieren Zeichenketten aus Strings.h:
| Seite | Verwendete Felder |
|---|---|
helpPage | title, body, bottomText, qrUrl |
updateCheckingPage | body |
noUpdatePage | body, bottomText |
updatingPage | title, body |
errorPage | title |
wifiDisconnectedPage | title, body, bottomText, qrUrl |
wifiConnectedPage | title, body, bottomText |
pairingPage | title, body |
Beispieldefinition:
static const TextPage helpPage = {
.title = helpTitle,
.body = helpBody,
.bottomText = helpBottom,
.qrUrl = helpQr,
};Zeichenketten
Alle UI-Zeichenketten befinden sich in Strings.h unter dem Namespace ui::strings. Sie werden mit PROGMEM im Flash gespeichert:
static const char helpTitle[] PROGMEM = "Get Help";
static const char helpBody[] PROGMEM = "On Discord,\nor GitHub";Progmem.h definiert PROGMEM als No-Op auf Nicht-Arduino-Plattformen, sodass die Zeichenketten sowohl auf ESP32 als auch auf nativen Test-Builds kompiliert werden.
Kategorien
| Kategorie | Beispiele |
|---|---|
| Branding | researchAndDesire, kinkyMakers |
| Allgemeine Benutzeroberfläche | error, homing, idle, restart, settings, skip |
| Menü / Modi | simplePenetration, strokeEngine, streaming, pairing, update, wifi |
| Bewegungssteuerung | speed, stroke, sensation, depth, buffer, accel, max |
| Warnungen | speedWarning, homingTookTooLong, strokeTooShort |
| Hilfe | helpTitle, helpBody, helpBottom, helpQr |
| Aktualisierung | updateChecking, noUpdateBody, updatingTitle, updatingBody |
| WiFi | wifiSetup, wifiBody, wifiBottom, wifiQr, wifiConnected |
| Kopplung | pairingTitle, pairingBody |
| Muster | patternName0–patternName6, patternDesc0–patternDesc6 |
Musterarrays
Namen und Beschreibungen der Stroke Engine-Muster sind indizierte Arrays:
static const char* const strokeEngineNames[7] = {
patternName0, patternName1, patternName2,
patternName3, patternName4, patternName5,
patternName6,
};
static const char* const strokeEngineDescriptions[7] = {
patternDesc0, patternDesc1, patternDesc2,
patternDesc3, patternDesc4, patternDesc5,
patternDesc6,
};Menüpunkte
MenuItems.h ordnet Menu::-Enumerationswerte den Anzeigezeichenfolgen zu:
static const char* menuStrings[Menu::NUM_OPTIONS] = {
ui::strings::simplePenetration, ui::strings::strokeEngine,
ui::strings::streaming, ui::strings::pairing,
ui::strings::update, ui::strings::wifiSetup,
ui::strings::helpTitle, ui::strings::restart,
};Bilder und Symbole
Symbole (Schriftzeichen)
Statussymbole verwenden die Symbolschriftart Siji. Glyphen werden in DisplayConstants.h definiert:
namespace IconGlyph {
constexpr uint16_t WIFI_OFF = 0xe218;
constexpr uint16_t WIFI_CONNECTING = 0xe219;
constexpr uint16_t WIFI_CONNECTED = 0xe21a;
constexpr uint16_t WIFI_ERROR = 0xe21b;
constexpr uint16_t BLE_CONNECTED = 0xe00b;
constexpr uint16_t BLE_SMALL = 0xe0b0;
constexpr uint16_t EXCLAMATION = 0xe0b3;
}drawHeaderIcons() zeigt den WiFi- und BLE-Status in der oberen rechten Ecke des Bildschirms an. Fehlerzustände überlagern die Basisglyphe mit zusätzlichen Pixeln (Ausrufezeichen).
Logos (XBM-Bitmaps)
Logos sind PROGMEM XBM-Byte-Arrays in Logos.h:
RDLogo– Research & Desire, 57x50 PixelKMLogo– Kinky Makers, 50x50 Pixel
Sie werden über die Struktur LogoData und drawLogo() gezeichnet:
struct LogoData {
const char* title;
const uint8_t* bitmap;
int w, h, x, y;
};Hello-Animation
HelloAnimation.h enthält vorberechnete Frames mit Y-Positionen für die Boot-Animation „OSSM“. Jeder Frame gibt Y-Offsets pro Buchstabe an:
struct HelloFrame {
int heights[4]; // Y position for O, S, S, M
};drawHelloFrame() rendert einen einzelnen Frame. Die Testsuite fügt alle Frames zu einem GIF zusammen.
Andere Zeichenfunktionen
Die vollständige Rendering-API (ui.h):
| Funktion | Zweck |
|---|---|
drawTextPage() | Eine TextPage-Struktur rendern |
drawMenu() | Scrollbares Menü mit Auswahlhervorhebung |
drawPlayControls() | Geschwindigkeits-/Hub-/Empfindungsbalken für Betriebsmodi |
drawPreflight() | Sicherheitsüberprüfung vor der Bewegung (Geschwindigkeitsknopf auf Null) |
drawHeaderIcons() | WiFi- und BLE-Statussymbole |
drawQR() | Eigenständiger QR-Code |
drawHelloFrame() | Einzelner Boot-Animationsrahmen |
drawLogo() | XBM-Logo mit Titel |
Hilfs-Namespaces in DrawExtensions.h:
drawStr::centered()– horizontal zentrierter TextdrawStr::multiLine()– mehrzeiliger Text mit Zeilenumbruch und UTF-8-UnterstützungdrawStr::title()– Fett zentrierter Text an fester PositiondrawShape::scroll()– BildlaufleistenanzeigedrawShape::settingBar()– beschrifteter vertikaler Balken mit FüllstanddrawShape::settingBarSmall()– kompakter vertikaler Balken (keine Beschriftung)
Testen und visuelle Ausgabe
Die Anzeigebibliothek verfügt über native Tests, die jede Bildschirmvariante rendern und Bilder zur visuellen Überprüfung exportieren.
Tests ausführen
pio test -e test -f test_displayFür die PNG-Konvertierung muss ImageMagick (magick) installiert sein. Ohne sie werden weiterhin PBM-Dateien generiert, PNGs werden jedoch übersprungen.
Wie es funktioniert
- Tests erstellen eine rein softwarebasierte U8g2-SSD1306-128x64-Anzeige (keine Hardware, No-op-I2C)
- Jeder Test ruft
ui::draw*()-Funktionen auf, um in den Puffer zu rendern savePBM()schreibt den Puffer als PBM P4-Bitmap- Nachdem alle Tests abgeschlossen sind, konvertiert ImageMagick PBM in PNG (invertierte Farben, 400 % Skalierung).
- Hello-Animationsframes werden zu einem GIF zusammengesetzt
Ausgabestruktur
test/test_display/_output/
├── pbm/ # Raw bitmaps (generated by tests)
│ ├── textpage/
│ │ ├── combos/ # Field combinations (title+body, all fields, etc.)
│ │ ├── scroll/ # Scroll positions (0%, 50%, 100%)
│ │ ├── stress/ # Edge cases (UTF-8, long strings)
│ │ ├── qr/ # QR code variants
│ │ ├── body/ # Body rendering with/without title
│ │ ├── subtitle/ # Subtitle font sizing
│ │ ├── pages/ # All predefined pages
│ │ └── patterns/ # Stroke engine pattern descriptions
│ ├── header_icons/ # WiFi × BLE status combinations
│ ├── logos/ # RD logo, KM logo
│ ├── hello/ # Animation frames
│ ├── menu/ # Menu rendering variants
│ └── play_controls/ # Preflight, play modes
└── png/ # Same structure, converted to PNGTestabdeckung
| Testdatei | Was es abdeckt |
|---|---|
test_textpage.cpp | TextPage-Feldkombinationen, Stresstests, QR, Untertitelüberlauf, Textlayout |
test_pages.cpp | Alle vordefinierten Seiten, Beschreibungen der Stroke Engine-Muster |
test_scroll.cpp | Bildlaufleiste bei Grenz- und Randfallwerten |
test_header_icons.cpp | Alle 20 WiFi × BLE-Statuskombinationen |
test_logos.cpp | RD-Logo, KM-Logo |
test_hello.cpp | Alle Hello-Animationsframes, GIF-Generierung |
test_menu.cpp | Menü mit verschiedenen Elementanzahlen, langem Text und Zeilenumbrüchen |
test_play_controls.cpp | Steuerung für Preflight, Simple Penetration, Stroke Engine und Streaming |