Tips en Trucs 2019

Meten en regelen

Onze apparaten zitten vol sensoren, deze produceren meetresultaten die ingezet worden om zaken bij te sturen. Zo wordt de temperatuur van de processor in uw computer gemeten om bij oververhitting de processor wat te laten afkoelen door deze minder te belasten. Zo gaat deze langer mee.

Twee weken terug gebruikten we een webcamera om de lichtintensiteit in een ruimte te meten en daarmee de helderheid van de monitor mee te regelen.

De hier gepresenteerde opdrachten en scripts ga je waarschijnlijk niet letterlijk gebruiken, maar ze bevatten wel veel opdrachten waarmee je gegevens kunt afzonderen, opslaan en verwerken met behulp van wiskundige berekeningen.

Meten

Elektronische sensoren werken niet zoals menselijke sensoren. We ervaren vochtige warmte als veel warmer (zelf ondraaglijk) als droge warmte. We merken een brandende kaars in het donker goed op, maar in de zon verdwijnt deze in een overvloed van zonlicht.

Er bestaan dan ook verschillende manieren om bijvoorbeeld de lichtintensiteit via een webcam te meten. Ten eerste door de met de webcam genomen foto te analyseren (zie Schermhelderheid automatisch aanpassen):

dany@pindabook:~> convert fswebcam.jpg -colorspace gray -format "%[fx:1*mean]" info:
0.366426

Je kunt de lichtintensiteit van de omgeving ook bepalen aan de hand van de manier waarop de foto is genomen. Sommige programma's om foto's te nemen slaan die gegevens op in de foto zelf (EXIF gegevens). Om een goede foto te nemen passen camera's naargelang het omgevingslicht hun diafragma-opening (FNumber), sluitertijd (ExposureTime) en de filmgevoeligheid (ISOSpeedRatings) automatisch aan. Aan de hand van deze EXIF waarden kan je op een andere manier de hoeveelheid omgevingslicht meten.

Op een Raspberry Pi kan je met raspistill een foto met deze EXIF gegevens via een PiCamera maken:

pi@rpipindanet:~ $ raspistill -n -w 800 -h 480 -o brightness.jpg

Om de EXIF waarden uit te lezen, installeer je ImageMagick:

pi@rpipindanet:~ $ sudo apt-get install imagemagick

De EXIF gegevens vraag je op met de opdracht:

pi@rpipindanet:~ $ identify -verbose brightness.jpg 
Image: brightness.jpg
  Format: JPEG (Joint Photographic Experts Group JFIF format)
  Mime type: image/jpeg
  Class: DirectClass
  Geometry: 800x480+0+0
...
  Orientation: Undefined
  Properties:
    date:create: 2019-08-24T14:12:01+02:00
...
    exif:ExposureProgram: 3
    exif:ExposureTime: 22108/1000000
    exif:Flash: 0
    exif:FlashPixVersion: 48, 49, 48, 48
    exif:FNumber: 20000/10000
    exif:FocalLength: 30390/10000
    exif:ImageLength: 480
    exif:ImageWidth: 800
    exif:InteroperabilityOffset: 908
    exif:ISOSpeedRatings: 50
    exif:Make: RaspberryPi
...
    signature: 85b95c3277e4a09728dddac3d06d13a47018639df994045af199a41b7e36b45d
  Profiles:
    Profile-exif: 25626 bytes
  Artifacts:
    filename: brightness.jpg
    verbose: true
  Tainted: False
  Filesize: 289KB
  Number pixels: 384K
  Pixels per second: 4.267MB
  User time: 0.090u
  Elapsed time: 0:01.090
  Version: ImageMagick 6.9.7-4 Q16 arm 20170114 http://www.imagemagick.org

Om de EXIF gegevens te verwerken, sla je ze op in een variabele:

pi@rpipindanet:~ $ jpginfo=$(identify -verbose brightness.jpg)

De waarden om de lichtintensiteit van de omgeving te berekenen, haal je er als volgt uit:

pi@rpipindanet:~ $ FNumber=$(grep FNumber <<< "$jpginfo" | awk '{print $2}')
pi@rpipindanet:~ $ ExposureTime=$(grep ExposureTime <<< "$jpginfo" | awk '{print $2}')
pi@rpipindanet:~ $ ISOSpeedRatings=$(grep ISOSpeedRatings <<< "$jpginfo" | awk '{print $2}')
pi@rpipindanet:~ $ echo $FNumber $ExposureTime $ISOSpeedRatings
20000/10000 22108/1000000 50

Uiteindelijk bereken je met deze gegevens het omgevingslicht:

pi@rpipindanet:~ $ lux=$(awk "BEGIN {printf \"%.2f\", ($FNumber * $FNumber) / ($ISOSpeedRatings * $ExposureTime)}")
pi@rpipindanet:~ $ echo $lux
3.62

Deze berekende waarde wordt dan uiteindelijk beschikbaar gemaakt via een http (apache) verbinding. Daar het opzetten van een http verbinding ons te ver zou afleiden, vermeld ik hier dat we daarvoor deze in een speciale http server map opslaan. Het script gedeelte op mijn Raspberry Pi verantwoordelijk voor het maken en opslaan van de gemeten lichtintensiteit wordt dan:

if [ ! -d /var/www/html/motion/day ]; then
  mkdir -p /var/www/html/motion/day;
fi
if [ ! -d /var/www/html/data ]; then
  mkdir -p /var/www/html/data;
fi
now=$(date +%H:%M)
raspistill -n -w 800 -h 480 -o /var/www/html/motion/day/$now.jpg
find /var/www/html/motion/day/*.jpg -mtime +0 -type f -delete

# Calculate lux
jpginfo=$(identify -verbose /var/www/html/motion/day/$now.jpg)
FNumber=$(grep FNumber <<< "$jpginfo" | awk '{print $2}')
ExposureTime=$(grep ExposureTime <<< "$jpginfo" | awk '{print $2}')
ISOSpeedRatings=$(grep ISOSpeedRatings <<< "$jpginfo" | awk '{print $2}')
lux=$(awk "BEGIN {printf \"%.2f\", ($FNumber * $FNumber) / ($ISOSpeedRatings * $ExposureTime)}")
echo $lux  > /var/www/html/data/lux
if [ ! -f /var/www/html/data/luxmax ]; then
  echo 0 > /var/www/html/data/luxmax
fi
luxmax=$(cat /var/www/html/data/luxmax)
if [ ! -f /var/www/html/data/luxmin ]; then
  echo 1000000 > /var/www/html/data/luxmin
fi
luxmin=$(cat /var/www/html/data/luxmin)
if [ ${lux%.*} -eq ${luxmax%.*} ] && [ ${lux#*.} \> ${luxmax#*.} ] || [ ${lux%.*} -gt ${luxmax%.*} ]; then
  luxmax=$lux
fi
if [ ${lux%.*} -eq ${luxmin%.*} ] && [ ${lux#*.} \< ${luxmin#*.} ] || [ ${lux%.*} -lt ${luxmin%.*} ]; then
  luxmin=$lux
fi
echo $luxmax > /var/www/html/data/luxmax 
echo $luxmin > /var/www/html/data/luxmin

Naast het opslaan van de lichtintensiteit, wordt ook een minimum en maximum waarde berekend en opgeslagen. Deze waarden hebben we nodig om straks de helderheid van de monitor te berekenen.

Regelen

Nu we lichtintensiteit van het omgevingslicht kennen, kunnen we deze gebruiken om bijvoorbeeld de helderheid van onze monitor te regelen. Een eerste methode staat beschreven in het artikel Schermhelderheid automatisch aanpassen. Deze hield echter weinig rekening met de werking van ons oog en de verwerking van de oogbeelden door onze hersenen. Een realistischer beeldregeling krijg je door de schermhelderheid niet lineair aan te passen. Het script om de helderheid van de monitor automatisch aan te passen wordt dan:

#!/bin/bash
# brightness.sh

# Bepaal de map van het script
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

# Controleer of het script al gestart is!
aantal=$(ps aux | grep -v "grep" | grep "$0" | wc -l)
# Bij een eerste start wordt het script gestart,
# bij een tweede start wordt het draaiende script afgesloten
if [ $aantal -gt 2 ]; then # Sluit het draaiende script af
  proces=$(ps aux | grep -v "grep" | grep "$0" | awk '{print $2}')
  kill $proces
  exit
fi

# Test op de melding Invalid MIT-MAGIC-COOKIE-1 key
xhost 2>&1 | grep "Invalid MIT-MAGIC-COOKIE-1 key"
if [ $? = 0 ]; then
  echo "Invalid MIT-MAGIC-COOKIE-1 key melding omzeilen"
  xhost local:+
fi

while :
do
    # Welke monitor is actief?
    monitor=$(xrandr | grep " connected" | cut -f1 -d " ")
    echo "Aangeslote monitor: $monitor"

    # Haal het omgevingslicht op
    lux=$(curl -k https://rpipindanet/data/lux)
    if [ $? = 0 ]; then # Ophalen van meting gelukt en kan dus opgeslagen worden
        echo "Omgevingslicht: $lux"
        maxlux=$(curl -k https://rpipindanet/data/luxmax)
        minlux=$(curl -k https://rpipindanet/data/luxmin)
        echo Maximaal gemeten omgevingslicht: $maxlux
        echo Minimaal gemeten omgevingslicht: $minlux
        rangelux=$(awk "BEGIN {printf \"%.2f\", $maxlux - $minlux}")
        echo Bereik gemeten omgevingslicht: $rangelux
        rellux=$(awk "BEGIN {printf \"%.2f\", ($lux - $minlux) / $rangelux}")
        rellux=$(awk "BEGIN {printf sqrt($rellux)}")
        echo Relatief omgevingslicht: $rellux
        helderheid=$(awk "BEGIN {printf \"%.2f\", 0.60 + $rellux * 0.40}")

        # Huidige helderheid
        huidige=$(xrandr --verbose | grep -i brightness | cut -f2 -d ' ' | head -n1)

        # Helderheid geleidelijk aanpassen
        echo "Helderheid aanpassen van $huidige naar $helderheid"
        if [ ${huidige%.*} -eq ${helderheid%.*} ] && [ ${huidige#*.} \< ${helderheid#*.} ] || [ ${huidige%.*} -lt ${helderheid%.*} ]; then
            for i in $( LANG=en_US seq $huidige 0.01 $helderheid ); do
                echo verhogen: $i
                xrandr --output $monitor --brightness $i
                sleep 1
            done
        else
            for i in $( LANG=en_US seq $huidige -0.01 $helderheid ); do
                echo verlagen: $i
                xrandr --output $monitor --brightness $i
                sleep 1
            done
        fi
    fi
        
    # Wacht 1 minuut
    sleep 60
done

De regel

rellux=$(awk "BEGIN {printf sqrt($rellux)}")

zorgt voor het niet lineaire verloop van de regeling (sqrt = vierkantswortel).

Let ook op het gedeelte waarbij we de helderheidsaanpassing van het beeldscherm geleidelijk laten verlopen. Plotse grote helderheidssprongen zijn namelijk storend.

Meten en regelen