ドアセンサーを使って、Raspberry Piで検知できるようにします。ドアの開閉を検知してディスプレイに全画面表示とスピーカーから音声を流しています。センサーや電子部品は数百円程度ですので、非常に安価に製作可能です!

全画面で大きな文字列や画像を表示したり、効果音を鳴らす手法は、今回に限らず様々な応用に使えます。なお、今回のコンテンツでは、ハンダ付けなしで出来るように使用部品を工夫しています。

学べること

部品購入

必要なスキル

概要

ラズパイやラズパイ周辺機器の購入については「全体概要」チャプターの「必要なスキル」のリンク先を参照してください。本チャプターではそれ以外に必要となる部品を挙げています。

1つずつ説明して、最後に具体的な購入先例をリンクでまとめます。

電子部品

今回はハンダごてを一切使わなくても作れるように部品を工夫しています。4点ありますが、送料除けば600円程度です。ハンダ付け出来る人はブレッドボードと端子台は不要です。

工具・文具

プラスドライバーは端子台のネジを締めるのに使用します。大きさは、ダイソーの精密ドライバーセットの中に入っているプラスドライバーの場合、#0もしくは#1で締めることが出来ました。
両面テープはドアセンサーを仮止めするのに便利です。セロテープなどでも代用可能です。
それぞれ家にあるモノで問題ありません。100均でも購入可能です。

まとめ

具体的な購入先のまとめです。
これはあくまで一例です。代用できる他の部品を使っていただいて構いません。

機器

購入先

 備考

ドアセンサー

秋月電子通商

ジャンパーワイヤ(オス-メス)

秋月電子通商

他の色でもOK。赤、黒、青、黄

ミニブレッドボード

秋月電子通商

ハンダ付け出来る人は不要

ターミナルブロック2P・縦・小

秋月電子通商

ハンダ付け出来る人は不要

精密ドライバー

ダイソー、秋月電子通商

ハンダ付け出来る人は不要。家にあるものでもOK

両面テープ

ダイソー、Amazonなど

家にあるものでもOK

※ダイソーとAmazonのリンクは割愛します。
※ダイソーのネットショップは1個単位では買えませんので、お近くのショップで直接購入することをお勧めします。

ここからは、ラズパイ及び周辺部品の接続、Raspberry Piの初期設定、VNC接続などの準備が出来ている前提で話を進めます。

「Geany」はRaspberry Pi OSに標準でインストールされているGUIプログラミングエディタです。

画像引用元:https://www.geany.org/

Raspberry Pi OS にはもう一つ、Pythonプログラミング専用のGUIエディタ「Thonny」がインストールされているのですが、2021年12月の時点では日本語入力が出来ない不具合があるため、今回はGeanyを採用します。

作業場所

始めにコードを保存するための作業場所を作成しましょう。ユーザーのホームディレクトリの直下にworkspaceというフォルダを作成します。ファイルマネージャーで作成しても構いません。

cd ~
mkdir workspace

起動

以下の手順で開きます。
メニューアイコン > プログラミング > Geany

とりあえず実行

コードを書いて実行してみましょう。
※見やすさのためにフォントサイズを大きくしています。

以下の文字列をコピペして、「紙飛行機アイコン」をクリックしてください。

print('hello')

どこにファイルを保存するかを聞かれるので、先ほど作ったworkspaceをダブルクリックします。

ファイル名はtest.pyとしてください。
ターミナルが立ち上がり、プログラムが実行され、真ん中にhelloという文字列が出ていれば成功です。
※見やすさのためにフォントサイズを大きくしています。

ターミナル上でEnterキーを押すと終了です。

ターミナルの前半に以下の文字列が表示されていますが、これは「パスワードを変えた方が良いよ」というセキュリティの警告であり、プログラムとは直接関係ありません。気になる人はRaspberry Piの設定からパスワードを変更しましょう。

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.

一度保存が出来ているので、helloを別の文字列にして「紙飛行機アイコン」をクリックするだけで、実行されるようになります。

画面の説明

様々なメニューや表示がありますが、今後使うのは「コードを書く場所」と「4つのアイコン」のみです。

  1. 新しいファイルを作成
  2. 既存のファイルを開く
  3. 現在のファイルを保存
  4. 現在のファイルを実行/表示

