This Gist documents a complete, reproducible setup for a Raspberry Pi–driven wall touchscreen that:
-
Turns the monitor off after N minutes of no activity.
-
Turns the monitor on when:
- The camera detects motion nearby, or
- The user touches/moves the mouse/keyboard.
-
Re‑applies touchscreen rotation/calibration every time the display wakes.
Assumptions:
- Raspberry Pi OS Bookworm.
- Running an X11 session (Openbox/LXDE or similar).
- A USB webcam (or any V4L2 camera) visible as
/dev/video0. - Monitor supports DPMS standby (most do).
sudo apt update
sudo apt install motion x11-xserver-utils xprintidle v4l-utilsmotion: detects movement using the camera.x11-xserver-utils: providesxsetfor DPMS control.xprintidle: reads the X11 idle timer (touch/KB/mouse).v4l-utils: camera debugging tools.
echo $XDG_SESSION_TYPE
# should print: x11On Bookworm/X11, DPMS may be disabled until Screen Blanking is enabled.
sudo raspi-config
# Display Options -> Screen Blanking -> EnableReboot once after changing this.
Run these in your desktop session:
export DISPLAY=:0
xset dpms force off
sleep 2
xset dpms force onIf force works, you’re good.
We want our daemon to decide when to sleep, not the desktop’s own timers.
Run once (and later add to autostart if desired):
export DISPLAY=:0
xset s off
xset s noblank
xset -dpms
xset dpms 0 0 0Notes:
- This disables timeouts, but DPMS can still be forced on/off by the daemon.
Confirm your camera is detected:
v4l2-ctl --list-devicesYou should see something like:
/dev/video0
If your camera appears under a different node, update Motion’s video_device accordingly.
This script writes a fresh epoch timestamp every time Motion sees movement.
sudo tee /usr/local/bin/presence_event.sh >/dev/null <<'EOF'
#!/bin/bash
# Called by motion when movement is detected.
# Write a timestamp to a tmp file read by the presence daemon.
echo "motion $(date +%s)" > /tmp/presence_event
EOF
sudo chmod +x /usr/local/bin/presence_event.shOverwrite /etc/motion/motion.conf with the file in the companion Gist motion.conf (see files list).
Key requirements inside the config:
daemon off(so systemd keeps it running)video_device /dev/video0on_motion_detected /usr/local/bin/presence_event.sh- outputs disabled (no photos/movies)
Restart Motion:
sudo systemctl enable motion
sudo systemctl restart motion
sudo systemctl status motion --no-pager -lExpect:
Active: active (running)
Move in front of the camera, then:
cat /tmp/presence_eventExample:
motion 1763827202
If this file doesn’t update while you move, Motion sensitivity needs tuning (section 7).
If you already have a working rotation/calibration script, keep using it. This setup assumes it is located at:
~/auto-rotate.shYour daemon will re‑run this script on every wake.
If your script name/path differs, update the ROTATE_SCRIPT variable in presence_panel.py.
Place the Python daemon from the companion file presence_panel.py into:
~/presence_panel.py
chmod +x ~/presence_panel.pyManual test:
python3 ~/presence_panel.pyBehavior:
- If the monitor is off and motion is detected, DPMS wakes it.
- If someone taps/moves input,
xprintidleresets the timer even if the camera misses it. - After wake,
~/auto-rotate.shruns so touch calibration matches rotation. - Screen turns off after
OFF_TIMEOUTseconds of no motion/input.
Most false positives/negatives are fixed by adjusting a small set of Motion parameters.
Edit:
sudo nano /etc/motion/motion.conf
sudo systemctl restart motion| Setting | What it does | How to tune |
|---|---|---|
threshold |
How much change counts as motion | Increase to reduce false positives; decrease if it misses people. Start around 1500. |
noise_level |
Filters low‑level pixel noise | Increase if flickering light triggers motion. |
event_gap |
Seconds without motion before an event ends | Increase if you want fewer start/stop events. Doesn’t affect wake timing much if you use on_motion_detected. |
width/height |
Detection resolution | Lower = less CPU and less sensitivity to small changes. 640×480 is a good start. |
framerate |
Detection FPS | Lower = less CPU. 5 FPS is usually enough for presence. |
-
Watch logs live:
sudo journalctl -u motion -f
-
Walk in/out, note triggers.
-
Change one parameter.
-
Restart Motion.
-
Repeat.
- Triggers from TV/windows: raise
threshold, raisenoise_level, or physically aim the camera away. - Misses slow movement: lower
threshold, increaseframerateslightly. - Too much CPU: lower
width/height, lowerframerate, disable streaming (already disabled here).
Create the service:
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/presence-panel.servicePaste:
[Unit]
Description=Presence-based panel control (X11)
[Service]
ExecStart=/usr/bin/python3 %h/presence_panel.py
Restart=on-failure
[Install]
WantedBy=default.targetEnable/start:
systemctl --user daemon-reload
systemctl --user enable presence-panel
systemctl --user restart presence-panel
systemctl --user status presence-panel --no-pager -l-
Ensure hook lines are not commented.
-
Confirm Motion is reading
/etc/motion/motion.conf. -
Check for script exec errors in logs:
sudo journalctl -u motion --no-pager | tail -n 120
-
Ensure
/tmp/presence_eventtimestamp changes when you move. -
Confirm daemon is running:
systemctl --user status presence-panel
- Verify your
~/auto-rotate.shworks by running it manually. - Ensure the path is correct in
presence_panel.py.
motion.conf— full Motion configuration used here.presence_panel.py— full presence daemon.presence-panel.service— systemd user unit.
Here's what the current (very basic) display setup looks like, its primary use is as a schedule reminder / discussion aid:
I've got other plans on the horizon, but for now it serves its purpose well.