Valentinstag - Deine Nähe lässt mein Herz rasen - AZ-Delivery

Am Valentinstag verschenken wir oft Blumen, Kärtchen oder Süßigkeiten. Kleine Aufmerksamkeiten kommen immer irgendwie gut an. Dieses Jahr haben wir uns ein kleines Projekt ausgedacht, für das wir erneut das 8x32 Dot Matrix Display verwenden. Wenn Sie die AZ-Freundin zum Valentinstag nachbauen möchten, schauen Sie gern noch einmal in den Blogbeitrag vom letzten Jahr.

Da der Valentinstag für viele Liebenden eine größere Bedeutung und Liebe auch mit Nähe zu tun hat, nutzen wir in diesem Jahr einen Näherungssensor. Sie kennen das vermutlich: Ihnen kommt eine Person sehr nahe, die ihnen viel bedeutet, dann steigt der Puls und es wird eventuell sehr warm. Das wollen wir mit dem diesjährigen Projekt simulieren. Auf dem Display stellen wir zwei Animationen dar. Diese ändern sich, wenn man sich dem Sensor nähert. Der Puls wird schneller und das rote Licht der LEDs stellt die ansteigende Wärme dar. Los geht’s.

Was wir benötigen

Anzahl Bauteil
1 MAX7219 8x32 4 in 1 Dot Matrix LED Modul
1 Nano V3.0 100% Arduino-kompatibel mit Nano V3 oder
1 ESP8266 D1 Mini oder
1 ESP32 Devkit C V4
1 VL53L0X ToF Abstandssensor
Jumper Kabel
Breadboard
PC mit Arduino IDE und Internetverbindung


Die Wahl des Mikrocontrollers steht Ihnen frei. Der Quellcode funktioniert auf den gängigen UNOs, Nanos, ESP8266, ESP32. Sie müssen lediglich auf das jeweilige Pinout achten. Die Pins, an die der ToF-Sensor und das Matrix-Display angeschlossen werden, sind durch die I²C- bzw. SPI-Schnittstellen vorgegeben.

Hinweis: Das Matrix Display muss mit 5V versorgt werden. Einige ESP32 bieten keinen Ausgang mit dieser Spannung. Sie müssen dann entweder eine externe Spannungsquelle, oder einen anderen Mikrocontroller verwenden.

Schaltpläne

Microkontroller Nano oder UNO:


ESP8266 (hier D1 Mini):


ESP32 (hier Devkit C V4):

 

Der ToF Sensor wird wie folgt angeschlossen:

VL53L0X

Nano / UNO

VIN

5V oder 3.3V

GND

GND

SCL

SCL (A5)

SDA

SDA (A4)

GPIO1

-

XSHUT

-

VL53L0X

ESP8266 D1 Mini

VIN

5V oder 3.3V

GND

GND

SCL

SCL (D1 / GPIO5)

SDA

SDA (D2 / GPIO4)

GPIO1

-

XSHUT

-

VL53L0X

ESP32

VIN

5V oder 3.3V

GND

GND

SCL

SCL (GPIO 22)

SDA

SDA (GPIO 21)

GPIO1

-

XSHUT

-


Das Matrix-Display wird wie folgt angeschlossen, wobei der CS-Pin frei wählbar ist. Es sollte dafür jedoch nicht der SS oder MISO Pin verwendet werden (Info aus dem Quellcode der LEDMatrixDriver Bibliothek):

MAX7219 Matrix Display

Nano / UNO

VCC

5V

GND

GND

DIN

MOSI (D11)

CS

D9

CLK

SCK (D13)

MAX7219 Matrix Display

ESP8266 D1 Mini

VCC

5V

GND

GND

DIN

MOSI (D7, GPIO 13)

CS

D3 (GPIO 0)

CLK

SCK (D5, GPIO 14)

MAX7219 Matrix Display

ESP32

VCC

5V

GND

GND

DIN

MOSI (GPIO 23)

CS

GPIO 32

CLK

SCK (GPIO 18)

Bibliotheken

Wie Sie den VL53L0X ToF Sensor verwenden, wurde bereits in der entsprechenden Blogreihe beschrieben. Als Bibliothek verwende ich hier die VL53L0X.h von Pololu:


Um das Matrix-Display anzusteuern, können Sie verschiedene Bibliotheken verwenden. In dem Blog Individuelle Zeichen auf dem Matrix Display anzeigen zeigt Ihnen Albert Vu, wie Sie die MD_Parola Bibliothek (bzw. MD_MAX72xx) verwenden. Diese liefert ein sehr umfangreiches Arsenal an Beispielquellcodes. Im Arduino Playground wird die Bibliothek LEDControl beschrieben. Ich habe mich für die Bibliothek LEDMatrixDriver entschieden und als Basis den Beispielcode aus der DrawSprites.ino verwendet:


Eine weitere Bibliothek ist notwendig, da die Werte des Sensors in ein anderes Werteverhältnis für die Helligkeit der LEDs, sowie Geschwindigkeit der Animation gebracht werden müssen. Normalerweise kann man dafür die enthaltene Funktion map() verwenden. Die ist leider sehr langsam in der Ausführung. Die Bibliothek FastMap schafft da etwas Abhilfe:

Programmfunktion

Wir verwenden ein Matrix Display, dass aus vier Segmenten mit jeweils 8x8 LEDs (Dots) zusammengesetzt ist. Auf dem ersten Segment soll ein pulsierendes Herz angezeigt werden. Die anderen drei Segmente zeigen eine animierte EKG-Spitze. Abhängig vom Abstand zum Sensor sollen das Herz schneller oder langsamer pulsieren, die LEDs heller oder dunkler leuchten und auch die EKG-Spitze schneller oder langsamer durchlaufen.

Quellcode

Ich habe aus dem Beispiel Single der ToF-Sensor Bibliothek, dem Beispielcode DrawSprites.ino aus der LEDMatrixDriver Bibliothek und dem FastMap Demo Code eine Basis geschaffen, um ein passendes Programm für unser Projekt umsetzen zu können.

Zunächst werden Bitmaps für das pulsierende Herz benötigt. Außerdem ein Bild für die EKG-Spitze. Natürlich ist durch die geringe Bildauflösung von 8x8 Pixeln die Darstellung der Bilder in ihrer Qualität eingeschränkt. Man wird aber erkennen, was es ist.

Im Blogbeitrag von Albert Vu finden Sie eine Vorlage, um Bitmap Arrays zu erstellen. Ich habe den Online Generator auf dieser Webseite verwendet. Alternativ bietet sich auch diese Webseite an, dort kann man gleich mehrere Bitmaps erzeugen und sie als Array generieren. Man klickt sich seine Bilder zusammen und kopiert dann nur den Quellcode.

Den fertigen Sketch für die Arduino IDE können Sie hier herunterladen: direkter Download

Detaillierte Beschreibung des Programms

An dieser Dieser Stelle möchte etwas genauer auf den Quellcode eingehen. Nach den Includes der Bibliotheken folgt mittels define die Einstellung für den ToF-Sensor. Da wir bei diesem Projekt auf hohe Geschwindigkeiten angewiesen sind, habe ich den HIGH_SPEED Modus verwendet. Der maximale Messbereich ist dann zwar auf ca. 60cm begrenzt. Das lässt sich jedoch verkraften. Als Nächstes wird geprüft, ob es sich um den eingestellten Mikrocrontroller (über das Menü Werkzeuge -> Board) um einen AVR (UNO, Nano), ESP8266 oder ESP32 handelt. Ich habe dort noch einmal zusätzlich die Pins für die SPI-Schnittstelle (für das Matrix-Display) als Kommentare eingefügt. Hier wird auch jeweils der CS-Pin definiert, der frei wählbar ist.

Anschließend wird die Größe des Displays festgelegt. Wir nutzen vier Segmente mit je 8 Pixeln in der Breite (und Höhe). Diese Werte werden dann für die LEDMatrixDriver Instanz genutzt.

Es folgen dann die Deklarationen und Definitionen der Bitmaps. Für die bessere Lesbarkeit habe ich sie in Binärform angelegt. Die EKG-Spitze ist ein einzelnes Byte-Array. Das pulsierende Herz ist ein zweidimensionales Byte-Array. Das macht es leichter, mit einer Schleife darüber zu iterieren. So erzeugen wir die Animation des pulsierenden Herzens.

Weiter unten folgen dann die verschiedenen Variablen. intens_min und intens_max sind durch die LEDMatrixDriver Bibliothek festgelegt. Die Helligkeit der LEDs lässt sich damit in 10 Stufen einstellen. 0 bedeutet allerdings nicht, dass eine LED ausgeschaltet wird. Es stellt nur den geringsten Helligkeitswert dar, der möglich ist.

heartBeatDelayDefault legt die Geschwindigkeit fest, wie langsam die Animation durchlaufen soll, wenn kein Objekt in der Nähe ist. heartBeatDelayMin ist die schnellste Geschwindigkeit. Mit HEART_ANIM_DELAY kann man die Geschwindigkeit des pulsierenden Herzens einstellen.

Die beiden Objektinstanzen FastMap mapper_heartBeat und FastMap mapper_intensity rechnen für uns die eingehenden Sensorwerte in die Geschwindigkeit der EKG-Animation bzw. der LED-Helligkeit um.

Die Funktion drawSprite() habe ich aus dem Beispielquellcode übernommen. Sie gibt das Bitmap auf dem Display aus.

Im setup() werden die I²C-Schnittstelle, der ToF-Sensor und die fastMapper initialisiert. Außerdem wird der Modus für den ToF-Sensor eingestellt und anhand dessen der Maximale Wertebereich als Variable definiert. Anschließend wird das Display aktiviert.

In der loop()-Funktion wird geprüft, ob ein ToF-Sensor angeschlossen ist. Wenn nicht, wird die Animationsgeschwindigkeit und -helligkeit mit den Standardwerten durchlaufen. So wäre es möglich, das Programm ohne Sensor zu testen. Wenn Sie einen Sensor anderer Art anschließen möchten, müssen Sie an dieser Stelle die Abfrage ändern oder entfernen. Ist ein Sensor vorhanden (isToFpresent), wird dieser ausgelesen. Mit dem zurückgegebenen Abstandswert wird dann entschieden, ob wieder die Standardgeschwindigkeit, oder ein neues Animationstempo eingestellt wird.

Das Gleiche gilt auch für die Helligkeit der LEDs. Es folgt dann die Animation der EKG-Spitze. Diese ist unterbrechungsfrei (ohne delay()-Funktion) programmiert. Für das Prinzip empfehle ich Ihnen das Beispielprojekt BlinkWithoutDelay. Die Hauptschleife wird mit der schnellstmöglichen Geschwindigkeit endlos durchlaufen. Es wird dann die Zeit gestoppt und mit der verglichen, die wir vorher festgelegt haben. Ist diese erreicht, wird der Programmteil in der if-Abfrage ausgeführt. Hier wird die EKG-Spitze auf dem Display dargestellt. Damit wir eine Bewegung sehen, wird der X-Wert inkrementiert, also hochgezählt. So wird unser Symbol bei jedem Durchlauf um ein Pixel nach rechts verschoben. Wenn die maximale Breite des Displays überschritten ist, wird die X-Position auf den Startwert zurückgestellt.

Ich habe an dieser Stelle außerdem einen Schalter eingebaut, der die Herzschlag-Animation steuert. Diese soll auch nur einmal durchgeführt werden. Sie wird dann gesperrt und erst freigegeben, wenn die Spitze durchgelaufen ist. Dann beginnt alles von vorn.

Die Animation des pulsierenden Herzens funktioniert ähnlich. Hier ist die Zeit zwischen den einzelnen Bitmaps (das Herz erscheint, wird größer, dann wieder kleiner und verschwindet) konstant. Damit das Herz dann scheinbar schneller schlägt (im gleichen Takt wie die EKG-Spitze), startet diese Animation immer nur dann, wenn auch die EKG-Spitze startet. Daher der Schalter in der vorhergehenden if-Abfrage. Ohne diese Implementierung würde das Herz einfach ständig pulsieren. Auf diese Weise erhalten wir eine flüssige Animation und das Herz kann abhängig vom Abstand zum Sensor unterschiedlich schnell schlagen.

Das Auslesen des Sensors und auch alle anderen Befehle brauchen Zeit. Da die Animation auf dem Display davon abhängt, sollte die Zeit nicht zu lang werden. Aus diesem Grund habe ich versucht, das Programm zeitlich zu optimieren. Als Erstes habe ich natürlich die delay()-Funktion aus den Beispielen verbannt. Sie würde sonst verursachen, dass der Sensor erst ausgelesen wird, wenn die Zeit abgelaufen ist. Ich habe statt long- hauptsächlich int-Variablen verwendet, da der Prozessor diese schneller verarbeiten kann. Das ist eigentlich etwas unsauber, denn die millis()-Funktion gibt eigentlich unsigned long zurück. Den ToF-Sensor habe ich auf HIGH_SPEED eingestellt, auch wenn damit der Messbereich kürzer ist. Außerdem habe ich für das Skalieren der Sensorwerte auf Geschwindigkeit und Helligkeit statt der map()- die fastMap()-Funktion verwendet. Als Resultat erscheint auf dem Display eine ganz brauchbare Animation. Wenn man bedenkt, dass nur ein Prozessor alles erledigt, ist das ganz gut. Das hängt natürlich auch vom verwendeten Mikrocontroller ab.

Schauen Sie sich in diesem kurzen Video an, wie das zusammengebaute Valentine-Projekt am Ende aussieht: 

 

Mögliche Erweiterungen

Es ist auch möglich, andere Abstandssensoren wie den HC-SR04 Ultraschallsensor oder einen Infrarotabstandssensor zu verwenden. Die Messwerte geben Sie an die fastMap()-Funktion. Eventuell müssen Sie die Messbereiche (minRange und maxRange) anpassen.

Die skalierten Sensorwerte lassen sich auch umkehren, sodass bei kleinem Abstand die Geschwindigkeit langsamer wird, oder bei größerem Abstand schneller. Dafür kann man einen Schalter an einen der digitalen Pins anschließen, um z.B. zwischen den beiden Modi “frisch verliebt” und “40 Jahre verheiratet” umzuschalten. Ohne Schalter brauchen Sie lediglich diese beiden Zeilen so zu verändern, dass die Zielvariablen vertauscht werden:

vorher:

    mapper_heartBeat.init(minRange, maxRange, heartBeatDelayMin, heartBeatDelayDefault);
    mapper_intensity.init(minRange, maxRange, intens_max, intens_min);

nachher:

    mapper_heartBeat.init(minRange, maxRange, heartBeatDelayDefault, heartBeatDelayMin);
    mapper_intensity.init(minRange, maxRange, intens_min, intens_max);


Wir wünschen einen schönen Valentinstag.

Andreas Wolter

für AZ-Delivery Blog

Für arduinoSpecials

4 comments

Andreas Wolter

Andreas Wolter

@Andy
@Florian

Thank you for your advice / Danke für Euren Hinweis.
(english below)
Für eine flüssigere Animation hatte ich die Variablen als “int” deklariert. Das ist etwas unsauber, denn millis() gibt unsigned long zurück. Wie Andy schon bemerkt hat, ist der Wertebereich wesentlich kleiner und somit kann die Zeit nicht mehr verglichen werden. Ich habe nun die Deklaration folgendermaßen geändert:
// millis for nonblocking Animation
unsigned long animZeit = 0;
unsigned long heartAnimZeit = 0;

Alle anderen Variablen bleiben int. Das ist immer noch unsauber, da kein ordentlicher Typecast durchgeführt wird. Das nehme ich in Kauf, damit die Animation flüssig bleibt. Die Arduino IDE hilft da ein wenig nach.
Es sollte nun funktionieren.

Ich wünsche einen schönen Valentinstag.

English:
For a smoother animation, I had declared the variables as “int”. This is a bit messy, because millis() returns unsigned long. As Andy has already noticed, the value range is much smaller and thus the time can no longer be compared. I have now changed the declaration as follows:
// millis for nonblocking animation
unsigned long animTime = 0;
unsigned long heartAnimTime = 0;

All other variables remain int. This is still messy, as no proper typecast is performed. I put up with this to keep the animation smoothly. The Arduino IDE helps a little.

It should work now. Have a nice Valentine’s Day.

Andreas

Florian

Florian

Hallo,
das gleiche Problem wie Andy schrieb habe ich auch: nach etwa 1 Minute läuft die Animation auf voller Geschwindigkeit. Die Helligkeit funktioniert wie vorher unverändert. Gibt es dafür eine Lösung?
Gruß
Florian

Andy

Andy

I love this and have made it for my wife on Valentines day, but it seems that after a minute of so the heartbeat animation always runs at full speed, this seems to correspond with the variables heartAnimZeit and animZeit exceeding 65535 and resetting to zero I have this happen after a longer period my making them unsigned long but I’d be grateful to see if it is possible to correct this somehow?

Angelo De Lucia

Angelo De Lucia

Molto bello il progetto, spiegato molto bene.
Complimenti , grande lavoro , grandissimi.

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery