Licht messen mit dem Odroid-Go, einem Fotowiderstand und einem Spannungsteiler

Einleitung

Der Odroid-Go ist ein kleiner Tausendsassa, den meisten vermutlich durch seine Fähigkeiten zur Emulation alter Spiele-Konsolen oder Homecomputer bekannt. In meinem Artikel bei Golem stelle ich ihn ausführlich vor.

Ich persönlich finde ihn aber auch wegen seiner Möglichkeiten darauf zu programmieren und dabei sogar, über seinen Expansion Port, Kontakt mit der Außenwelt aufnehmen zu können, toll!

Mit diesem Tutorial möchte ich Euch einen Einstieg in die wunderbare Welt der Informatik geben, verbunden mit ein klein wenig Elektronik-Experimentierkasten Feeling!

Das Ziel des Tutorials ist es, den Odroid-Go an unserer unmittelbaren Umgebung teilhaben zu lassen, indem wir mit Hilfe eines Fotowiderstandes die Umgebungshelligkeit messen und anschließend mit dem Odroid-Go weiterverarbeiten.

Motivation

Viele von uns Nerds können programmieren. Und solange wir uns in der reinen Welt des Digitalen bewegen, finden wir uns auch ziemlich gut zurecht. Was aber oft auf der Strecke bleibt, ist das Verständnis für die Möglichkeiten, die analoge Außenwelt in das digitale Geschehen einzubinden. Denn dies ist weniger schwer, als wir im ersten Moment denken.

Arduino?

Den Arduino gibt es so eigentlich nicht (mehr). Mittlerweile ist es ein Überbegriff für eine Reihe von Kleinstcomputern ohne Tastatur und Bildschirm, der meines Wissens nach ursprünglich dazu entwickelt wurde, eher technologiefremden Menschen wie Künstlern und Designern eine Möglichkeit in die Hand zu geben, Computersteuerungen auf simple Art und Weise in ihre Projekte zu integrieren. Man nennt solche Computer auch Mikrocontroller.

Im Rahmen dieses Gedankens wurde eine IDE (Integrierte Entwicklungsumgebung) entwickelt – das ist eine Art Textverarbeitung für das Schreiben von Computerprogrammen – mit der Code für diesen kleinen Computer geschrieben werden kann.

ESP32

Im Laufe der Jahre wurde der ursprüngliche Kleinstcomputer immer wieder weiterentwickelt und auch Firmen, die nichts mit den ursprünglichen Erfindern zu tun hatten, entwickelten kompatible Mikrocontroller. Die aktuelle Spitze dieser Entwicklung sind für mich Geräte auf der Basis des ESP32 Mikrocontrollers von espressiv.

Er bietet sehr viele interessante Fähigkeiten, auf die ich im Rahmen dieses Textes nicht weiter eingehen möchte. Jedenfalls ist er im Vergleich zum ersten Arduino und auch zu vielen älteren Computern sehr schnell (240 Mhz) und hat sogar zwei  Kerne. Was bedeutet, dass er zwei Dinge gleichzeitig tun kann. Und er besitzt sogar die Fähigkeit über sein eingebautes WiFi ins Internet gehen zu können!

Wie auch immer, der ESP32 ist die Basis des Odroid-Go! Er versetzt den Odroid-Go in die Lage, alte Computer nachzumachen und damit eine ‘Retro-Konsole’ sein zu können!

Also, wenn ihr Euch den Odroid-Go für dieses Tutorial gekauft habt und dann feststellt, dass Euch das Programmieren und Experimentieren doch keine Freude bereitet, so könnt Ihr ihn immer noch als Retro-Konsole verwenden! Das ist doch etwas, oder?

Was macht den Odroid-Go zum idealen Einstiegs-Arduino?

Neben dem wirklich starken ESP32 und seiner Retro-Konsolen-Fähigkeit, sind dies die zusätzlich eingebauten Bauteile, wie:

  • Display – Ihr könnt darauf Daten ausgeben.
  • Lautsprecher – Ihr könnt Geräusche erzeugen oder einen Audio-Stream aus dem Internet abspielen.
  • Tasten – Ihr könnt Eure Programme steuern.
  • Eingebauter Akku, Ihr könnt den Odroid-Go vom Entwicklungscomputer trennen, ihn herum tragen und Eure neuesten Programmkreationen Euren Freunden zeigen.
  • microSD-Karten Slot, Ihr könnt umfangreiche Datenmengen speichern.
  • Expansion-Port, das coolste überhaupt, denn Ihr könnt den Odroid-Go mit einem Steckbrett verbinden und elektronische Schaltungen entwickeln, die mit einem Computer zusammen arbeiten!

