Building a Custom Perspective Kiosk on Linux Hardware

Just thought I would chuck a little guide together to get a working Perspective Kiosk on simple Raspberry Pi 4 Hardware.

Main Features:

  • Webkit Browser Engine: supported by Ignition as part of Safari compatibility.

  • Full Kiosk Mode: No desktop OS on this system, no other apps, no way to exit.

  • Hardware Acceleration: Panel PC hardware supported by linux is able to be utilised.

  • On Screen Keyboard: Critical missing piece of 99% of similar attempts. Auto opens and closes with focus on text entry elements.

  • Auto-Boot: System Boots on power on, no login and ready to go in 25seconds.

  • Auto Network Refresh: The system will refresh the page automatically when a network change is detected.

  • No Menus: No interface items at all besides the rendered webpage.

  • Touchscreen Support: Any touchscreen that works in linux is usable.

I have built and tested this on the Seeed Studios ReTerminal-DM, which is a $700AUD 10.1" Raspberry Pi Compute Module 4 based panel computer.

This guide assumes you are able to work out simple tasks like setting an IP and editing a text file on linux.
Use this at own risk.

Installing Raspbian

Install a copy of the latest version of (6.1.21 or higher) Raspbian Lite OS, follow the manufacturer guide for your device, for me, I followed:

*If you have the ReTerminal-DM, don’t do the touchscreen orientation section.

** Critical steps here - Enable SSH, Set a username and password.

This should get you set up with a Pi that boots to a console login page with the username and password that you chose when uploading the image.

Setting Up the Kiosk

Log in to this console with SSH, this is now your base for all the commands on the unit.

Enter the following into the terminal:

$ sudo apt update

$ sudo apt install snapd

You will also need to reboot your device. This can be accomplished from the terminal (and from the desktop), but make sure you save any open documents first:

$ sudo reboot

After this, install the core snap in order to get the latest snapd.

$ sudo snap install core

core 16-2.45.2 from Canonicalâś“ installed

Now install Ubuntu Frame with the following command:

$ sudo snap install ubuntu-frame

On Raspbian, Ubuntu Frame does not start by default. To change this default run the following command:

$ sudo snap set ubuntu-frame daemon=true

Now install wpe-webkit-mir-kiosk:

$ sudo snap install wpe-webkit-mir-kiosk

Now we connect it to the display server:

$ sudo snap connect wpe-webkit-mir-kiosk:wayland

As per above, on Raspbian, wpe-webkit-mir-kiosk does not start by default. To change this default run the following command:

$ sudo snap set wpe-webkit-mir-kiosk daemon=true

Now we set the target url for your perspective session, which here is the demo ignition on IA web server.

$ sudo snap set wpe-webkit-mir-kiosk url=http://demo.inductiveautomation.com

Now start the service:

$ sudo snap start wpe-webkit-mir-kiosk

You should now see on screen the perspective session you linked to.

On Screen Keyboard

Next we configure the On Screen Keyboard:

$ sudo snap install ubuntu-frame-osk

Make sure it’s connected to the display server interface:

$ snap connect ubuntu-frame-osk:wayland

And make it a service:

$ sudo snap **set** ubuntu-frame-osk daemon=true

You can switch between light and dark mode with the theme option:

$ sudo snap set ubuntu-frame-osk theme=dark

$ sudo snap set ubuntu-frame-osk theme=light

Now we have a working system on the screen, and if you have a working touchscreen, it should be able to interact with the session as normal.

Cosmetic Fixes

On my system, the LCD was set up in portrait orientation, which is not the install direction for the unit, so to fix this we open up some settings.

$ sudo nano /boot/cmdline.txt

Use the arrow keys to navigate to the end of the line and add the following:

fbcon:rotate=1

Then we save and exit the file (+W , Y , )

(1 = Rotate Clockwise 90 degrees, 2 = 180, 3 = 270)

$ sudo reboot

Now your Pi should boot and display the screen in the right orientation.

You may notice that the system will always boot to a browser error saying “dns resolution failed”, This is because the system boots up and starts the browser before the network interface is connected and online.

To fix this, we do a little hack fix (There is likely a nice way to do this, but this works if a little messy).

$sudo nano /lib/dhcpd/hooks/01-Test

Now we add at the bottom of the file the following:


if [ $reason = “BOUND”] || [ $reason = “STATIC”];
then
    snap set wpe-webkit-mir-kiosk url=https://demo.inductiveautomation.com
fi

This effectively reloads the url every time the network either gets a new DHCP address or is configured with a Static IP, which happen every time you reconnect a network.

I have a bunch of other tweaks figured out, but this should get you started pretty well.

Limitations

One note of warning though, the Raspberry Pi has very limited processing power, and you will need to build any pages you want to display fairly well and follow the optimisation tips in the forum to keep your loading times down to a reasonable time.

I can see a bunch of people on Maker Edition using this for simple projects, and that is probably the best use of limited hardware like this.

16 Likes

i follow this path, but on a full installation of ubuntu. everything seems to be working. Except the dragging of popup. Popup move for a few pixels and then just stop. This was tested on multiple hardware (Capacitive, resistive screen) various compute unit and we still get the same behavior. Dragging popup with a mouse works perfectly. Do you see such behavior at your side ?

I think this is something around the way the touchscreen is handling a click and drag touch event. There may be some modes in either Wayland or the mir-kiosk system that can work on it, they have a pretty active github issue tracker for both.

We went down this same path for a few weeks on our production kiosk HMIs (running Ubuntu Server 22.04) but found the wpe-webkit-mir-kiosk browser to be buggy. Especially, we found that the app would crash every time an operator tried trending - specifically when trying to use the drop-down menu to change the date span (minute, hour, day, etc.). Other drop-downs resulted in similar experiences.

However, I'm a fan of the ubuntu-frame & ubuntu-frame-osk !

We are currently trialing the firefox snap on ubuntu-frame in kiosk mode. While a bit more difficult to set up as a service running wayland, it has certainly improved operability!

A solution using ubuntu-frame & ubuntu-frame-osk together with perspective workstation / jxbrowser is high on the want-list.

2 Likes

You not the only one, I stopped my search due to the same bugs.

I was thinking of doing a comparison to the firefox and chromium waylnad snaps to see what they brought to the table, I definitely don't think that the mir-kiosk is the perfect solution, but it is easy for simple use.

Agreed. The frequent crashes of mir-kiosk made it a non-starter, even though the ease-of-installation/configuration/use was a huge benefit. Firefox has, so far, proven to be more stable, albeit far more complicated to configure. Recent Ignition versions include updates to Java, and recent Perspective Workstation versions include updates to JxBrowser, of which supports Wayland. When time allows...I intend to dabble...

A bash script to migrate from the above mir-kiosk snap to Firefox snap might resemble:

#!/bin/bash
# Store URL used on mir-kiosk:
echo "INFO    | Storing kiosk URL..."
my_Url=$(snap get wpe-webkit-mir-kiosk url)
echo "INFO    | my_Url=${my_Url}"

# Uninstall Mir-Kiosk:
echo "INFO    | Removing webkit-mir-kiosk snap..."
snap remove --purge wpe-webkit-mir-kiosk

# Install Firefox Snap:
echo "INFO    | Installing Firefox snap..."
snap install firefox
snap connect firefox:wayland

# Create Service to run Firefox in Ubuntu-Frame:
echo "INFO    | Creating Service to run Firefox at startup..."
cat > /etc/systemd/system/firefox-frame.service << EOF
[Unit]
# https://www.freedesktop.org/software/systemd/man/systemd.unit.html#%5BUnit%5D%20Section%20Options
Description=Firefox Wayland Kiosk
After=snap.ubuntu-frame.daemon.service snap.ubuntu-frame-osk.daemon.service ignition.service getty.target
Wants=snap.ubuntu-frame.daemon.service snap.ubuntu-frame-osk.daemon.service ignition.service
Conflicts=display-manager.service
StartLimitIntervalSec=0

[Service]
# https://discourse.ubuntu.com/t/environment-variables-for-wayland-hackers/12750
Type=simple
Restart=always
RestartSec=3
Environment=WAYLAND_DISPLAY=wayland-0
Environment=MOX_CRASHREPORTER_DISABLE=1
Environment=GDK_BACKEND=wayland
Environment=MOZ_ENABLE_WAYLAND=1
Environment=HOME=/root
Environment=XDG_RUNTIME_DIR=/run/user/0
Environment=XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
ExecStartPre=/snap/bin/firefox --CreateProfile "default"
ExecStartPre=-/usr/bin/sleep 10
ExecStart=/snap/bin/firefox -P default --kiosk --private-window "${my_Url}"
Nice=15

[Install]
WantedBy=graphical.target
EOF

echo "INFO    | Reload daemon, start kiosk, enable service at startup..."
systemctl daemon-reload
systemctl start firefox-frame
systemctl enable firefox-frame

Some notes on the above method:

  • Less-than-elegant 10 second delay added to startup due to Ignition service running, but Perspective module & web page not available (504 Not Found) until additional delay.
  • Default profile used. Custom profile should be created (to limit navigation to specific websites, etc.) Ref: How to customize the firefox snap with enterprise policies - Documentation - Ubuntu Community Hub
  • Edit* The above migration was intended for Ubuntu Server.
  • Edit** Updated Firefox service to move all 'Requires' to 'Wants' to improve reliability.
1 Like

Interestingly I tried this.
Firefox does seem faster and more efficient on slow hardware.
OSK works just fine natively.

Oddly the Systemctl startup doesnt work on mine at boot, shows it starting then immediately stopping the service.
Manually running the systemctl start works just fine.

I experienced a similar issue when the default profile did not exist. I added the line to create a profile as part of the service start up which fixed my issue. Perhaps yours is related?
Try executing the command to --CreateProfile manually, any errors?

I did get this working just fine on another HMI, my only change is I am working on a reliable way to verify Edge is actually starting before launching the browser.
Turns out Edge reports 200OK as a HTTP status well before the "Ignition is starting" splash screen shows.

Just to follow on here, looks like there has been an update to Raspbian OS that has dumped the X11 window manager in favour of Wayland.

On the plus side, they have dropped a new update of Firefox that has optimisations for for the Pi, but is also set up for Wayland reliably.

Because the system we are playing with here is using Snapd, it will update automatically and I found my browser speed on this has increased massively, and has properly enabled multi process so the cpu is much snappier.

Currently on my list of wants is a reliable way to load the target perspective page when it is loaded rather than using a delay, as various hardware takes variable time to start up.

I also was looking for an elegant way to load the perspective page. On some hardware, we increased the delay to 60sec. I started looking at 3rd party tools to see if web page was loaded (and refresh periodically if-not) but gave up for the sake of simplicity.
I'd really like to revisit the end-goal of running Perspective Workstation on ubuntu-frame. I haven't spent much time with the ARM processors & Ubuntu Core. Wondering if you could run Perspective Workstation using OpenJDK snap w/ matching Java version in your Ignition version?

I would love to get workstation running but in the end, its too much right now to get working on embedded environments, and doesn't give us any extra features.

I have been working on a little while loop that exists in the service file for firefox, something like ExecStartPre:

#!/bin/bash

until [ "`curl --silent --show-error --max-time 1 -I http://localhost:8088 | grep 'Gateway is Starting'`" != "" ];
do
  sleep 2
done

Basically polls the localhost till the "Gateway is Starting" is found in the response.
Then it starts the browser.

That would be more elegant.
The below should also return a JSON string of the gateway status:

curl --silent --max-time 1 --fail "http://localhost:8088/StatusPing"

Something resembling one of the following:

{"state":"STARTING"}
{"state":"RUNNING", "details":"COMMISSIONING"}
{"state":"RUNNING"}

etc.

2 Likes

Been playing around with a few options of reading gateway status:

$ curl --silent --max-time 1 --fail "http://localhost:8088/StatusPing"
{"state":"RUNNING"}
$ curl --silent --max-time 1 --fail "http://localhost:8088/system/gwinfo"
ContextStatus=RUNNING;GatewayAddress=(...);(...other stuff...)
$ ./ignition.sh status
Ignition-Gateway (installed with systemd) is running: PID:######, Wrapper:STARTED, Java:STARTED

And, what might contain my favorite response:

$ ./gwcmd.sh --info
Gateway Name: My_Gateway_Name
Gateway Version: 8.1.## (64-bit)
Web Server Status: RUNNING
Gateway Status: RUNNING
Http Port: 8088
Https Port: 8043

Unfortunately, response time from gwcmd is lengthy (I observed ~5 seconds if gateway is running, ~30 second timeout if not). Seems like the StatusPing URL seems to be the fast & reliable method. Might be why the Docker 'perform-commissioning.sh' script uses it.

Edit 2024.10.17: While perusing the Ignition install directory (Ubuntu Server), I see there a few files in there that also contain the status of the gateway while the gateway is: 'STARTING', 'STARTED', 'STOPPING'. However, files seem to disappear after gateway is stopped:

$ cd /path/to/ignition
$ cat ./Ignition-Gateway.status
STARTED
$ cat ./Ignition-Gateway.java.status
STARTED
$ ignition stop
Stopping Ignition-Gateway...
$ cat ./Ignition-Gateway.status
cat: ./Ignition-Gateway.status: No such file or directory
1 Like

So, finally got around to doing a whole heap of testing and development.
The "StatusPing" seems to work quite nicely, I am having some Cross Site Origins issues with it, but it seems to work normally.
I would love to know whether it's somehow excluded from demo.inductiveautomation.com after some sort of rate limit.

I have developed a micro web host that runs on the system that boots with the OS, as soon as firefox loads, it opens up localhost:8080, which is a pretty simple splash screen with the company logo. Underneath this is a 1 second polling StatusPing, and as soon as the status ping returns state: RUNNING the system counts down from 10 seconds and redirects to a chosen url.
At any point in the sequence, you can tap the screen and it will hide the splash screen and it throws up a screen with diagnostics on it.
Next stage is to throw a manual IP settings dialog on that diagnostics page as well as a redirect host and URL dialog. Then throw on a simple authentication prompt.

The advantage of all this is that thee HMI boots to a nice clean splash screen in about 20 seconds, then cleanly waits for network and host connectivity before transitioning to the Perspective loader.

By running things this way, we take out all the pauses and wait times from scripts and it is all done on an "as loaded" basis. The web app will even detect the NIC status if the unit is unplugged.

Couple this with a simple plymouth or similar boot animation, and the end user will never see a command window or boot log.

On top of this, snap install ubuntu-frame-vnc and some small configuration to redirect ports and you have a nice remote access system that just works, and doesn't resize the screen like M$ RDP.

2 Likes

Tried using Perspective workstation, but im not running 64bit on my dev system, so that failed.

So, I made the whole thing almost seamless.
Boot times are about 25sec, most of that is actually just dhcpcd trying to sort out the network addressing.
I think you can take off 10 seconds of that if you set the Pi to a static address.
The internal web splash screen immediately runs in Firefox, and if the network connects and the host in the config file reports that the Ignition server is RUNNING, the system opens the chosen URL.
The underlying system is a python webserver that is serving a single page, as well as a bunch of REST endpoints. The endpoints resolve the current host IP, as well as the host status, and the redirect URL.
Javascript on the single served page converses with the API endpoints and manages the splash screen, the diagnostics polling and the redirect.
You can also edit the Redirect URL to point to whatever you want, all straight from the front of the panel.
Because the panel will only redirect if the host is up, if the host is down, the splash screen will indefinitely stay running, and with the magic of SVGs I actually have small part of the company logo turn red to indicate the host is down.
I have momentarily run out of time to make the host url editable, but that will be next.

Below is the diagnostics screen:

2 Likes

This is excellent! I'm envious that you are making the time to develop a universal solution for an Ignition kiosk. We have been chasing other fires for a while now, really looking forward to the day when I can lock myself in a lab (or hire-out a Canonical Dev) to develop the same.
A couple of thoughts I had on your posts above:

I wonder if this additional boot time could be minimized by making Ethernet interfaces configured with 'optional: true' during boot process. Unsure of ramifications with your deployments.

I was looking at doing something similar. I was leaning towards utilizing frame-diagnostic for this, which obviously limits the display to text-only, and w/o a user input. Then again, we're not expecting these gateways to reboot very often, and our IP addresses are local (w/ NAT), statically assigned, and 'never' change.

100% agree! 'Just works', and, like a charm! This, and ubuntu-frame-osk, are primary reasons for the chosen snap-package development path.

I did find a hiccup w/ firefox-frame route. We had several gateways 'randomly' shut down Firefox and did not restart. My best guess is that snap packages are refreshing (as they do, automatically), and Firefox is being asked to shut down by a dependency, but not being restarted afterward. There are no error logs, only an entry for the time that Firefox was stopped normally... For now, we have disabled automatic snap refresh w/ snap refresh --hold. Again, hoping for more time in a lab to play around.

1 Like

So, I have just got my hands on:

This unit is almost perfect for this perspective workstation.
Without optimising Ubuntu server, my boot is 30sec to a loaded page.
The unit is not at all sluggish and loads big complex pages as fast as my laptop.