This was done. Many times. Even here.
A simple PIR (passive infrared) connected to Rpi pin can be monitored and when no motion is detected for some time the TV gets turned off. This saves energy and potentially extends the TV life span.
The problem with (most) PIRs
The PIR devices are contain two main components: the actual sensor that converts light into electric signals and a very high-gain amplifier. And here the problem begins. The sensor gives a very weak signal that need to be amplified so it can be detected. The amplifier however will happily amplify anything on its input, including any interference. And RPI being a decently strong EM signal emitter (WiFi radio) when placed close to the sensor will cause unwanted tripping.I have tried the $5 sensors that worked great in the past, both with arduino and other laptops-picture-frames. These worked great as there was no interference source nearby.
The aluminium foil shield did not work. Neither did attempt to decouple power supply with capacitors (apparently it is an inducted interference rather than a noise conducted via supply/ground lines).
However, I found this particular mini PIR sensor resilient to the interference. The cost being the sensitivity - it's detecting motion only in less then 3-4 meters away, while the other sensors were double this.
Bash the code
The complete code (bash script) is on github here:https://github.com/michkrom/pictureframe
The main parts to make PIR work are:
- script to read the GPIO pin state: gpiord
- scripts to turn TV on and off
- main controller script monitoring the PIR and deciding when to turn the TV on or off: pf.sh
Bit and nuts of the script:
Keep time: these two lines store current time int the form of number of seconds since 'epoh'. This is convenient when comparing them later.
lastmotion=`date +%s`
...
nowts=`date +%s`
Here we read the pin #18, it's either 0 or 1.
pinstate=`gpiord 18`
And here we observe detected motion ($pinstate equal 1) and note when this happened.
if [ $pinstate -eq 1 ]; then
lastmotion=$nowts
fi
Then compute how long it was since last time something moved
let lastmotionage=$nowts-$lastmotion
Then decide to show or not (by extending existing schedule condition)
if [ $lastmotionage -lt 600 ] && [ "$timeok" = true ]; then
show=true
fi
That's it.