[ラズパイ] LED点灯(Lチカ)でPythonの勉強がてらカスタムクラスを作った話

 I2Cのことばっかりやってたら、GPIOを用いてLEDを光らせるとか全くスルーしてたw。Pythonもまともに打てないし、そもそもクラスとかいう前に制御文(if、while、for)とかも、まともに書けないので、勉強がてらLチカするクラスを作ってみようと日曜日に思い立った

 まず、LED、ブレッドボード、コード、抵抗は以下のセットについてきたやつで事足りるとおもう。あと、テスターがあると確認しやすい。なぜかうちにはテスターが3台ある。閉店する個人電気店の知り合いからもらったやつ。半田こてとかももらった。

テスターとか上のやつとかで十分と思う。

GPIOピンとLEDの接続方法だけど、色々なサイトに説明されているのでよく読んでね。

LED光らせたことない人にはなんで抵抗いれないとダメなん?って思う人いるとおもう。まずはオームの法則を覚えよう。というか、義務教育ではやってるはずなので思い出しましょう。オームのてんとう虫は有名で、求めたいものを指で隠すと式ができます。仲間にハジキのてんとう虫とかもいますね(なついww)

電池に豆電球つないで光らせた感覚でLEDに直で電池をつなぐとLEDがぶっ飛びます(意外に大丈夫なこともあるけどダメですw)。理由は、豆電球(フィラメント))がそれ自体が抵抗を持っているので、定電圧(乾電池1.5Vとか)なら決まった電流が流れる。これはオームの法則という大原則かあるから。

LEDは半導体なので電圧によって抵抗が変わる(過大電流が流れてしまって壊れる)ので、電流を制限するために抵抗を入れなくてはなりません。適当な抵抗を入れましょうって書いてるところも多いですが、初心者にはその適当すらわからんのですよ!!。計算めんどくせーって思う人がいると思うけど、以下のサイトで簡単にできます

このついてきたLEDのデータシートがないので、Amazonで買った部品セットのチュートリアルになんか書いてあるかと思い、目を通してみた。レッスン1のLED点灯のページを見て必要な物のリストをみると・・・あれ・・・抵抗使って無くない( ´∀` )?

 気を取り直して予想でledの順電圧VF=2.0Vで、流したい電流10mA~15mAでいいんじゃないでしょうか?(適当w)。そうすると86~129Ωとでます。なので100Ωをつかっていきます。ほかのサイトみると330Ω使ってる人多いですが4mAぐらいで暗くないですか?どうですかね?ラズパイのGIO電圧は3.3V、最大電流50mAなので大丈夫かと思います。

(訂正11/18) 3.3VのGPIOは1ピン当たり最大16mAらしいです。最大電流50mAというのはGPIO合計値ですので1pinに50mA流すと壊れます。上はVF=2.0vで100Ωだと13mAとなりギリギリすぎると思いましたので訂正します。今回は3つのLEDを8mA×3= 24mA程度でつけたいと思います。計算すると162Ωとなりますので、200Ωあたりを選択しておくと無難でしょう。

サクサクつなげましょう。

で、コードですが他のサイトではLチカさせるのに大体、GPIO18につないだ場合

import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(18, GPIO.OUT)
while True:
    GPIO.output(11, True)
    time.sleep(2)
    GPIO.output(11, False)
    time.sleep(2)

ってな感じで書いてありますが、LEDチカチカさせるにはいいんですけど、なんかの処理中にチカチカさせるとかなると、チカチカループをいちいち書くのって・・・汎用性が悪くないですか?Pythonの勉強兼ねてスレッドを用いてクラス化してみようと思いました。

myled.pyにクラスMyLed作ってみました。いきなりコードですw。スレッドの操作の説明は長ーくなるので別サイトに任せます

import RPi.GPIO as GPIO
import time
import threading

class MyLED():
    def __init__(self, gpio_num: int):
        self.gpio_num = gpio_num
        self.offLed = False
        self.rate = 100
        GPIO.setwarnings(False)
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(gpio_num, GPIO.OUT)

    def Light_ON(self, sec=0, rate=100, freq=50):
        def LedOnThread(sec, freq):
            led = GPIO.PWM(self.gpio_num, freq)
            led.start(self.rate)

            start_time = time.time()
            while True:
                led.ChangeDutyCycle(self.rate)
                if (sec > 0 and (time.time()-start_time) > sec) or self.offLed:
                    break
            led.stop

        self.rate = rate
        if self.chkLedOnThread():
            self.Light_OFF()
        self.th = threading.Thread(target=LedOnThread, args=(sec, freq,))
        self.th.start()

    def Light_OFF(self):
        if not hasattr(self, 'th'):
            return
        if self.th.isAlive():
            self.offLed = True
            self.waitLedOnThread()
            self.offLed = False

    def Hotaru_ON(self, sec=0, interval=2, r=30, freq=50):
        def HotaruThread(sec, r, freq):
            self.rate = 0
            led = GPIO.PWM(self.gpio_num, freq)
            led.start(self.rate)

            start_time = time.time()
            while True:
                for i in range(r):
                    if (sec > 0 and (time.time()-start_time) > sec) or self.offLed:
                        led.stop
                        return
                    self.rate = 100*i/r
                    time.sleep(interval/2/r)
                    led.ChangeDutyCycle(self.rate)
                for i in range(r):
                    if (sec > 0 and (time.time()-start_time) > sec) or self.offLed:
                        led.stop
                        return
                    self.rate = 100-100*i/r
                    time.sleep(interval/2/r)
                    led.ChangeDutyCycle(self.rate)
            led.stop

        if self.chkLedOnThread():
            self.Light_OFF()
        self.th = threading.Thread(target=HotaruThread, args=(sec, r, freq,))
        self.th.start()

    def Flash_ON(self, sec=0,rate=100,f_sec=0.1, freq=50):
        def FlashThread(sec, freq):
            led = GPIO.PWM(self.gpio_num, freq)
            led.start(self.rate)

            start_time = time.time()
            while True:

                led.ChangeDutyCycle(self.rate)
                c_time = time.time();
                while (time.time()-c_time) < f_sec: if (sec > 0 and (time.time()-start_time) > sec) or self.offLed:
                        led.stop
                        return

                led.ChangeDutyCycle(0)
                c_time = time.time();
                while (time.time()-c_time) < f_sec: if (sec > 0 and (time.time()-start_time) > sec) or self.offLed:
                        led.stop
                        return

            led.stop

        self.rate = rate
        if self.chkLedOnThread():
            self.Light_OFF()
        self.th = threading.Thread(target=FlashThread, args=(sec, freq,))
        self.th.start()
        
    def chkLedOnThread(self):
        if hasattr(self, 'th'):
            return self.th.isAlive()
        else:
            return False

    def waitLedOnThread(self):
        if hasattr(self, 'th'):
            self.th.join()

    def changeRate(self, rate):
        self.rate = rate

    def CleanUp(self):
        GPIO.cleanup(self.gpio_num)

 軽く説明。MyLED(GPIO番号)でインスタンスを生成。

  • Light_ONメソッド LED点灯。secは点灯秒数(0だとずっと点灯)、rateはLEDの明るさ、freqはPWMの周波数(通常は50でOK)
  • Hotaru_ONメソッド ホタルのような瞬きを。secは点灯時間、intervalはついて消えるまでの時間、rは変化するレート(数値が多いほど諧調が細かくなる)、freqは同じ
  • Flash_ONメソッド 点滅点灯。f_secは点滅間隔時間。他はLight_ONと一緒
  • waitLedOnThreadメソッド Light_ON、Hotaru_ON、Flash_ONはスレッドで動作するのでそのスレッドが終わるまで待機するメソッド。sec=0を指定した場合呼び出すと無限ループになるので注意
  • changeRateメソッド Ligtht_ONメソッド、Flash_ONメソッドど動作してるスレッドのLEDの明るさを変える。Hotaru_ONは意味なし
  • Light_OFFメソッド LEDの各種点灯スレッドを止める
  • CleanUpメソッド GPIOピンの開放

 このクラスではGPIO.outputメソッドだと明るさを制御できないから、LEDの点灯制御はPWMをすべて使いました。

で、実際のクラスを使って点灯させるコード。GPIO18に緑LEDと抵抗、GPIO17に赤LEDと抵抗、GPIO27に青LEDと抵抗を接続しています。GNDはブレッドボードでまとめて接続。Frizingを使って初めて書いてみたけど・・・なんか重なって下手w

import time
from myled import MyLED

ins = MyLED(18)
ins2 = MyLED(17)
ins3 = MyLED(27)

try:
    ins.Hotaru_ON(0)
    ins2.Flash_ON(0,10,0.1)
    ins3.Hotaru_ON(0,1.6)
    
    ins.waitLedOnThread()

finally:
    ins.Light_OFF()
    ins.CleanUp()
    ins2.Light_OFF()
    ins2.CleanUp()
    ins3.Light_OFF()
    ins3.CleanUp()


 スレッドで動作するので、パラレルにLEDを制御することができるようになりました。上のコードだとins.waitLedOnThread()がないと、実行の瞬間finallyに流されてしまうので、待機のために呼び出している(insはホタルで永遠に点灯中なので永遠に待つことになる)。

 これだと、他のセンサーの検出中とか、動作中とかに使いやすくなるのかなと思う。でもPythonさわって1週間程度なので・・・参考程度に…