朝からLEDの並列接続やら制限抵抗を実践しながらお勉強して、午後はプログラム言語のPythonのお勉強。
お勉強というか、
・ラズパイ初心者向きの本
・アマゾンで購入したラズパイ用電子工作キットの説明書
・最近購入した中級者?向きのラズパイでのパーツ制御の解説書
・他、インターネットの参考ページ等
それらのエルチカ(LEDを光らせよう!)のプログラム(Python)の記述が、結構違うんですよね。
2年前にラズパイを触って、いつの間にか挫折してしまった原因の一つではあると思います。
特に違うのが
if __name__ == “__main__”:
が有るか、無いかの違いと、
メインプログラムのループ方法やそれの離脱方法なんですよね。
で、私的には色々調べまして、この
if __name__ == "__main__":
は、「別のプログラムでimportを宣言したとき、メインの実行を行わないためにある」と理解しました。
Pythonでは、プログラムをimport宣言した時に、一度中身を実行するらしく、これを記述することによって、それ以降のメインプログラムがimportの宣言時では動かず、関数として呼び出したとき、またはOSから直接呼び出した時だけ動くようになるとのこと。
これをグーグル先生に検索してもらっても、なかなか納得のいく回答を得られるページに辿り着けず、1時間はなんだよこれは~!!という状態でした。
で、そもそもこの記述の必要性は、簡単な電子工作をプログラム一つで実行する際は、必要ないんですよね。
必要になるのは、いろんな機能を持った電子工作(ロボットとか)の制御プログラムを制御の種類毎に別途用意して、それを呼び出して使うときだと思います。たとえば、液晶LCDモジュールの表示用プログラムとかですね。
ループ処理
ループ処理の記述も、参考にする本やネット記事によって全然違いますが、こちらもラズパイに標準で付随している、「Thonny」というアプリ上で、Pythonを記述、実行する際には、あまり必要性というか、正確性というか、なんとでもなるんですよね。
クラッシュとか無限ループしても、「Thonny」というアプリ上で止めれますので。
で、私としては理解できないプログラムを真似して使うよりは、自分で理解して使いたいので、前回のエルチカの記事では、5回ループして終了という、自分でも理解できる方法で記述しました。
で、今日の午後で、「CTRL + C」を押したら、ループ解除。ついでに、電子工作の「押しボタン」を押したら、ループ解除。という2つの終了条件を備えたループプログラムを自分で納得できるまでお勉強。
おそらく、4時間はグーグル先生のお世話になりました。
参考にしたサイトPythonでデバイスを制御しよう プッシュボタンを扱う
このサイトの、関連コラムを2回は読み返しました。
もうね、他人様の書いたPythonって、オリジナルの変数名なのか、それとも標準というかPythonに指示をするための単語なのか、理解に苦しむんですよね。
私はMSXでBASICを覚えて、それ以降プログラム言語を全然触ってこなかったので、やたらと長い変数名が使えるのに頭が慣れないんですよね~。
繰り返しのforに使う変数は、iとかjとかhあたりという固定概念が残っているというか。
今日、一番??になったのは、その参考サイトで記述されていた
sys.exit(main())
です。
sys.exit(0)とsys.exit(1)を説明しているサイトはすぐに見つかるのですが、このカッコの中に関数としてメインプログラムを入れる意味と、入れてもいいのか??という疑問を解消するのに、時間がかかりました。
ようは、これも、プログラムを他のプログラム上で呼び出して使ったときに、正常に処理が終わりました(= sys.exit(0) )という記録をOS上に残すためですね。
で、sys.exit(main())は、main()関数を実行して、main関数の戻し値(return 0)が代入されるということを理解するのに小一時間。
試しに、print(main())を記述して、適当にmain関数の戻し値をreturn 129 とかにして実行したら、main関数が実行後(エルチカ後)に129と表示されました。
ま~なので、おそらく実践的なプログラムの記述方法なんでしょうね。
うまく動かないのがソフトなのかハードなのか
で、最終的に物理ボタンでループを解除するプログラムを書いて、実行してみたら、どうにもボタンを押しても反応が悪いんですよね。
LEDを点灯させるのに、time.sleepで0.5秒とか止めるので、その間にボタンを押したかどうかの判定が止まって、うまく作動しないのかな。
でも、0.001秒を500回繰り返すとかで判定の間を用意してみたり、逆に10秒程度連続でsleepしてみたり、bouncetimeという待ち時間の数値を極端に変化させてみたりしても、同じような不安定な挙動で、
「あ~意味わからん!!」
と、匙を投げそうになったのですが、
ふと、
「これって、ちゃんと基盤に刺さってないのでは?」
と、気づいて、少々強引に基盤に押し込んだら、見事に改善されました。
結果として、time.sleepで10秒点灯させ続けても、好きなタイミングで押しボタンの割り込み処理はできました。
で、ここからもまだ壁がたくさんありまして、どうやってループを止めるのか?という部分で、失敗を繰り返しました。
とりあえず、ボタンを押されたときに return を 実行すれば、メイン関数が終了するかなと思いきや、ループは生き続け、GPIOのエラー発生。
では、return の代わりに sys.exit(0) を記述すれば、終了できるかと思えば、こちらもループは生き続け、「Thonny」からエラー警告発生。
なので、晩御飯を食べながら模索した結果が、while True: の無限ループの記述の True を 変数にして判定すればいいのでは閃き、それを実践。
正直、グローバル変数とかの設定とか、宣言とか、今一歩理解できていませんが、我ながら良い感じに出来上がりました。
動画と使用プログラム(Python)
私の覚書用です。
興味ない人が動画見ても、何をやっているか不明かと思います^^;
#ラズパイのGPIOを制御するために、Rpi.GPIOライブラリーを読み出し
import RPi.GPIO as GPIO
#時間に関する制御をするために、time関数を読み出し
import time
#キーボード(ctrl+c)の監視用
import sys
#使用するLEDのGPIO番号(セットモードはBCM)(物理ピン番号はBOARD)
LedPin1 = 17
LedPin2 = 27
LedPin3 = 22
#使用する終了ボタンのGPIO番号
endBotann = 26
#終了ボタンが押されたら0false ループ解除用
G_break_ch = True
#初期設定 setup()関数の定義
def setup():
#立ち上がり時のGPIOのエラーメッセージを非表示
GPIO.setwarnings(False)
#GPIOのピン番号指定方法(通常はGPIO番号 BCM)(物理ピン番号での指定は BOARD)
GPIO.setmode(GPIO.BCM)
#使用するGPIOピンを入力(IN)で使用するのか、出力(OUT 3.3v)で使用するのかの設定
#initialは初期の状態 1=GPIO.HIGH=True
#初期設定がHighなのは、LEDのカソード(ー)をGPIOに繋げるため
GPIO.setup(LedPin1,GPIO.OUT,initial=1)
GPIO.setup(LedPin2,GPIO.OUT,initial=1)
GPIO.setup(LedPin3,GPIO.OUT,initial=1)
#終了ボタンの設定 GPIO内部のプルアップ抵抗を使用 ボタンの先はGND
GPIO.setup(endBotann,GPIO.IN,pull_up_down=GPIO.PUD_UP)
# 立ち下がり(GPIO.FALLING)を検出する(プルアップなので通常時1/押下時0)
GPIO.add_event_detect(endBotann,GPIO.FALLING,bouncetime=100)
# イベント発生時のコールバック関数を登録
GPIO.add_event_callback(endBotann,Botann_pressed)
# ボタンが押された時に呼び出されるコールバック関数
# gpio_no: イベントの原因となったGPIOピンの番号
def Botann_pressed(gpio_no):
global G_break_ch
# メッセージを表示
print('GPIO番号',gpio_no,'に接続のボタンが押されました')
# 基幹プログラムのループ解除用
G_break_ch = False #False=0
#LED 点灯時間調整 (割り込み用のスキマ時間には関係なかった time.sleep(10)とかでも瞬時に割り込める)
def led_time(ledtime):
for loop_i in range(ledtime):
time.sleep(0.1)
#メインプログラム1 main1()関数の定義 LEDの点灯と消灯
def main1():
#1番目 LED ON 0.5秒点灯
print('LED1 ON')
GPIO.output(LedPin1,GPIO.LOW)
led_time(5)
#1番目消灯 2番目 LED ON 0.5秒点灯
print('LED2 ON')
GPIO.output(LedPin1,GPIO.HIGH)
GPIO.output(LedPin2,GPIO.LOW)
led_time(5)
#2番目消灯 3番目 LED ON 0.5秒点灯
print('LED3 ON')
GPIO.output(LedPin2,GPIO.HIGH)
GPIO.output(LedPin3,GPIO.LOW)
led_time(5)
#3番目消灯 0.5秒待機
print('LED OFF')
GPIO.output(LedPin3,GPIO.HIGH)
led_time(5)
#1番目 LED ON 0.5秒点灯
print('LED1 ON')
GPIO.output(LedPin1,GPIO.LOW)
led_time(5)
#2番目 LED ON 0.5秒点灯
print('LED2 ON')
GPIO.output(LedPin2,GPIO.LOW)
led_time(5)
#3番目 LED ON 0.5秒点灯
print('LED3 ON')
GPIO.output(LedPin3,GPIO.LOW)
led_time(5)
#LED3個ともOFF 0.5秒消灯
print('LED OFF')
GPIO.output(LedPin1,GPIO.HIGH)
GPIO.output(LedPin2,GPIO.HIGH)
GPIO.output(LedPin3,GPIO.HIGH)
led_time(5)
#エンディングプログラム destroy()関数の定義
def destroy():
#LED OFF
GPIO.output(LedPin1,GPIO.HIGH)
GPIO.output(LedPin2,GPIO.HIGH)
GPIO.output(LedPin3,GPIO.HIGH)
#GPIOの初期化
GPIO.cleanup()
#基幹プログラム main1をループ [clrl+c] でループ離脱
def main():
try:
setup()
while G_break_ch:
main1()
except KeyboardInterrupt:
print('ctrl+c が検出されました')
print('プログラムを終了します')
destroy()
return 0
#importで他のプログラム等から呼び出し時に、実行しないための記述
if __name__ == "__main__":
sys.exit(main())
#return 0 でエラーなく終了したことになる。sys.exit(0)という結果


コメント