Netzteil-Display mit Nextion-HMI #2

1

16.07.2017 von plaintron

Weiter geht es mit dem HMI-Display für mein Labornetzteil. Im ersten Teil haben wir uns einige grundlegende Funktionen des Nextion-Editors angesehen und das Grundlayout für die erste Display-Seite angelegt. Nun soll noch ein beweglicher, analoger Zeiger die Spannung anzeigen.

Das Nextion-Element „Gauge“ ist, wie schon erwähnt, hier nicht zu gebrauchen, da es sich nicht über den Rand des Displays verschieben lässt, um nur einen Ausschnitt des Vollkreises sichtbar zu machen. Deshalb verwenden wir eine der Zeichenfunktionen des Display-Moduls, den Befehl „line“. Dieser zeichnet eine Strecke vom Punkt x0, y0 zum Punkt x1, x2, und zwar mit einer Stärke von 1 Pixel.

Unser analoges Voltmeter hat einen Gesamtwinkel von 90 Grad. Der Einfachheit halber teilen wir deshalb die maximale Eingangsspannung in 90 Einzelschritte. Das vereinfacht im Beispiel die Berechnungen und kann später auf eine höhere Genauigkeit umgestellt werden. Als Eingangsparameter verwenden wir den Wert eines Analog-Eingangs am Arduino. Dieser Wert liegt zwischen 0 und 1023. Über ein Poti, das an den Pins A0, A1 und A2 angeschlossen ist (siehe Zeichnung oben), wird eine analoge Spannung zwischen 0 und 5 Volt eingestellt. Der Zeiger soll den Bewegungen des Potis folgen.

Wie in der Zeichnung zu sehen, können wir mit etwas Trigonometrie die Anfangs- und Endpunkte des Zeigers berechnen. Wir legen fest, dass 1 Grad auf der Skala genau einem Teilschritt des Messbereichs entspricht. Um mit den Funktionen sin() und cos() arbeiten zu können, muss der Winkel im Bogenmaß vorliegen. 90 Grad entsprechen Pi/2. Entsprechend berechnen wir die kleinste Wertänderung mit Pi/2048.

Für die Winkelberechnung soll unser Wert von -45 Grad bis +45 Grad laufen. Deshalb subtrahieren wir 512 vom Messwert, der damit zwischen -Pi/4 und +Pi/4 liegt. Grundsätzlich könnten wir das alles auch im positiven Bereich berechnen, aber so lässt es sich zur Erklärung einfacher visualisieren.

Folgende Werte mit den zugehörigen Variablen benötigen wir zunächst für einen einfachen Zeiger:

float a = 0;         // der Messwert vom Analogpin A1
int x = 0;           // Endpunkt Linie
int y = 0;           // Endpunkt Linie
int x0 = 0;          // Anfangspunkt Linie
int y0 = 0;          // Anfangspunkt Linie
int x2 = 0;          // Endpunkt Linie 2
int y2 = 0;          // Endpunkt Linie 2
int len = 180;       // Länge des Zeigers
int width = 0;       // Breite des vom Zeiger überdeckten Bereichs
int height = 0;      // Höhe des vom Zeiger überdeckten Bereichs
int left = 0;        // Linke Seite des Zeigerbereichs
int top = 0;         // Oberkante des Zeigerbereichs
float deg = PI/2048; // Kleinster Messwert

Einige Variablen erklären sich erst später. Für einen ersten Test benötigen wir nur a, x, y, x0, y0, len und deg.

Die setup()-Funktion sieht so aus:

void setup() {
 pinMode(A0, OUTPUT);
 pinMode(A1, INPUT);
 pinMode(A2, OUTPUT);
 digitalWrite(A0, HIGH);
 digitalWrite(A2, LOW);
 Serial.begin(9600);
 Serial1.begin(115200);
}

Die Analogpins A0 und A2 werden auf 0V und 5V gesetzt, Pin A2 misst die Spannung. Serial ist mein Debug-Kanal und über Serial1 wird das Display angesteuert.

Nun werden die Werte für x und y in der Schleifenfunktion berechnet.

a = ((float)analogRead(A1)-512.0) * deg;
x = len * sin(a);
y = len * cos(a);

Damit lässt sich der Zeiger schon zeichnen. Der Ursprung des Zeigers liegt im Moment bei 0,0. Das müssen wir noch ändern und die gesamte Skala um eine halbe Bildschirmbreite nach rechts verschieben. Außerdem liegt die erste Zeile des Bildschirms und damit y=0 am oberen Ende der Displayfläche. Also muss die y-Achse noch auf den Kopf gestellt werden. Allerdings soll der Y-Nullpunkt 10 Pixel Abstand vom unteren Rand haben. Bei einer Display-Höhe von 240 Punkten lautet die Rechnung also 230-y.

So sieht die komplette Loop-Funktion jetzt aus:

void loop() {
 reset_pointer();
 a = ((float)analogRead(A1)-512.0) * deg;
 x = len * sin(a);
 y = len * cos(a);
 x2 = (x * 88) / 100;
 y2 = (y * 88) / 100; 
 x0 = (94*x) / y + 159; 
 x = x + 159;
 y = 230 - y; 
 width = abs(x - x0) + 4;
 height = y0 - y + 4; 
 left = (x < x0) ? x - 2: x0 - 2; 
 top = y-3; 
 draw_pointer();
 delay(40);
}

40ms Delay führen zu einer Bildwiederholrate von 25Hz, also schnell genug, um für flüssige Bewegungen zu sorgen. Einige Zeilen sind noch erklärungsbedürftig. Ich berechne ein x2 und ein y2, die kleiner sind als x und y. Damit verdicke ich den Zeiger etwas, damit er besser sichtbar ist. Die Werte width, height, left und top dienen später dazu, den zuletzt gezeichneten Zeiger wieder zu löschen. Dazu wird er mit einer Farbe oder einem Teil des Hintergrundbildes überschrieben, denn der Zeiger ist letztendlich nur eine Folge von Punkten auf dem Display und lässt sich nicht einfach wieder entfernen. Er wird auch nicht durch ein Objekt repräsentiert, dessen Eigenschaften man ändern kann.

Es wird also ein Zeiger auf das Display gemalt und kurz bevor ein neuer Zeiger erscheint, wird eine recheckige Fläche gefüllt, um den alten Zeiger zu entfernen. Das passiert in der Funktion reset_pointer(), die ganz zu Anfang aufgerufen wird, damit sie mit den Werten des zuvor gezeichneten Zeigers arbeiten kann.

void reset_pointer(){
 sprintf(cmd, "picq %d,%d,%d,%d,0",left,top,width,height);
 Serial1.print(cmd); 
 t();
}

Hier sehen wir, wie ein Befehl an das Display gesendet wird. Der Befehl lauter hier „picq“ und bedeutet, dass ein Bereich mit dem Inhalt eines Bildes gefüllt werden soll. Danach folgen die Parameter für Anfangspunkt, Breite, Höhe und Bild. Das Bild muss bereits auf dem Displaymodul gespeichert sein und wird über eine ID (hier 0) aufgerufen. Jeder Befehl wird mit drei Byte vom Wert 255 terminiert. Das macht hier die Funktion t(), die wie folgt aussieht:

void t(){
 Serial1.write(0xff);
 Serial1.write(0xff);
 Serial1.write(0xff); 
}

Werfen wir noch einen Blick auf die Funktion draw_pointer():

void draw_pointer(){ 
 sprintf(cmd,"line %d,%d,%d,%d,50712",x0-1,y0,x2+158,230-y2);
 Serial1.print(cmd); 
 t();
 sprintf(cmd,"line %d,%d,%d,%d,50712",x0+1,y0,x2+160,230-y2);
 Serial1.print(cmd); 
 t();
 sprintf(cmd,"line %d,%d,%d,%d,0",x0,y0,x,y);
 Serial1.print(cmd); 
 t();
}

Hier sehen wir, dass drei Zeiger nebeneinander gezeichnet werden. Zuerst zwei kürzere Linien in grau (Farbwert 50712) links und rechts vom Hauptzeiger, dann ein schwarzer (Farbwert 0), längerer Strich. Auf die Art lässt sich sogar ein leichter Antialias-Effekt erzielen, damit die Linie nicht zu treppig wirkt.

Der Zeiger reagiert nun prompt und flüssig auf Poti-Bewegungen. Allerdings brauchen wir noch eine kleine Änderung. Bisher wird der Zeiger über die gesamte Länge angezeigt. Ich möchte aber nur den oberen Teil sichtbar machen, da unterhalb des Ziffernblatts weitere Anzeige-Elemente liegen, die nicht vom Zeiger übermals werden sollen.

Das erreiche ich durch eine Änderung des festen Wertes y0=94, welcher das untere Ende des Zeigers festlegt. Ein entsprechender x-Wert muss berechnet werden. Das passiert mit der Zeile x0 = (94*x) / y + 159;

Das Ergebnis sieht schon wie ein richtiges Zeigerinstrument aus:

Eine zusätzliche Herausforderung ist die digitale Spannungsanzeige, welche genau im Zeigerbereich liegt. Die reset-Funktion würde hier die Ziffern einfach wegwischen bzw. bei umgekehrter Zeichnungsreihenfolge würden die neu gezeichneten Ziffern einen Teil des Zeigers löschen. Wie ich dieses Problem löse, zeige ich im nächsten Teil.

Advertisements

Ein Kommentar zu “Netzteil-Display mit Nextion-HMI #2

  1. […] Netzteil-Display mit Nextion-HMI Teil 2 […]

    Gefällt mir

Kommentieren

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

Blogverzeichnis - Bloggerei.de
%d Bloggern gefällt das: