Day / night switching (OAP & AA) and brightness control using a TSL2561 sensor
=======================
Update 24/08/2021
Just a
Just an update on this script.
I found that in practice that the script was too reactive to temporary ambient light changes (e.g. shade, trees etc.) and quite distracting when driving. Rather than putting more work into the script fine tuning / troubleshooting I decided to use the script below (which uses the average light reading based on 10 separate readings and also increases / descreases brightness level more slowly).
https://github.com/Grawa/brisensor/blob/...iSensor.py - taken from this post - https://bluewavestudio.io/community/show...p?tid=2398 by grawa
The script has the same functionality as the original in that it both changes brightness level and switches day / night mode for OAP & AA using GPIO.
To date I have found that the brightness levels change far less often, day / night mode also continues to change correctly. There is one minor problem which I have yet to look at - under lower light circumstances the screen will go black for one cycle of the script (i.e. for a couple of seconds). I will put some debugging in and take a look at this.
==================================================
Based on the excellent script originally posted by plasmaflow - https://bluewavestudio.io/community/show...hp?tid=672
I have updated the script so that in addition to automatically changing the screen brightness based on light levels it also toggles / triggers day night mode (also based on light level). Day / night mode is toggled / triggered by setting a nominated GPIO pin (which is monitored by OAP) high or low.
This method suits me as I cannot use headlights on to trigger Day / Night mode as headlights are required 24/7 where I live. I did try using the inbuilt TSL2561 functionality under OAP to switch day / night mode but was unable to get this to work satisfactorily (and in addition I wanted the screen brightness to also change depending on light level). I was also unable to get the phone to control Day / Night mode in AA under Android 11.
With the script night mode is set when the light level drops below a customisable level - so not only sunset but also when driving into a tunnel or dark car park etc. On sunrise or exit from tunnel etc. day mode and the appropriate brightness level is again automatically set. To stop the screen brightness fluctuating continuously /flapping (e.g. when driving in and out of shade or trees etc.) the interval between checks is customisable.
As the script runs independently of OAP it is not necessary to configure TSL2561 sensor support under OAP.
Configuration under OAP involves setting Day / Night mode for both OAP and AA to GPIO and setting the GPIO pin to be monitored as appropriate.
It is necessary to have a working / configured TSL2561 sensor under Raspberry Pi OS before starting.
Script and service files attached below.
README:
SETTINGS FILE (lightsensor_env.sh):
SERVICE FILE (lightsensor.service):
SCRIPT (service_lightsensor.py):
=======================
Update 24/08/2021
Just a
Just an update on this script.
I found that in practice that the script was too reactive to temporary ambient light changes (e.g. shade, trees etc.) and quite distracting when driving. Rather than putting more work into the script fine tuning / troubleshooting I decided to use the script below (which uses the average light reading based on 10 separate readings and also increases / descreases brightness level more slowly).
https://github.com/Grawa/brisensor/blob/...iSensor.py - taken from this post - https://bluewavestudio.io/community/show...p?tid=2398 by grawa
The script has the same functionality as the original in that it both changes brightness level and switches day / night mode for OAP & AA using GPIO.
To date I have found that the brightness levels change far less often, day / night mode also continues to change correctly. There is one minor problem which I have yet to look at - under lower light circumstances the screen will go black for one cycle of the script (i.e. for a couple of seconds). I will put some debugging in and take a look at this.
==================================================
Based on the excellent script originally posted by plasmaflow - https://bluewavestudio.io/community/show...hp?tid=672
I have updated the script so that in addition to automatically changing the screen brightness based on light levels it also toggles / triggers day night mode (also based on light level). Day / night mode is toggled / triggered by setting a nominated GPIO pin (which is monitored by OAP) high or low.
This method suits me as I cannot use headlights on to trigger Day / Night mode as headlights are required 24/7 where I live. I did try using the inbuilt TSL2561 functionality under OAP to switch day / night mode but was unable to get this to work satisfactorily (and in addition I wanted the screen brightness to also change depending on light level). I was also unable to get the phone to control Day / Night mode in AA under Android 11.
With the script night mode is set when the light level drops below a customisable level - so not only sunset but also when driving into a tunnel or dark car park etc. On sunrise or exit from tunnel etc. day mode and the appropriate brightness level is again automatically set. To stop the screen brightness fluctuating continuously /flapping (e.g. when driving in and out of shade or trees etc.) the interval between checks is customisable.
As the script runs independently of OAP it is not necessary to configure TSL2561 sensor support under OAP.
Configuration under OAP involves setting Day / Night mode for both OAP and AA to GPIO and setting the GPIO pin to be monitored as appropriate.
It is necessary to have a working / configured TSL2561 sensor under Raspberry Pi OS before starting.
Script and service files attached below.
README:
Written by PlasmaFlow with sources form Crankshaft and others.
Date 1/22/2019 plasmaflow@ivns.net
Updated 09/11/2020 by 765GHF to enable GPIO based Day / Night mode switching
This software allows the use of a TSL2561 Sensor to set Day / Night mode (using GPIO) and to control screen brightness automatically (based on light level).
The service writes the brightness value to the brightness file on the Pi at:
/sys/class/backlight/rpi_backlight/brightness
The service toggles the state of the nominated GPIO pin in order to set Day / Night mode (Day / Night mode under OAP must be configured to use GPIO)
Files included are:
readme - this file
lightsensor_env.sh - settings file py.
lightsensor.service - Service definition file.
service_lightsensor.py - script
### Prerequisites
Working / configured TSL2561 under Raspberry Pi OS
Ensure selected GPIO pin isn't already in use
### Software
The software is installed with the following commands:
sudo mkdir -p /opt/lightsensor
Copy lightsensor_env.sh & service_lightsensor.py to /opt/lightsensor
sudo chmod +x service_lightsensor.py
sudo chmod +x lightsensor_env.sh
sudo cp lightsensor.service /etc/systemd/system
- Edit (use sudo) /opt/lightsensor/lightsensor_env.sh and set the monitored GPIO pin(e.g. 21)
- Edit (use sudo) /opt/lightsensor/service_lightsensor.py and modify TSL2561 address if required
- Under OAP, Settings, Day / Night enable GPIO mode for OAP and AA
- Under OAP, Settings, Day / Night set GPIO Pin (e.g. 21)
sudo systemctl enable lightsensor.service
sudo systemctl start lightsensor.service
- check output in /var/log/daemon
### Sample Service Status
pi@raspberrypi:/opt/lightsensor $ sudo service lightsensor status
? lightsensor.service - Pi Lightsensor Service
Loaded: loaded (/etc/systemd/system/lightsensor.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2020-11-09 10:50:44 CET; 24s ago
Process: 1384 ExecStartPost=/bin/touch /tmp/auto_brightness_active (code=exited, status=0/SUCCESS)
Main PID: 1383 (python3)
Tasks: 1 (limit: 1173)
CGroup: /system.slice/lightsensor.service
+-1383 /usr/bin/python3 /opt/lightsensor/service_lightsensor.py
Nov 09 10:50:44 raspberrypi systemd[1]: Starting Pi Lightsensor Service...
Nov 09 10:50:44 raspberrypi systemd[1]: Started Pi Lightsensor Service.
Nov 09 10:50:45 raspberrypi python3[1383]: 2020/11/09 10:50:45: INFO: Lux = 2 | Level 1 | Setting Night mode (1)
Nov 09 10:51:06 raspberrypi python3[1383]: 2020/11/09 10:51:06: INFO: Lux = 643 | Level 5 | Setting Day mode (0)
SETTINGS FILE (lightsensor_env.sh):
# Originally Used in Crankshaft.
# Kudos to the guys at Crankshaft for giving me a headstart for controlling the brightness using the TSL2561
#
# Updated 09/11/2020 by 765GHF
#
# GPIO Trigger for Day/Night
# GPIO wich triggers Day (open gpio)/Night (closed to gnd) of GUI
DAYNIGHT_PIN=21
### Screen ###
# Brightness related stuff
# brightness file (default for rpi display: /sys/class/backlight/rpi_backlight/brightness)
BRIGHTNESS_FILE=/sys/class/backlight/rpi_backlight/brightness
# brightness values
BR_MIN=30
BR_MAX=255
BR_STEP=25
BR_DAY=255
BR_NIGHT=30
# Custom brightness control
# Note: this command is called after every brightness change - can slow down for example the brightness
# slider depending on execution speed - the process is called with "&" so call is not waiting for exit!
# Sample call which will be executed on request: "CUSTOM_BRIGHTNESS_COMMAND brightnessvalue &"
#
# Note: To allow backup and restore your command must be located on /boot/crankshaft/custom/
# otherwise it will not be transfered during updates!
#
# To disable leave empty
CUSTOM_BRIGHTNESS_COMMAND=
# Auto brightness control based on tsl2561 light sensor
# Check interval sensor 5,10,15,20,25,30
TSL2561_CHECK_INTERVAL=5
# Switch to night on this level or lower (0 = disabled / 1-5)
TSL2561_DAYNIGHT_ON_STEP=2
# Switch levels for brightness sensor in lux
LUX_LEVEL_1=5
LUX_LEVEL_2=20
LUX_LEVEL_3=100
LUX_LEVEL_4=200
LUX_LEVEL_5=500
# Set this display brightness by switch levels
DISP_BRIGHTNESS_1=30
DISP_BRIGHTNESS_2=90
DISP_BRIGHTNESS_3=150
DISP_BRIGHTNESS_4=210
DISP_BRIGHTNESS_5=255
SERVICE FILE (lightsensor.service):
[Unit]
Description=Pi Lightsensor Service
ConditionPathExists=/opt/lightsensor/service_lightsensor.py
ConditionPathExists=/opt/lightsensor/lightsensor_env.sh
ConditionPathExists=/sys/class/backlight/rpi_backlight/brightness
[Service]
Type=simple
ExecStart=/usr/bin/python3 /opt/lightsensor/service_lightsensor.py
ExecStartPost=/bin/touch /tmp/auto_brightness_active
ExecStop=/bin/rm /tmp/auto_brightness_active
Restart=on-failure
StartLimitInterval=60
RestartSec=5
StartLimitBurst=3
[Install]
WantedBy=multi-user.target
SCRIPT (service_lightsensor.py):
#!/usr/bin/python3 -u
import RPi.GPIO as GPIO
import smbus
import os
import subprocess
from time import sleep
import logging
LOG_LEVEL = 'DEBUG'
DayNight = 2
# Originally Used in Crankshaft.
# Kudos to the guys at Crankshaft for giving me a headstart for controlling the brightness using the TSL2561
#
# This file was re written to write the brightness variable in /sys/class/backlight/rpi_backlight/brightness
# re written by PlasmaFlow 1/22/19
#
# Hacked 09/11/2020 by 765GHF to set Day/Night mode in OAP / AA based on light level using GPIO
#
def get_var(varname):
try:
CMD = 'echo $(source /opt/lightsensor/lightsensor_env.sh; echo $%s)' % varname
p = subprocess.Popen(CMD, stdout=subprocess.PIPE, shell=True, executable='/bin/bash')
return int(p.stdout.readlines()[0].strip())
except:
CMD = 'echo $(source /opt/lightsensor/lightsensor_default_env.sh; echo $%s)' % varname
p = subprocess.Popen(CMD, stdout=subprocess.PIPE, shell=True, executable='/bin/bash')
return int(p.stdout.readlines()[0].strip())
# ---------------------------------
# the addresss of TSL2561 can be
# 0x29, 0x39 or 0x49
BUS = 1
TSL2561_ADDR = 0x39
# ---------------------------------
i2cBus = smbus.SMBus(BUS)
daynight_gpio = get_var('DAYNIGHT_PIN')
sleep_time = get_var('TSL2561_CHECK_INTERVAL')
# GPIO set up
# specify pin numbering format
GPIO.setmode(GPIO.BCM)
# set pin to output mode
GPIO.setup(daynight_gpio, GPIO.OUT)
# Logging setup
logging.basicConfig(
format='%(asctime)s: %(levelname)s: %(message)s',
datefmt='%Y/%m/%d %H:%M:%S',
level=LOG_LEVEL
)
# Start messure with 402 ms
# (scale factor 1)
i2cBus.write_byte_data(TSL2561_ADDR, 0x80, 0x03)
lastvalue = 0
GPIO.output(daynight_gpio,0)
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_5')))
file.close()
sleep (1)
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_4')))
file.close()
sleep (1)
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_3')))
file.close()
sleep (1)
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_2')))
file.close()
GPIO.output(daynight_gpio,1)
sleep (1)
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_1')))
file.close()
sleep (3)
while True:
# Have to set GPIO mode each iteration as OAP sets mode to input
GPIO.setup(daynight_gpio, GPIO.OUT)
# read global brightness read low byte
LSB = i2cBus.read_byte_data(TSL2561_ADDR, 0x8C)
# read high byte
MSB = i2cBus.read_byte_data(TSL2561_ADDR, 0x8D)
Ambient = (MSB << 8) + LSB
logging.debug('Ambient:%s' %Ambient)
# read infra red read low byte
LSB = i2cBus.read_byte_data(TSL2561_ADDR, 0x8E)
# read high byte
MSB = i2cBus.read_byte_data(TSL2561_ADDR, 0x8F)
Infrared = (MSB << 8) + LSB
logging.debug('Infrared:%s' %Infrared)
# Calc visible spectrum
Visible = Ambient - Infrared
logging.debug('Visible:%s' %Visible)
# Calc factor Infrared/Ambient
Ratio = 0
Lux = 0
if Ambient != 0:
Ratio = float(Infrared)/float(Ambient)
logging.debug('Ratio:%s' %Ratio)
# Calc lux based on data sheet TSL2561T T, FN, and CL Package
if 0 < Ratio <= 0.50:
Lux = 0.0304*float(Ambient) - 0.062*float(Ambient)*(Ratio**1.4)
elif 0.50 < Ratio <= 0.61:
Lux = 0.0224*float(Ambient) - 0.031*float(Infrared)
elif 0.61 < Ratio <= 0.80:
Lux = 0.0128*float(Ambient) - 0.0153*float(Infrared)
elif 0.80 < Ratio <= 1.3:
Lux = 0.00146*float(Ambient) - 0.00112*float(Infrared)
else:
Lux = 0
Luxrounded = round(Lux,1)
logging.debug('Lux = %s' %Luxrounded)
if lastvalue != Luxrounded:
lastvalue = Luxrounded
#Set display brightness
if Luxrounded <= get_var('LUX_LEVEL_1'):
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_1')))
file.close()
step = 1
logging.debug('Setting brightness to step %d' %step)
elif Luxrounded > get_var('LUX_LEVEL_1') and Luxrounded < get_var('LUX_LEVEL_2'):
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_2')))
file.close()
step = 2
logging.debug('Setting brightness to step %d' %step)
elif Luxrounded >= get_var('LUX_LEVEL_2') and Luxrounded < get_var('LUX_LEVEL_3'):
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_3')))
file.close()
step = 3
logging.debug('Setting brightness to step %d' %step)
elif Luxrounded >= get_var('LUX_LEVEL_3') and Luxrounded < get_var('LUX_LEVEL_4'):
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write( str(get_var('DISP_BRIGHTNESS_4')))
file.close()
step = 4
logging.debug('Setting brightness to step %d' %step)
elif Luxrounded >= get_var('LUX_LEVEL_5'):
file = open("/sys/class/backlight/rpi_backlight/brightness", "w")
file.write(str(get_var('DISP_BRIGHTNESS_5')))
file.close()
step = 5
logging.debug('Setting brightness to step %d' %step)
logging.debug('GPIO pin is %s' %daynight_gpio)
logging.debug('GPIO status is %s' %GPIO.input(daynight_gpio))
logging.debug('Step to change to night is %s' %get_var('TSL2561_DAYNIGHT_ON_STEP'))
logging.debug('Step is %s' %step)
logging.debug('Day / Night mode is %d' %DayNight)
# Set day / night mode based on step
if step <= get_var('TSL2561_DAYNIGHT_ON_STEP') and DayNight != 1:
logging.info('Lux = %d | Level %d | Setting Night mode (1)', Luxrounded, step)
GPIO.output(daynight_gpio,1)
DayNight = 1
elif step > get_var('TSL2561_DAYNIGHT_ON_STEP') and DayNight !=0:
logging.info('Lux = %d | Level %d | Setting Day mode (0)', Luxrounded, step)
GPIO.output(daynight_gpio,0)
DayNight = 0
sleep (sleep_time)
GPIO.cleanup()