Bis auf den Expansion-Port findet Ihr diese Dinge normalerweise nicht bei einem Arduino. In diesem kleinen Tutorial kann ich Euch nicht alles zeigen, aber ich zeige Euch, wie man:

  • auf dem Display malen kann,
  • die Tasten abfragt und
  • den Expansion-Port nutzen kann.

Mit diesem Grundwissen ausgerüstet, sollte es Euch möglich sein, viele weitere Experimente vorzunehmen und Euch mit Hilfe des Internets den Rest beizubringen. 🙂

Allerlei Vorbereitungen

Hardware

Für das Tutorial benötigt Ihr:

Ich habe Euch einmal diese 3 Artikel bei Pollin heraus gesucht, da ich meinen Odroid-Go auch von dort habe. Wer schon einen Odroid-Go besitzt, der tut sich wahrscheinlich mit den 5,50 Euro Versandkosten schwer, wenn er die beiden Bauteile für lediglich 1,02 Euro kaufen könnte. Ihr könnt versuchen sie persönlich bei einem örtlichen Elektronik-Händler zu kaufen. Bei uns in Deutschland gibt es ja in verschiedenen Städten die Firma Conrad. Vielleicht kennt Ihr auch eine/einen ElektronikbastlerIn, die haben solche Bauteile manchmal in ihrer Krabbelkiste.

Der Widerstandswert muss nicht unbedingt 2.2 kΩ sein. Auch ähnliche Werte werden vermutlich gut funktionieren.

Software

Um den Odroid-Go programmieren zu können, sind ein paar Vorbereitungen notwendig. Da es dazu mittlerweile viele Anleitungen im Internet gibt, habe ich mir eigene Erklärungen dazu gespart und verweise Euch hiermit an passende Stellen im Internet. Bitte befolgt die dort beschriebenen Anleitungen. Anschließend solltet Ihr eine für die Programmierung des Odroid-Go passende Umgebung haben.

Installation der Arduino-IDE

Oben verglich ich die Arduino-IDE mit einer Textverarbeitung. Das stimmt nur insoweit, dass es die IDE unter anderem erlaubt, mit Hilfe eines eingebauten Editors, Programmcode einzugeben und zu editieren. Doch sie kann bei weitem mehr. Sie ist  Dreh- und Angelpunkt aller Tätigkeiten rund um die Programmierung Arduino-kompatibler Mikrocontroller. Mit ihr können Bibliotheken und die Unterstützung für die verschiedenen Mikrocontroller installiert werden. Sie startet die Kompilier- und Link-Vorgänge und uploadet bei Erfolg das fertige Programm auf den angeschlossenen Mikrocontroller.

Installation der Arduino-IDE

Installation des USB-Treibers für ESP32 Mikrocontroller

Irgendwie muss man ja mit dem PC Kontakt zum Odroid-Go aufnehmen. Das geschieht, wie so oft, mit einem passenden Treiber für eine USB-Schnittstelle. Dieser wird hier eingerichtet.

Installation des USB-Treibers für ESP32 Mikrocontroller

Einrichten der Unterstützung von ESP32 Mikrocontrollern

Damit die Arduino-IDE weiß, wie man mit einem ESP32 Mikrocontroller umgehen muss, benötigt sie einiges an Informationen, sowie einen sogenannten Core. Das ist ein Software-API (Application Programming Interface), also eine Anwendungs­programmier­schnittstelle. Unter anderem sind hier auch in Python geschriebene Skripte enthalten, die das Software-API aktuell halten können und für den Upload verantwortlich sind.

Einrichten der Unterstützung von ESP32 Mikrocontrollern

Nachdem die Unterstützung eingerichtet wurde, muss man der Arduino IDE noch mitteilen, dass bei Kompiler- und Upload-Vorgängen die Unterstützung für den ESP32 verwendet werden soll. Dies geschieht durch die Auswahl von ‘ESP32 Dev Module’ über den Menüpunkt ‘Werkzeuge->Board’ der Arduino IDE. Jetzt sind wir soweit, dass ein eingegebener Code auch tatsächlich für den Odroid-Go kompiliert und auf einen angeschlossenen Odroid-Go hochgeladen wird.

Bibliotheken

Obwohl es für den Odroid-Go eine spezielle Bibliothek gibt, habe ich mich entschlossen die Programmierung mit anderen Bibliotheken vorzunehmen, da ich die dabei erworbenen Kenntnisse auch mit anderen Mikrocontrollern verwenden kann.

Darum verwende ich für die Programmierung des Displays Bodmers ‘TFT_eSPI’:

https://github.com/Bodmer/TFT_eSPI

Sie lässt sich über die ‘Bibliothek einbinden->Bibliotheken verwalten…’ der Arduino-IDE ganz leicht einrichten.

Die Bibliothek ‘TFT_eSPI’ unterstützt viele verschiedene Displays. Darum müssen wir für den Odroid Go noch ein paar Vorbereitungen treffen, damit die Bibliothek auch weiß, wie sie ihn ansprechen muss. Dazu ladet Euch die Datei ‘Setup_Odroid_Go.h‘ von meinem Server herunter und kopiert sie nach:

C:\Users\USER\Documents\Arduino\libraries\TFT_eSPI\User_Setups

auf euren PC. Die Datei beschreibt die Pins vom Odroid Go, über die das Display angesteuert wird. Damit ‘TFT_eSPI’ weiß, das es jetzt diese Datei benutzen soll, müsst Ihr in der Datei:

C:\Users\Thomas Ell\Documents\Arduino\libraries\TFT_eSPI\User_Setup_Select.h

die Zeile:

#include <User_Setups/Setup_Odroid_Go.h>

einfügen und vor die Zeile:

#include <User_Setup.h>

Kommentarzeichen ‘//’ einfügen.

Für die Abfrage der Tasten verwende ich diese Bibliothek:

https://github.com/LennartHennigs/Button2

Sie muss als ZIP-Datei downgeloadet und in das Verzeichnis ‘C:\Users\Thomas\Documents\Arduino\libraries’ kopiert werden. Anschließend muss die Arduino IDE neu gestartet werden.

Ohne Löten

Wer öfters einmal etwas ausprobieren will, macht das üblicherweise mit einem Steckbrett, wie diesem Labor-Steckboard von Pollin. Für die Verbindung vom Odroid-Go zum Steckbrett und von da zu den einzelnen Bauteilen besorgt man sich ein Steckbrücken Sortiment.

Aber es geht auch einfacher. Für unser Experiment stecken wir die Bauteile einfach in den Expansion-Port. Betrachtet man die Rückseite des Odroid-Go, kann man durch das transparente Gehäuse den Expansion-Port am oberen Rand sehen und direkt links und rechts davon, die Ziffern 1 und 10. Die einzelnen Steckkontakte sind durchnummeriert und dies ermöglicht es mir genau anzugeben, in welche Kontakte die Beinchen der Bauteile gesteckt werden müssen.

Der Fotowiderstand muss zwischen die Kontakte 1 und 4 gesteckt werden und der Widerstand zwischen die Kontakte 4 und 6. Die Beinchen der Bauteile sind ziemlich dünn, speziell die vom Fotowiderstand. Da sich das eine Beinchen aber den Kontakt 4 mit dem Widerstand teilen muss, passt es hier ganz gut. Am Kontakt 1 kann es helfen, wenn man das Beinchen faltet und es dann versucht in den Kontakt zu stecken. Jetzt kann das Reinstecken wiederum etwas schwergängig werden und man hilft sich am besten mit einer kleinen Zange oder einer Pinzette. Sind die beiden Bauteile korrekt in die Kontakte gesteckt, war es das schon mit der Hardware!

Theorie

Ein Computer, und damit auch der Odroid-Go, ist ein elektronisches Gerät. Um mit ihm zu kommunizieren, muss man sich in die Welt der Elektronik begeben und die hat ziemlich viel mit elektrischer Spannung zu tun. Die meisten von Euch wissen vermutlich, dass ein Computer eigentlich nur bis 2 zählen kann. Was bedeutet, das er nur 0 und 1 versteht. O und 1 kann man durch elektrische Spannung ausdrücken: Der Wert 0 kann bedeuten, das keine Spannung da ist und der Wert 1 kann bedeuten, dass eine Spannung vorhanden ist. Das ist etwas grob. Meist möchte man wissen, wie hoch eine Spannung genau ist. Hier kommt die Lösung.

