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

数字の代わりに光で時刻を伝える時計です。6個のLEDで秒・分・時を2進数で表示します。LEDは左から32、16、8、4、2、1の値を表し、点灯しているLEDの値を足すと現在の値が分かります。1つのボタンで秒・分・時を切り替え、長押しで時刻を設定できます。毎正時にはLEDが順番に光ります。
動画
回路図

ソースコード
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 Ωに下げてください。上位の桁を別の色にすると読み取りやすくなります。
