Contribute an automatic display brightness example

Standalone smoothed display auto-brightness, reactive to the on-board
LUX and battery ADC sensors, with debugging keys so it's easier to test
(and play with).

Closes pull requests #628 and #629, which did this to the retro_badge
example, but made it too complicated.
This commit is contained in:
LionsPhil 2023-03-02 00:21:50 +00:00
parent 8e91108ce5
commit 0a6d6b91b3
1 changed files with 115 additions and 0 deletions

View File

@ -0,0 +1,115 @@
# Automatic brightness example.
import time
from machine import ADC, Pin
from pimoroni import Button
from picographics import PicoGraphics, DISPLAY_TUFTY_2040
import micropython
# Constants for automatic brightness adjustment.
# Below about 3/8ths, the backlight is entirely off. The top of the range is OK.
BACKLIGHT_LOW = micropython.const(0.375)
BACKLIGHT_HIGH = micropython.const(1.0)
# The luminance sensor seems to cover the whole 16-bit range pretty well from
# buried in shadow to bright phone torch in its face, but setting a lower high
# point will make it generally bias brighter in calm room lighting, and a higher
# low will use the darkest backlight without needing to completely hard-mask the
# sensor in complete darkness.
LUMINANCE_LOW = micropython.const(384)
LUMINANCE_HIGH = micropython.const(2048) # 65535 to use the full range.
# If on battery, and the supply voltage drops below this, force minimum
# backlight brightness.
# The bottom of the Tufty2040 input range is 3.0v, so values below that likely
# will not trigger before it cuts out.
LOW_BATTERY_VOLTAGE = micropython.const(3.1)
# Hold button A to pretend the LUX sensor is seeing maximally bright light.
# (To test darkness, put your finger over the sensor! :D )
button_a = Button(7, invert=False)
# Hold button B to pretend the system is on battery, and the battery is low.
button_b = Button(8, invert=False)
# Pins and analogue-digital converters we need to set up to measure sensors.
lux_vref_pwr = Pin(27, Pin.OUT)
lux = ADC(26)
vbat_adc = ADC(29)
vref_adc = ADC(28)
usb_power = Pin(24, Pin.IN)
display = PicoGraphics(display=DISPLAY_TUFTY_2040)
display.set_backlight(1.0)
display.set_font("bitmap8")
WHITE = display.create_pen(255, 255, 255)
BLACK = display.create_pen(0, 0, 0)
WIDTH, HEIGHT = display.get_bounds()
# Returns a tuple of the raw luminance value, and the brightness to now use.
def auto_brightness(previous: float) -> (float, float):
luminance = lux.read_u16()
if button_a.is_pressed: # Debug key.
luminance = 65535
luminance_frac = max(0.0, float(luminance - LUMINANCE_LOW))
luminance_frac = min(1.0, luminance_frac / (LUMINANCE_HIGH - LUMINANCE_LOW))
backlight = BACKLIGHT_LOW + (luminance_frac * (BACKLIGHT_HIGH - BACKLIGHT_LOW))
# Use the previous value to smooth out changes to reduce flickering.
# The "32" value here controls how quickly it reacts (larger = slower).
# The rate at which the main loop calls us also affects that!
backlight_diff = backlight - previous
backlight = previous + (backlight_diff * (1.0 / 32.0))
return (luminance, backlight)
# Returns a tuple of voltage (fake value if on USB), "is on USB", and "is low".
def measure_battery() -> (float, bool, bool):
if button_b.is_pressed: # Debug key.
return (3.0, False, True)
if usb_power.value():
return (5.0, True, False)
# See the battery.py example for how this works.
vdd = 1.24 * (65535 / vref_adc.read_u16())
vbat = ((vbat_adc.read_u16() / 65535) * 3 * vdd)
low_battery = False
if vbat < LOW_BATTERY_VOLTAGE:
low_battery = True
return (vbat, False, low_battery)
backlight = BACKLIGHT_LOW
while True:
# Turn on VREF and LUX only while we measure things.
lux_vref_pwr.value(1)
(vbat, on_usb, low_battery) = measure_battery()
if low_battery:
backlight = BACKLIGHT_LOW
else:
(luminance, backlight) = auto_brightness(backlight)
lux_vref_pwr.value(0)
# Set the new backlight value.
display.set_backlight(backlight)
# Show our measurements.
display.set_pen(WHITE)
display.clear()
display.set_pen(BLACK)
display.text(f"Backlight: {backlight * 100:03.0f}%", 8, 8, WIDTH - 8, 4)
if low_battery:
display.text("Luminance: Ignored, low battery!", 8, 48 + (20 * 1), WIDTH - 8, 2)
else:
display.text(f"Luminance: {luminance:05.0f} (of 65535)", 8, 48 + (20 * 1), WIDTH - 8, 2)
if on_usb:
display.text("Battery: Ignored, on USB.", 8, 48 + (20 * 2), WIDTH - 8, 2)
else:
display.text(f"Battery: {vbat:.2f}v", 8, 48 + (20 * 2), WIDTH - 8, 2)
display.text("Hold A to pretend the room is bright.", 8, HEIGHT - (20 * 4), WIDTH - 8, 2)
display.text("Hold B to pretend the battery is low.", 8, HEIGHT - (20 * 2), WIDTH - 8, 2)
display.update()
time.sleep(0.1)