Maker.io main logo

Build an Animated Digital Kiosk with a Raspberry Pi

2024-09-09 | By Don Wilcher

License: See Original Project Raspberry Pi

Digital signage is fantastic for drawing people’s attention to whatever is on display. These ‎animated signs are versatile and ideal for many situations, such as fairs and expos. They are ‎also helpful for advertising products or services, for example, in a store window. This article ‎discusses a simple, beginner-friendly project that turns a Raspberry Pi and any compatible ‎display into a lively digital sign that can play animations and slideshows – perfect for making a ‎bold impression.‎

sign_1

Project Goals

Users of a digital kiosk display should not have to spend much time setting up the device, for ‎example, when relocating a stall from one event to a new location. Instead, the digital signage ‎should turn on automatically when plugged in and display previously defined slides and videos. ‎However, users should still be able to adjust some settings without disassembling the device or ‎editing source code or configuration files. Thus, the Pi must operate headless without an external ‎keyboard and mouse.‎

The final unit should be self-contained and have one potentiometer for adjusting the delay ‎between slides. Further, the device should have a single push button to safely power down the ‎Raspberry Pi before unplugging the device.‎

Required Components

Qty Part

‎1‎ Raspberry Pi Model 3B

‎1‎ HDMI Cable

‎1‎ HDMI Monitor

‎1‎ ‎10K Potentiometer

‎1‎ ‎10µF Electrolytic Capacitor‎

‎1‎ Pushbutton

‎1‎ Pack of Jumper Wires

Building the Hardware

The Raspberry Pi GPIO does not have analog input pins that would allow obtaining precise ‎readings of a potentiometer’s turn angle. Instead, you will have to get more creative when ‎hooking up a variable resistor without using an external analog-to-digital converter (ADC). As an ‎alternative, you can utilize a capacitor with known capacitance. Together with the potentiometer ‎‎– which is nothing more than a variable resistor – you can build a simple RC circuit to determine ‎the resistor value and, thus, the potentiometer’s turn angle. This approach is not as precise as ‎using an analog input, but it helps cut down the project’s complexity. As the potentiometer is only ‎used to switch between three modes, the limited accuracy is no problem. Besides the ‎potentiometer, the project also contains a single push button for shutting down the Raspberry Pi:‎

schemeit_2

This image shows the project’s schematic diagram. Scheme-It link

Note that this project uses a Raspberry Pi Model 3B as that version of the SBC has a single full-‎sized HDMI port, which further helps reduce the number of components in the final build. ‎However, you can replace it with newer models if you need more processing power or want to ‎utilize multiple displays.‎

Prerequisites and Installing the Required Programs

When setting up the Raspberry Pi for a digital kiosk display, ensure that you choose the ‎complete Raspbian installation that includes a desktop environment, as the slideshow program, ‎responsible for displaying the digital signage content, relies on a graphical UI. Further, the ‎operating system should be configured to automatically boot into the desktop environment and ‎automatically login as the Pi user after startup. Both settings can be found in the raspi-config ‎program.‎

autologin_3

Choosing the desktop autologin boot option ensures that the computer can automatically start ‎displaying the slideshow after a reboot.‎

Once the Pi is up and running, the following command can be used to install the MPV media ‎player:‎

Copy Code
sudo apt install mpv

Configuring the Raspberry Pi Shutdown Overlay

The Raspbian OS has several overlays for controlling hardware functions, and one is particularly ‎handy for this project. By adding a single line to a configuration file, you can easily add a ‎shutdown button to any RPi-based project. The configuration file can be edited in any text editor, ‎for example, using the following command:‎

Copy Code
sudo nano /boot/firmware/config.txt

Note that older versions of Raspbian used a different file to configure this feature:‎

Copy Code
sudo nano /boot/config.txt

Once you find the correct file, you need to append the following line to the configuration file:‎

Copy Code
dtoverlay=gpio-shutdown,gpio_pin=3

After adding the line, the system needs to reboot to enable the additional overlay:‎

Copy Code
sudo reboot now

Doing so instructs the system to load the relevant dtoverlay on startup, which tells the system to ‎listen for the specified GPIO pin being pulled low. In this case, the system monitors pin three, ‎which is directly shorted to GND when the push button is pressed, thus triggering the OS ‎shutdown procedure.‎

Writing the Slideshow Management Program

The Raspberry Pi needs to monitor whether the kiosk display program is still running and restart ‎the software if it has crashed or stopped. The monitor must also detect changes in the ‎potentiometer turn angle and adjust the slideshow timing if necessary. The Pi runs a Python ‎application that detects potentiometer value changes and restarts the slideshow program if ‎required.‎

The script begins by importing some necessary modules and initializing a few variables that ‎control the program behavior:‎

Copy Code
import RPi.GPIO as GPIO
import subprocess
import time

potWiper = 17

# Adjust these as needed
minVal = 25000
midVal = 37500
maxVal = 50000

slideshowDelay = {
    "MIN": 5,
    "MED": 15,
    "MAX": 30
}
slideshowFolder = '/home/pi/slides'

currentPotAngle = "MIN"
mpvProcess = None

The potWiper variable holds the GPIO number of the pin connected to the potentiometer's wiper. ‎The minVal, midVal, and maxVal variables store the threshold values for choosing one of the ‎three delay options in the slideshowDelay map. The program determines the potentiometer's ‎angle by first discharging the capacitor and then allowing the voltage to rise again. It runs a loop ‎during this process, counting the iterations until the voltage increases enough to pull the GPIO pin ‎high. This count reflects the potentiometer's angle.‎

Next, the program contains two helper functions for initializing the potentiometer and for ‎determining its turn angle:‎

Copy Code
def initPotentiometer():
    GPIO.setmode(GPIO.BCM)

def readPotentiometer():
    reading = 0
    GPIO.setup(potWiper, GPIO.OUT)
    GPIO.output(potWiper, GPIO.LOW)
    time.sleep(0.1)
    GPIO.setup(potWiper, GPIO.IN)
    while GPIO.input(potWiper) is GPIO.LOW and reading <= maxVal:
        reading += 1
    reading = maxVal - reading
    # Return one of three states, according to the turn angle
    if (reading <= minVal):
        return "MIN"
    elif (reading > minVal and reading < midVal):
        return "MED"
    else:
        return "MAX"

The readPotentiometer helper resets the counter with each call. It then pulls the potWiper pin low ‎to discharge the capacitor during the 100-millisecond wait period. The function then changes the ‎pin to an input and counts how many cycles the capacitor takes to recharge. As the capacitance ‎and voltage remain fixed, the duration depends solely on the potentiometer’s turn angle, which ‎changes the resistance. The longer it takes to charge up, the higher the resistance. The function ‎continues when the pin is pulled high, or the counter exceeds a particular value. This additional ‎check prevents stalling the application. Finally, the function returns one of three strings, ‎depending on the counter value.‎

The main part of the script calls these helper functions in an endless loop:

Copy Code
try:
    initPotentiometer()
    while True:
        potAngle = readPotentiometer()
        time.sleep(0.25)
        if (potAngle is not currentPotAngle or mpvProcess is None or mpvProcess.poll() is not None):
            print("Detected delay change")
            startMPV(slideshowDelay[potAngle])
            currentPotAngle = potAngle
        else:
            time.sleep(5)
except KeyboardInterrupt:
    print("Program ended by user!")
finally:
    GPIO.cleanup()
    if mpvProcess:
        endMPV()
    print("Monitor program shutdown complete!")

The try-except-finally construct checks whether a user hits CTRL + C on the keyboard to stop ‎the program. If it detects the key combination, the script shuts down the media player and frees ‎the GPIO resource. The application reads the potentiometer value within the endless while loop ‎using the function described above. It then checks whether the new reading differs from the ‎previous value. The program calls the startMPV helper function when it detects a change or ‎notices that the media player program is inactive. Otherwise, the application sleeps for five ‎seconds before repeating the checks.‎

The following two helper functions manage the external media player:‎

Copy Code
def endMPV():
    global mpvProcess
    mpvProcess.kill()
    mpvProcess = None

def startMPV(delay):
    global mpvProcess, slideshowFolder
    if mpvProcess:
        endMPV()
    mpvProcess = subprocess.Popen([
        'mpv',
        '--image-display-duration={}'.format(int(delay)),
        '--loop-playlist=inf',
        '-fs',
        '--ontop',
        '--no-border',
        slideshowFolder
    ])

The endMPV helper method stops a previously started MPV process, and the startMPV can ‎spawn a new child process using the subprocess library. When starting a new MPV instance, the ‎program passes in some command-line arguments to ensure the slideshow is always displayed ‎on top of all other applications in fullscreen mode. The parameters also instruct MPV to repeat ‎the slides infinitely. They also pass the potentiometer-controller delay value to the MPV media ‎player.‎

Adding a Startup Service

Finally, the operating system must start the custom Python script after booting up and loading ‎the desktop environment. For that purpose, the script must be executable using the following ‎command:‎

Copy Code
chmod +x /path/to/the_script.py

Next, create a new system service using the following command:‎

Copy Code
sudo nano /etc/systemd/system/kiosk_script.service

The following code snippet defines what the service does and when it should run. In this case, ‎the service instructs the system to run the Python program after loading the desktop ‎environment and to restart the program whenever it crashes:‎

Copy Code
[Unit]
Description=Run the kiosk Python application
After=graphical.target
Requires=graphical.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/pi/digital_kiosk.py
Restart=always
User=pi
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/pi/.Xauthority

[Install]
WantedBy=default.target

‎After saving the file, the system service daemon needs to be restarted to pick up the new ‎service:‎

Copy Code
sudo systemctl daemon-reload‎

The service can then be enabled and started using the following commands:‎

Copy Code
sudo systemctl enable kiosk_script.service

sudo systemctl start kiosk_script.service

Finally, it’s worth checking whether the service is running by typing the following command:‎

Copy Code
sudo systemctl status kiosk_script.service

The system should report that the service is active. If it instead reports an error or the service is ‎stopped, go back, and double-check the service file for typos. Adjusting the path to the Python ‎script may be necessary if the file has a different name or is stored in a directory other than the ‎pi user’s home folder.‎

screenshot_4

This screenshot demonstrates that following the outlined steps results in the system picking up ‎the service and starting it as expected.‎

Summary

Digital signage can be a stunning centerpiece of convention booths or a point of sale. The ‎animated display helps draw attention to highlights by flipping through predetermined slides and ‎videos to showcase products and services.‎

Building a kiosk display using a Raspberry Pi and an HDMI monitor is a breeze. It requires only ‎the SBC, an HDMI monitor, and a few cables. An additional push-button and a small RC circuit, ‎built using a potentiometer and a capacitor, can further enhance the user experience by allowing ‎operators to change settings on the fly.‎

The custom Python script is the heart of this project. It regularly checks if the MPV media player ‎is still running and restarts it if necessary. It also monitors whether a user requested to change ‎the slideshow delay using the potentiometer.‎

As the Pi doesn’t have analog inputs, the script determines the potentiometer’s angle by ‎measuring how long it takes to charge the capacitor. Whenever the script detects a change, it ‎restarts the media player with the selected delay value. The OS monitors the push button and ‎powers off the computer when pressed.‎

制造商零件编号 SC0022
SBC 1.2GHZ 4 CORE 1GB RAM
Raspberry Pi
¥284.90
Details
制造商零件编号 P569-001
CABLE M-M HDMI-A 1' SHLD
Tripp Lite
¥72.28
Details
制造商零件编号 P0915N-FC15BR10K
POT 10K OHM 1/20W PLASTIC LINEAR
TT Electronics/BI
¥24.26
Details
制造商零件编号 450BXC10MEFC10X20
CAP ALUM 10UF 20% 450V RADIAL TH
Rubycon
¥5.29
Details
制造商零件编号 1949
JUMPER WIRE F TO F 12" 28AWG
Adafruit Industries LLC
¥32.15
Details
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum