Building a Custom Perspective Kiosk on Linux Hardware

Indeed, that looks like a capable machine. Will keep it in mind.
We ordered a few of these recently, good experience so far!

We've been using OnLogic for 95% of our Edge deployments. While we've had a few units sent back for RMA, dealing with their team is always easy, well worth the minor frustrations we've encountered.

On a side note, looks like others have made some good headway on a Perspective-native solution for on-screen-keyboard:

Forum: https://forum.inductiveautomation.com/t/perspective-text-field-numeric-keypad/71229/26?u=chris_bingham
(Edit: Updated Link) Blog: https://aott33.github.io/2024/07/25/perspective-keypad.html
Exchange: https://inductiveautomation.com/exchange/2380/installation

I still like the idea of OS keyboard (mobile users vs desktop vs touchscreen, etc.). However, that Exchange keyboard is looking promising.

I got in touch with OnLogic, but after the appalling customer service experience, they are off the list for any future considerations.

If you read the overview on that keypad, you will see that I wrote the original one he based it on about 18 months ago. He has attributed this nicely in the description, which is actually very nice of him, so kudos to him, also his graphics look nicer than mine.
This keypad is great, but it fails every time on login. So thats why I abandoned it as a solution for touchscreen HMI usage.
The only way to get the login to work with a keypad without editing IA code is to use the OS keyboard.

1 Like

I've been playing around with other Chromium packages. Promising results so-far with Electron. OSK (ubuntu-frame-osk) working as-expected, drop-down menus not crashing the browser. I followed this example (Packaging an Electron application (Quick Start) as an IoT GUI - Docs - Ubuntu Community Hub) as a starting point. Basically, on a development box (with certain dependencies pre-installed, including ubuntu-frame):

cd ~
git clone https://github.com/MirServer/iot-example-graphical-snap.git
cd iot-example-graphical-snap
git checkout 22/Electron-quick-start
# Update default URL @ parts:electron-helloworld:override-build
sudo nano snap/snapcraft.yaml 
# Update chromium flags @ exec $SNAP/.../electron-quick-start
sudo nano wrapper/wrapper.sh
lxd init --auto
snapcraft
sudo snap install --dangerous *.snap
/snap/iot-example-graphical-snap/current/bin/setup.sh
# Edit: Comment below due to plugs should be connected in setup script above.
# sudo snap connect iot-example-graphical-snap:wayland
sudo snap set iot-example-graphical-snap daemon=true

Now taking some vacation for a week. Hope to return and finish setting up a deployable kiosk to test out...

1 Like

This time, focusing exclusively on snap packages. Here are the solutions I've been toying with:

  • Chromium Browser via electron-quick-start (inspiration from the above iot-example snap script, opted for Quick Start | Electron instead)
    • Pros: Lots. Snap package is easy to deploy, should work on Ubuntu Core (needs testing), easy to update... Easy to add additional flags to browser call w/ integration to snap environment vars: 'snap set url', 'snap set host-rules', etc. App loads after reboot without any fancy footwork (i.e. automatically AFTER ubuntu-frame). OSK fully functional at IDP. Scroll bars are meaningful, window rescales as expected, etc.
    • Cons: Found an issue (perhaps the only one, albeit significant) whereby the OSK appears only briefly within Perspective Project when clicking on an input field, then closes & does not reappear :slightly_frowning_face: .
  • Perspective Workstation Snap w/ Xwayland functionality. I was able to package Xwayland and a window manager in a snap, and display a -different- Java app on ubuntu-frame. I've been working through packaging Perspective Workstation in the snap, but keep hitting various Java errors I'm trying to work through.
    • Pros: TBD
    • Cons: The developer. :slight_smile: I haven't been able to successfully load the workstation-linux.jar to test anything yet. Snap environment & confinement are all uphill for this guy. Luckily, I've got a firehose nearby to keep me hydrated.
1 Like

Right now I have a bit of a rollout script for ubuntu-server.
Pre-requisites are Ignition Edge installed and internet access.
Not the neatest but it boots into perspective after the gateway runs.

#!/bin/bash
# Install Ubuntu-Frame:
echo "INFO    | Installing Ubuntu Frame snap..."
snap install ubuntu-frame
snap set ubuntu-frame daemon=true
snap connect ubuntu-frame:wayland

# Install UbuntuFram OSk:
echo "INFO    | Installing Frame OSK snap..."
snap install ubuntu-frame-osk
snap set ubuntu-frame-osk daemon=true
snap set ubuntu-frame-osk theme=dark
snap connect ubuntu-frame-osk:wayland

my_Url="http://demo.inductiveautomation.com"
echo "INFO    | my_Url=http://127.0.0.1:8088/data/perspective/client/Edge"

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

# Install Firefox Snap:
echo "INFO    | Installing CURL ..."
apt install curl

# 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]
Description=Firefox Wayland Kiosk
After=snap.ubuntu-frame.daemon.service snap.ubuntu-frame-osk.daemon.service Ignition-Gateway.service getty.target
Wants=snap.ubuntu-frame-osk.daemon.service
Requires=snap.ubuntu-frame.daemon.service Ignition-Gateway.service
Conflicts=display-manager.service
StartLimitIntervalSec=0

[Service]
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=/usr/bin/sleep 10
ExecStartPre=/snap/bin/firefox --CreateProfile "default"
ExecStartPre=sh /usr/bin/local/runKiosk.sh
ExecStart=/snap/bin/firefox -P default --kiosk --private-window "http://127.0.0.1:8088/data/perspective/client/Edge"
Nice=15

[Install]
WantedBy=graphical.target
EOF

# Create delay script to wait for working gateway:
echo "INFO    | Creating script to delay starting till Ignition Edge is running...."
cat > /usr/bin/local/runKiosk.sh << EOF
#!/bin/bash

until [ "`curl --silent --max-time 1 --fail "http://127.0.0.1:8088/StatusPing" | grep 'RUNNING'`" != "" ];
do
  sleep 2
done
EOF

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

:fireworks: Happy 4th! :fireworks:
I previously mentioned I was going to integrate frame-diagnostic text while our gateway was starting... didn't realize how straightforward that process was going to be. Turns out, with ubuntu-frame running, printing something on the screen is as easy as creating & writing to a file in the ubuntu-frame diagnostic folder (@ $SNAP_COMMON/diagnostic/diagnostic.txt). For my ubuntu server install (with default screen showing after running ubuntu-frame), the below displays some fun text on the screen:

echo -e "Time for a break? Visit:\nhttp://www.pythonchallenge.com/" > /var/snap/ubuntu-frame/common/diagnostic/diagnostic.txt

Taking it a step further, I've modified your runKiosk.sh script to the following for a first-pass at a 'Current Status' display until kiosk is started:

#!/bin/bash
startTime=$EPOCHSECONDS
gatewayState="(ERROR)"
diagFile=/var/snap/ubuntu-frame/common/diagnostic/diagnostic.txt

fx_GatewayRunning () {
  # Poll gateway for status object:
  local gatewaySts=$(curl --silent --max-time 1 --fail "http://127.0.0.1:8088/StatusPing" || true)
  # Extract value of 'state' key from gateway status object:
  gatewayState=$(echo ${gatewaySts} | grep -m1 -oP '"state"\s*:\s*"\K[^"]+')
  # Check for Gateway State = "RUNNING", return True/False to caller:
  [[ "${gatewayState}" == "RUNNING" ]]
  return
}

# Poll Ignition Status, loop until "RUNNING" returned:
until fx_GatewayRunning;
do
  # Calculate elapsed time:
  elapsedTime=$(($EPOCHSECONDS-$startTime))
  if [ $elapsedTime -gt 120 ]; then
    # Timeout elapsed. Print error message to contact support.
    echo -e "Error: Timeout while Starting Ignition Kiosk\nPlease Contact Support" > "${diagFile}"
  else
    # Generate a variable number of dots (1-5, incrementing every 3 seconds):
    numDots=$(((elapsedTime / 3 % 5) + 1)) # Number after modulo sould match # characters in "....." below:
    varDots=$(printf "%.*s" $numDots ".....")
    # Update Diagnostic text on window server:
    echo -e "Checking Ignition Gateway Status\nPlease Wait${varDots}\nStatus: ${gatewayState}" > "${diagFile}"
  fi
  sleep 1
done

# Ignition Gateway is Running:
echo -e "Ignition Gateway is Running\nStarting Ignition Kiosk..." > "${diagFile}"
sleep 2

I haven't installed Ignition (nor kiosk) on my test machine, to determine whether the diagnostic file needs to be removed (or, preferably, updated to 'Contact Support' or some other error-handling text after kiosk is running), but everything in this script appears to function with a remote gateway as it restarts. The "...." after "Please Wait" update every few seconds.:

Note that the 2-minute timeout only updates the display on the screen, it will still advance if the gateway is ever started.

5 Likes

I had to move my blog post to a different website. Updated link is below if you want to update the link in your post:

2 Likes

I updated the Firefox service above to move all [Requires] to [Wants].
I learned that this was a primary factor in reliability of the Firefox service.
Probably should have read the manual a bit closer:

If this unit gets activated, the units listed will be activated as well. If one of the other units fails to activate, and an ordering dependency After= on the failing unit is set, this unit will not be started. Besides, with or without specifying After=, this unit will be stopped (or restarted) if one of the other units is explicitly stopped (or restarted).

Often, it is a better choice to use Wants= instead of Requires= in order to achieve a system that is more robust when dealing with failing services.

1 Like

Interestingly enough, I have had one of these systems deployed for a few months now with no issues. The wait on boot for the network to be up and the ignition instance to be reachable seems to be fairly good, and the boot screens aren't all polished on all platforms, but overall its still better than windows and way faster and more reliable.

2 Likes

https://www.freedesktop.org/wiki/Software/Plymouth/

1 Like

I am not an expert, but I have had numerous issues where Plymouth doesn't load at all. Even with a default install of ubuntu desktop, it will not load the plymouth boot screens. I think it is something to do with the order in which certain kernel modules load in the boot sequence in relation to the rest of the system. On the Phoenix VL3 2340, Plymouth works nicely. On the Seeed ReTerminal DM, Plymouth never loads (Which is odd, because its a Raspberry Pi and it normally works on them). On the Advantech PPC 3100 Plymouth works ok.

Are these vendor supplied OS’s?

No, vendors don't supply Ubuntu Server as we are using in this thread.