保存してない状態で実行した場合は、自動的に保存されてから実行されますので、3については実質あまり使わないかもしれません。

コードを書く場所の文字を大きくしたい場合は表示 > フォントを変更 から Sizeを変えて下さい。デフォルトは10です。

コピペの注意点

PCでコピーした文字列をVNCのGeanyに貼り付ける際、画像のように選択すると、コピー情報が上書きされてしまうので注意しましょう。

解決策は選択せずに貼り付けることです。
少しわかりずらいと思いますので、動画を作成しました。

少し手間が増えますが、先に削除してから貼り付けるようにしましょう!

プログラミングの進め方

次のチャプター以降ではたくさんコードを作ってゆきますが、ファイルを新規作成しても良いですし、test.pyに追記や上書きでも良いです。皆さんが進めやすいように行ってください。

Pythonとは

Pythonは最近よく使われているプログラミング言語のうちの1つです。

主な特徴を3つ挙げます。

✔ インデント

ぱっと見の特徴として「インデント」があります。これは中カッコ{ }を使わず、スペースでブロックのまとまりを表現しています。なので、見た目がスッキリとしているのが特徴です。
先程の画像の場合だとprint('hello')とa = 100が1つのブロックに相当します。

✔ 実行が簡単

プログラミング言語によっては、「ビルド」や「コンパイル」という工程を経てエラーをゼロにしないと実行できない場合があります。Pythonはそのような工程は不要で、コードを書いてすぐに実行できます。エラーが起きた時点でプログラムが終了となるため、初心者にとっても使いやすい言語になっています。

✔ 多彩な用途

Pythonは今回のコンテンツで使うような「組み込み」用途だけではなく、機械学習やディープラーニングなどの「データサイエンス」、サーバー側の処理を担う「Webバックエンド」にも使うことが可能で、非常に多彩です。

では、ここからプログラミングに入ります。
初心者の方は、読むだけではなく、実際にGeanyにコピペして実行し色々とコードを自分なりに改造しながら進めることをお勧めします。

コメント

記号#を書くことで、その1行がコメント行になります。
コメントはプログラム実行時に無視されます。主にプログラムに関する注釈を記述するために使用します。

#こんな感じで書きます 
print('hello') 

以下のように途中からも書けます。

print('hello') #この書き方もOK 

どちらも実行結果としては変わりません。

まとめてコメント化

コメントは注釈を書く目的以外に、プログラムで不具合が出た時の切り分けにも活用できます。
具体的には、不具合がありそうな部分に関して、一時的にコードの複数行をコメント化します。#を1行ずつ書いてゆくのは大変ですので、エディタの便利な機能を使います。

Geanyの場合は、対象箇所を選択した後にCtrl+Eキーで、選択した行をコメント化することができます。逆にコメント化から元に戻す場合も同じキーで行うことができます。
他のエディタの場合はキーが異なりますのでご注意ください。

なお、Geanyの場合はデフォルト設定だと#だけではなく# ~を付加します。~自体は消しても問題はありませんが、消した場合はCtrl+Eキーではコメント化を元に戻せませんので、手動で修正が必要となります。

変数と代入

変数とは1つの値が保存でき、何度も書き換え可能な役割を持っています。
= を書くことにより、左側の変数に右側の値を入れるという意味になります。数学の=とは意味が異なるので注意が必要です。 例えばa = 3776というのは変数aに数値3776を代入しているという意味です。 Pythonの場合は、整数も小数も文字列も同じように扱えます。
ただし文字列の場合は、変数なのか値なのか判別がつかないため、クォート記号'で囲む必要があります。

a = 3776
b = 3.14
c = 'jellyfish'
d = ''
count = 100

実行しても何も表示されません。

Pythonの場合はダブルクォートとシングルクォートのどちらでも良いのですが、本コンテンツではシングルクォート'に統一します。

d = ''とありますが、空の文字列という意味です。数値の初期値として0がよく使われますが、文字列の初期値としては''が良く用いられます。

=の前後に半角スペースがありますが、あってもなくても構いません。見やすさのために入れています。ただし、全角ではなく半角で書かないとエラーになりますのでご注意ください。 結構ありがちなエラーなので、試しに全角スペースを入れてみて、以下のようなエラー表示になることを確かめてみて下さい。

SyntaxError: invalid character in identifier

print

既に使っていたので分かると思いますがprintは、文字列や変数を表示します。
必ず()を使って、その中に文字列を記述します。

print('Mt.Fuji is 3776 meters high')

以下は変数を表示している例です。

a = 3776
print(a)

カンマ記号,を使うことにより、複数の文字列や変数を同じ行に表示させることができます。

a = 3776
print('Mt.Fuji', a)

打ち込みやすいように英語にしていますが、文字列には日本語も使用可能です。

演算

記号 + - * / を用いて四則演算が可能です。

a = 100 + 200
print(a)

a = 1000 - 1
print(a)

a = 60 * 60
print(a)

a = 300 / 60
print(a)

右側に変数を用いることも可能です。

a = 5
b = a * 60
print(b)

左側と右側で同じ変数を使用することも可能です。

a = 100
a = a + 1
print(a)

a = a + 1 は a の値を 1 増やしたという意味になります。
このような形で数値を1増やすことをプログラミング用語で「インクリメント」と呼んでいます。

int

intを使うと「整数値」に変換することができます。
以下は「小数値」を「整数値」に変換する例です。

pi = 3.14
print(pi)

pi = int(pi)
print(pi)

元の値が「文字列」であったとしても半角数字であるならば「整数値」に変換されます。

fuji = '3776'
print(fuji)

fuji = int(fuji)
print(fuji)

出力後はどちらも変わりませんが、変数fujiとしては前者が「文字列」で後者は「数値」という違いがあります。
printの場合は「文字列」「小数値」「整数値」のどれでも問題ないのですが、「整数値」でないとエラーになる場面があるため、そのようなときにintを活用します。

input

キーボードから入力されたデータを文字列として取得できます。
カッコ内には表示させたい文字列を書きます。

num = input('number : ')

print(num)

記号:は見やすさのために入れている文字列であり、pythonの文法とは関係ありません。

inputで得られる値は数値ではなく文字列であることがポイントです。例えば、以下のようなコードはエラーが出ます。

#エラーになる
num = input('number : ')
num = num * 2
print(num)

例えば10を入力すると1010という結果になります。掛け算とは異なり、10という文字列を2回繰り返すという処理になったためです。

対策例としてintを使う手法があります。

num = input('number : ')
num = int(num) * 2
print(num)

int(数値)と書くことで文字列から数値に変換されます。

インデント設定

この後に使うwhileやifにおいてインデント(字下げ)というものを使います。
通常は半角スペースキー2つ以上で表しますが、Tabで代用することも可能です。
コードエディタによってインデントの初期設定が異なります。

Geanyでは「Tab」が使われていますが、標準的に使われる「半角スペース4つ」に変更します。
ただし、キーボードで打ち込むときは「半角スペース4つ」をわざわざ打ち込まなくても大丈夫です。Geany側で自動的に挿入してくれたり、Tabキーを使うことで「半角スペース4つ」がまとめて挿入されます。

メニューから 編集 > 設定 を選びます。

エディタ > インデント にて「形式」をタブから空白に変更してOKを押します。

設定変更を反映させるために一旦バツ印をクリックして閉じます。

再びファイルを開いて、どこかでTabキーを押してください。
上記のGIFアニメのようにカーソルで確認して、スペース4つ分が出来ていれば成功です。
もし、4つのスペースになってない場合は、新規でファイルを作成してみて下さい。

while True:

強制終了されるまで無限にプログラムを繰り返すことが出来ます。
while True: の下に繰り返したい処理ブロックをインデントして記述します。

while True:
    a = input('input : ')
    print(a)

無限にループされるため、強制終了するときはCtrl + Cキーを押します。

次にprintを5つ追加し2つだけインデント、3つはインデントなしにしてみました。

while True:
    a = input('input : ')
    print(a)
    print('hello1')
    print('hello2')
print('hello3')
print('hello4')
print('hello5')

