Im vorherigen technischen Artikel „Smart Display RS485 Introduction“ wurden die Grundlagen der RS485-Schnittstelle und des Modbus-Protokolls erläutert. In diesem Artikel gehen wir anhand eines Beispiels mit mehreren Seiten auf weitere häufig verwendete Techniken bei der Entwicklung mit Smart Display ein. Dazu gehören:
- Seitenwechsel mit dem Button widget.
- Aktualisieren von widget-Werten nach einem Seitenwechsel.
- Erzeugen von unterschiedlich langen Signaltönen mit dem Buzzer.
- Verwendung des Graph widget.
Benötigte Komponenten
- Ein Smart Display mit RS485-Schnittstelle. In diesem Beispiel verwenden wir ein 3.5" Smart Display.
- Arduino (Uno oder Mega2560).
- RS485-Erweiterungsboard für Arduino (Link zu DFRobot).
- Potentiometer (VR).
Verdrahtung
- Potentiometer: Vcc mit 5V, GND mit GND und den Ausgang mit Arduino A0 verbinden.
- Anschluss A des RS485-Erweiterungsboards mit RS485+ des Smart Display verbinden.
- Anschluss B des RS485-Erweiterungsboards mit RS485- des Smart Display verbinden.
Aufbau des Beispiels
Das Projekt besteht aus drei Seiten, zwischen denen über das Button widget gewechselt werden kann. Die Inhalte und Funktionen der einzelnen Seiten sind wie folgt aufgebaut:
Seite 1:
Auf der rechten Seite befindet sich eine Schaltfläche zum Wechseln auf die nächste Seite. Darunter befindet sich ein Horizontal Slider zur Einstellung der Displayhelligkeit. Die Vorschau sieht wie folgt aus:

Seite 2:
Auf der linken Seite befindet sich eine Schaltfläche zum Wechseln auf die vorherige Seite und auf der rechten Seite eine Schaltfläche zum Wechseln auf die nächste Seite. Zusätzlich stehen im unteren Bereich drei Schaltflächen zur Verfügung, mit denen kurze, mittlere und lange Signaltöne erzeugt werden können.

Seite 3:
Auf der linken Seite befindet sich eine Schaltfläche zum Wechseln auf die vorherige Seite. Ein Potentiometer dient zur Simulation eines analogen Eingangssignals. Die erfassten Werte werden über das Graph widget dargestellt.

Da in diesem Beispiel ein RS485-Erweiterungsboard verwendet wird, muss vor Beginn der Programmierung die erforderliche Bibliothek über den Library Manager installiert werden.

Erstellen der Seiteninhalte
Zunächst werden die Seiteninhalte mit GUI Builder erstellt. Texte können direkt in das Text widget der jeweiligen Seite eingegeben werden und müssen daher nicht zusätzlich im Programm definiert werden.

Die detaillierte Konfiguration der widgets ist in der folgenden Tabelle dargestellt:

Je nach verfügbarer Hardware kann ein geeignetes Smart Display Modell ausgewählt und die Benutzeroberfläche mit unterschiedlichen widgets gestaltet werden. Wichtig ist lediglich, dass die widget ID und der type auf jeder Seite mit den im Programm verwendeten Definitionen übereinstimmen. Um die Umsetzung möglichst einfach zu halten, empfiehlt es sich, die Seiteninhalte anhand der in dieser Tabelle angegebenen widget IDs und types zu erstellen.
Im nächsten Schritt folgt die Programmierung.
Erläuterung des Beispielprogramms
(1) Include-Dateien
Nach dem Herunterladen der ModbusMaster-Bibliothek muss diese über die #include-Direktive eingebunden werden:

Zur einfacheren Verwaltung wurden außerdem einige Smart Display-bezogene Konstanten in der Datei SmartDisplayModbus.h zusammengefasst. Diese Datei muss ebenfalls eingebunden werden.
(2) Umrechnung der Widget ID in eine Registeradresse
Im vorherigen Artikel wurde bereits erläutert, wie eine widget ID in die entsprechende Registeradresse umgerechnet wird:

Diese Tabelle wurde ursprünglich für eine maximale Anzahl von 10 widgets pro Seite erstellt. Mittlerweile können pro Seite bis zu 64 widgets verwendet werden. Für die ersten 10 widgets ist die Tabelle weiterhin gültig. Ab dem 11. widget muss jedoch ein neuer Registerbereich verwendet werden. Zu diesem Zweck wurden die folgenden Makros definiert:

Zur einfacheren Verwendung wird eine Funktion definiert, die Lese- und Schreibzugriffe anhand der widget ID ermöglicht und die Umrechnung in die entsprechende Registeradresse innerhalb der Funktion durchführt.

(3) Widget ID und GUI-Design
Bei der Erstellung der Seiteninhalte mit GUI Builder wurde bereits darauf hingewiesen, dass die widget IDs konsistent bleiben müssen. Daher werden die widget IDs im Programm als Konstanten definiert:

Diese Konstanten erleichtern die Zuordnung der einzelnen widget-Typen zur tatsächlichen Seitengestaltung. Wird das GUI-Design später geändert, sollten auch diese Werte überprüft und entsprechend abgeglichen werden, um Fehler zu vermeiden.
(4) Buzzer
Zur Ansteuerung des Buzzers verwendet die Firmware einen Zähler, um eine definierte Verzögerung zu erzeugen, und gibt anschließend ein 0/1-Signal an den Buzzer aus. Weitere Zähler steuern die Ein- und Ausschaltzeiten des Signals. Zur Erzeugung eines Signaltons müssen insgesamt vier Register beschrieben werden:

Zur Einstellung der Signaldauer wird eine Funktion verwendet, der SMARTDISPLAY_BUZZER_HIGH als Parameter übergeben wird. Ein entsprechendes Beispiel ist nachfolgend dargestellt:

Der Buzzer wird nur dann aktiviert, wenn sich der Wert von SMARTDISPLAY_BUZZER_ACTIVE vom zuvor geschriebenen Wert unterscheidet. Deshalb wird der Wert innerhalb der Funktion als static gespeichert und vor jedem erneuten Aufruf invertiert.
In der Seitenverarbeitungsfunktion können anschließend abhängig vom jeweiligen Zustand der Schaltflächen Signaltöne mit unterschiedlicher Dauer erzeugt werden:

(5) Seitenwechsel
Vor einem Seitenwechsel muss zunächst in den CONFIG_MODE gewechselt werden. Nachdem die neue Seite eingestellt wurde, wird wieder in den DISPLAY_MODE gewechselt. Beim Seitenwechsel lädt das Smart Display alle widgets der neuen Seite neu. Um Lese- oder Schreibzugriffe zu vermeiden, bevor dieser Ladevorgang abgeschlossen ist, wird üblicherweise eine kurze Verzögerung eingefügt. Die erforderliche Verzögerungszeit hängt von der Komplexität der jeweiligen Seite ab. Falls nach einem Seitenwechsel ein ungewöhnliches Verhalten auftritt, kann die Verzögerungszeit erhöht werden, um zu prüfen, ob sich das Problem dadurch beheben lässt.

Nach einem Seitenwechsel werden die widgets der neuen Seite zunächst mit ihren Standardwerten geladen. Daher wird ein Ablauf implementiert, der den Seitenwechsel steuert, die widget-Werte speichert und diese bei Bedarf wiederherstellt.
Zunächst wird ein Funktionszeiger für die Seitenverarbeitung definiert. Dadurch lässt sich eine Seitentabelle erstellen und jeder Seite eine entsprechende Verarbeitungsfunktion zuweisen:

Anschließend wird die Seitentabelle erstellt:

Die Konstante MAX_WIDGETS_PER_PAGE legt fest, für wie viele widgets pro Seite die Werte gespeichert werden. Werden später weitere widgets hinzugefügt, muss dieser Wert entsprechend angepasst werden.
Die eigentliche Verarbeitung des Seitenwechsels und der widgets erfolgt anschließend in der Funktion loop():

Wenn die aktuelle Seite _curPage nicht mit der nächsten Seite _nextPage übereinstimmt, wird das Flag newPage auf true gesetzt. Dadurch erkennt die Seitenverarbeitungsfunktion, dass eine Initialisierung erforderlich ist. Als Beispiel betrachten wir Page0:

Ist der Parameter init auf true gesetzt, wurde die Seite gerade neu aufgerufen. In diesem Fall wird WIDGET_P0_BUTTON_NEXT auf 0 gesetzt. Beim Wechsel auf eine neue Seite hat die Schaltfläche zunächst den Standardwert 0. Der entsprechende Befehl – in diesem Beispiel der Wechsel zu PAGE_1 – soll nur ausgeführt werden, wenn die Schaltfläche gedrückt wird und sich ihr Wert von 0 auf 1 ändert. Deshalb muss der gespeicherte Wert wieder auf 0 gesetzt werden, da er durch eine vorherige Betätigung bereits auf 1 gesetzt worden sein kann.
Auch das Slider widget wird beim Laden zunächst mit dem Wert 0 initialisiert. Wurde die Helligkeit zuvor bereits geändert, muss daher der aktuelle Helligkeitswert _brightness wiederhergestellt werden.
In allen anderen Fällen (init ist false) werden die widgets abhängig davon verarbeitet, ob sich ihre Werte geändert haben. Abschließend werden die Werte aller widgets gespeichert, damit sie beim nächsten Verarbeitungszyklus als Referenz zur Verfügung stehen.
Zum Setzen der nächsten Seite _nextPage wird die folgende Funktion verwendet:

Mit Ausnahme des ersten Aufrufs überprüft die Seitenverarbeitungsfunktion bei jeder Ausführung die widget-Werte. Erkennt sie, dass sich der Wert einer Schaltfläche von 0 auf 1 geändert hat, wird die folgende Funktion aufgerufen, um einen Seitenwechsel anzufordern:

(6) Graph Widget
Das Graph widget stellt die geschriebenen Werte grafisch dar. In diesem Beispiel wird ein an A0 angeschlossenes Potentiometer verwendet, um ein analoges Eingangssignal zu simulieren. Die Werte werden anschließend über das Graph widget dargestellt. Da der analoge Eingangsbereich des Arduino von 0 bis 1023 reicht, das Graph widget jedoch Werte von 0 bis 100 verarbeitet, muss der Wertebereich mit der Funktion map() entsprechend angepasst werden.
Das Graph widget aktualisiert die Anzeige nur, wenn sich der Wert ändert. Soll die Darstellung in regelmäßigen Abständen aktualisiert werden, kann der Wert periodisch überprüft werden. Ist der neu gelesene Wert mit dem vorherigen Wert identisch, wird er geringfügig verändert, indem 1 addiert oder subtrahiert wird. Auf diese Weise lässt sich eine kontinuierlich wirkende, regelmäßige Aktualisierung der Graph-Anzeige realisieren.

Fazit
Damit ist die Erläuterung des Beispielprogramms abgeschlossen. Wir hoffen, dass dieses Beispiel zu einem besseren Verständnis der Entwicklung mit Smart Display beiträgt. Es kann außerdem als Ausgangsbasis für zukünftige Projekte verwendet werden und dabei helfen, den Entwicklungsaufwand zu reduzieren. Das vollständige Programm steht unter folgendem Link zum Download zur Verfügung: Modbus-MultiPageOperation on GitHub.
Falls Sie mit RS485 / Modbus noch nicht vertraut sind, finden Sie weitere grundlegende Informationen im vorherigen technischen Artikel: Smart Display RS485 Introduction.