Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contribute an automatic display brightness example #701

Merged
merged 1 commit into from
Mar 3, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions micropython/examples/tufty2040/autobright.py
Original file line number Diff line number Diff line change
@@ -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)