Tufty 2040 > code > Tufty 2350

@BillyTPilgrim has helped me get it to run as an App. I was saving it as _init_.py when it should have been double underscores, __init__.py.
@SirFico
And I now have the min max temperature working. =)

    global max_temperature
    if t >= max_temperature:
        max_temperature = t
    global min_temperature
    if t <= min_temperature:
        min_temperature = t

Still a few things to work on. Battery state, charging or percent left. And reading the light sensor (lux) to do auto backlight / screen brightness. That’s for another day, I’m going to take a break for now. I have degrative disks in my neck and back. Right now my neck is telling me to take a break from hunkering over the keyboard.

@alphanumeric Splendid, that takes care of the min and max :) Enjoy a well earned break, its easy to get carried away once one gets stuck into some coding.

Actually I have a similar horizontal bar graph designed for different colour breaks (for a different screen) and use it for a graphic display of greenhouse sensor readings and I also record max and min values.

But I goofed as I recorded the max and min values in relation to a human day. For a greenhouse the ‘day’ should really start a short while after sunrise so about 6 am at the moment, and of course it will vary as the seasons progress. Its on my vague to do list to find a chart of sunrise times and amend my max and min times accordingly. So your post reminds me to get on with it before the growing season starts :-)

You can query sunrise/sunset from Open-Meteo. Since they provide forecasts and the forecast for these variables is very stable, you can do it e.g. on a weekly schedule.

@bablokb Thanks, thats a very useful site, just what I was looking for.

I’ve found the code I need for the battery state. =)

badge.battery_level()
#Returns an int representing the battery level as a percentage from 0 to 100.
print (badge.battery_level())

badge.battery_voltage()
#Returns a float representing the current battery voltage.
print (badge.battery_voltage())

badge.usb_connected()
#Returns a boolean reflecting whether the USB cable is currently connected.
print(badge.usb_connected())

badge.is_charging()
#Returns a boolean reflecting whether the battery is currently charging.
print(badge.is_charging())

It’s plugged in and fully charged.

MPY: soft reboot
100
4.214976
True
False

I’ve also found the code to read the built in light sensor.

badge.light_level()
#Returns the level detected by the light sensor as a raw u16 value.
print (badge.light_level())
MPY: soft reboot
1936

I’ll have to go dig out my flashlight and play around with what values I get. Been there and done that with my Tufty 2040.

Something doesn’t quit seem right with the case lights code though?
Or I’m doing something wrong?

@BillyTPilgrim You don’t happen to know where the code is stored for the battery widget that’s displayed on the Badge OS… App select screen is?

EDIT: It looks like its in /system/apps/menu/ui.py

Looks like on Tufty you wanna be looking at apps/menu/ui.py from line 87 onward :D

Thanks, I found it. I think you posted while I was doing my edit? I didn’t see your post until after I clicked Save Edit.

def draw_header():
    # create animated header text
    dots = "." * int(math.sin(badge.ticks / 250) * 2 + 2)
    label = f"BadgeOS{dots}"
    pos = (5, 2)

    # draw the OS title
    screen.pen = phosphor
    screen.text(label, *pos)

    # draw the battery indicator
    if badge.is_charging():
        battery_level = (badge.ticks / 20) % 100
    else:
        battery_level = badge.battery_level()
    pos = (137, 4)
    size = (16, 8)
    screen.pen = phosphor
    screen.shape(shape.rectangle(*pos, *size))
    screen.shape(shape.rectangle(pos[0] + size[0], pos[1] + 2, 1, 4))
    screen.pen = background
    screen.shape(shape.rectangle(pos[0] + 1, pos[1] + 1, size[0] - 2, size[1] - 2))

    # draw the battery fill level
    width = ((size[0] - 4) / 100) * battery_level
    screen.pen = phosphor
    screen.shape(shape.rectangle(pos[0] + 2, pos[1] + 2, width, size[1] - 4))


Ok, I’m now working on the auto brightness function.

from badgeware import set_brightness
set_brightness(1.0)

