Caliper, der Plan

caliper
8. September 2026
PAP-022

Caliper ist eine Erweiterung für LVGL, die Oberflächen auf Controller-Boards vermessbar macht. Sie trennt körperliche Maße von räumlichen, hält die Gestaltungswerte an genau einer Stelle, gibt allem, was Text trägt, eine Mindestbreite statt einer festen, und prüft auf jedem Bau, ob das Ergebnis noch stimmt. Dieses Papier ist zugleich die Bauanleitung: es nennt die Typen, die Dateien, die Ordner und die Aufgaben, aus denen die Issues geschnitten werden. Das Bedienteil LEVEL ist der erste Nutzer und bleibt vorerst der einzige.

Inhalt

  1. Wozu es das gibt
  2. Was es ist und was LVGL bleibt
  3. Das Panel und die zwei Arten von Maß
  4. Die Werte und ihre eine Quelle
  5. Wie ein Bildschirm entsteht
  6. Die Komponenten
  7. Die Prüfungen
  8. Wie es gebaut und eingebunden wird
  9. Der Weg dahin

Anhang

  1. Die Messumgebung
  2. Die Typenskala nachgerechnet
  3. Geprüfte Belege

1. Wozu es das gibt

Eine Oberfläche für ein Controller-Board wird heute für ein Display entworfen und lebt dann in dessen Zahlen. Beim Bedienteil LEVEL ist das nachweisbar: der Erzeuger seiner Entwürfe setzt 1716 Maße, und jedes einzelne davon steht in Bildpunkten dieses einen Panels. Ein zweites Display hieße, sie alle neu zu entscheiden.

Der naheliegende Ausweg, alles mit einem Faktor zu skalieren, ist falsch, und zwar messbar. Eine Fingerkuppe misst neun Millimeter. Auf dem Panel dieses Projekts sind das 66 Punkte, weil 800 auf 480 über fünf Zoll 7,35 Bildpunkte je Millimeter ergeben. Auf einem Sieben-Zoll-Panel mit 1024 auf 600 ist die Dichte geringer, nämlich 6,68, und dieselbe Fingerkuppe misst dort 60 Punkte. Ein Faktor aus der Breite sagte 84. Das sind vierzig Prozent daneben, auf jeder einzelnen Taste.

Drei weitere Dinge fallen in dieselbe Lücke. Ein Knopf mit fester Breite schneidet ein längeres Wort ab, sobald die Oberfläche eine zweite Sprache trägt. Die Gestaltungswerte stehen an zwei Orten, im Gerät und in den Dokumenten, die es beschreiben, und laufen lautlos auseinander. Und ob eine Fläche noch anzutippen und ein Text noch zu lesen ist, sieht niemand, bevor das Gerät auf dem Tisch steht.

Caliper beantwortet diese vier Fragen einmal, statt sie in jedem Projekt neu zu stellen.

2. Was es ist und was LVGL bleibt

Caliper ersetzt LVGL nicht und tritt auch nicht neben es. Es ist eine Erweiterung, fest an LVGL gebunden, und es registriert sich als dessen Thema. Damit trägt auch ein Bedienelement, das jemand unmittelbar von LVGL nimmt, dieselbe Gestaltung.

Die Bindung ist eine Entscheidung und kein Zufall. Was an einer Oberfläche wirklich Arbeit macht, hängt an LVGL: jedes Bedienelement ist ein LVGL-Objekt, Layout heißt dort Flex und Grid, die Breite eines Wortes kommt aus LVGLs Schriftmetrik, und die Prüfungen lesen den fertigen Objektbaum. Was ohne LVGL bliebe, sind ein paar Umrechnungen und eine Tabelle mit Zahlen. Eine Trennschicht dafür wäre eine Naht ohne Nutzen.

Geschrieben ist Caliper in C++, in der Fassung von 2020, und ruft LVGL als das C an, das es ist. Die Sprache ist hier kein Geschmack, sondern trägt genau den Punkt, um den es geht: die zwei Arten von Maß können verschiedene Typen sein. Ein Millimeter und ein Bildpunkt sind in C beide eine Zahl, und nichts hält jemanden davon ab, das eine für das andere einzusetzen. Als eigene Typen lässt der Übersetzer es nicht zu, und der Fehler, den dieses Projekt gerade auf einem Panel gefunden hat, kann gar nicht erst geschrieben werden.

Der zweite Gewinn ist, dass die Umrechnungen zur Übersetzungszeit stattfinden können. Wo das Panel schon beim Bauen feststeht, und das ist der Normalfall auf einem Gerät, kostet eine Fingerkuppe in Millimetern zur Laufzeit nichts; steht es erst beim Start fest, rechnet dieselbe Zeile dann. Gebaut wird ohne Ausnahmen, ohne Laufzeittypen und ohne Speicheranforderung nach dem Start, also in den Einstellungen, die ESP-IDF für C++ ohnehin voreinstellt.

Tabelle 1 Was LVGL mitbringt und was Caliper hinzufügt
AufgabeWerWie
Zeichnen, Teilbild-Auffrischung, Eingabe, SchriftenLVGLunverändert übernommen
Layout aus Stapeln und RasternLVGLFlex und Grid, von Caliper nur richtig eingestellt
Stile und ThemenLVGLCaliper liefert ein Thema, keine zweite Stilwelt
Das Panel und seine DichteCalipereine Beschreibung, aus der jedes körperliche Maß folgt
Millimeter und Leseabstand als EinheitCalipereigene Typen, Umrechnung über die Dichte
Die GestaltungswerteCalipereine Quelle, aus der der Header und die Dokumente entstehen
Mindestbreiten für alles, was Text trägtCalipergemessen gegen jede Sprache des Baus
Die Komponenten einer BediengeräteoberflächeCaliperstatus_bar, sidebar, List, Row, Card, keyboard und was Tabelle 5 sonst nennt
Die PrüfungenCaliperauf jedem Bau, gegen den fertigen Objektbaum

3. Das Panel und die zwei Arten von Maß

Am Anfang steht eine Beschreibung des Panels: seine Breite, seine Höhe und wie viele Bildpunkte auf einen Millimeter gehen. Aus Breite und Höhe folgt alles Räumliche, also wie breit eine Spalte wird und wie viele Zeilen unter den header passen. Aus der Dichte folgt alles Körperliche, und aus dem Leseabstand die Schrift.

Tabelle 2 Die Einheiten und wofür jede steht
TypWas er bedeutetWoher der Wert kommt
PointEin Bildpunkt des Panels, ganzzahligErgebnis jeder Umrechnung, und das Einzige, was LVGL sieht
MillimeterEin körperliches Maß auf der Glasscheibevom Entwurf gesetzt, etwa neun Millimeter für eine Fingerkuppe
ReadingEin Leseabstand in Millimeternvom Entwurf gesetzt, etwa 400 für einen Fließtext
PanelDas Gerät, für das gerechnet wirdje Gerät einmal, zur Übersetzungszeit oder beim Start

Der Übersetzer nimmt einen Millimeter nicht an, wo ein Point hingehört, und umgekehrt. Wer umrechnen will, muss das Panel fragen, und genau dabei fällt auf, dass man es überhaupt braucht.

namespace cal {

struct Point      { int32_t value; };
struct Millimeter { float   value; };
struct Reading    { float   millimetres; };

constexpr Millimeter operator""_mm(long double size);
constexpr Point      operator""_pt(unsigned long long size);

struct Panel {
    Point width;
    Point height;
    float points_per_mm;

    constexpr Point operator()(Millimeter size) const;
    constexpr Point TypeSize(Reading distance) const;
};

inline constexpr Panel kJc8048w500{ .width = 800_pt, .height = 480_pt,
                                    .points_per_mm = 7.35f };

static_assert(kJc8048w500(token::kFingertip) == 66_pt);

}

Die letzte Zeile ist kein Beispiel, sondern die Prüfung selbst. Eine Fingerkuppe auf diesem Panel misst sechsundsechzig Punkte, und sobald das nicht mehr stimmt, baut die Bibliothek nicht. Daneben stehen dieselben Zusicherungen für den Daumen, für die vier Grade der Schrift, für die drei Abstände und für die Ecken.

Tabelle 3 Dieselbe Fingerkuppe auf verschiedenen Panels
PanelPunkteZollPunkte je mm9 mm in Punkten
JC8048W500, das dieses Projekt vermisst800 × 4805,07,3566
ein Sieben-Zoll-Panel1024 × 6007,06,6860
dasselbe, mit einem Faktor aus der Breite gerechnet1024 × 6007,0entfällt84

Die Schrift folgt derselben Logik, nur über den Winkel. Eine Versalhöhe erscheint dem Auge unter einem Winkel, und lesbar ist sie, solange dieser Winkel groß genug bleibt. Aus dem Leseabstand und dem Winkel folgt die Versalhöhe in Millimetern, daraus über die Dichte ihre Höhe in Punkten und über das Verhältnis der Schrift die Schriftgröße. Der Winkel ist ein Wert der Gestaltung und steht in der Wertedatei. Er ist an der Skala des Bedienteils nachgerechnet und liegt bei 6,1 Milliradiant, was Anhang B zeigt.

Die untere Grenze steht fest

Caliper rechnet ab 800 auf 480 und nicht darunter. Der Grund ist die Tastatur: zehn Tasten zu je einer Fingerkuppe brauchen mit ihren Rinnen 732 Punkte, und zwischen den Rändern stehen 752 zur Verfügung. Auf einem schmaleren Panel hilft kein Layout, sondern nur eine andere Tastatur, und das ist eine Entscheidung über das Produkt.

4. Die Werte und ihre eine Quelle

Abstände, Radien, die Typenskala, die Palette und die Mindestgrößen sind die Gestaltung. Sie stehen in einer Datei, die nur Werte enthält und keinen Code, und aus ihr entstehen der Header für das Gerät und das Modul, mit dem die Dokumente gezeichnet werden. Beide erzeugten Dateien werden nie von Hand geändert, also können sie sich nicht widersprechen.

Das Format ist TOML, weil Python es seit Fassung 3.11 ohne Zusatzpaket liest und ein Mensch es ohne Anleitung schreibt. Abstände stehen darin in Millimetern und nicht in Punkten: ein Rand gilt dem Auge und dem Finger, und auf einem dichteren Panel soll er gleich aussehen und nicht gleich viele Bildpunkte breit sein.

[spacing]
edge     = "2.18 mm"    # Bildschirmrand zu allem darin
group    = "3.27 mm"    # zwei Gruppen nebeneinander
inset    = "2.72 mm"    # Kante einer Fläche zu ihrem Inhalt
line_gap = "1.63 mm"    # zwei Zeilen, die zusammengehören

[touch]
fingertip = "9.0 mm"
thumb     = "12.0 mm"
minimum   = "6.53 mm"   # der Boden, gegen den die Prüfung misst

[type]
cap_angle_mrad = 6.1    # unter diesem Winkel steht jede Versalhöhe
cap_ratio      = 0.7    # Versalhöhe je Punkt Schriftgröße
title   = "935 mm"      # Leseabstand, aus dem der Grad folgt
heading = "560 mm"
body    = "405 mm"
small   = "345 mm"

[radius]
tile = "2.18 mm"        # die eine Ecke des Entwurfs

[colour]
bg      = "#111111"
surface = "#1b1b1b"
accent  = "#009ce9"

Die vier Abstände sind aus den Graden zurückgerechnet, die der Entwurf heute trägt, und nicht auf runde Zahlen gesetzt. Der Grund steht in Anhang B: die Skala war als Sammlung gewachsen, und die genannten Abstände ergeben unter einem einzigen Winkel nicht dieselben vier Grade. Zurückgerechnet tun sie es, auf den Punkt genau, und damit ist die Skala eine Regel, ohne dass sich ein Bildschirm ändert. Was gerundet aussieht, war vorher die Ungenauigkeit.

Daraus entsteht ein Header, in dem jeder Wert constexpr ist und seinen Typ trägt. Ein Abstand ist ein Millimeter und keine nackte Zahl, also lässt er sich nirgends einsetzen, wo Punkte erwartet werden, ohne durch das Panel zu gehen.

// Erzeugt aus tokens/caliper.toml. Nicht von Hand ändern.
namespace cal::token {
inline constexpr Millimeter kEdge{2.18f};
inline constexpr Millimeter kInset{2.72f};
inline constexpr Millimeter kFingertip{9.0f};
inline constexpr Reading    kBody{405.0f};
inline constexpr float      kCapAngleMrad = 6.1f;
}

Der Name im Header folgt aus der Gruppe, in der der Wert steht: was unter spacing steht, heißt k und der Name, was unter layout steht, kBand und der Name. Das ist mechanisch und wird nicht entschieden, damit ein neuer Wert nicht darüber nachdenken lässt, wie er heißen soll.

Eine Regel steht dabei nicht als Zahl, sondern als Rechnung: eine Ecke innerhalb einer anderen ist die äußere weniger dem Abstand zwischen beiden. Wer sie als zwei Zahlen schreibt, bekommt beim nächsten Wechsel eine eingeschnürte Ecke. Caliper rechnet sie, und die Prüfung meldet jede, die anders herausgekommen ist.