どのような実行結果になったか確かめてみて下さい。
また、インデントを変えるとどうなるか色々改造してみて下さい。

まとめてインデント

処理ブロックが長い場合など、まとめてインデント可能です。

インデントを増やしたい場合は、ブロックを選択した後にTabキーを押すことで一気にインデントされます。
逆にインデントを減らしたい場合は、ブロック選択後 Shift + Tabキーを押してください。

条件分岐

変数の値に応じて処理を変えたい場合に「条件分岐」を行います。
Pythonのデータ型について、整数、小数、文字列を扱ってきましたが、それ以外にブール型というものがあります。これはTrueもしくはFalseのどちらかの値のみ持つ型です。主に条件分岐と組合せて使用します。

working = True

if working:
    print('Busy')

if 変数:と書くことで、「変数」がTrueのときにインデントされたブロックの処理を行います。今回はTrueなのでBusyという表示になりました。

では変数をFalseにした場合を見てみます。

working = False

if working:
    print('Busy')

今回はFalseなので何も処理されずにプログラムが終了しました。
Falseのときに何か処理したい場合はelse:を追加します。

working = False

if working:
    print('Busy')
else:
    print('Not Busy')

これでTrueの場合はBusy、Falseの場合はNot Busyと表示されます。

今度は変数にブール型以外を使う場合の例です。具体的には if 条件式: と記述します。
今回はinputを活用しました。以下のコードを実行してyesを入力したり、それ以外の文字列を入力してみてください。

answer = input('Do you like Python? ')

if answer == 'yes':
    print('Good!')

==は左の変数と右の値を比較して同じならばTrue、それ以外はFalseと処理します。
条件式がTrue のときに処理ブロックを実行し、条件式がFalseのときは処理ブロックは無視されます。

同様にelse:も使えます。

answer = input('Do you like Python? ')

if answer == 'yes':
    print('Good!')
else:
    print('Too bad')

条件をさらに分けたい場合はelif 条件式:を使います。

answer = input('Do you like Python? ')

if answer == 'yes':
    print('Good!')
elif answer == 'no':
    print('Too bad')
else:
    print('What?')

yesのときはGood!、noのときはToo bad、それ以外のときはWhat?と表示されます。

andを使うことで複数の条件を簡単に扱うことができます。

answer1 = input('Do you like Python? ')
answer2 = input('Do you like Raspberry Pi? ')

if answer1 == 'yes' and answer2 == 'yes':
    print('Great!!')

どちらの入力もyesだった場合のみGreat!!が表示されます。
ifブロックの中にifを書くことでも実現可能ですが、andを使った方がシンプルです。

While 条件式:

whileはwhile True:だけではなく、ifのように条件式も扱えます。

isExecution = True

while isExecution:
    answer = input('Do you like Python? ')
    if answer == 'yes':
        print('Good!')
        isExecution = False
    else:
        print('Too bad')

print('end')

yesと入力されるまでプログラムは無限に続きます。
isExecutionは普通の変数です。変数はこのように小文字と大文字を混ぜて使っても問題ありません。
yesが入力されたときだけisExecutionがFalseになります。それを受けて次のループ開始時にはwhile False:と同等になるため、ループ処理から抜けてendを表示してプログラムを終了しています。
ループが始まる前にisExecutionをTrueで初期化しておくのがポイントです。

try except

プログラム中にエラーが発生するとエラーが出て強制終了します。
例えば、以下のコードは数値を入力すると2倍の値を出力し、0が入力されると終了する例です。半角数値を入力しているときは問題ないですが、文字列を入力してみてください。

isExecution = True

while isExecution:
    num = input('input number : ')
    num = int(num) * 2
    print(num)

    if num == 0:
        isExecution = False

print('end')        

ValueError: invalid literal for int() with base 10:などというエラーが出て強制終了してしまいます。

エラーがあった場合、途中で強制終了せず、エラーの箇所はスキップして最後まで進めたい場合があります。そのようなときにtry exceptを活用します。
以下のコードの場合は文字列が入力されエラーになると、途中で強制終了せずErrorという表示を行い、endを表示してプログラムを終了します。

