A Raspberry Pi kiosk system as set up here is intended to open a browser on startup, showing and regularly refreshing a given website, e.g. a system status, weather information, transport schedules and thelike.

To set up this kiosk system, we need

  • a Raspberrby Pi, installed with a standard Ubuntu server image
  • a monitor (touch functionality optional)

Based on a standard Ubuntu server image, we have to add some necessary components first:

sudo apt-get install openbox xdg-utils xinit xdotool

openboxis a light-weight and fast window manager, the xdg-utils are tools for the graphical user interface, xinit starts the X server, and the xdotool we’ll use to send instructions from the command line to applications running within the X environment.

Next, we install Google Chrome as snap package:

sudo snap install chromium 

Now, we have to amend the /etc/xdg/openbox/autostart file in order to switch off all screen savers, activate ctrl+alt+backspace for stopping the X server (for debugging purposes when we are at the console), clean up after a chromium crash, and finally start chromium and load the desired website after waiting some time. (This workaround was necessary due to the fact that Chrome loses every URL that might be given as command line parameter for start on the first refresh. If you know how to fix this better, please let me know.)

#
# no screensaver etc
#
export DISPLAY=:0
xset s off
xset s noblank
yset -dpms

#terminate X with ctrl/alt/backspace
setxkbmap -option terminate:ctrl_alt_bksp

#
# clean up crash
#
# /home/pi/snap/chromium/common/chromium/
#
sed -i 's/"exited_cleanly":false/"exited_cleanly":true/' ~/snap/chromium/common/chromium/'Local State'
sed -i 's/"user_experience_metrics.stability.exited_cleanly":false/"user_experience_metrics.stability.exited_cleanly":true/' ~/snap/chromium/common/chromium/Variations
sed -i 's/"exit_type":"[^"]\+"/"exit_type":"Normal"/' ~/snap/chromium/common/chromium/Default/Preferences

# start browser
chromium --disable-prompt-on-repost --disable-pinch --start-fullscreen &
# wait for browser to start and open a new reloadable tab (workaround via sleep)
sleep 160 && xdg-open https://www.example.com

In order to have the X server started after login, we are adding to our user’s .bashrc the line

[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && startx -- -nocursor

In order to login automatically on boot, we create a service unit /lib/systemd/system/getty@.service as follows:

#  SPDX-License-Identifier: LGPL-2.1-or-later
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
After=rc-local.service

# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes

# IgnoreOnIsolate causes issues with sulogin, if someone isolates
# rescue.target or starts rescue.service from multi-user.target or
# graphical.target.
Conflicts=rescue.service
Before=rescue.service

# On systems without virtual consoles, don't start any getty. Note
# that serial gettys are covered by serial-getty@.service, not this
# unit.
ConditionPathExists=/dev/tty0

[Service]
# the VT is cleared by TTYVTDisallocate
# The '-o' option value tells agetty to replace 'login' arguments with an
# option to preserve environment (-p), followed by '--' for safety, and then
# the entered username.
ExecStart=-/sbin/agetty --autologin pi --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
IgnoreSIGPIPE=no
SendSIGHUP=yes

# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION

[Install]
WantedBy=getty.target
DefaultInstance=tty1

In the Servicesection, please replace pi by the username of your local user.

We make the service available at boot time by

cd /etc/systemd/system/getty.target.wants/
sudo ln -sf /lib/systemd/system/getty@.service .

Finally, in order to refresh the browser view every minute, we add the following entry to /etc/crontab:

*/1 *   * * *   pi    export XAUTHORITY=/home/pi/.Xauthority && export DISPLAY=:0 && xdotool search --onlyvisible --class chromium windowfocus && xdotool key F5 > /dev/null 2>&1

Again, replace pi by your local user.

That’s it! Now we have a kiosk system on a Pi booting into a chrome browser that loads and refreshes a given website every minute.