Tabelle 4 Was in der Wertedatei steht und was daraus folgt
GruppeWas darin stehtWas im Gerät daraus wird
spacingRand, Gruppenabstand, Innenabstand, ZeilenabstandMillimeter, über das Panel in Punkte
touchFingerkuppe und DaumenMillimeter, zugleich die Untergrenze der Prüfung
typeSehwinkel, Versalverhältnis, je Grad ein LeseabstandSchriftgrößen, aus Winkel und Abstand gerechnet
radiusdie eine Ecke des Entwurfsjede weitere Ecke folgt aus ihr und dem Innenabstand
colourdie Paletteunverändert, denn eine Farbe hat kein Maß

5. Wie ein Bildschirm entsteht

Ein Bildschirm sagt, was auf ihm steht und in welcher Ordnung. Wo etwas steht, entscheiden die Komponenten. Das ist der ganze Unterschied zu einer abgezählten Anordnung, und er ist der Grund, warum dasselbe Ergebnis auf einem anderen Panel steht, ohne dass jemand nachrechnet.

Die Fläche zerfällt in status_bar, header, sidebar und den Inhalt dazwischen. Jedes davon nimmt sich seine Höhe aus dem, was es trägt, und der Inhalt bekommt, was übrig bleibt. Einen footer gibt es nicht: was unten steht, ist die Wiedergabesteuerung, und Platz dafür hält der Inhalt nur auf einem Bildschirm, der eine trägt. Darunter arbeitet LVGLs Flex-Layout; Caliper stellt es nur richtig ein, statt eine zweite Layoutmaschine daneben zu setzen.

cal::Screen screen{cal::kJc8048w500};

screen.sidebar({ .icons = {&house, &sofa, &notes, &gear}, .active = 1 });
screen.status_bar({ .clock = "21:14", .network = &signal });
screen.Header("Räume", "4 Räume");

cal::Object rooms = cal::List(screen.Content(), screen.panel());
cal::Row(rooms, screen.panel(), "Wohnzimmer", "42", &sofa);
cal::Row(rooms, screen.panel(), "Esszimmer", "38", &sofa);

screen.player_controller({ .title = "Blue in Green", .playing = true });
cal::InspectAndLog(screen);

Was ein Bildschirm nicht enthält, sind Zahlen. Weder eine Zeilenhöhe noch ein Rand noch eine Position steht dort, weil alle drei aus den Werten und dem Panel folgen. Wo eine Anordnung wirklich eine eigene Zahl braucht, ist das ein Zeichen, dass eine Komponente fehlt.

Zusammengesetzte Komponenten tragen ihre Teile als eigene Typen darin. In den Dokumenten trennt ein Punkt die beiden, also heißt der Balken eines Schiebers dort slider.track; in C++ ist es cal::Slider::Track. Die Zuordnung ist mechanisch, damit ein Lauf die Liste im Dokument gegen den Code halten kann.

6. Die Komponenten

Die Komponenten stammen nicht aus einer Ideensammlung, sondern aus einer Oberfläche, die es gibt: das Bedienteil führt siebenundvierzig davon, jede mit einer Zeichnung in Originalgröße und allen Maßen daneben, in PAP-018-C. Caliper übernimmt davon, was jede Bediengeräteoberfläche braucht. Was dem Produkt gehört, bleibt beim Produkt.

Tabelle 5 nennt links den Namen im Entwurf und rechts den, unter dem er in C++ steht. Die beiden sehen verschieden aus, weil der Entwurf in Python geschrieben ist und die Bibliothek dem Google-Stil folgt, und sie meinen dasselbe. Wo ein Name in der Bibliothek anders lautet als im Entwurf, steht er daneben.

Tabelle 5 Was Caliper heute trägt und was beim Bedienteil bleibt, nach dem, wofür eine Komponente da ist
WofürBei CaliperBei LEVEL
die Ränder des BildschirmsScreen::status_bar, Screen::Header, Screen::sidebar, Screen::player_controllerwas in ihnen steht, also die vier Bereiche und was gerade spielt
untereinanderList, Row, MarkCurrent, ChooseOne, LeadsAway, Chosenroom_card
etwas sagenScreen::card, CentredBlock, ContentUnavailableView, Headingnichts
etwas tunButton, IconButton, MediaButtons, Screen::keyboard, TextFieldnichts
etwas zeigenPlayingInfo, Spacernow_playing mit Spalte, album_grid mit Kachel
Bildzeichennichts; ein Zeichen ist ein Bild, das LVGL zeichnetwordmark, meter_glyph, die Zeichen selbst
GrundformenSquircle, SquircleImage, SquircleMaskengraved

Noch nicht bei Caliper, aber im Entwurf vorhanden, sind der Schieber mit Balken und Griff, das Fähnchen über einer Taste, der Fortschrittsbalken und der Schalter einer Zeile. Sie stehen in Stufe 5, die als Einzige noch offen ist.

Die Namen tragen nichts aus dem Produkt. Eine Liste heißt list und nicht nach dem, was in ihr steht, denn das entscheidet der Bildschirm und nicht das Framework. Sie stapelt, was sie bekommt: list.row ist ihre schlichte Zeile mit Namen links, Wert rechts und einer Haarlinie darunter, row die anzutippende mit Grund, Zeichen und einem Zeichen am Ende. Beide stehen in derselben Liste.

Die Liste rollt. Sie nimmt so viele Zeilen, wie sie bekommt, zeigt so viele, wie zwischen header und footer Platz haben, und der Rest kommt beim Schieben nach. Das ist die teuerste Bewegung, die dieses Panel kennt, und der Kasten weiter unten nennt den Preis. Bezahlt wird er trotzdem, weil eine Liste rollt.

Zwei Regeln gelten für alle. Was Text trägt, hat eine Mindestbreite und keine feste, damit ein längeres Wort den Knopf breiter macht, statt darin abgeschnitten zu werden; gemessen wird gegen jede Sprache, die der Bau kennt, denn ein Wort, das nur in der zweiten zu lang ist, fällt sonst niemandem auf. Und jede Komponente rechnet aus dem Rahmen, den sie bekommt.

Was der Durchsatz den Komponenten vorschreibt

Auf dem Board dieses Projekts rechnet der Chip rund 4,3 Millionen Bildpunkte je Sekunde, gemessen mit zwei unabhängig gebauten Bibliotheken und in beiden gleich. Ein Vollbild sind 384.000 Bildpunkte, also bleiben elf bis dreizehn davon je Sekunde. Ein Ausschnitt dagegen läuft mit der vollen Bildrate des Panels. Daraus folgt für die Komponenten zweierlei: ein Wechsel der ganzen Fläche geschieht sofort und ohne Blende, und animiert wird nur, was einen Ausschnitt betrifft. Eine rollende Liste kostet dieselbe Rate, und sie rollt trotzdem. PAP-021 trägt die Messung.

Eine Komponente steht in der Liste, die das Bedienteil nicht führt: Spacer, die leere Fläche zwischen zwei Dingen. Mit einer Größe ist sie genau so groß, ohne eine wächst sie und schiebt, was links und rechts von ihr steht, an die Enden. Das ist dieselbe Komponente in zwei Zuständen und nicht zwei, und es ist dieselbe Sache, die SwiftUI Spacer nennt.

Ihre Richtung kommt vom Behälter und nicht von ihr selbst. In einer Zeile wächst sie in die Breite, in einer Spalte in die Höhe, und dafür braucht sie weder einen Parameter noch eine zweite Komponente. Senkrecht ist sie genauso brauchbar wie waagerecht: was oben steht und was unten, ist erste Zeile, Spacer, letzte Zeile.

Sie wird gebraucht, sobald eine Anordnung nicht gleichmäßig ist. Die beiden Tasten eines Schrittes stehen an den Enden ihres Bandes, also sind sie Taste, Spacer, Taste, und niemand rechnet dafür eine Position aus der Breite des Bildschirms. Wo eine Reihe wirklich gleichmäßig verteilt ist, tut es die Rinne des Bandes und Flex mit seiner eigenen Verteilung; ein Spacer je Lücke wäre dort ein Objekt zu viel, und auf einem Controller kostet jedes eines.

Zum Zentrieren taugt sie dagegen nicht, und das ist keine Lücke. Ein einzelnes Objekt in der Mitte einer Fläche ist eine Ausrichtung, also eine Eigenschaft des Objekts und kein Objekt daneben. Vier Spacer dafür bräuchten zwei geschachtelte Behälter, weil Flex in einer Richtung arbeitet, und kosteten sechs Objekte für das, was eine Ausrichtung ohne eines tut. Steht ein ganzer Stapel mittig, also Zeichen, Überschrift, Zeile und eine Bedienung darunter, macht das CentredBlock: er zählt Höhen und Lücken zusammen und setzt den Stapel als Ganzes. Von Hand gesetzt sitzt so ein Stapel fast immer zu tief, weil das Auge die Zeilen zählt und die Lücken vergisst.

Eine Komponente trägt ein Bild, und das ist die einzige, die etwas holt statt nur zu rechnen. artwork bekommt eine Adresse und eine Größe und liefert eine Fläche, die entweder das Bild zeigt oder den Platzhalter. Was dahinter liegt, gehört ihr nicht: LVGL entpackt, und wer die Daten heranschafft, entscheidet das Produkt, weil eine Datei auf einer Speicherkarte und ein Abruf über das Netz verschiedene Dinge sind. Was Caliper festlegt, ist die Größe und dass es einen Platzhalter gibt, denn ein Bild, das noch nicht da ist, ist der Normalfall und kein Fehler. Der Zwischenspeicher gehört LVGL und ist einzustellen, weil er ab Werk auf null steht und ein Gitter dann leer bleibt, ohne dass eine Meldung darauf hinweist.

Ein Zustand bekommt keine eigene Komponente. Der löschende Knopf ist der Knopf in der Warnfarbe, die Leiste beim Einrichten ist dieselbe Leiste in einem anderen Zustand, und die Kachel ohne Bild ist dieselbe Kachel ohne Bild. Beim Bedienteil sind auf diese Weise sieben Namen wieder verschwunden, und jeder von ihnen war ein zweites Bauteil, das dasselbe tat.

7. Die Prüfungen

Dies ist der Teil, den kein Oberflächenwerkzeug mitbringt, und der Grund, warum Caliper mehr ist als ein Thema. Nach jedem Aufbau eines Bildschirms läuft eine Reihe von Prüfungen über den fertigen Objektbaum. Sie sind Teil des Baus und kein Testwerkzeug daneben, und ein Fehlschlag bricht ihn ab.

Tabelle 6 Was auf jedem Bau geprüft wird
PrüfungDie RegelWas sie verhindert
TrefferflächeJede Bedienfläche misst in beiden Richtungen mindestens eine Fingerkuppeeine Taste, die der Finger nicht trifft und die auf dem Rechner groß genug aussieht
RandNichts steht näher an einer Kante des Panels als der RandInhalt, der am Gehäuse klebt
PassungKein Text ist breiter als die Fläche, die ihn trägt, in keiner Spracheein Wort, das nur in der zweiten Sprache abgeschnitten wird
EckeEine Ecke in einer Ecke ist die äußere weniger dem Abstanddie eingeschnürte Ecke, die man sieht und nicht benennen kann
RasterJede Kante liegt auf dem Raster des Entwurfszwei Flächen nebeneinander, um einen halben Punkt versetzt
LeisteNichts im Inhalt reicht unter den header oder unter die Wiedergabesteuerungeine Zeile, die von einer Leiste verdeckt wird

Zwei der sechs treten zurück, sobald etwas in einem rollenden Bereich steht: die Leiste und der untere Rand. Eine Zeile unterhalb der Kante ist dort der Sinn der Sache und kein Fehler, und sie klebt auch an keinem Gehäuse, weil sie gar nicht auf dem Glas steht. Die Seiten hält der Rand weiterhin, und die anderen vier Prüfungen laufen unverändert.

Ein Befund nennt die Komponente, die Regel und beide Zahlen, damit niemand nachmessen muss:

caliper: touch target: "button" is 48, needs 66 (352,404 120x48)

Hält ein Bildschirm alle sechs, meldet der Lauf screen holds und sonst nichts. Das ist die Zeile, an der sich ablesen lässt, dass eine Anordnung stimmt, und sie steht im Protokoll des Geräts wie im Fenster auf dem Schreibtisch.

Dieselben Prüfungen laufen ohne Gerät, denn LVGL baut auch für den Rechner. Ein Bildschirm lässt sich im Test aufbauen und vermessen, ohne dass ein Board angeschlossen ist. Das ist zugleich der Weg, auf dem sich später zeigen lässt, dass eine Oberfläche auf einem zweiten Panel hält: sie wird dafür gebaut, und die Prüfungen sagen, ob sie passt.

8. Wie es gebaut und eingebunden wird

Caliper ist eine ESP-IDF-Komponente und wird über ihre Repository-Adresse gezogen. Derselbe Baum baut ohne ESP-IDF für den Rechner, wo die Tests laufen.

Tabelle 7 Die Ordner der ESP-IDF-Komponente
OrdnerWas darin liegt
include/caliperDie öffentlichen Header: Panel, Einheiten, Thema, Komponenten, Prüfungen
srcWas nicht im Header stehen muss, also alles, was LVGL anfasst
tokenscaliper.toml, die eine Quelle der Werte
toolsDer Erzeuger, der daraus Header und Python-Modul schreibt
testDie Tests für den Rechner, ohne Board
examplesEin Bildschirm, der sich allein bauen und ansehen lässt

Der Erzeuger läuft vor dem Übersetzen und nicht von Hand. Wer die Wertedatei ändert und nur baut, bekommt den neuen Header, und wer den Header ändert, bekommt ihn beim nächsten Bau überschrieben. Das ist der ganze Mechanismus, der das Auseinanderlaufen verhindert.

9. Der Weg dahin

Gebaut wird in fünf Stufen, und jede ist für sich brauchbar. Jede Zeile der Tabelle ist als eigenes Issue geschnitten; die Nummern sind die im Repository. Vier der fünf stehen, und mit ihnen zeichnet das Bedienteil seine Bildschirme. Offen ist die fünfte, in der die Komponenten nachkommen, die der Entwurf noch verlangt.

Tabelle 8 Die Stufen, ihre Aufgaben und woran ihr Ende zu erkennen ist
StufeIssueStandDie Aufgaben darinFertig, wenn
1#1stehtDie Wertedatei anlegen; den Erzeuger schreiben, der daraus den Header und das Python-Modul schreibt; die Ecke als Rechnung statt als Zahl; beide Ausgaben mit dem Vermerk versehen, dass sie erzeugt sindDer Erzeuger der Paper des Bedienteils liest die Werte von dort und zeichnet unverändert weiter
2#2stehtDie Typen Point, Millimeter und Reading; die Beschreibung des Panels; die Umrechnung über die Dichte; die Schriftgröße aus dem Sehwinkel; die Mindestbreite eines BedienelementsTests rechnen Fingerkuppe und Schriftgröße für drei Panels aus, und der static_assert für das eigene Panel steht
3#3stehtDer Durchgang über den Objektbaum; die sechs Prüfungen mit ihren Meldungen; der Bau für den Rechner mit LVGLs Desktop-Anbindung; je Prüfung ein Test, der sie absichtlich rot laufen lässtEin zu kleines Bedienelement lässt den Bau scheitern und die Meldung nennt beide Zahlen
4#4stehtDas Thema aus den erzeugten Werten; status_bar, header und footer mit dem Inhalt dazwischen; header, row, card und button; die Mindestbreite an allem, was Text trägtEin Bildschirm des Bedienteils steht auf dem Board und die Prüfungen sind still
5#5offenAuflage, Schieber mit seinen Teilen, Tastatur mit ihren Ebenen und dem Fähnchen, das Zeichen in seinen Spielarten, Fortschritt, Bild und der Satz darunterAlle Bildschirme des Bedienteils stehen

Was danach möglich wird, gehört noch nicht zum Plan und steht hier als das, worauf er zuläuft: LVGL läuft auch auf dem Rechner, also könnten die Zeichnungen in den Dokumenten aus derselben Oberfläche kommen, die auf dem Gerät läuft. Dann können Dokument und Gerät nicht mehr auseinanderlaufen. Was das kostet, ist die Schärfe der heutigen Vektorzeichnungen mit ihren Maßlinien, und ob sich das lohnt, entscheidet sich, wenn die Stufen oben stehen.

Anhang

Die Umgebung, in der gemessen wurde, die Rechnung hinter der Typenskala und die Fundstellen. Für den Plan oben braucht man das hier nicht.

A. Die Messumgebung

Alle körperlichen Zahlen dieses Dokuments gelten für ein Gerät und einen Tag. Was auf einem anderen Panel gilt, rechnet Caliper aus dessen Beschreibung, und was hier steht, ist der Fall, an dem es geprüft wurde.

Tabelle 9 Woran gemessen wurde
WasStandHerkunft
BoardJC8048W500, ESP32-S3, Panel 800 × 480 auf fünf Zoll, an /dev/cu.usbserial-11110gemessen
WerkzeugketteESP-IDF v5.3.2 unter ~/esp/esp-idf, xtensa-esp32s3-elf-gcc 13.2.0, das C++20 trägtgemessen
Dichte7,35 Bildpunkte je Millimeterabgeleitet
Firmwaredas Bedienteil selbst, auf Caliper gebaut: Startbild, Einrichtung bis ins Netz, Home, Räume und die Einstellungen. Dieselben Bildschirme laufen in einem Fenster auf dem Rechnergemessen
LVGL9.5.0 mit esp_lvgl_port 2.9.0, auf dem Board gelaufen und vermessengemessen
Durchsatzrund 4,3 Millionen Bildpunkte je Sekunde, also elf bis dreizehn Vollbilder; mit Slint und mit LVGL derselbe Wertgemessen
Vergleichspanel1024 × 600 auf sieben Zoll, nur gerechnet; kein solches Panel lag vorabgeleitet

Blind war die Messung an einer Stelle, und das ist die wichtigste: es gibt kein zweites Panel auf dem Tisch. Die Zahlen für sieben Zoll stammen aus der Diagonale und nicht aus einer Messung, und ob eine Oberfläche dort wirklich hält, sagt erst der Bau dafür.

B. Die Typenskala nachgerechnet

Die Typenskala des Bedienteils nennt zu jedem Grad einen Leseabstand. Rechnet man daraus den Sehwinkel der Versalhöhe zurück, sollten alle Grade denselben Winkel ergeben, wenn die Skala eine Regel ist und keine Sammlung von Werten. Drei von vier tun das.

Tabelle 10 Die Grade der Skala und der Winkel, unter dem sie erscheinen
GradPunkteVersalhöhe mmAbstand mmWinkel mrad
TITLE605,759506,05
HEADING363,456505,31
BODY262,494006,23
SMALL222,113506,03

TITLE, BODY und SMALL liegen bei 6,05, 6,23 und 6,03 Milliradiant, also innerhalb von drei Prozent beieinander. HEADING fällt mit 5,31 heraus. Entweder ist die Überschrift kleiner, als ihr genannter Abstand verlangt, oder ihr Abstand ist ein anderer: bei sechsunddreißig Punkten trägt sie 565 Millimeter, und für die genannten 650 bräuchte sie 41 Punkte.

Für Caliper ist das kein Nebenbefund, sondern die Entscheidung, wie die Skala entsteht. Der Winkel steht in der Wertedatei, die Abstände stehen daneben, und die Grade folgen daraus. Damit ist die Skala eine Regel, und ein Ausreißer fällt beim nächsten Mal auf, statt sich einzunisten. Welcher der beiden Werte bei HEADING der richtige ist, entscheidet sich am Gerät und nicht auf dem Papier.

C. Geprüfte Belege

Tabelle 11 Aussagen dieses Dokuments und ihre Fundstellen
AussageFundstelle
Das Panel misst 800 auf 480 bei 7,35 Punkten je Millimeterbuild_screens.py:31 bis :33
Eine Fingerkuppe sind 66 Punkte und meint neun Millimeterbuild_screens.py:125
Die Typenskala ist über Leseabstände begründetbuild_screens.py:107 und :120
Die vier Abstände des Entwurfs in Punktenbuild_screens.py:39 bis :41 und :84
Das Spaltenraster geht auf 800 aufbuild_screens.py:53 bis :55
Der Erzeuger setzt 1716 Maße, alle in Bildpunkten dieses PanelsLauf von build_screens.py, 8. September 2026
Siebenundvierzig Komponenten mit Zeichnung und MaßenPapers/level/components.html, PAP-018-C
Sieben Namen sind entfallen, weil sie Zustände warenderselbe Ort, Abschnitt 10
Zehn Tasten brauchen 732 von 752 Punktengerechnet aus KEY_W, KEY_GAP und EDGE
Die Winkel der Typenskalagerechnet aus Grad, CAP_RATIO und PX_PER_MM, 6. September 2026
Die Werkzeugkette ist vollständig installiert~/esp/esp-idf und ~/.espressif/tools gelistet, 6. September 2026
Vier der fünf Stufen stehen, die fünfte ist offenIssues #1 bis #5 im Repository, 8. September 2026
Python liest TOML seit Fassung 3.11 ohne Zusatzpakettomllib in der Standardbibliothek; hier läuft 3.14.7
Rund 4,3 Millionen Bildpunkte je Sekunde, elf bis dreizehn VollbilderMessung auf dem Board am 6. September 2026, PAP-021
Slint und LVGL erreichen bei Flächen und Text denselben Wert12,5 gegen 12,1 Bilder je Sekunde im selben festen Ablauf, derselbe Ort
LVGL 9.5.0 und esp_lvgl_port 2.9.0 laufen auf diesem Boarddependencies.lock des Versuchsaufbaus, geflasht und vermessen
LVGLs Bildspeicher steht ab Werk auf nullCONFIG_LV_CACHE_DEF_SIZE, Vorgabe 0; das Gitter blieb ohne Meldung leer