Last active
October 1, 2020 09:42
-
-
Save aphlysia/3c7b040b164fc577b6dcfd2a6882561c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
micro:bit の 25 分タイマー | |
使い方 | |
A ボタンを押すとタイマーがスタート。 | |
タイマーが動いているときに A を押すと一時停止する。 | |
一時停止から復帰するには A を押す。 | |
一時停止中に B を押すと、リセット。 | |
更新内容 | |
リセットした状態で sleep になってから B を押しても描画がリセットされない問題を修正。 | |
''' | |
from microbit import * | |
import utime | |
height = 5 | |
width = 5 | |
goal_t = 25 * 60 # [s] | |
sleep_t = 5 * 60 # [s] | |
dt = 0.1 # 処理間隔 [s] | |
led_update_interval = 5 # dt をこの回数繰り返すと LED を更新 | |
off_level = '0' # LED 消灯の強度 | |
debug = False | |
def update_LED(start_time, image, on_level, force=False): | |
now = utime.ticks_ms() | |
t = utime.ticks_diff(now, start_time) / 1000 | |
if t >= goal_t: | |
image = off_level * (height * width) | |
else: | |
m = int(t // 60) | |
i = m # 今の[分]を表す画像の位置 | |
c = on_level if (force or image[i] == off_level) else off_level # 今の[分]を点滅 | |
if i > 0: | |
image = image[:i-1] + off_level + c + image[i+1:] | |
else: | |
image = image[:i] + c + image[i+1:] | |
show_image(image) | |
return image | |
def show_image(image): | |
img = ':'.join([image[i*width: i*width+width] for i in range(width)]) # width 毎に : を加える | |
display.show(Image(img)) | |
class Timer: | |
STOP = 0 | |
WORKING = 1 | |
SUSPENDING = 2 | |
DONE = 3 | |
def __init__(self): | |
self.reset() | |
def reset(self): | |
self.update_on_level() | |
self.image = self.initial_image() | |
self.start_time = utime.ticks_ms() | |
self.count = 0 | |
self.status = self.STOP | |
self.blink_status = 0 | |
self.sleep = False | |
self.show() | |
def update_on_level(self): | |
# 周囲の明るさに合わせて光らせる強さを調整する | |
self.on_level = str(round(display.read_light_level() * 4 / 255 + 5)) | |
def initial_image(self): | |
return self.on_level * (height * width) | |
def start(self): | |
if self.status == self.DONE: | |
self.reset() | |
elif self.status != self.STOP: return | |
if self.sleep: | |
''' | |
sleep 時はここで明るさを調整。 | |
そうでないときは直前の reset 時に取得した明るさを使う。 | |
そうしている理由は、タイマースタート時に LED が一瞬消えるのを防ぐため。 | |
(明るさを取得するときに一瞬 LED が消える) | |
''' | |
self.update_on_level() | |
self.image = self.initial_image() | |
self.start_time = utime.ticks_ms() | |
self.status = self.WORKING | |
self.show() | |
def suspend(self): | |
if self.status != self.WORKING: return | |
self.status = self.SUSPENDING | |
self.count = 0 | |
self.blink_status = 0 | |
self.suspend_start_time = utime.ticks_ms() | |
self.suspend_image = update_LED(self.start_time, self.image, self.on_level, force=True) | |
def restart(self): | |
if self.status != self.SUSPENDING: return | |
now = utime.ticks_ms() | |
diff = utime.ticks_diff(now, self.suspend_start_time) | |
self.start_time = utime.ticks_add(self.start_time, diff) | |
self.status = self.WORKING | |
def show(self): | |
show_image(self.image) | |
def tick(self): | |
if self.status == self.STOP and self.sleep == False: | |
now = utime.ticks_ms() | |
t = utime.ticks_diff(now, self.start_time) / 1000 | |
if t >= sleep_t: | |
self.sleep = True | |
display.clear() | |
if self.status == self.WORKING: | |
self.count += 1 | |
if self.count == led_update_interval: | |
# LED を更新 | |
self.image = update_LED(self.start_time, self.image, self.on_level) | |
self.count = 0 | |
now = utime.ticks_ms() | |
t = utime.ticks_diff(now, self.start_time) / 1000 | |
if t >= goal_t: | |
self.status = self.DONE | |
display.clear() | |
elif self.status == self.SUSPENDING: | |
# 一時停止中は全体を点滅 | |
self.count += 1 | |
if self.count == led_update_interval: | |
# LED を更新 | |
if self.blink_status == 0: | |
display.clear() | |
self.blink_status = 1 | |
else: | |
show_image(self.suspend_image) | |
self.blink_status = 0 | |
self.count = 0 | |
timer = Timer() | |
while True: | |
# ボタン操作 | |
n_a = button_a.get_presses() | |
n_b = button_b.get_presses() | |
if n_a > 0 and n_b > 0 and timer.status == timer.WORKING: | |
if debug: | |
# 最後の 10 秒へ飛ばす | |
timer.image = off_level * 24 + timer.on_level | |
diff = (24 * 60 + 50) * 1000 | |
now = utime.ticks_ms() | |
timer.start_time = utime.ticks_add(now, -diff) | |
elif n_a > 0 and n_b == 0: | |
''' | |
停止中またはタイマーが終了した後にボタンAを押すと、タイマーを作動。 | |
タイマーが作動中にボタンAを押すと、一時停止 (タイマーが進まなくなる)。 | |
一時停止中にボタンAを押すと、作動中に戻す。 | |
''' | |
if timer.status in (timer.STOP, timer.DONE): | |
timer.start() | |
elif timer.status == timer.WORKING: | |
timer.suspend() | |
else: | |
timer.restart() | |
elif n_a == 0 and n_b > 0 and ( | |
timer.status in (timer.SUSPENDING, timer.DONE, timer.STOP)): | |
''' | |
一時停止中またはタイマーが終了した後にボタンBを押したらリセット | |
''' | |
timer.reset() | |
timer.tick() | |
sleep(int(dt * 1000)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment