赤と緑のLEDを使って、最も簡単な押しボタン式歩行者用信号機システムを作成します。
とはいえ、いきなりシステム全体に取り組むのではなく、できるだけ簡単な、単純なものから始めます。それは、押しボタンのない信号機です。
目次
押しボタンのない歩行者用信号機
次のように動作するシステムにします。
- 緑が5秒間点灯
- 緑が5回点滅
- 赤が5秒間点灯
- (1)に戻ります。これの繰り返し
配線
このシステムは、時間経過に沿って赤と緑のLEDが点いたり消えたり、点滅したりするだけのもので、赤と緑のLEDに配線するだけです。
Picoの左下端のGP15 -> (抵抗器) -> (+)LED赤(-) -> グランドレール -> GND
その1つ上のGP14 -> (抵抗器) -> (+)LED緑(-) -> グランドレール -> GND
Raspberry Pi Pico | 抵抗器 | LED(赤) |
---|---|---|
GP15 | <---> | VCC(+、長い足) |
GND | GND(-、短い足) |
Raspberry Pi Pico | 抵抗器 | LED(緑) |
---|---|---|
GP14 | <---> | VCC(+、長い足) |
GND | GND(-、短い足) |
プログラムコード
from machine import Pin
import utime
# GP15は赤、GP14は緑
red_led = Pin(15, Pin.OUT)
green_led = Pin(14, Pin.OUT)
# 2つとも消してスタート
red_led.off()
green_led.off()
# ずっと
while True:
# 緑を5秒点灯
green_led.on()
utime.sleep(5)
# 緑を0.5秒消して、点滅に移る
green_led.off()
utime.sleep(0.5)
# 5回繰り返す
for i in range(5):
# 緑を0.5秒間隔で点滅
green_led.on()
utime.sleep(0.5)
green_led.off()
utime.sleep(0.5)
# 赤を5秒点灯
red_led.on()
utime.sleep(5)
# 赤を消して、ループの先頭に戻る
red_led.off()
このコードでは、machineライブラリからPin()関数だけをインポートしているので、machineライブラリの他の機能はインポートされなくなります。またPin()関数を呼び出すとき、前にmachine.を付ける必要がなくなります。
whileループの前までは、このプログラムの言わば初期設定(最初に必要な準備)で、重要な部分はwhileループの中にあります。while Trueは無限ループなので、プログラムを停止させない限りずっとつづきます。何がずっとつづくのかは、半角スペース4個分のインデントの付いた以降の行が示しています。
whileループでは、まず(1)が実行されます。(初期設定で消えていた)緑のLEDを点灯し、プログラムを一時停止してその状態を5秒つづけるので、LEDは5秒間点灯しつづけることになります。
それが終わると、プログラムの実行はfor inループに移ります。ここではrange(5)が指定されているので、以降につづくインデントされた行を5回繰り返して実行します。(2)がその内容です。これは(1)の停止時間を変えただけのもので、緑のLEDが点灯しその状態が0.5秒つづいた後、LEDが消えてその状態が0.5秒つづき、これが5回繰り返されるので、LEDは点滅して見えることになります。
for inループの終わりでは緑のLEDを消しているので、for in ループの繰り返しが終わると、緑のLEDが消えた状態で、プログラムの実行は(3)に移ります。ここでは赤のLEDを点灯し、その状態を5秒つづけた後、消しています。red_led.off()の後、停止時間を設けていないので、消えた次の瞬間に、whileループの先頭に戻ることになります。これは、両方のLEDが消えている状態はないということです。
from machine import Pin と同様、sleep()に関しても、from utime import sleep と記述すると、utime.sleep()の代わりにsleep()だけでよくなります。
押しボタン式歩行者用信号機
次は、今作成したシステムに押しボタンを組み込んでいきます。
押しボタン式歩行者用信号機の動作
絵にすると、次のように表すことができます。
信号機の赤は最初から点灯しています。そして押しボタンが押されると、その2秒後に赤が消え、緑が点灯します。緑は5秒間点灯し、この間歩行者は横断できます。5秒間の点灯後に緑は5回点滅します。このとき歩行者は急いで渡るかあきらめることになります。5回の点滅後、緑が消え、再び赤が点灯します。
上図を見ると、簡単そうに思えますが、実は少し考える必要があります。
待ち受けの状態を作る
信号機はずっと赤のままで、ボタンが押されたら緑になる、というのは、プログラミングの観点から見ると、「赤がずっと点灯していて、どのタイミングか分からないがボタンが押されたら、緑信号への動作を開始する」という動作です。ボタンはいつ押されるか分からないので、ボタンが押されたかどうかをずっと監視しつづける必要があります。
これは、自動販売機の投入口にコインを入れるのと同じです。販売機の投入口は朝であろうが深夜であろうが、いつコインが投入されるか分からないので、24時間、コインの投入を待ち受ける必要があります。
待ち受けは、while Trueの中にif文を入れることで作成できます。
while True:
if button.value() == 1:
# ボタンが押されたときのコードを記述
配線
配線は下図のように行います。これは、前の信号機の配線に、押しボタンの配線を加えたものです。押しボタンでは3V3(OUT)ピンとGP0ピンを使用しています。
Raspberry Pi Pico | 抵抗器 | LED(赤) |
---|---|---|
GP15 | <---> | VCC(+、長い足) |
GND | GND(-、短い足) |
Raspberry Pi Pico | 抵抗器 | LED(緑) |
---|---|---|
GP14 | <---> | VCC(+、長い足) |
GND | GND(-、短い足) |
Raspberry Pi Pico | 押しボタン |
---|---|
3V3(out) | ピン1 |
GP0 | ピン2 |
プログラムコード
from machine import Pin
import utime
# GP15は赤、GP14は緑
red_led = Pin(15, Pin.OUT)
green_led = Pin(14, Pin.OUT)
# 押しボタンはGP0
button = machine.Pin(0,machine.Pin.IN,machine.Pin.PULL_DOWN)
# 赤を点灯、緑を消してスタート
red_led.on()
green_led.off()
print("お待ちください")
# ずっと
while True:
if button.value()==1:
print("ボタンが押された")
utime.sleep(2) # 2秒待つ
red_led.off() # 赤を消す
green_led.on() # 緑を点灯
print("横断できます")
utime.sleep(5)
green_led.off() # 一瞬消してfor inに移る
utime.sleep(0.5)
# 5回繰り返す
for i in range(5):
# 緑を0.5秒間隔で点滅
green_led.on()
utime.sleep(0.5)
green_led.off()
utime.sleep(0.5)
# 赤点灯
red_led.on()
print("お待ちください")
プログラムを実行すると、最初赤いLEDが点灯し、Thonnyの[シェル]に「お待ちください」と表示されます。ボタンを押すと、[シェル]に「ボタンが押された」と表示され、以降も想定通り進行します。
コードを包含関係から見てみる
whileループでは、if button.value() =1 を条件として待ち受け状態を作り、信号のすべての動作をif文の中に入れています。あるものが別のものを含む包含関係で言うと、while Trueがすべてを包み、その中でif文が信号の動作のコード全部を包んでいる、という構造になっています。
これは下図の左のように、四角形で表すことができます。またScratch風にすると、下図の右のようになります。
信号のすべての動作をif文で包み、それをwhile Trueで包んでいるので、信号はボタンが押されたときにしか変わらず、押されていないときには何も変わらないことになります。