No Dimmer Switch? Turn It Off At Sunset.
Teaching a Raspberry Pi touchscreen to sleep when there's nothing left to watch
The Problem
One of my PiPiece units runs as a dedicated bird watcher, running around the clock. Once the sun goes down there's nothing left to detect, so the HyperPixel4 touchscreen just holds the last captured photo on screen so it isn't blank. That's the right call for the display content. The problem is it holds it at full brightness, all night, in what's normally a dark room.
The Insight
My first idea was a CSS overlay: fade the display client-side once it got dark. I dropped that fast. A translucent layer drawn over a panel that's still blasting at full brightness underneath isn't dimming, it's just painting over the problem. Real dimming had to happen in hardware.
Digging into the HyperPixel4's kernel overlay turned up the actual constraint. Its backlight is wired through gpio-backlight, a Linux driver whose max_brightness is fixed at 1. There's no PWM node behind it, so /sys/class/backlight/*/brightness only ever accepts 0 or 1. No dimmer switch, just a light switch. "Dim it at night" had to become "turn it off at night, and back on the instant someone needs it."
What I Built
The sunset side of this already existed: server/lib/sunset.js had a hand-rolled nautical sunset calculation driving the report scheduler. I mirrored it into a sunrise function (same astronomy, the local-hour anchor and the sign of one term flip), then built a small scheduler in server/lib/backlight.js that discovers the brightness sysfs path at runtime, rather than hardcoding a device name that isn't guaranteed stable across kernel versions, and writes 0 at sunset and 1 at sunrise.
Two problems remained. The sysfs file is root-owned by default, so the server can't write it without elevated privileges. A udev rule fixed that: match the whole backlight subsystem and grant write access on device add, installed by a small setup script the same way this project already grants USB and touchscreen permissions.
The second was more interesting. If the screen is off and someone walks up to check on the feeder at 11pm, they need a way to see it that doesn't involve a screen they can't see. The touch digitizer runs independently of the GPIO backlight, so it still registers a tap on a completely dark panel. A tap now calls a wake endpoint that turns the backlight back on and starts a short inactivity timer, and only lets it go dark again if it's still actually nighttime when that timer fires.
The Takeaway
The requirement was "dim the screen at night." The hardware answered with "you get on or off, pick one." Chasing the original requirement (a smooth fade) would have meant fighting the kernel's existing consumer of that GPIO pin with a software PWM hack. Accepting the hardware's actual shape (binary, but toggleable) turned it into a scheduling problem I already had most of the pieces for. The constraint didn't block the feature, it just told me what the feature actually was.
The sunrise math, the backlight scheduler, and the tap-to-wake path are all in the PiPiece repo, linked below.
Built with Claude Code, reviewed by me.

