Work / kiosk

kiosk

Raspberry Pi 4B kiosk system — CW-2400 check-weigher dashboard. Docs, preflight checklist, and all WiFi/display stability fixes.

checkweigher · 2026 · Active

Stack

checkweigher · industrial-iot · kiosk · plc · raspberry-pi

Source on GitHub ↗ · Talk to me →

Readme

Raspberry Pi 4B kiosk running the CW-2400 check-weigher dashboard at Sai Samarth Engineering.

This repository is the system runbook: the full configuration, every stability fix applied, and the procedures to verify or restore it.

Contents

1. System overview

The Pi runs a full-screen kiosk showing the CW-2400 check-weigher dashboard. It serves a local web app from a Python HTTP server on port 8765 and displays it in Chromium kiosk mode under the Wayland compositor labwc, managed by lightdm with auto-login.

Hard requirement: the machine must be reachable over SSH at 192.168.0.212 whether or not the display is connected. Most of the fixes below exist to guarantee that.

2. Hardware

3. Software stack

4. Network configuration

Static IP on wlan0

wlan0 is pinned to a static address so SSH is always at the same place:

IP:      192.168.0.212/24
Gateway: 192.168.0.1
DNS:     8.8.8.8, 8.8.4.4
Metric:  100

Set in /etc/NetworkManager/system-connections/sai @samarth.nmconnection:

[ipv4]
method=manual
address1=192.168.0.212/24,192.168.0.1
dns=8.8.8.8;8.8.4.4;
route-metric=100

WiFi priority

IPv6

Disabled on wlan0 (method=disabled) to avoid address churn that can confuse SSH sessions.

5. WiFi stability fixes

These resolve WiFi slowness and SSH disconnects that occurred when the USB touchscreen or HDMI display was connected.

Fix 1 — WiFi power save disabled

Problem: BCM4345/6 power save puts the chip to sleep between packets. USB HID events from the touchscreen interrupt the sleep/wake cycle, causing latency spikes of several hundred milliseconds.

/etc/NetworkManager/NetworkManager.conf

[connection]
wifi.powersave=2                 # 2 = always off, 3 = always on

[device]
wifi.scan-rand-mac-address=no    # stable MAC during background scans

/etc/NetworkManager/system-connections/sai @samarth.nmconnection (and D-link)

[wifi]
powersave=2

/etc/NetworkManager/dispatcher.d/99-wifi-powersave-off

#!/bin/bash
# Re-enforce power save off every time wlan0 comes up (survives reconnects)
[ "$1" = "wlan0" ] && [ "$2" = "up" ] && iw dev wlan0 set power_save off

Verify: iw dev wlan0 get power_save must print Power save: off.

Fix 2 — brcmfmac background roaming scans disabled

Problem: The driver runs background roaming scans roughly every 30 s. Each scan costs about 100 ms of packet loss.

/etc/modprobe.d/brcmfmac.conf

options brcmfmac roamoff=1

Takes effect on the next reboot, or after sudo rmmod brcmfmac && sudo modprobe brcmfmac.

Fix 3 — USB device power management

Problem: USB autosuspend on the WaveShare touchscreen caused repeated connect/disconnect cycles (visible in dmesg). Each disconnect triggers a USB bus reset that can briefly stall other USB-adjacent drivers.

usbcore.autosuspend=-1 was already set globally in /boot/firmware/cmdline.txt; these device-specific udev rules add a second layer.

/etc/udev/rules.d/99-waveshare-usb.rules

# WaveShare WS170120 touchscreen
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0eef", ATTRS{idProduct}=="0005",
    TEST=="power/autosuspend", ATTR{power/autosuspend}="-1"
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0eef", ATTRS{idProduct}=="0005",
    TEST=="power/control", ATTR{power/control}="on"

# VIA Labs USB 2.0 Hub
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="3431",
    TEST=="power/control", ATTR{power/control}="on"

Fix 4 — static IP applied live

After editing the NetworkManager profile, a reconnect is needed to switch from DHCP-assigned to fully static:

sudo nmcli con reload
sudo nmcli con up "sai @samarth"

Verify: ip addr show wlan0 — the word dynamic must not appear next to the IP.

6. Display and kiosk startup fixes

These resolve SSH failure when HDMI and the USB touchscreen are connected at boot.

Fix 5 — lightdm waits for the network

Problem: lightdm.service had no dependency on network-online.target. Booting with the display connected, lightdm auto-logged in and launched labwc and Chromium while the WiFi driver was still associating. Chromium's GPU process (60% CPU) and network service immediately flooded the still-initialising link, dropping SSH.

/etc/systemd/system/lightdm.service.d/wait-for-network.conf

[Unit]
After=network-online.target
Wants=network-online.target

Apply with sudo systemctl daemon-reload. Resulting boot sequence:

kernel → udev → NetworkManager → [WiFi associates + gets IP] → network-online.target → lightdm → labwc → apps

Fix 6 — Chromium kiosk removed from autostart

Problem: Chromium at boot consumed roughly 60% CPU (GPU process) plus 48% CPU (renderer) — about 110% of one core — starving the WiFi interrupt handler. Combined with the race condition in Fix 5, this made SSH impossible when the display was plugged in at boot.

Status: disabled. The Chromium block in ~/.config/labwc/autostart is commented out and ~/.config/systemd/user/kiosk-server.service is disabled. See section 8 to restore.

Fix 7 — touch device mapped to the correct output

Problem: rc.xml mapped the touchscreen to NOOP-1, a non-existent output. Every session start, labwc tried to apply the mapping, failed, and retried — producing continuous compositor errors and spurious CPU load.

~/.config/labwc/rc.xml

<!-- Before (wrong): -->
<touch deviceName="WaveShare WS170120" mapToOutput="NOOP-1" mouseEmulation="yes"/>

<!-- After (correct): -->
<touch deviceName="WaveShare WS170120" mapToOutput="HDMI-A-2" mouseEmulation="yes"/>

HDMI-A-2 matches both the kanshi output profile and the physical connection.

Fix 8 — kanshi display profile populated

Problem: ~/.config/kanshi/config was empty (0 bytes). The display profile existed only in config.init and was never applied, so resolution and refresh rate were not set deterministically at login.

~/.config/kanshi/config

profile {
    output HDMI-A-2 enable scale 1.000000 mode 1280x800@63.038 position 0,0 transform normal
}

7. Current state

8. Re-enabling the kiosk

Step 1 — start the backend

systemctl --user enable --now kiosk-server.service
curl http://127.0.0.1:8765/api/status     # verify

Step 2 — re-enable Chromium

Edit ~/.config/labwc/autostart and uncomment the Chromium block — remove the # from each line of the lwrespawn /usr/bin/chromium ... section.

Step 3 — restart the session

loginctl terminate-session $XDG_SESSION_ID   # or simply reboot

9. Troubleshooting

SSH unreachable after boot with the display connected

iw dev wlan0 get power_save                            # must be: off
ip addr show wlan0                                     # must not say: dynamic
systemctl cat lightdm.service | grep After             # must include network-online.target
ps aux | grep chromium                                 # should not be running
nmcli -f autoconnect-priority,name,state con show      # check priorities

WiFi drops when the touchscreen USB is plugged in

Read the full README on GitHub →

Thoughts about this

Logging Mitsubishi PLC Data in Real Time on a Raspberry Pi

How I bridge a Mitsubishi PLC to a Raspberry Pi for continuous, reconnect-safe register polling — and why the edge is the right place to do it. Industrial IoT · May 2024 · 8 min read

Building a High-Speed Check-Weigher with IR and Motion Sensors

Capturing a stable weight from a product moving at speed is mostly a timing problem. Here's how IR and motion sensors solve it. Embedded Hardware · Apr 2024 · 10 min read

Pushing Files Over SMB from a Headless Raspberry Pi

A small but load-bearing detail of factory IoT: getting generated files onto the network share reliably, from a device with no screen. Edge Computing · Feb 2024 · 5 min read

More work

View all →

plc-checkweigher

One-command installer for the Mitsubishi PLC check-weigher system. Python · Raspberry Pi · Mitsubishi PLC · PDF Reports · npm CLI

checkweigher

High-speed industrial check-weigher on the edge. Python · Raspberry Pi 4 · XT1000 · T16 Load Cell

Tovex-CRM

Modular CRM platform with real-time dashboards. TypeScript · TypeScript · Modular Architecture · Real-time Dashboards

/ Work / {{ project.name }} {{ project.category }}

{{ project.name }}

{{ project.tagline }}

{{ m }}

Overview

{{ project.description }}

npm i {{ project.npm }}

Highlights

{{ f.n }}

{{ f.text }}

Stack

{{ t }}

Readme

{{ b.s }}

{{ b.s }}

{{ b.s }}

{{ b.s }}
{{ b.s }}
  • {{ it }}

Read the full README on GitHub →

Thoughts about this

More work

View all →

/ Work

Project not found.

That project doesn't exist. Head back to the full list.