Toggle Navigation
Hatchery
Eggs
Timer
__init__.py
Users
Badges
Login
Register
MCH2022 badge?
go to mch2022.badge.team
__init__.py
raw
Content
""" This project is based on collins watch++ which was inspired by yrlfs digiclk and base functionality (clock) is taken from there. https://badge.team/projects/watch https://github.com/Ferdi265/card10-digiclk Thanks to lortas for the battery rendering code """ import buttons import display import os import utime import light_sensor import power import color import ujson import vibra import struct VIBRATETIME = 150 STOPWATCHCOLOR = (color.CHAOSBLUE, color.CHAOSBLUE_DARK) TIMERCOLOR = (color.CAMPGREEN, color.CAMPGREEN_DARK) ALARMCOLOR = (color.COMMYELLOW, color.COMMYELLOW_DARK) def ceil_div(a, b): return (a + (b - 1)) // b def tip_height(w): return ceil_div(w, 2) - 1 def draw_tip(d, x, y, w, c, invert=False, swapAxes=False): h = tip_height(w) for dy in range(h): for dx in range(dy + 1, w - 1 - dy): px = x + dx py = y + dy if not invert else y + h - 1 - dy if swapAxes: px, py = py, px d.pixel(px, py, col=c) def draw_seg(d, x, y, w, h, c, swapAxes=False): tip_h = tip_height(w) body_h = h - 2 * tip_h draw_tip(d, x, y, w, c, invert=True, swapAxes=swapAxes) px1, px2 = x, x + (w - 1) py1, py2 = y + tip_h, y + tip_h + (body_h - 1) if swapAxes: px1, px2, py1, py2 = py1, py2, px1, px2 d.rect(px1, py1, px2, py2, col=c) draw_tip(d, x, y + tip_h + body_h, w, c, invert=False, swapAxes=swapAxes) def draw_Vseg(d, x, y, w, l, c): draw_seg(d, x, y, w, l, c) def draw_Hseg(d, x, y, w, l, c): draw_seg(d, y, x, w, l, c, swapAxes=True) def draw_grid_seg(d, x, y, w, l, c, swapAxes=False): sw = w - 2 tip_h = tip_height(sw) x = x * w y = y * w l = (l - 1) * w draw_seg(d, x + 1, y + tip_h + 3, sw, l - 3, c, swapAxes=swapAxes) def draw_grid_Vseg(d, x, y, w, l, c): draw_grid_seg(d, x, y, w, l, c) def draw_grid_Hseg(d, x, y, w, l, c): draw_grid_seg(d, y, x, w, l, c, swapAxes=True) def draw_grid(d, x1, y1, x2, y2, w, c): for x in range(x1 * w, x2 * w): for y in range(y1 * w, y2 * w): if x % w == 0 or x % w == w - 1 or y % w == 0 or y % w == w - 1: d.pixel(x, y, col=c) def draw_grid_7seg(d, x, y, w, segs, c): if segs[0]: draw_grid_Hseg(d, x, y, w, 4, c) if segs[1]: draw_grid_Vseg(d, x + 3, y, w, 4, c) if segs[2]: draw_grid_Vseg(d, x + 3, y + 3, w, 4, c) if segs[3]: draw_grid_Hseg(d, x, y + 6, w, 4, c) if segs[4]: draw_grid_Vseg(d, x, y + 3, w, 4, c) if segs[5]: draw_grid_Vseg(d, x, y, w, 4, c) if segs[6]: draw_grid_Hseg(d, x, y + 3, w, 4, c) DIGITS = [ (True, True, True, True, True, True, False), (False, True, True, False, False, False, False), (True, True, False, True, True, False, True), (True, True, True, True, False, False, True), (False, True, True, False, False, True, True), (True, False, True, True, False, True, True), (True, False, True, True, True, True, True), (True, True, True, False, False, False, False), (True, True, True, True, True, True, True), (True, True, True, True, False, True, True) ] BATTERY_COLOR_GOOD = [0, 128, 0] BATTERY_COLOR_OK = [128, 100, 0] BATTERY_COLOR_BAD = [128, 0, 0] def get_bat_color(v): if v > 3.8: return BATTERY_COLOR_GOOD if v > 3.6: return BATTERY_COLOR_OK return BATTERY_COLOR_BAD def render_battery(display, pos_x=142, pos_y=72): v = os.read_battery() c = get_bat_color(v) if not c: return display.rect(pos_x, pos_y, pos_x + 15, pos_y + 7, filled=True, col=c) display.rect(pos_x + 15, pos_y + 2, pos_x + 17, pos_y + 5, filled=True, col=c) if v < 4.0: display.rect(pos_x + 11, pos_y + 1, pos_x + 14, pos_y + 6, filled=True, col=[0, 0, 0]) if v < 3.8: display.rect(pos_x + 6, pos_y + 1, pos_x + 11, pos_y + 6, filled=True, col=[0, 0, 0]) if v < 3.6: display.rect(pos_x + 1, pos_y + 1, pos_x + 6, pos_y + 6, filled=True, col=[0, 0, 0]) render_charging(display, pos_x + 6, pos_y) def render_charging(display, pos_x, pos_y): v_in = power.read_chargein_voltage() if v_in > 4.0: c = [0, 0, 0] c_shade = [120, 120, 120] display.pixel(pos_x + 1, pos_y, col=c) display.pixel(pos_x + 1, pos_y, col=c) display.pixel(pos_x + 2, pos_y, col=c_shade) display.pixel(pos_x + 1, pos_y + 1, col=c) display.pixel(pos_x, pos_y + 1, col=c_shade) display.pixel(pos_x + 1, pos_y + 2, col=c) display.pixel(pos_x, pos_y + 2, col=c) display.pixel(pos_x, pos_y + 3, col=c) display.pixel(pos_x + 1, pos_y + 3, col=c) display.pixel(pos_x + 2, pos_y + 3, col=c) display.pixel(pos_x + 3, pos_y + 3, col=c_shade) display.pixel(pos_x + 2, pos_y + 4, col=c) display.pixel(pos_x + 3, pos_y + 4, col=c) display.pixel(pos_x + 4, pos_y + 4, col=c) display.pixel(pos_x + 1, pos_y + 4, col=c_shade) display.pixel(pos_x + 3, pos_y + 5, col=c) display.pixel(pos_x + 4, pos_y + 5, col=c) display.pixel(pos_x + 3, pos_y + 6, col=c) display.pixel(pos_x + 4, pos_y + 6, col=c_shade) display.pixel(pos_x + 3, pos_y + 7, col=c) display.pixel(pos_x + 2, pos_y + 7, col=c_shade) def render_num(d, num, x, Color): draw_grid_7seg(d, x, 0, 7, DIGITS[num // 10], Color) draw_grid_7seg(d, x + 5, 0, 7, DIGITS[num % 10], Color) def render_colon(d, Color): draw_grid_Vseg(d, 11, 2, 7, 2, Color) draw_grid_Vseg(d, 11, 4, 7, 2, Color) def render_bar(d, num, Color): d.rect(0, 72, num, 80, col=Color) def ctrl_backlight(d): light = light_sensor.get_reading() display_brightness = int(light // 4) if light >= 4 else 1 # display_brightness = int(light-12) if light >= 14 else 1 display_brightness = 100 if light > 300 else display_brightness d.backlight(display_brightness) def save_to_flash(): if 'variables.json' in os.listdir('/apps/timer'): f = open('/apps/timer/variables.json', 'r') try: variables = ujson.loads(f.read()) except: variables = ujson.loads('{"mode": 0,"runstamp": 0,"pausestamp": 0,"starttime": 0,"pausetime": 0}') f.close() if ( variables["mode"] != mode or variables["runstamp"] != runstamp or variables["pausestamp"] != pausestamp or variables["starttime"] != starttime or variables["pausetime"] != pausetime or not ("mode" and "runstamp" and "pausestamp" and "starttime" and "pausetime" in variables) ): variables["mode"] = mode variables["runstamp"] = runstamp variables["pausestamp"] = pausestamp variables["starttime"] = starttime variables["pausetime"] = pausetime f = open('/apps/timer/variables.json', 'w') f.write(ujson.dumps(variables)) f.close() TIMER_START = 0 TIMER_RUN = 1 TIMER_PAUSE = 2 TIMER_ALARM = 3 STOPWATCH_RUN = 4 STOPWATCH_PAUSE = 5 RESET = 6 pressed_prev = 0 button_startstopp_time = 0 button_min_time = 0 button_sec_time = 0 flashlight = 0 vibrated = False mode = TIMER_START runstamp = 0 pausestamp = 0 starttime = 0 pausetime = 0 if 'variables.json' in os.listdir('/apps/timer'): f = open('/apps/timer/variables.json', 'r') try: variables = ujson.loads(f.read()) if "mode" and "runstamp" and "pausestamp" and "starttime" and "pausetime" in variables: mode = variables["mode"] runstamp = variables["runstamp"] pausestamp = variables["pausestamp"] starttime = variables["starttime"] pausetime = variables["pausetime"] except: pass f.close() light_sensor.start() with display.open() as d: while True: t = utime.time_ms() t_year, t_month, t_mday, t_hour, t_min, t_sec, t_wday, t_yday = utime.localtime() if mode == STOPWATCH_RUN: showtimems = t - runstamp elif mode == TIMER_RUN: showtimems = runstamp - t +1000 if showtimems < 1000: mode = TIMER_ALARM else: showtimems = pausestamp if mode == TIMER_ALARM: Background = ALARMCOLOR[0] Color = ALARMCOLOR[1] showtimems = t - runstamp if showtimems > 6000000: showtimems = 0 pausestamp = 0 starttime = 0 mode = TIMER_START save_to_flash() elif (mode == TIMER_START and pausestamp > 0) or mode == TIMER_RUN or mode == TIMER_PAUSE: Background = TIMERCOLOR[0] Color = TIMERCOLOR[1] else: Background = STOPWATCHCOLOR[0] Color = STOPWATCHCOLOR[1] d.clear(Background) # d.clear() # Color = color.from_hsv(((hour*60+min)*360/1439+240), 1, 1) showtimes = showtimems/1000 if mode != TIMER_ALARM or ((showtimems%1000)<500): if (mode != STOPWATCH_RUN and mode != TIMER_RUN) or ((showtimems%500)<250): render_colon(d,Color) text = ("0"+str(t_hour))[-2:]+":"+("0"+str(t_min))[-2:] #Time if mode == STOPWATCH_RUN or mode == STOPWATCH_PAUSE or mode == RESET or (mode == TIMER_START and pausestamp == 0): sec = abs(int(showtimes%60)) min = abs(int((showtimes/60)%60)) hour = abs(int((showtimes/3600)%24)) day = abs(int(showtimes/86400)) text += " " + str(day)+"d" + ("0"+str(hour))[-2:]+"h" #Extended Time else: sec = abs(int(showtimes%60)) min = abs(int((showtimes/60)%100)) if starttime < 60000: text += " " text += ("0"+str(int(starttime/60)))[-2:] + "m" + ("0"+str(int(starttime%60)))[-2:] #Added Time d.print(text, fg=Color, bg=None, posx=5, posy=52) render_num(d, min, 1,Color) render_num(d, sec, 13,Color) if (mode == TIMER_RUN or mode == TIMER_PAUSE) and starttime > 0 and showtimes-1 > 0: render_bar(d, int(140*((showtimes-0.999)/(starttime))), Color) if mode == TIMER_START and pausestamp > 0: render_bar(d, 140, Color) render_battery(d) d.update() #ctrl_backlight(d) if mode == TIMER_ALARM and ((showtimems%1000)<500): if not vibrated: vibra.vibrate(VIBRATETIME) vibrated = True else: vibrated = False pressed = buttons.read(buttons.BOTTOM_LEFT | buttons.TOP_RIGHT | buttons.BOTTOM_RIGHT) if pressed & buttons.TOP_RIGHT and not pressed_prev & buttons.TOP_RIGHT: button_startstopp_time = t elif not pressed and pressed_prev & buttons.TOP_RIGHT: if button_startstopp_time+250 < t: os.exec("/main.py") else: if mode == STOPWATCH_RUN: mode = STOPWATCH_PAUSE pausestamp = t - runstamp elif mode == STOPWATCH_PAUSE: mode = STOPWATCH_RUN runstamp = t - pausestamp elif mode == TIMER_START: starttime = int(pausestamp/1000) if starttime == 0: mode = STOPWATCH_RUN runstamp = t else: mode = TIMER_RUN runstamp = t + pausestamp elif mode == TIMER_RUN: mode = TIMER_PAUSE pausestamp = runstamp - t + 1000 elif mode == TIMER_PAUSE: mode = TIMER_RUN runstamp = pausestamp + t -1000 elif mode == TIMER_ALARM: mode = TIMER_START if starttime < 6000: pausestamp = starttime*1000 else: pausestamp = 5999000 save_to_flash() if pressed & buttons.BOTTOM_LEFT and pressed & buttons.BOTTOM_RIGHT: mode = RESET pausestamp = 0 starttime = 0 elif mode == RESET and pressed == 0: mode = TIMER_START save_to_flash() if (pressed & buttons.BOTTOM_LEFT or pressed & buttons.BOTTOM_RIGHT): if mode == TIMER_PAUSE or mode == STOPWATCH_PAUSE: pausestamp = int(pausestamp/1000)*1000+999 if mode == TIMER_START or mode == TIMER_PAUSE or mode == STOPWATCH_PAUSE or mode == TIMER_ALARM: if pressed & buttons.BOTTOM_LEFT and not pressed_prev & buttons.BOTTOM_LEFT: button_min_time = t if mode == TIMER_ALARM: pausestamp = 0 starttime = 0 mode = TIMER_START save_to_flash() else: pausestamp = int(pausestamp + 60000) starttime += 60 if pausestamp >= 6000000: pausestamp -= 6000000 starttime -= 6000 elif pressed & buttons.BOTTOM_LEFT and button_min_time+250 < t: button_min_time += 100 pausestamp = int(pausestamp + 60000) starttime += 60 if pausestamp >= 6000000: pausestamp -= 6000000 starttime -= 6000 if pressed & buttons.BOTTOM_RIGHT and not pressed_prev & buttons.BOTTOM_RIGHT: button_sec_time = t if mode == TIMER_ALARM: pausestamp = 0 starttime = 0 mode = TIMER_START save_to_flash() else: pausestamp = (int(pausestamp/1000+1)*1000) starttime += 1 if (int(pausestamp/1000)%60) == 0: pausestamp -= 60000 starttime -= 60 elif pressed & buttons.BOTTOM_RIGHT and button_sec_time+250 < t: button_sec_time += 100 pausestamp = (int(pausestamp/1000+1)*1000) starttime += 1 if (int(pausestamp/1000)%60) == 0: pausestamp -= 60000 starttime -= 60 pressed_prev = pressed