BACKLIGHT_LOW = micropython.const(0.35)
BACKLIGHT_HIGH = micropython.const(1.0)
LUMINANCE_LOW = micropython.const(800)
LUMINANCE_HIGH = micropython.const(3072)  # 65535 to use the full range.

def auto_brightness(previous: float) -> (float, float):
    '''
    luminance = lux.read_u16()
    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))
    backlight_diff = backlight - previous
    backlight = previous + (backlight_diff * (1.0 / 8.0))
    return (luminance, backlight)
    '''
    luminance = badge.light_level()
    print (badge.light_level())
    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))
    backlight_diff = backlight - previous
    backlight = previous + (backlight_diff * (1.0 / 8.0))
    return (luminance, backlight)

backlight = BACKLIGHT_LOW

def update():
    
    (luminance, backlight) = auto_brightness(backlight)
    set_brightness(backlight)  
MPY: soft reboot
- ERROR:   File "badgeware/__init__.py", line 52, in __call__
  File "<stdin>", line 282, in update
NameError: local variable referenced before assignment

I’m back to that NameError: local variable referenced before assignment. And I don’t know what to set as Global? Assuming that’s what I have to do?

Got it.

def update():
    global backlight
    (luminance, backlight) = auto_brightness(backlight)
    set_brightness(backlight)
    print (backlight)

Adjusted the min value to 0.45 and it’s looking good. =)

1 Like

This is my latest, and as far as I know, working code. I have replicated what I had done on my Tufty 2040, and added a bit here and there.
By default auto brightness is enabled, and the case lighting is off. Pressing the UP button turns the case lighting on, and sets the back light to full brightness. Pressing the A button will re enable the auto brightness. It has no effect on the case lighting. Pressing the DOWN Button turns the display back light to off and turns off the case lighting.
Pressing and Holding the B button will swap the Time Date with Battery info, voltage and % left.
Pressing the C button will reset the Min Max temperature readings to the current Temperature.
If the temperature goes to Zero 'C or lower, the temperature graph switches to Winter mode. -30 to +30. If it then goes back above 12 'C it switches back to Summer mode. 0 to +30 I “think” it’s 30, I’d have to decipher what I did originally to verify. The marker just stops when it bumps up against the edge of the screen. The numerical display will keep going though and track the actual reading.

import time
import micropython
badge.mode(HIRES)
from badgeware import set_brightness
set_brightness(1.0)
BACKLIGHT_LOW = micropython.const(0.5)
BACKLIGHT_HIGH = micropython.const(1.0)
LUMINANCE_LOW = micropython.const(80)
LUMINANCE_HIGH = micropython.const(3072)  # 65535 to use the full range.
backlight = BACKLIGHT_LOW

def auto_brightness(previous: float) -> (float, float):
    luminance = badge.light_level()
    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))
    backlight_diff = backlight - previous
    backlight = previous + (backlight_diff * (1.0 / 8.0))
    return (luminance, backlight)

from breakout_bme280 import BreakoutBME280
from machine import I2C

temperature_sensor = BreakoutBME280(I2C())
last_ticks = badge.ticks

t = 0
p = 0
h = 0
A = 1
C = 1

screen.font = rom_font.ignore
screen.pen = color.white
h_color = screen.pen
p_color = screen.pen
background = color.rgb(60, 15, 10)
phosphor = color.rgb(246, 135, 4)

def get_reading():
    global t, p, h
    temperature, pressure, humidity = temperature_sensor.read()
    pressuremb = pressure / 100
    t = round(temperature)
    h = round(humidity)
    p = round(pressuremb)
    
get_reading()
time.sleep (0.25)
get_reading()
min_temperature = t
max_temperature = t

def describe_humidity(h):
    global h_color
    if h < 30:
        description = "Low"
        h_color = color.red
    elif 30 <= h <= 60:
        description = "OK"
        h_color = color.green
    elif 60 < h < 80:
        description = "High"
        h_color = color.yellow
    elif h >= 80:
        description = "Very High"
        h_color = color.orange        
    return description

def describe_pressure(p):
    global p_color
    if p < 982:
        description = "Very Low"
        p_color = color.red
    elif 982 <= p < 1004:
        description = "Low"
        p_color = color.yellow
    elif 1004 <= p < 1026:
        description = "OK"
        p_color = color.green
    elif 1026 <= p < 1048:
        description = "High"
        p_color = color.blue
    elif p >= 1048:
        description = "Very High"
        p_color = color.orange
    return description

def draw_graph():
    global C
    if t <= 0:
        C = 0
    if t >= 12:
        C = 1    
    
    if C == 1:
        scaled_temp = int(t * 10)
        if scaled_temp <= 9:
            scaled_temp =9
        if scaled_temp >= 310:
            scaled_temp =310
        screen.pen = color.yellow
        screen.rectangle(9,89,116,19)
        screen.pen = color.white
        screen.circle(9,98,9)
        screen.pen = color.green
        screen.rectangle(127,89,111,19)
        screen.pen = color.orange
        screen.rectangle(240,89,28,19)
        screen.pen = color.red
        screen.rectangle(270,89,44,19)
        screen.circle(310,98,9)    
    else:    
        scaled_temp = int((t * 5) + 160)
        if scaled_temp <= 9:
            scaled_temp =9
        if scaled_temp >= 310:
            scaled_temp =310
        screen.pen = color.blue
        screen.circle(9, 98, 9)
        screen.rectangle(12, 89, 92, 19)
        screen.pen = color.white
        screen.rectangle(106, 89, 53, 19)
        screen.pen = color.yellow
        screen.rectangle(161,89,58,19)
        screen.pen = color.green
        screen.rectangle(221,89,58,19)
        screen.pen = color.orange
        screen.rectangle(281,89,13,19)
        screen.pen = color.red
        screen.rectangle(296,89,15,19)
        screen.circle(310,98,9)
            
    scaled_humid = int(h * (320 / 100))
    if scaled_humid <= 9:
        scaled_humid =9
    if scaled_humid >= 310:
        scaled_humid =310
    humidity_text = describe_humidity(h)
    screen.pen = (h_color)
    screen.text(humidity_text,130,118)
    
    scaled_press = int((p - 960) * 3)
    if scaled_press <= 9:
        scaled_press =9
    if scaled_press >= 310:
        scaled_press =310
    pressure_text = describe_pressure(p)
    screen.pen = (p_color)
    screen.text(pressure_text,130,184) 
    screen.pen = color.red
    screen.circle(9, 230, 9)
    screen.circle(9, 164, 9)
    screen.rectangle(12,221,50,19)
    screen.rectangle(12,155,82,19)
    screen.pen = color.yellow
    screen.rectangle(64,221,62,19)
    screen.rectangle(192,155,62,19)
    screen.pen = color.green
    screen.rectangle(128,221,62,19)
    screen.rectangle(96,155,94,19)
    screen.pen = color.blue
    screen.rectangle(192,221,62,19)
    screen.pen = color.orange
    screen.rectangle(256,221,52,19)
    screen.circle(310, 230, 9)
    screen.rectangle(256,155,55,19)
    screen.circle(310,164,9)
    screen.pen = color.black
    screen.line(127,221,127,240)
    screen.circle((scaled_temp),98,9)
    screen.circle((scaled_humid),164,9)
    screen.circle((scaled_press),230,9)
    screen.pen = color.white
    screen.circle((scaled_temp),98,5)
    screen.circle((scaled_humid),164,5)
    screen.circle((scaled_press),230,5)

def describe_month(month):
    if month == 1:
        description = "Jan"
    elif month == 2:
        description = "Feb"  
    elif month == 3:
        description = "Mar"
    elif month == 4:
        description = "Apr"              
    elif month == 5:
        description = "May"              
    elif month == 6:
        description = "Jun"              
    elif month == 7:
        description = "Jul"              
    elif month == 8:
        description = "Aug"              
    elif month == 9:
        description = "Sep"
    elif month == 10:
        description = "Oct"             
    elif month == 11:
        description = "Nov"              
    elif month == 12:
        description = "Dec"            
    return description

def describe_day(day):
    if day == 1:
        description = "st"
    elif day == 2:
        description = "nd"     
    elif day == 3:
        description = "rd" 
    elif day == 21:
        description = "st"      
    elif day == 22:
        description = "nd"  
    elif day == 23:
        description = "rd"
    elif day == 31:
        description = "st"
    else:
        description = "th"
    return description

def draw_battery():
    if badge.is_charging():
        battery_level = (badge.ticks / 20) % 100
    else:
        battery_level = badge.battery_level()
    pos = (275, 16)
    size = (32, 16)
    screen.pen = phosphor
    screen.shape(shape.rectangle(*pos, *size))
    screen.shape(shape.rectangle(pos[0] + size[0], pos[1] + 4, 2, 8))
    screen.pen = background
    screen.shape(shape.rectangle(pos[0] + 1, pos[1] + 1, size[0] - 2, size[1] - 2))
    width = ((size[0] - 4) / 100) * battery_level
    screen.pen = phosphor
    screen.shape(shape.rectangle(pos[0] + 2, pos[1] + 2, width, size[1] - 4))

def update():
    screen.pen = color.black
    screen.clear()
    
    global last_ticks
    if badge.ticks - last_ticks >= 2000:
        last_ticks = badge.ticks
        get_reading()
                    
    if badge.pressed(BUTTON_A):
        global A
        A = 1
    if badge.pressed(BUTTON_C):
        C = 1
        global min_temperature
        min_temperature = t
        global max_temperature
        max_temperature = t
        time.sleep(0.2)  
    if badge.pressed(BUTTON_UP):
        global A
        A = 0
        set_brightness(1)
        badge.caselights(1)
    if badge.pressed(BUTTON_DOWN):
        global A
        A = 0
        set_brightness(0)        
        badge.caselights(0)
    if A == 1:
        global backlight
        (luminance, backlight) = auto_brightness(backlight)
        set_brightness(backlight)
    if badge.held(BUTTON_B):
        screen.pen = color.white
        screen.text ("Battery", 5,9)
        screen.text("{:0.1f}V".format(badge.battery_voltage()), 100, 9)
        screen.text("{:0.0f}%" .format(badge.battery_level()), 160, 9)
    else:
        year, month, day, hour, minute, second, weekday = rtc.datetime()
        #print(f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}")
        screen.pen = color.white
        if weekday == 0:
            weekday_text = "Mon"
        if weekday == 1:
            weekday_text = "Tue"
        if weekday == 2:
            weekday_text = "Wed"
        if weekday == 3:
            weekday_text = "Thu"
        if weekday == 4:
            weekday_text = "Fri"
        if weekday == 5:
            weekday_text = "Sat"
        if weekday == 6:
            weekday_text = "Sun"                      
        if hour == 0:
            time_text = f"{12}:{minute:02} AM"
        elif 0 < hour < 10:
            time_text = f"{hour:1}:{minute:02} AM"         
        elif 10 <= hour < 12:
            time_text = f"{hour:2}:{minute:02} AM"
        elif hour == 12:
            time_text = f"{hour:2}:{minute:02} PM"   
        elif hour >12:
            hour = hour - 12
            if hour <10:
                time_text = f"{hour:1}:{minute:02} PM"
            elif 10 <= hour < 12:
                time_text = f"{hour:2}:{minute:02} PM"
            elif hour == 12:
                time_text = f"{hour:2}:{minute:02} AM"

        screen.text(f"{time_text}  {weekday_text}  {describe_month(month)} {day}{describe_day(day)}",5,9)
    
    draw_battery()
    
    screen.pen = color.white
    screen.text("{:0.0f}°C" .format(t), 5, 52)
    screen.text("{:0.0f}%".format(h), 5, 118)
    screen.text("{:0.0f}mb".format(p), 5, 184)
    max_temperature
    if t >= max_temperature:
        max_temperature = t
    min_temperature
    if t <= min_temperature:
        min_temperature = t
    screen.pen = color.yellow
    screen.text(f"Min {min_temperature:.0f}", 110, 52)
    screen.text(f"Max {max_temperature:.0f}", 225, 52)    
     
    draw_graph()
    
run(update)