try:
    isExecution = True

    while isExecution:
        num = input('input number : ')
        num = int(num) * 2
        print(num)

        if num == 0:
            isExecution = False
except:
    print('Error')

print('end')  

プログラム中にエラーがあった場合、強制終了せず、例外的な処理としてexceptのブロックに処理が移ります。

このままだとエラー内容が分かりませんので、エラー内容を表示するようにします。

try:
    isExecution = True

    while isExecution:
        num = input('input number : ')
        num = int(num) * 2
        print(num)

        if num == 0:
            isExecution = False

except Exception as e:
    print(e)
    
print('end') 

変更箇所はexceptのところの2行です。
このように記述することでエラー内容を出力させることができます。

import と time

今までは「組み込み関数」と呼ばれるPythonコードだけを扱ってきました。
ここではプログラムを途中で一時停止するコードを扱います。timeというモジュールの中に入っているコードを使いますが、「組み込み関数」にないコードを扱う場合は、最初にモジュールを取り込む必要があります。

import time

これだけ実行しても何も表示されませんが、この記述の後にtime.xxxのような書き方で各コードを扱うことが可能となります。

以下のコードを実行してみて下さい。

import time

print('hello')
time.sleep(3)
print('hello')

helloと表示してから次のhelloが出るまでの3秒間はプログラムを一時停止しています。

time.sleep(sec)と書くことでsec秒間、プログラムを一時停止できます。主にループの中で使います。小数値を設定することも可能です。

import time

while True:
    print('hello')
    time.sleep(0.5)

プログラム終了には強制終了が必要です。

リスト

リストは他のプログラミングでは配列と呼ばれています。
簡単に言うと、一つの値ではなく、一連の値を格納できる変数です。
各要素をカンマで区切り、カッコ[ ]内に記述します。

numbers = [10, 20, 30, 40, 50]
print(numbers)

そのままprintするとリストの形のまま表示されます。
Pythonではインデックス(要素の番号)は0から始まります。例えばインデックス2の場合は3番目の要素をアクセスすることになります。

numbers = [10, 20, 30, 40, 50]
print(numbers[2])

値を全部取り出すには以下のように書きます。

numbers = [10, 20, 30, 40, 50]
print(numbers[0])
print(numbers[1])
print(numbers[2])
print(numbers[3])
print(numbers[4])

要素数が多い場合、上記のような書き方だと非常に長くなってしまいます。また要素数を変更する度に修正が必要です。そこで、for inを使うことで簡単に扱うことが出来ます。

for in

whileを使った繰り返し以外に、for inを使った繰り返しがあります。
for inは主にリストと一緒によく使われます。

numbers = [10, 20, 30, 40, 50]
for n in numbers:
    print(n)

for inはリストから順に値を取り出して変数に代入し、ブロック部の処理を繰り返します。
具体的にfor n in numbers:ではnumbersから要素を1つ取り出しnに代入しブロックのprint(n)を実行するということを繰り返し、numbersから取り出す要素がなくなったら終了するという意味です。

タプル

タプルは、簡単にいうと要素を変更できないリストです。リストは後から要素を変更できますが、タプルはできません。
初期化時のカッコの形が()でリストと異なりますが、参照時のカッコはリストと同じ[]が使えます。
例えば、色情報(青・緑・赤)や画像の大きさ(幅・高さ)を指定するときに使われます。

color = (255, 255, 255)
print(color)

size = (1920, 1080)
print(size[0])

関数の形式

関数とは、ざっくり言うと、値を渡すと処理を行い結果を値で返すものです。
渡す値のことを「引数(ひきすう)」、返ってくる結果の値を「戻り値(もどりち)」と言います。
ただし、引数が無い関数、戻りが無い関数、引数が複数がある関数、戻り値が複数ある関数など、様々な形式が存在します。
実は既に使用していたintやprintは関数です。いくつかの関数の形式を見てみましょう。

#戻り値1個、引数1個
pi = int(3.14)
#戻り値なし、引数1個
print('hello')
#戻り値なし、引数3個
print('Hello', 'Python', 'Programming')

以下の例は戻り値が複数ある例です。
time.gmtimeは秒単位で書かれたUNIX時間を、年月日時分秒などに変換する関数です。これ自体は今回のコンテンツでは使用しません。戻り値を複数返す関数が存在するということを頭に入れて下さい。

#戻り値9個、引数1個
import time
year, mon, day, hour, minu, sec, wday, yday, isdst = time.gmtime(1617466516)

以下のように、引数に名前=値の形式で指定する引数もあり、キーワード引数と呼ばれています。
sep=で引数を指定することで、区切り文字を半角スペースから引数で指定した文字列に変えることができます。これ自体は今回のコンテンツでは使用しません。キーワード引数と言う書き方があるということを頭に入れて下さい。

#戻り値なし、引数3個 + キーワード引数1個
print('Hello', 'Python', 'Programming', sep='---')

以上がPython基礎プログラミングでした。

ブレッドボード

ブレッドボードはハンダ付け不要で回路を組むことができる便利なボードです。
ボードに複数の穴が開いており、そこに電子部品やジャンパー線を挿して回路を組みます。
名称の由来はパンを切る時にパンをのせる板「breadboard」です。

長手方向に対して、垂直の列5穴は見えないところで電気的に繋がっています。
電気的に繋がっていることを「導通している」といいます。

上記の画像で、同じピンク色の長方形の中にある穴同士は導通しています。それ以外は一切導通していません。
なお、今回のブレッドボードは線対称であるため、180°回転しても同じ構造です。

ジャンパー線

ジャンパー線はブレッドボードやラズパイに挿して導通させることができるワイヤーです。
今回使用するジャンパー線は「オス―メス」タイプですが、他にも「オス-オス」「メス―メス」が存在します。
オス側をブレッドボード、メス側をラズパイに挿して使います。

白以外にも様々な色のジャンパー線がありますが、機能は性能には一切関係ありません。何の信号かをぱっと見て分かるようにするために色の使い分けをする場合があります。今回は白一色しか使わないため、特に気にする必要はありません。

今回は15cmの長さですが、ジャンパー線の長さは何種類かあります。ちなみに今回の「オス―メス」タイプであれば、ジャンパー線にジャンパー線を連結させて延長し長くすることも可能です。

結線

では、ラズパイとブレッドボードにジャンパー線を結線してください。
ラズパイ側は下の画像と同じ向きにした時に、黒いピン上側の左上から3番目と6番目に繋ぎます。
ブレッドボード側は中央の位置に挿していますが、導通する同じ列に置かなければ別の位置でも構いません。

実際の写真だとこんな感じになります。

この結線は電気的に繋がることはないため、この時点では回路としての意味はありません。

ラズパイのピン配

ラズパイの上端の黒い端子部がGPIOという機能をもったピンの集まりです。
GPIOはGeneral Purpose Input/Outputの略で、日本語だと「汎用入出力」という言葉です。
ここにセンサーやLED、モーターなどを繋いで電子回路を組んでいきます。GPIOをソフトウェアから電気的に制御することで様々な電子制御が可能となります。
GPIOはそれぞれ「端子番号」と「GPIO番号」という2種類の番号を持っているため、間違わないように注意しましょう。

実はこの黒い端子部には、GPIOだけでなく、その他3種類の端子も存在します。

具体的にはこちらの画像から確認できます。

画像引用元:raspberrypi.org

小さな黒丸の中に書いてある1~40の番号が「端子番号」です。
その端子がGPIOの場合はGPIO 17のように横に番号が書いてあります。これが「GPIO番号」です。
GPIO 2(SDA)のようにカッコ()と文字列が書いてある端子もありますが、これはGPIO以外の用途としても使える信号線(例えばシリアル信号など)です。

5V powerは5V電源という意味です。例えば小さなサーボモーターの電源供給に使います。同様に3V3 powerは3.3V電源という意味です。例えばICの電源供給に使います。
Groundは電圧0Vの基準となる端子で、電池でいうところのマイナス端子にあたります。GNDとも記させることもあります。
電源やGNDは流せる電流に上限がありますので注意が必要です。

今回のコンテンツで扱うのは通常の「GPIO信号」と「GND」のみです。

Linuxコマンドでピン配確認

ラズパイの端子は暗記する必要はありません。上記のサイトにアクセスして確認すればOKですし、もっと素早く確認する方法があります。
Raspberry Pi OSにはピン配を確認するのに非常に便利がコマンドが最初から入っています。ラズパイのターミナルを立ち上げて以下のコマンドを打ち込んでみて下さい。

pinout

このような図が表示されたかと思います。

上の画像で1と水色で表示されているのは端子番号が1番であることを示しています。

以下のように色分けされているので分かりやすいと思います。

非常にシンプルで手早く確認できますので、ぜひ活用してください。

今回はラズパイのGNDとGPIO18にジャンパー線を接続しました。
次のチャプターではプログラムでGPIOを制御します。

ラズパイでGPIO制御を行う方法はいくつかありますが、今回はPythonで簡単に扱えるRPi.GPIOというモジュールを活用します。

import

RPi.GPIOはRaspberry Pi OSに最初から入っており、importするだけで使えます。

import RPi.GPIO as GPIO

上記はimport A as Bという書き方です。
これはAというモジュールをimportして、その後はBという形で使うという意味です。例えばRPi.GPIO.input(18)と書くべきところをGPIO.input(18)と短い形で書くことができます。

setmode

GPIOのモードをsetmodeを使って設定します。
前のチャプターで確認した通り、GPIOには「端子番号」と「GPIO番号」の2種類が存在しました。プログラム上で「端子番号」を使う場合はGPIO.BOARDを、「GPIO番号」を使う場合はGPIO.BCMを指定します。

今回は「GPIO番号」を使うため、GPIO.BCMを指定します。

import RPi.GPIO as GPIO

#GPIO初期設定 
GPIO.setmode(GPIO.BCM)

実行しても何も起きません。

setup

GPIOは入力(IN)として使用するか、出力(OUT)として使用するかを最初に決定する必要があります。
LEDなどを点灯させる場合はGPIO.OUTを使いますが、今回はセンサー入力を扱うためGPIO.INを指定します。

さらに入力の場合は「内蔵プルアップ設定」「内蔵プルダウン設定」「設定なし」のどれかを指定する必要があります。
入力端子に何も入力がないときは不安定な状態になりますので、入力端子には必ずプルアップかプルダウンと呼ばれる処理を行うのが鉄則です。この処理を行うことで電気的に不安定な状態がなくなります。外付けの回路でプルアップ/プルダウン処理を行う場合は、設定なしを選びます。

今回は外付けの回路では処理は行わないため、内蔵設定が必要です。
プルアップなのかプルダウンなのかは入力信号の状態によって決まります。「GND導通」と「導通なし」が想定される回路の場合はプルアップ、「3V3導通」と「導通なし」が想定される回路の場合はプルダウンです。

今回は前者であるためプルアップ設定です。
これらを踏まえると以下のようになります。

import RPi.GPIO as GPIO

#GPIO初期設定 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力・プルアップ設定
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

実行しても何も起きません。
ここまではGPIOを使う際は、必ず書くテンプレートだと思ってください。
プルアップ/プルダウン処理について初めて聞く方は理解が難しかったかと思います。今回はその処理が必要であるということだけを抑えて下さい

input

実際にGPIO18の信号を読み取って表示します。

import RPi.GPIO as GPIO
import time

#GPIO初期設定 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力・プルアップ設定
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

#メインループ
while True:
    value = GPIO.input(18)
    print(value)
    time.sleep(0.03)

time.sleepで少しだけ一時停止を行っている理由は、「チャタリング」と呼ばれるノイズの対策のためです。チャタリングとは物理的な押しボタンなどでよくある現象で、1回押しただけなのに振動の誤差により複数回押されたことになっている状態です。少し待つことにより安定した入力が得られます。

実行すると1がずっと表示されます。

では、実行したまま、一方のジャンパー線を抜いて、他方のジャンパー線と同じ列に挿入したり、抜いたりしてみて下さい。 以下の結線図は同じ列に挿している様子です。

写真だとこんな感じです。

同じ列に挿したとき、つまりGPIO18とGNDが導通したときは0、導通していないときは1になることが確認できたかと思います。入力がGNDのときは0で、入力が未入力のときはプルアップ設定が効いて1になります。

試しに以下のようにプルアップ設定を無しにしてみると、不安定な状態とはどんな感じなのかを体験することが出来ます。

#GPIO18pinを入力・プルアップ設定
GPIO.setup(18, GPIO.IN) #プルアップ/プルダウン設定なし

ドアセンサー

ドアセンサーの内部には下の写真のような「リードスイッチ」と呼ばれる部品が入っています。
磁石が近くにあるとスイッチON、離れるとスイッチOFFします。なお、センサーを動作させるための電源は不要です。

画像引用元:秋月電子通商

今回のドアセンサーでは、線がついている方にリードスイッチ、線が付いてない方に磁石が入っています。両者を近づけると2本の白い信号線が導通、離れると非導通の状態になる仕組みです。

ドアセンサーを使えば、前回のチャプターで行ったジャンパー線の挿し/抜きが、磁石を近づける/遠ざけるというアクションで代用できるということです。

回路結線

ドアセンサーの線は直接ラズパイに挿すこともブレッドボードに挿すこともできません。そこで、ターミナルブロックを経由して接続します。
この画像のように結線します。まず、ターミナルブロックの2本の足がジャンパー線と同じ列になるようにブレッドボードに挿してください。ターミナルブロックは穴が空いている方が外側の向きです。

ターミナルブロックにドアセンサーの線を繋ぐには、以下の写真のようにプラスドライバーを使って締める必要があります。穴に線の端子部を入れて、ネジを締めることにより挟んで固定します。
ドアセンサーの2本の線に極性(プラスやマイナスなどの区別)はありませんので、どっちをどっちに繋いでも問題ありません。

これが回路の完成写真です。

プログラム実行

では前回のチャプターと同じコードを実行しましょう。

import RPi.GPIO as GPIO
import time

#GPIO初期設定 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力・プルアップ設定
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

#メインループ
while True:
    value = GPIO.input(18)
    print(value)
    time.sleep(0.03)

磁石を近づけたり、遠ざけたりしてみて下さい。
近づけるときの距離は以下の写真くらいの距離で十分です。

ターミナル画面に、近づいているときは0、遠ざかっているときは1が連続で表示されていれば成功です!

中間演習

前回のチャプターでは、0や1が連続的にずっと表示されていましたが、変化があったときだけ表示するプログラムを作成して下さい。

具体的には以下の2つの機能です。

回路結線は前回のチャプターと同じです。

解答

簡単なアンケートに答えて解答例を入手しよう!
解答を見る前に自分で作って悩むことがプログラミング向上に繋がります。

完全版

Pygameとはビデオゲームを製作するために設計されたクロスプラットフォームのPythonモジュール集で、Pythonでコンピュータグラフィクスと音声を扱うためのライブラリを含んでいます。
文字列や画像を表示したり、全画面で表示したり、BGMや効果音を鳴らすことが手軽にできます。

画像引用元:Pygame Github Page

完全版

Pygameを活用して、このように画面に大きく中央に文字列を表示させたいと思います。

完全版

完全版

画像表示

こちらの画像をPygameのウィンドウに表示します。

完全版

これまではラズパイ画面の中のウィンドウ表示でしたが、ラズパイの全画面の表示にチャレンジします。ちなみに、全画面表示は「フルスクリーン表示」とも呼ばれています。

完全版

Pygameを使って、以下の内容で全画面(フルスクリーン)表示を行ってください。

完全版

「いらっしゃいませ!」と「ありがとうございました」という音声を出力します。

完全版

総合演習

これまで学んだ内容を組合せて、動画のように物理的な開閉を行うと、状態変化に合わせて音声出力と表示を変更するシステムを完成させてください。
動画では引出しケースを使っていますが、何でも構いません。ディスプレイも直接接続ではなく、PCのVNC ViewerやスマホのVNC ViewerなどでもOKです。

完全版

今回のシステムは以下の特徴があるため、様々な所で応用可能です。

家にはドアが必ずあると思います。今回のセンサーは開くタイプのドア、引き戸タイプのドア、どちらにも適応可能なため、様々に応用できます。

完全版

コンテンツの変更履歴です。

完全版