Jeder, der schon einmal mit einem Taschenrechner gerechnet hat, der weiß, das es möglich ist mit einem elektronischen Gerät mehr, als 0 und 1, zu unterscheiden. Also wie machen die das, die Taschenrechner und die Computer?

Sie haben halt mehr als nur eine Stelle mit 0 und 1. Reiht man mehrere Stellen nebeneinander an, also zum Beispiel zwei Positionen, die jeweils 0 und 1 sein können, und gibt jeder Position eine Wertigkeit, so kann man schon bis 4 zählen:

  • 00 = 0
  • 01 = 1
  • 10 = 2
  • 11 = 3

Genau wie in unserem geläufigen Zehnersystem! Dort ist jede weitere Stelle, die links dazu kommt, 10 mal so viel wert, wie die rechts neben ihr stehende. Und beim ’01er’-System ist jede links dazu kommende Stelle genau doppelt so viel wert. Man nennt das auch Dualsystem!

Mit 12 Stellen kann man bis 4096 zählen! Das ist genau die Auflösung, die der ESP32 in seinen eingebauten Analog-Digital-Wandlern verwendet. Ein Analog-Digital-Wandler, kurz ADC genannt (kommt vom englischen analog-to-digital converter), dient dazu, eine analoge Größe, also zum Beispiel ein Gewicht, eine Länge, eine Temperatur, die Luftfeuchtigkeit, den Alkoholgehalt oder die Helligkeit in eine Folge von Nullen und Einsen zu verwandeln, so dass ein Computer sie verstehen kann. Damit der Analog-Digital-Wandler arbeiten kann, benötigt er ein Hilfsmittel, welches die analoge Größe in eine Spannung verwandelt. Denn der Wandler selbst kann keine der analogen Größen direkt verarbeiten! Das Hilfsmittel nennt sich Sensor und ist in unserem Fall ein Fotowiderstand.

Der Fotowiderstand verändert, wie sein Name schon andeutet, seinen elektrischen Widerstand in Abhängigkeit von der Helligkeit des auf ihn fallenden Lichtes. Um nun eine für den Analog-Digital-Wandler passende Spannung zu erzeugen, muss er in Kombination mit einem normalen Widerstand so mit dem Odroid-Go verbunden werden, dass eine durch einfallendes Licht veränderte Spannung am Eingang des Analog-Digital-Wandlers anliegt. Da der ADC nur maximal eine Spannung von 3.3 Volt verarbeiten kann, gilt es, den vom Fotowiderstand gegebenen Widerstandsbereich möglichst komplett in eine Spannung zwischen 0 und 3.3 Volt aufzuteilen. Ganz exakt muss man dabei nicht sein und auch der ADC selbst verrichtet seine Arbeit nicht perfekt, so dass es besser ist, ein wenig über 0 und ein wenig unter 3.3 Volt zu bleiben.

Auf keinen Fall funktioniert das, wenn man  einfach den Fotowiederstand mit einem Kontakt in den Eingang des ADC steckt und das andere Ende auf GND (0 Volt) oder VCC (3.3 Volt). Zwar würde dann (wenn VCC) vermutlich ein kleiner Strom durch den Fotowiderstand fließen, aber die Spannung bleibt gleich, egal wie viel Licht auf den Fotowiderstand fällt.

Der Trick ist es, den Fotowiderstand elektronisch so mit dem Odroid-Go zu verbinden, dass er in Abhängigkeit vom einfallenden Licht eine unterschiedliche Spannung am ADC Eingang erzeugt. Wer in Elektronik etwas bewandert ist, der weiß, dass man das mit einem Spannungsteiler machen kann.

Spannungsteiler

Einen Spannungsteiler kann man sich ähnlich der Gabelung eines Flusses vorstellen. Von einer Seite (oben) kommt viel Wasser und durch die Gabelung teilt sich das Wasser in zwei Teilströme auf. Ist der eine Abzweig größer als der andere, fließt dort natürlich auch mehr Wasser durch. Wenn wir uns jetzt vorstellen, dass einer der beiden Abzweige seine Größe dynamisch verändert, so entspricht dies dem Verhalten eines Spannungsteilers, bei dem der eine Abzweig der Fotowiderstand ist.

Beim Spannungsteiler werden zwei Widerstände mit jeweils einem ihrer Kontakte miteinander verbunden. Von den jeweils anderen Enden wird eines mit Masse (GND) bzw. 0 Volt und das andere mit einem Spannungspegel deutlich ungleich 0 Volt verbunden. Dieser Spannungspegel, nehmen wir einmal an, dass er 10 Volt beträgt, teilt sich nun im Verhältnis der beiden Widerstände auf. Angenommen, der eine Widerstand hat 4 kOhm, der andere 6 kOhm und letzterer ist der, der mit seinem einen Ende an 10 Volt hängt, so kann man mit einem Messgerät, an der Verbindungsstelle der beiden Widerstände und dem 10 Volt Anschluss, 6 Volt messen, und wenn man mit dem Messgerät zwischen Verbindungsstelle und 0 Volt misst, 4 Volt messen. Die Spannungen verhalten sich proportional zu den Widerstandswerten.

Mathematik

Wenn nun aber einer der Widerstände dynamisch ist, so ändert sich an diesem die Spannung im gleichen Verhältnis, wie sich das Widerstandsverhältnis ändert. Die Formel ist:

Spannung am Teilwiderstand = Gesamtspannung / Gesamtwiderstand * Teilwiderstand

Eine Beispielrechnung:

Angenommen, der 6 kOhm Widerstand verändert seinen Wert zu 3 kOhm, dann ist die daran abfallende Spannung:

Spannung am 3 kOhm Widerstand: 10 Volt / (4 + 3 kOhm = 7 kOhm) * 3 kOhm =

4.29 Volt!

Spannung am 4 kOhm Widerstand: 10 Volt / (4 + 3 kOhm = 7 kOhm) * 4 kOhm =

5.71 Volt!

Leider stehen uns beim ESP32 nur 3.3 Volt zum Aufteilen zur Verfügung. Wenn also der für die Aufteilung notwendige Widerstand mit dem festen Wert schon einen Teil der Spannung wegnimmt, so kann der verbliebene Rest nicht mehr den ganzen für den ADC in Frage kommenden Bereich abdecken. Die Kunst ist es jetzt, den Festwiderstand so zu wählen, dass man für den zu messenden Bereich des Fotowiderstands möglichst viel des in Frage kommenden Bereichs des ADC ausnutzt. Mit einem Widerstandsmessgerät habe ich den Fotowiderstand vermessen. Folgende Werte konnte ich dabei ermitteln:

Fotowiderstand minimal (hell) und maximal (dunkel): 100 Ohm – 1.2 MOhm

Übliche Werte bei uns im Haus: 100 Ohm – 50 kOhm

Hat man das mit dem Spannungsteiler einigermaßen verstanden, so ist klar, dass, wenn wir für den Festwiderstand einen Wert von 50 kOhm nehmen, für den Bereich von 100 Ohm bis 50 kOhm, lediglich knapp 1.6 Volt für den ADC zur Verfügung stehen, denn die anderen 1.6 Volt fallen ja am Festwiderstand ab. Von den 4096 Stufen des ADCs haben wir dann maximal nur noch 2048 übrig! Das funktioniert zwar, ist aber ungeschickt.

Ein bisschen schwieriger zum Nachvollziehen sind extremere Werte des Festwiderstands wie 100 Ohm und 1 MOhm. Darum ein paar Beispielrechnungen:

Festwiderstand: Bei 100 Ohm fällt am Lichtwiderstand eine Spannung zwischen 1.65 und 3.29 Volt ab. Das ist ein Bereich von 1,64 Volt und deckt immerhin die Hälfte des ADC ab. Aber eigentlich wäre es besser, wenn der variable Spannungsbereich größer wäre.

Festwiderstand: Bei 1 MOhm fällt am Lichtwiderstand eine Spannung zwischen 0.0003 und 0.16 Volt ab. Hier ist der noch übrig bleibende Bereich von 0.16 Volt nicht geeignet einen nennenswerten Bereich des ADC abzudecken. Laut randomnerdstutorials ist er auch noch in einem Bereich des ADC vom ESP32, der gar nicht mehr unterschieden werden kann!

Durch die beiden Beispielrechnungen wird deutlich, dass der Wert für den idealen Festwiderstand irgendwo zwischen 100 Ohm und 1 MOhm liegen muss! Doch wie bekommt man nun den besten Wert für den Festwiderstand für einen ganz bestimmten Bereich? Nehmen wir an, der Fotowiderstand bewegt sich im Bereich von 100 Ohm bis 50 kOhm und wir wollen den optimalen Widerstandswert für den Festwiderstand. Wie können wir den Wert ermitteln?

Noch mehr Mathematik

Bei der Lösung dieses Problems ist die Mathematik ein weiteres Mal unser Freund. Erinnerungen an  meine Schulzeit erzählen von einer mathematischen Lösungsmöglichkeit für Optimierungsprobleme. Dabei muss eine Funktion gefunden werden, die das Problem mit genau einer Variablen beschreibt. Davon kann man dann durch Differenzieren die erste Ableitung bilden. Diese setzt man gleich 0 und löst sie nach X auf…

Um uns dem Problem zu nähern, spielen wir einfach einmal mit ein paar willkürlich ausgedachten Werten herum. Nehmen wir an, der gesuchte Wert wäre 22000 Ohm, dann wäre die Spannung bei komplett dunkel (50 kOhm):

3.3/(22000 + 100)*100 = 2.29 Volt

und für maximal hell (100 Ohm):

3.3/(22000 + 50000)*50000 = 0.01 Volt

Das ist schon einmal gar nicht so schlecht. 2.29 – 0.01 = 2.28 Volt! Der Bereich zwischen 0  und 3.3 Volt wird schon ziemlich gut abgedeckt. Geht es noch besser?

Wir können die beiden Rechnungen in einer Funktion zusammen fassen:

3.3/(22000 + 50000)*50000 – 3.3/(22000 + 100)*100

Damit könnten wir direkt den überdeckten Spannungsbereich berechnen. Wenn wir jetzt die 22000 durch X ersetzen, so haben wir folgende Funktion:

3.3/(X + 50000)*50000 – 3.3/(X + 100)*100

Wir könnten jetzt sagen, dass jemand X so wählen soll, dass der von der Funktion zurückgegebene Wert maximal groß ist, denn dann haben wir die beste Abdeckung für den ADC-Eingang!

Dieser ‘Jemand’ können auch wir selbst sein. Bilden wir von dieser Funktion die erste mathematische Ableitung und damit die Steigung der Funktion zu jedem X, setzen diese gleich 0 und stellen dann nach X um, so haben wir den gesuchten Wert! Die erste Ableitung ist:

330/(x + 100)^2 – 165000/(x + 50000)^2

Gleich 0 setzen:

330/(x + 100)^2 – 165000/(x + 50000)^2 = 0

Wenn wir das nach X umstellen, so bekommen wir als Ergebnis die Werte 2236 und -2236 Ohm! Uns interessiert hier nur der positive Wert. Damit ist unser gesuchter Wert für den Festwiderstand:

2236 Ohm oder 2.2 kOhm!

Eine Testrechnung ergibt jetzt für den abgedeckten Bereich:

3.3/(2236 + 100)*100 = 0.14 Volt

3.3/(2236 + 50000)*50000 = 3.15 Volt

Und damit einen Bereich von 3.15 – 0.14 = 3.01 Volt!

Es ist aber auch zu erkennen, das alle Werte grob um 2,2 kOhm gut funktionieren würden. Darum nehmt einfach den Wert, der einigermaßen passt und an den Ihr am leichtesten heran kommt. Trotzdem wollte ich Euch einmal zeigen, wie man so einen Wert theoretisch exakt ermitteln kann. Übrigens rechne ich solche Dinge alle mit einem alten TI-89 aus. Der hat aber auch eine Funktion mit der Bezeichnung ‘fMax’, die bei Eingabe der entwickelten Funktion direkt den Widerstandswert ausrechnen kann. Wer keinen TI-89 besitzt, der hat vielleicht Interesse an einem Online-Service, mit dem man symbolisch rechnen kann. Auf der recht nüchternen Seite mathe online findet sich ein Online-Zugang zu Mathematica, mit dem es möglich ist, symbolische Ableitungen und vieles mehr, durchzuführen.

Wer so ein Tool gerne auf seinem PC oder Notebook haben möchte, der kann sich einmal Maxima ansehen.

Hier gibt es übrigens eine Möglichkeit Formel-Ausdrücke in MathML umzuwandeln.

Nach diesem nun doch sehr langen Ausflug in die Welt der Elektronik geht es jetzt wieder zurück zur Software.

Ausflug durch den Code

#include <TFT_eSPI.h>

Mit dieser Anweisung wird die Bibliothek eingebunden.

#define SCREENWIDTH 320
#define SCREENHEIGHT 240

Auflösung des Displays

#define BUTTON_1 32
#define BUTTON_2 33
#define SENSORPIN 15

Die verwendeten Pins werden mit Namen versehen, so dass das Programm leichter lesbar ist. Die beiden Buttons sind fest innerhalb des Gehäuses des Odroid Go verdrahtet, Pin 15 ist Pin 4 des expansion Port.

#define DRAWDELAY 10

Die Zeit, di vergehen soll bis der jeweils nächste Punkt gezeichnet werden soll.

int sensorValue = 0;
int range = 1;
int lastDraw = 0;
int xPos = 0;

Die im Programm verwendeten globalen Variablen.

TFT_eSPI tft = TFT_eSPI();

Initialisierung der Grafik-Bibliothek.

void setup(void) {
Serial.begin(115200);

Initialisierung der seriellen Schnittstelle, so dass man bei Bedarf leicht Ausgaben in den Code schreiben kann, die dann zum beispiel helfen Fehler zu suchen.

ledcSetup(1, 1000, 8); // PWM channel 1, 1000Hz, 8 bit
ledcAttachPin(TFT_BL, 1); // attach to TFT backlight
ledcWrite(1, 200); // set backlight

Dieses Stück Code ist für unser Programm nicht zwingend notwendig. Aber da der Odroid Go die Möglichkeit bietet, die Helligkeit der Hintergrundbeleuchtung einzustellen und die Helligkeit standardmäßig etwas gering ist, hab ich sie höher gestellt.

Zuerst wählt man einen PWM Kanal aus, gibt an mit welcher Frequenz das PWM-Signal erzeugt werden soll und mit welcher Auflösung das Verhältnis zwischen High und Low angegeben werden soll. Anschließend wird der Kanal einem bestimmten Pin des ESP32 zugewiesen und zum Schluss wird auf dem gewählten Kanal die Länge des High-Signals bestimmt. Um so höher der Wert, um so heller das Display.

tft.init();
tft.setRotation(3);

Das Display wird initialisiert und es wird angegeben von welcher Seite aus man das Display betrachtet. Das lässt sich am besten durch Ausprobieren ermitteln.

if (millis() - lastDraw > DRAWDELAY) {

Anfänger warten meistens mit dem Befehl ‘delay (milliseconds)’. Das hat aber den Nachteil, das in dieser Zeit das eigene Programm nichts anderes tun kann. Darum ist es schlauer, wenn man die zu verzögernden Befehle mit einer Abfrage umgibt, die prüft ob schon genügend Zeit vergangen ist!

sensorValue = analogRead(SENSORPIN);

Hier wird nun endlich das gemacht, auf das wir bis jetzt im ganzen Artikel hingearbeitet haben. Wir lesen auf dem ADC-Eingang die Spannung am Abzweig des Spannungsteilers ein!

switch (range) {
  case 1 : {
    float displayFactor = 4096 / SCREENHEIGHT;
    tft.drawPixel(xPos, SCREENHEIGHT - sensorValue / displayFactor + 1, TFT_WHITE);
    break;
  }
  case 2 : {
    sensorValue = floor(sensorValue - 2048);
    float displayFactor = 2048 / SCREENHEIGHT;
    tft.drawPixel(xPos, SCREENHEIGHT - sensorValue / displayFactor + 1, TFT_WHITE);
    break;
  }
  case 3 : {
   float displayFactor = 2048 / SCREENHEIGHT;
    tft.drawPixel(xPos, SCREENHEIGHT - sensorValue / displayFactor + 1, TFT_WHITE);
    break;
  }
}

Mit dem Code im ‘Switch’-Block realisieren wir so etwas wie Messbereiche. Denn trotz der Auflösung von 4096 Stufen des ADCs haben wir nur 240 Pixel für die Höhe. Wir müssen uns darum überlegen, wie man die Werte des ADCs am besten auf dem Display darstellt. Ich habe mir darum drei ‘Messbereiche’ ausgedacht:

‘case 1’ nimmt den ermittelten Wert und verteilt ihn einfach auf die lediglich 240 Pixel für die Höhe.

Bei ‘case 2’ gehe ich davon aus, dass sich die gemessenen Werte immer in der oberen Hälfte des ADCs befindet. Darum subtrahiere ich 2048, schneide alles unter = ab  und verteile den Rest auf die 240 Pixel für die Höhe.

Und bei ‘case 3’ gehe ich davon aus, dass sich die gemessenen Werte immer in der unteren Hälfte des Displays befinden. Darum verteile ich nur die Hälfte des vom ADC gelieferten Wertes auf das ganze Display.

xPos++;
if (xPos >= SCREENWIDTH) {
xPos = 0;

Die nächste X-Position wird ermittelt.

tft.fillScreen(TFT_BLACK);
tft.setTextSize(3);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(range), 10, 10);

Der gewählte Messbereich wird auf das Display geschrieben.

if (analogRead(BUTTON_1) < 150) {

Anfangs dachte ich die Eingänge der Tasten wären digitale Eingänge. Aber es sind in Wirklichkeit ebenfalls ADCs und darum darf man nicht einfach auf High oder Low testen, sondern muss anhand eines Schwellwertes entscheiden, ob eine Taste gedrückt wird oder nicht.

delay (200);

Das ‘delay’ ist unbedingt notwendig, da sonst das Programm den Messbereich unglaublich schnell weiterschalten würde und man nicht erkennen könnte, welchen man gerade gewählt hat.

lastDraw = millis();

Für die Feststellung der vergangenen Zeit ist es unabdingbar die aktuelle Zeit festzuhalten.

#include <TFT_eSPI.h>

#define SCREENWIDTH 320
#define SCREENHEIGHT 240

#define BUTTON_1 32
#define BUTTON_2 33

#define SENSORPIN 15

#define DRAWDELAY 10

int sensorValue = 0;
int range = 1;
int lastDraw = 0;
int xPos = 0;

TFT_eSPI tft = TFT_eSPI();

void setup(void) {
  Serial.begin(115200);

  // For brightness
  ledcSetup(1, 1000, 8);    // PWM channel 1, 1000Hz, 8 bit
  ledcAttachPin(TFT_BL, 1); // attach to TFT backlight
  ledcWrite(1, 200);        // set backlight

  // Init display
  tft.init();
  tft.setRotation(3);
  tft.fillScreen(TFT_BLACK);
}

void loop() {
  if (millis() - lastDraw > DRAWDELAY) {
    sensorValue = analogRead(SENSORPIN);
    //Serial.println (sensorValue);
    switch (range) {
      case 1 : {
          float displayFactor = 4096 / SCREENHEIGHT;
          tft.drawPixel(xPos, SCREENHEIGHT - sensorValue / displayFactor + 1, TFT_WHITE);
          break;
        }
      case 2 : {
          sensorValue = floor(sensorValue - 2048);
          float displayFactor = 2048 / SCREENHEIGHT;
          tft.drawPixel(xPos, SCREENHEIGHT - sensorValue / displayFactor + 1, TFT_WHITE);
          break;
        }
      case 3 : {
          sensorValue = sensorValue;
          float displayFactor = 2048 / SCREENHEIGHT;
          tft.drawPixel(xPos, SCREENHEIGHT - sensorValue / displayFactor + 1, TFT_WHITE);
          break;
        }
    }
    delay (10);
    xPos++;
    if (xPos >= SCREENWIDTH) {
      xPos = 0;
      tft.fillScreen(TFT_BLACK);
      tft.setTextSize(3);
      tft.setTextColor(TFT_GREEN);
      tft.drawString(String(range), 10, 10);
    }

    if (analogRead(BUTTON_1) < 50) {
      delay (200);
      range++;
      if (range > 3) {
        range = 1;
      }
      tft.fillScreen(TFT_BLACK);
      tft.setTextSize(3);
      tft.setTextColor(TFT_GREEN);
      tft.drawString(String(range), 10, 10);

    }
    if (analogRead(BUTTON_2) < 50) {
      delay (200);
      range--;
      if (range < 1) {
        range = 3;
      }
      tft.fillScreen(TFT_BLACK);
      tft.setTextSize(3);
      tft.setTextColor(TFT_GREEN);
      tft.drawString(String(range), 10, 10);
    }

    lastDraw = millis();
  }
}

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert