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

On Valentine's Day we often give away flowers, cards or sweets. Little gifts always go down well. This year we came up with a small project for which we are again using the 8x32 dot matrix display. If you want to recreate the AZ girlfriend for Valentine's Day, please take another look at the Blog post from last year.

Since Valentine's Day has a greater meaning for many lovers and love also has to do with closeness, this year we are using a proximity sensor. You probably know this: a person who means a lot to you comes very close, then your pulse rises and you may feel very warm. That's what we want to simulate with this year's project. We show two animations on the display. These change as you get closer to the sensor. The pulse gets faster and the red light of the LEDs represents the rising heat. Here we go.

What we need

Quantity Component
1 MAX7219 8x32 4 in 1 Dot Matrix LED Module
1 Nano V3.0 100% Arduino compatible with Nano V3 or
1 ESP8266 D1 Mini or
1 ESP32 Devkit C V4
1 VL53L0X ToF distance sensor
Jumper Cable
Breadboard
PC with Arduino IDE and internet connection


You are free to choose the microcontroller. The source code works on the common UNOs, Nanos, ESP8266, ESP32. You only have to pay attention to the respective pinout. The pins to which the ToF sensor and the matrix display are connected are specified by the I²C or SPI interfaces.

    Note: The matrix display must be supplied with 5V. Some ESP32s do not offer an output with this voltage. You then need either a external voltage source, or use another microcontroller.

    Circuit diagrams

    Microcontroller Nano or UNO:


    ESP8266 (here D1 Mini):


    ESP32 (here Devkit C V4):

     


    The ToF sensor is connected as follows:

    VL53L0X

    Nano / UNO

    VIN

    5V or 3.3V

    GND

    GND

    SCL

    SCL (A5)

    SDA

    SDA (A4)

    GPIO1

    -

    XSHUT

    -

    VL53L0X

    ESP8266 D1 Mini

    VIN

    5V or 3.3V

    GND

    GND

    SCL

    SCL (D1 / GPIO5)

    SDA

    SDA (D2 / GPIO4)

    GPIO1

    -

    XSHUT

    -

    VL53L0X

    ESP32

    VIN

    5V or 3.3V

    GND

    GND

    SCL

    SCL (GPIO 22)

    SDA

    SDA (GPIO 21)

    GPIO1

    -

    XSHUT

    -


    The matrix display is connected as follows, whereby the CS pin can be freely selected. However, the SS or MISO pin should not be used for this (information from the source code of the LEDMatrixDriver library):

    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)


    Libraries

    How to use the VL53L0X ToF sensor was already described in the corresponding blog series described. As a library I use the VL53L0X.h from Pololu:


    You can use various libraries to control the matrix display. In the blog Show individual characters on the matrix display Albert Vu shows you how to do the MD_Parola Library (resp. MD_MAX72xx) use. This provides a very extensive arsenal of sample source codes. in the Arduino Playground becomes the library LEDControl described. I chose the library LEDMatrixDriver decided and used the example code from the DrawSprites.ino used:


    Another library is necessary because the values ​​of the sensor have to be brought into a different ratio for the brightness of the LEDs and the speed of the animation. Usually you can use the included function for this map () use. Unfortunately, this is very slow in execution. The library FastMap creates a remedy:


    Program function

    We use a matrix display that is composed of four segments, each with 8x8 LEDs (dots). A pulsing heart should be displayed on the first segment. The other three segments show an animated EKG peak. Depending on the distance to the sensor, the heart should pulsate faster or slower, the LEDs should shine brighter or darker and the ECG tip should also run faster or slower.


    Source code

    I got from the example single the ToF sensor library, the sample code DrawSprites.ino A basis was created from the LEDMatrixDriver library and the FastMap demo code in order to be able to implement a suitable program for our project.

    First of all, bitmaps are needed for the beating heart. Also a picture for the EKG tip. Of course, due to the low image resolution of 8x8 pixels, the quality of the images is limited. But you will see what it is.

    in the Blog post by Albert Vu you will find a template to create bitmap arrays. I have the online generator on this website used. Alternatively, there is also this website on, there you can create several bitmaps and generate them as an array. You click your images together and then just copy the source code.

    You can download the finished sketch for the Arduino IDE here: direct download

    Detailed description of the program

    At this point I would like to take a closer look at the source code. After the includes of the libraries follows with define the setting for the ToF sensor. Since we rely on high speeds on this project, I have this HIGH_SPEED Mode used. The maximum measuring range is then limited to approx. 60cm. But that can be coped with. The next step is to check whether the set microcontroller (via the Tools -> Board menu) is an AVR (UNO, Nano), ESP8266 or ESP32. I have also added the pins for the SPI interface (for the matrix display) as comments. The CS pin, which can be freely selected, is also defined here.

    Then the size of the display is determined. We use four segments with 8 pixels each in width (and height). These values ​​are then used for the LEDMatrixDriver instance.

    This is followed by the declarations and definitions of the bitmaps. For better readability I have created it in binary form. The EKG tip is a single byte array. The beating heart is a two-dimensional byte array. That makes it easier to iterate over it with a loop. This is how we create the animation of the beating heart.

    The various variables follow below. intens_min and intens_max are determined by the LEDMatrixDriver library. The brightness of the LEDs can be adjusted in 10 levels. However, 0 does not mean that an LED is switched off. It only represents the lowest brightness value that is possible.

    heartBeatDelayDefault Specifies the speed at which the animation should run through slowly if there is no object nearby. heartBeatDelayMin is the fastest speed. With HEART_ANIM_DELAY you can adjust the speed of the beating heart.

    The two object instances FastMap mapper_heartBeat and FastMap mapper_intensity convert the incoming sensor values ​​into the speed of the ECG animation or the LED brightness for us.

    I took the function drawSprite () from the example source code. It outputs the bitmap on the display.

    in the set up() the I²C interface, the ToF sensor and the fastMapper are initialized. In addition, the mode for the ToF sensor is set and the maximum value range is defined as a variable on the basis of this. The display is then activated.

    In the loop ()Function is checked whether a ToF sensor is connected. If not, the animation speed and brightness will cycle with the default values. So it would be possible to test the program without a sensor. If you want to connect a different type of sensor, you have to change or remove the query at this point. Is there a sensor (isToFpresent), this is read out. With the returned distance value it is then decided whether the standard speed or a new animation speed is set.

    The same goes for the brightness of the LEDs. The animation of the ECG tip then follows. This is uninterrupted (without delay ()Function) programmed. For the principle, I recommend the sample project BlinkWithoutDelay. The main loop will go through endlessly at the fastest possible speed. The time is then stopped and compared with the one we have set beforehand. If this is reached, the program part is executed in the if query. Here the ECG tip is shown on the display. So that we can see movement, the X value is incremented, i.e. counted up. So our symbol is shifted one pixel to the right with each run. If the maximum width of the display is exceeded, the X position is reset to the start value. I also built a switch at this point that controls the heartbeat animation. This should only be carried out once. It is then blocked and only released when the tip has passed. Then everything starts all over again.

    The animation of the beating heart works in a similar way. Here the time between the individual bitmaps (the heart appears, becomes larger, then smaller again and disappears) is constant. So that the heart appears to beat faster (at the same rate as the ECG peak), this animation only starts when the ECG peak also starts. Hence the switch in the previous one if-Query. Without this implementation, the heart would just keep pulsing. In this way we get a fluid animation and the heart can beat at different speeds depending on the distance to the sensor.

    Reading out the sensor and all other commands take time. Since the animation on the display depends on it, the time should not be too long. For this reason I tried to optimize the program in terms of time. First of all, of course, I have them delay ()-Function banned from the examples. Otherwise it would mean that the sensor is only read when the time has expired. I took place long- mainly intVariables because the processor can process them faster. This is actually a bit dirty, because the millis ()Function actually there unsigned long back. I have the ToF sensor on HIGH_SPEED set, even if the measuring range is thus shorter. Also, I have to scale the sensor values ​​to speed and brightness instead of the map ()- the fastMap ()-Function used. As a result, a very useful animation appears on the display. When you consider that only one processor can do everything, that's pretty good. Of course, that also depends on the microcontroller used.

    Take a look at how the assembled Valentine project looks in the end in this short video:


    Possible extensions

    It is also possible to use other distance sensors like the HC-SR04 ultrasonic sensor or to use an infrared distance sensor. You give the measured values ​​to the fastMap ()-Function. You may have to set the measuring ranges (minRange and maxRange) to adjust.

    The scaled sensor values ​​can also be reversed, so that the speed is slower at a small distance, or faster at a greater distance. To do this, you can connect a switch to one of the digital pins, e.g. to switch between the two modes “newly in love” and “married for 40 years”. Without a switch, you only need to change these two lines so that the target variables are swapped:

    previously:

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

    later:

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


    We wish you a happy Valentine's Day.

    Andreas Wolter

    for AZ-Delivery Blog

    For 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. Install ESP32 now from the board manager
    2. Lüftersteuerung Raspberry Pi
    3. Arduino IDE - Programmieren für Einsteiger - Teil 1
    4. ESP32 - das Multitalent
    5. OTA - Over the Air - ESP programming via WLAN