GL.iNet-Router-Screen-Saver-BE3600

How it works

In plain words

The router’s front screen is normally drawn by GL.iNet’s own program. This project adds a small helper that watches the touchscreen. When nobody has touched it for a few seconds, the helper stops GL.iNet’s program and plays your animation on the screen instead. A single tap steps to your next saved animation; a quick double-tap stops the animation and starts GL.iNet’s program again, so the normal screen is back instantly. It never lets the two draw at the same time, because that would scramble the picture.

The rest of this page is for people who want the details.

The two owners of the display

The small front display of the GL-BE3600 is a single framebuffer (/dev/fb0, driver fb_st7789p3). GL.iNet’s own UI process, gl_screen, draws to it. Two programs drawing to one framebuffer at once produce garbage, so this project guarantees exactly one owner at a time:

        +--------------------------+
        |  STOCK                   |   gl_screen running, no player
        |  (normal GL.iNet UI)     |
        +--------------------------+
             |                 ^
   IDLE_SECONDS with        a double-tap
   no touch                 on the screen
             v                 |
        +--------------------------+ <--+
        |  CUSTOM                  |    | a single tap: next saved
        |  (your animation)        |    | animation, stay in CUSTOM
        +--------------------------+ ---+
        gl_screen stopped, be3600-player writing frames to /dev/fb0

Every hand-over kills the outgoing owner and waits for it to be gone before starting the incoming one.

Components

File Role
/usr/bin/be3600-screensaver The supervisor. Runs the state machine above. Only one can run at a time (an atomic lock in /tmp/be3600-screen.lock).
/usr/bin/be3600-player The native player: a small static aarch64 program that plays a .bea with exact timing. Source in native/.
/usr/bin/be3600-player.lua The same job in Lua, used if the native player isn’t installed.
/usr/bin/be3600-wait-touch.lua Finds the touchscreen and blocks until a finger touches it, then exits 0.
/usr/bin/be3600-bea-check.lua Validates a .bea file (BEA1 or BEA2) and prints its loop length.
/usr/sbin/be3600-anim The user-facing control: on, off, status, set, check, preview, doctor.
/etc/init.d/be3600-screensaver A procd service that runs the supervisor at boot and respawns it if it dies.
/etc/be3600-screen/config The settings (idle time, touch device, player).
/etc/be3600-screen/active.bea The animation that is played.
/etc/be3600-screen/animations/ The library: up to 3 saved animations by name, each looping 25 s or less. set saves the file here first, and keeps the animation it replaces as previous.
/etc/be3600-screen/verified The firmware version the display was last checked against (see below).

Touch detection

The touch controller is a Hynitron CST816X exposed as /dev/input/event0. When a finger lands it emits ABS_X/ABS_Y and ABS_MT_TRACKING_ID = 0, and ABS_MT_TRACKING_ID = -1 when it lifts. It does not send BTN_TOUCH. be3600-wait-touch.lua reads 24-byte struct input_event records and exits 0 on the first finger-down. It reports only “a finger landed”. It is used while the normal screen is up (to time the idle wait) and as the fallback described below; while an animation plays, the native player reads the touchscreen itself.

Gestures (native player)

While an animation plays, be3600-player --gestures reads /dev/input/event0 in the same process that draws the frames, so no touch can fall in a gap between helper programs (which is what made the old double-tap unreliable) and switching never restarts anything.

The buffering pitfall

The helper opens the device with f:setvbuf("no"). This line matters.

What was observed: with Lua’s default buffered I/O, f:read(24) on this device returned nil immediately, so the helper exited with its “could not read the device” code on every call, with nobody touching anything. In an earlier version of this project the supervisor treated that failure as a touch, so every animation vanished about one second after it started. With buffering off the helper blocks until a real touch and detects it correctly. The hardware and kernel driver were fine throughout: a raw dd of /dev/input/event0 blocked and delivered events, and the touch controller’s interrupt counter rose on every tap.

The likely cause (not confirmed): input devices reject reads smaller than one whole struct input_event (24 bytes), and a buffered stdio read on this router’s C library (musl) may reach the kernel slightly smaller than that. Treat the mechanism as a hypothesis; the fix is what was verified.

Supervisor behaviour worth knowing

Screen pages

The native player draws the pages itself. A page is a function that draws a 284 x 76 picture the way you look at the strip; it is turned into the display’s layout when it is sent to the screen. Pages and animations are all “slots” in one carousel, so the drag-and-slide of the gestures works between any two of them.

native/ is included by be3600-player.c in dependency order, each file named after what is in it:

   
draw.inc anti-aliased shapes, gradients and Inter type, onto a 284 x 76 canvas
sample.inc the live numbers, and the files be3600-widgetd writes
pages_basic.inc clock, network speed, vitals, router info
pages_network.inc clients, internet, data usage, VPN, health
qr.inc a QR encoder, for the Wi-Fi page
pages_touch.inc alerts, and the pages a touch does something to
pages_extra.inc analog, aurora, doctor, talkers
pages.inc the table of them all
hello.inc the welcome after installing

The supervisor remembers which firmware and kernel version it last checked (the verified file). If the version is different, for example after an upgrade, it first probes the display layout (76x284, 16 bpp, 152-byte stride), the stock screen service and the touchscreen. If the probe passes, it remembers the new version and carries on. If it fails, it leaves the normal screen alone, logs the reason, and tries again every minute.

Persistence

How the installer works

Install.cmd / install.sh never copy files one by one. They pack router/, setup/ and animations/ into a single tar stream and pipe it through one ssh session that unpacks it into /tmp on the router and runs setup/router-install.sh. One session means you type the password once, and no scp (OpenWrt’s SSH server has no SFTP) is involved. On Windows the stream is attached with cmd.exe’s < redirection because PowerShell’s own pipe re-encodes data and would corrupt the archive.

setup/router-install.sh then: checks that this really is a BE3600-style router (exit code 3 if not, which makes the desktop installer ask for another address); installs the files and the be3600-uninstall command; unpacks the bundled animation only if the router has no valid one; and starts the service.

Set-Animation.cmd / set-animation.sh validate a .bea locally first, then stream just that file into be3600-anim set on the router in one session.