#202初級

Raspberry Pi Picoバイナリ時計(LED 6個 + ボタン)

数字を使わない時計です!Raspberry Pi Picoで時刻を2進数で表示する時計を作ります。6個のLEDは32-16-8-4-2-1の桁を表し、点灯しているLEDの値を足すと秒・分・時を読み取れます。1つのボタンで表示を切り替えたり時刻を設定したりでき、毎正時にはLEDが順番に光ります。

0.0(0)
Raspberry Pi Picoバイナリ時計(LED 6個 + ボタン)
数字の代わりに光で時刻を伝える時計です。6個のLEDで秒・分・時を2進数で表示します。LEDは左から32、16、8、4、2、1の値を表し、点灯しているLEDの値を足すと現在の値が分かります。1つのボタンで秒・分・時を切り替え、長押しで時刻を設定できます。毎正時にはLEDが順番に光ります。

動画

回路図

Raspberry Pi Picoバイナリ時計(LED 6個 + ボタン) - Circuit

ソースコード

MicroPython
1# ─────────────────────────────────────────
2#   BlueGrays · bluegrays.com
3#   Raspberry Pi Picoバイナリ時計(LED 6個 + ボタン)
4# ─────────────────────────────────────────
5# 時刻を2進数で表示する時計です。6つのLEDが32-16-8-4-2-1の位で、点灯した
6# LEDの合計が値になります。短く押すと秒、分、時を切り替え、長押しでその項
7# 目を設定します。毎時ちょうどに光のスイープが走り、ボード上のLEDが秒を刻
8# みます。
9#
10# ピン:
11#   GP2  -> LED 1(bit 0)、220 ohmを通す
12#   GP3  -> LED 2(bit 1)、220 ohmを通す
13#   GP4  -> LED 4(bit 2)、220 ohmを通す
14#   GP5  -> LED 8(bit 3)、220 ohmを通す
15#   GP6  -> LED 16(bit 4)、220 ohmを通す
16#   GP7  -> LED 32(bit 5)、220 ohmを通す
17#   GP15 -> ボタン(もう片方の足はGNDへ)
18#   LED  -> ボード上のLED(秒の刻み)
19#   GND  -> LEDのカソード、ボタン
20
21from machine import Pin
22import time
23
24# ============ 設定 ============
25START_TIME = (12, 59, 50)  # 開始時刻(時、分、秒)
26SEC, MIN, HOUR = range(3)
27START_MODE = SEC       # 起動時に表示する項目
28LONG_MS = 800          # 長押しの時間
29DEBOUNCE_MS = 20       # ボタンのチャタリング除去の時間
30MODE_MS = 500          # モードの目印を表示する時間
31BLINK_MS = 300         # 設定モードでの点滅の周期
32SET_TIMEOUT_MS = 10000 # 設定モードは操作がこの時間止まると保存
33SWEEP_STEP_MS = 60     # 毎時のスイープの1ステップ
34SWEEP_ROUNDS = 2       # スイープの回数
35TICK_MS = 100          # ボード上のLEDの光る長さ
36LOOP_MS = 5            # ループのステップ
37
38# bit 0(1)... bit 5(32)の順
39LED_PINS = (2, 3, 4, 5, 6, 7)
40MODE_NAMES = ("SANIYE / SECONDS", "DAKIKA / MINUTES", "SAAT / HOURS")
41# モードの目印:左から1、2、3個のLED(秒、分、時)
42MODE_MARKS = (0b100000, 0b110000, 0b111000)
43
44
45# ============ ハードウェア ============
46leds = [Pin(gp, Pin.OUT, value=0) for gp in LED_PINS]
47# "LED"という名前でPicoでもPico WでもボードのLEDが見つかります(WではGPピンではありません)
48tick_led = Pin("LED", Pin.OUT, value=0)   # PicoのボードのLED
49
50
51class Button:
52    # プルアップのボタン:押すと0を読む。update()は1 = 押した、-1 = 離した
53    def __init__(self, gp):
54        self.pin = Pin(gp, Pin.IN, Pin.PULL_UP)
55        self.down = False
56        self.changed_at = time.ticks_ms()
57
58    def update(self, now):
59        pressed = self.pin.value() == 0
60        if pressed != self.down and time.ticks_diff(now, self.changed_at) >= DEBOUNCE_MS:
61            self.down = pressed
62            self.changed_at = now
63            return 1 if pressed else -1
64        return 0
65
66    def held_ms(self, now):
67        return time.ticks_diff(now, self.changed_at)
68
69
70button = Button(15)
71shown = -1                 # LEDの最後のパターン
72
73
74def show_bits(pattern):
75    # パターンが変わったら6つのLEDに書く:bit i -> LED i
76    global shown
77    if pattern == shown:
78        return
79    shown = pattern
80    for i, led in enumerate(leds):
81        led.value((pattern >> i) & 1)
82
83
84def binary(v):
85    # 6桁の2進数の文字列、例:13 -> 001101
86    return "".join("1" if v >> i & 1 else "0" for i in range(5, -1, -1))
87
88
89# ============ 時計 ============
90clock_s = (START_TIME[0] * 3600 + START_TIME[1] * 60 + START_TIME[2]) % 86400
91start = time.ticks_ms()
92next_second = time.ticks_add(start, 1000)
93tick_off_at = start
94
95
96def hms():
97    return clock_s // 3600, clock_s // 60 % 60, clock_s % 60
98
99
100def field_value(m):
101    h, mi, s = hms()
102    return (s, mi, h)[m]
103
104
105def time_text():
106    return "%02d:%02d:%02d" % hms()
107
108
109# ============ 状態 ============
110mode = START_MODE
111setting = False            # 設定モード
112long_done = False          # 今回の押下の長押しは処理済み
113marker_until = start       # モードの目印が終わる時刻
114sweep_at = None            # 光のスイープの開始
115blink_ref = start          # 点滅のサイクルの開始
116last_press = start         # 設定モードでの最後の押下
117
118
119def report(prefix):
120    v = field_value(mode)
121    print("%s %s: %d = %s  (%s)" % (prefix, MODE_NAMES[mode], v, binary(v), time_text()))
122
123
124def save_setting(now):
125    global setting
126    setting = False
127    print("Saat ayarlandi / Time set: %s" % time_text())
128
129
130def on_short(now):
131    global mode, marker_until, clock_s, blink_ref, last_press
132    if setting:
133        # 設定モードでは短く押すたびに選んだ項目に1を足す
134        h, mi, s = hms()
135        if mode == HOUR:
136            h = (h + 1) % 24
137        else:
138            mi = (mi + 1) % 60
139        clock_s = h * 3600 + mi * 60 + s
140        blink_ref = now
141        last_press = now
142        report("+1")
143    else:
144        mode = (mode + 1) % 3
145        marker_until = time.ticks_add(now, MODE_MS)
146        report("Mod / Mode")
147
148
149def on_long(now):
150    global setting, clock_s, next_second, blink_ref, last_press
151    if setting:
152        if mode == MIN:
153            # 分を設定したあと、秒は0からやり直す
154            clock_s -= clock_s % 60
155            next_second = time.ticks_add(now, 1000)
156        save_setting(now)
157    elif mode == SEC:
158        # 秒のモードで長押しすると秒が0になる(時報に合わせる)
159        clock_s -= clock_s % 60
160        next_second = time.ticks_add(now, 1000)
161        print("Saniye sifirlandi / Seconds zeroed: %s" % time_text())
162    else:
163        setting = True
164        blink_ref = now
165        last_press = now
166        report("Ayar / Set")
167
168
169def frame(now):
170    # 今のLEDのパターン:スイープ > モードの目印 > 設定(点滅)> 値
171    if sweep_at is not None:
172        step = time.ticks_diff(now, sweep_at) // SWEEP_STEP_MS
173        pos = step % 10
174        return 1 << (pos if pos < 6 else 10 - pos)     # 往復
175    if time.ticks_diff(marker_until, now) > 0:
176        return MODE_MARKS[mode]
177    if setting and time.ticks_diff(now, blink_ref) // BLINK_MS % 2:
178        return 0
179    return field_value(mode)
180
181
182# ============ メインループ ============
183print("== BlueGrays · bluegrays.com ==")
184print("Pico Ikili Saat / Binary Clock")
185print("Kisa basis: alan degistir, uzun basis: ayarla / Short press: next field, long: set")
186print("LED'ler / LEDs: 32 16 8 4 2 1")
187report("Mod / Mode")
188
189next_loop = time.ticks_ms()
190while True:
191    now = time.ticks_ms()
192
193    # 秒のカウンター:次の秒は前の秒に足していくので、ずれません
194    if time.ticks_diff(now, next_second) >= 0:
195        next_second = time.ticks_add(next_second, 1000)
196        clock_s = (clock_s + 1) % 86400
197        tick_led.value(1)
198        tick_off_at = time.ticks_add(now, TICK_MS)
199        if clock_s % 60 == 0:
200            print("Saat / Time %s" % time_text()[:5])
201        if clock_s % 3600 == 0 and not setting:
202            sweep_at = now                 # 毎時ちょうど:光のスイープ
203    if not setting and time.ticks_diff(now, tick_off_at) >= 0:
204        tick_led.value(0)
205    if sweep_at is not None and time.ticks_diff(now, sweep_at) >= SWEEP_STEP_MS * 10 * SWEEP_ROUNDS:
206        sweep_at = None
207
208    # ボタン:短く押すと離したときに、長押しはLONG_MSが過ぎた時点で動作します
209    b = button.update(now)
210    if b == 1:
211        long_done = False
212        sweep_at = None                    # 押すとスイープが止まる
213    elif button.down and not long_done and button.held_ms(now) >= LONG_MS:
214        long_done = True
215        on_long(now)
216    elif b == -1 and not long_done:
217        on_short(now)
218
219    if setting:
220        tick_led.value(1)          # 設定中はボード上のLEDを点灯
221        if time.ticks_diff(now, last_press) >= SET_TIMEOUT_MS:
222            save_setting(now)
223
224    show_bits(frame(now))
225
226    # 固定ステップ:次の5 msまで待つ
227    next_loop = time.ticks_add(next_loop, LOOP_MS)
228    wait = time.ticks_diff(next_loop, time.ticks_ms())
229    if wait > 0:
230        time.sleep_ms(wait)
231    else:
232        next_loop = time.ticks_ms()
233

よくある質問

バイナリ時計はどう読みますか?

LEDは左から32、16、8、4、2、1の値を表します。点灯しているLEDの値を足してください。001101なら8、4、1が点灯しているので、値は13です。時は最大23なので、時を表示しているときは左端のLEDは点灯しません。シリアルモニタには各値とその2進数表記が出力されます。

LEDはなぜ6個で足りるのですか?

6ビットでは0から63まで数えられます(2の6乗 = 64通り)。秒と分は最大59、時は最大23なので、すべて6ビットに収まります。時・分・秒を同時に表示するには、それぞれにLEDの列が必要です(合計17個)。

電源が切れると時刻はリセットされますか?

はい。Picoには電池でバックアップされた時計がないため、起動するたびに`START_TIME`から始まります。時刻を保持するには、DS1307などの電池付きRTCモジュールを追加できます。このプロジェクトでは、電源を入れてからボタンで時と分を数秒で設定できます。

LEDの色を変えても使えますか?

はい。赤・緑・黄・オレンジのLEDは、3.3 Vで220 Ωの抵抗を使えば十分に点灯します。青と白のLEDは約3 Vが必要で、3.3 Vでは暗く見えることがあります。その場合は抵抗を100 Ωに下げてください。上位の桁を別の色にすると読み取りやすくなります。