고무-리모컨+α사용자 정의 - 사용자 4(LED/버저/표시)

🏁데모



📚기사 목록

  • 그 개념
  • 둘째, 모터 제어
  • 셋째: 팔의 컨트롤

  • 4 – LED/버저/표시← 기사
  • 📍본문의 범위

  • LED의 ON/OFF
  • 버저의 ON/OFF
  • LCD 디스플레이
  • 라고 설명했다.

    🔧부품 일람


    부품 이름
    개수
    컨텐트
    시험을 준비하다
    RaspberryPiZeroWH
    1
    크랜베리 파이 제로 본체
    추월전자
    캠 로봇
    1
    캠 로봇 본체
    Amazon
    SG90
    2
    SG90 호스트
    추월전자
    LED
    2
    LED 섀시
    -
    LED용 저항
    2
    저항
    -
    압전 버저(PKM13 EPYH4000)
    1
    BEEP 음용
    추월전자
    OLED
    1
    모니터 128x64dot
    추월전자
    ※ 모든 부품은 그 개념 참조

    연결 다이어그램


    LED



    Pin
    LED
    시험을 준비하다
    GND
    GND
    LED1/2
    BCM16
    LED 1 - 양극
    -
    BCM20
    LED 2 - 양극
    -

    버저



    Pin
    버저
    시험을 준비하다
    GND
    버저 연결부
    무극성
    BCM21
    버저 연결부
    무극성

    OLED



    Pin
    OLED
    시험을 준비하다
    BCM2
    SDA
    -
    BCM3
    SCL
    -
    GND
    GND
    -
    3.3V
    VDD
    -

    💻컨디션


    개발 환경

  • 크랜베리 파이
  • Linux rpi 5.10.17-v7l+ #1403 SMP Mon Feb 22 11:33:35 GMT 2021 armv7l GNU/Linux
  • Python
  • Python 3.7.3 (default, Jan 22 2021, 20:04:44)
  • 크랜베리 파이 설정


    OLED-I2C의 유효성


    이 장치는 I2C 통신을 사용하여 제어합니다.
    I2C로 통신할 수 있도록 설정해야 합니다.
    다음 명령을 사용하여 I2C를 효율적으로 사용할 수 있습니다.
    $ sudo raspi-config
    
  • Interface Options 선택
  • I2C 선택
  • 예 또는 예 선택
  • 이렇게 하면 효과가 있다(한 번에 OK)
  • 설치 모듈


    에는 OS의 클린 설치 사전 요구 사항이 기재되어 있습니다.

    apt


    pigpio 라이브러리는 Raspberry Pi를 제어하는 GPIO 라이브러리입니다.
    다음 명령은 처음 설치할 때만 필요합니다.
    $ sudo apt install pigpio
    $ sudo service pigpiod start
    $ sudo systemctl enable pigpiod.service
    
    또한 일본어를 표시하는 글꼴을 설치합니다.
    $ sudo apt-get install fonts-ipafont
    

    pip


    Python 관련 모듈을 설치합니다.

    LED,버저


    $ python3 -m venv env
    $ source env/bin/activate
    (env) $ pip install pigpio
    (env) $ pip install gpiozero
    (env) $ pip install icecream
    
    icecream은 디버깅용입니다.
    꼭 필요한 건 아니지만 확인 시 사용합니다.
    처리가 느리다고 생각되면 print 문장으로 바꿔 주세요.

    OLED


    (env) $ pip install adafruit-circuitpython-ssd1306
    (env) $ pip install smbus2
    (env) $ pip install pillow
    

    📝절차.


    마지막으로 모든 모듈의 협업을 위해 대기열을 사용했습니다.

    LED 제어


    코드 - LED


    led.py
    from queue import Queue
    import threading
    import time
    from gpiozero import LED
    from gpiozero.pins.pigpio import PiGPIOFactory
    import sys
    from icecream import ic
    
    # LEDのピン設定
    PIN_LED_NO1 = 16
    PIN_LED_NO2 = 20
    
    LED_DICT = {
        "no1" : PIN_LED_NO1,
        "no2" : PIN_LED_NO2,
    }
    
    
    class LedThread(threading.Thread):
        """
        LED管理
        例:
        queue経由で、{"name":"no1", "action":"on"}
        を取得すると、LED1を点灯
        """
        def __init__(self):
            threading.Thread.__init__(self)
            self.stop_event = threading.Event()
            self.setDaemon(True)
    
            self._rcv_que = Queue()
            self._leds = {}
    
            # 各ピンをLED設定
            factory = PiGPIOFactory()
            for key, pin in LED_DICT.items():
                self._leds[key] = LED(pin, pin_factory=PiGPIOFactory())
            return
    
        def stop(self):
            self.stop_event.set()
            return
    
    
        def run(self):
            while True:
                value = self.rcv_que.get()
                ic("[led_th]", value)
                
                if "led" not in value["type"]:
                    ic("[led_th]", "error!!!")
                    continue
                
                if value["name"] in self._leds:
                    name = value["name"]
                    on_off = True if ("on" in value["action"]) else False
                    self._write_leds(name, on_off)
            return
    
        @property
        def rcv_que(self):
            return self._rcv_que
    
        def _write_leds(self, name, on_off):
            if on_off:
                self._leds[name].on()
            else:
                self._leds[name].off()
            return
    
    
    def main():
        import time
    
        led_th = LedThread()
        led_th.start()
        q = led_th.rcv_que
    
        q.put({"type": "led", "name": "no1", "action": "on"})
        time.sleep(3)
        q.put({"type": "led", "name": "no1", "action": "off"})
        time.sleep(1)
        q.put({"type": "led", "name": "no2", "action": "on"})
        time.sleep(3)
        q.put({"type": "led", "name": "no2", "action": "off"})
        time.sleep(1)
    
        led_th.stop()
       
        return
    
    if __name__ == "__main__":
        main()
    

    실행 단계 - LED


    (env) $ python led.py
    
    캠 로봇은 다음과 같은 순서에 따라 동작한다.
  • LED 1포인트 3초
  • LED 1
  • 끄기
  • LED 2포인트 3초
  • LED 2
  • 끄기

    버저 제어


    코드 버저


    buzzer.py
    from queue import Queue
    import threading
    import time
    from gpiozero import TonalBuzzer
    from gpiozero.tones import Tone
    from gpiozero.pins.pigpio import PiGPIOFactory
    from icecream import ic
    
    # BUZZERのピン設定
    BUZZER_PIN = 21
    
    # Midi note: 'C4' - ド
    # Midi note: 'D4' - レ
    # Midi note: 'E4' - ミ
    # Midi note: 'F4' - ファ
    # Midi note: 'G4' - ソ
    # Midi note: 'A4' - ラ
    # Midi note: 'B4' - シ
    # Midi note: 'C5' - ド
    
    BUZZER_DICT = {
        "buzzer" : BUZZER_PIN,
    }
    
    
    class BuzzerThread(threading.Thread):
        """
        ブザー管理
        例:
        queue経由で、{"type":"buzzer", "time": "300", "bfreq":"2000"}
        を取得すると、ブザー音を300msec鳴らす
        """
        def __init__(self):
            threading.Thread.__init__(self)
            self.stop_event = threading.Event()
            self.setDaemon(True)
    
            self._rcv_que = Queue()
            self._buzzer = {}
            for key, pin in BUZZER_DICT.items():
                self._buzzer[key] = TonalBuzzer(pin, pin_factory=PiGPIOFactory())
            return
    
        def stop(self):
            self.stop_event.set()
            # cleanup
            for key in self._buzzer:
                self._buzzer[key].stop()
            return
    
    
        def run(self):
            while True:
                # time.sleep(0.050)
                item = self.rcv_que.get()
                ic("[buzzer_th]", "run : get : ", item)
                
                if "buzzer" not in item["type"]:
                    ic("[buzzer_th]", "error!")
                    continue
    
                ms_time = int(item["time"]) / 1000
                # item["note"] : 'C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5'
                self._buzzer[item["name"]].play(Tone(item["note"]))
                time.sleep(ms_time)
                self._buzzer[item["name"]].stop()
            return
    
        @property
        def rcv_que(self):
            return self._rcv_que
    
    
    def main():
        import time
    
        buzzer_th = BuzzerThread()
        buzzer_th.start()
        q = buzzer_th.rcv_que
    
        q.put({"type": "buzzer", "name": "buzzer", "time": "500", "note": "C4"}) # do
        time.sleep(1)
        q.put({"type": "buzzer", "name": "buzzer", "time": "500", "note": "D4"}) # re
        time.sleep(1)
        q.put({"type": "buzzer", "name": "buzzer", "time": "500", "note": "E4"}) # mi
        time.sleep(1)
        q.put({"type": "buzzer", "name": "buzzer", "time": "500", "note": "F4"}) # fa
        time.sleep(1)
    
        buzzer_th.stop()
       
        return
    
    if __name__ == "__main__":
        main()
    

    단계 실행 - 버저


    (env) $ python buzzer.py
    
    캠 로봇은 다음과 같은 순서에 따라 동작한다.
  • 명d1초
  • 천둥 1초
  • 명미 1초
  • 명법 1초
  • 사운드 중지
  • 디스플레이 제어


    코드 - 표시


    oled.py
    from queue import Queue
    import threading
    import time
    from systems import SystemsData
    
    # Imports the necessary libraries...
    import socket
    import fcntl
    import struct
    import board
    import digitalio
    from PIL import Image, ImageDraw, ImageFont
    import adafruit_ssd1306
    
    import sys
    from icecream import ic
    
    # OLED設定
    DISP_WIDTH = 128
    DISP_HEIGHT = 64
    DEVICE_ADDR = 0x3C
    
    # PATH_FONT = "./ipaexm.ttf"
    PATH_FONT = "/usr/share/fonts/truetype/fonts-japanese-gothic.ttf"
    
    class OledThread(threading.Thread):
        """
        OLED管理
        例:
        queue経由で、{"type":"oled", "time": "3000", "disp":"ip"}
        disp : ip / clear
        """
        def __init__(self):
            ic()
            threading.Thread.__init__(self)
            self.stop_event = threading.Event()
            self.setDaemon(True)
    
            self._rcv_que = Queue()
            self._sysdat = SystemsData()
    
            # Setting some variables for our reset pin etc.
            RESET_PIN = digitalio.DigitalInOut(board.D4)
            TEXT = ""
    
            # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display
            i2c = board.I2C()
            self._oled = adafruit_ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, i2c, addr=DEVICE_ADDR, reset=RESET_PIN)
    
            # font
            self._font10 = ImageFont.truetype(PATH_FONT, 10)
            self._font12 = ImageFont.truetype(PATH_FONT, 12)
            self._font14 = ImageFont.truetype(PATH_FONT, 14)
            self._font16 = ImageFont.truetype(PATH_FONT, 16)
            self._font18 = ImageFont.truetype(PATH_FONT, 18)
    
            # Clear display.
            self._oled.fill(0)
            self._oled.show()
            return
    
        def stop(self):
            ic()
            self.stop_event.set()
            # cleanup
            self._oled.fill(0)
            self._oled.show()
            return
    
    
        def run(self):
            ic()
            while True:
                item = self.rcv_que.get()
                ic(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name, item)
                
                if "oled" not in item["type"]:
                    print("[oled_th]", "error : type")
                    continue
                
                self._recvice(item)
            return
    
        @property
        def rcv_que(self):
            return self._rcv_que
    
        def _recvice(self, item):
            ic()
            val_time = int(item["time"]) / 1000
            val_disp = item["disp"]
    
            def display_ip():
                ic()
                def get_ip_address(ifname):
                    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                    return socket.inet_ntoa(
                        fcntl.ioctl(
                            s.fileno(),
                            0x8915,  # SIOCGIFADDR
                            struct.pack("256s", str.encode(ifname[:15])),
                        )[20:24]
                    )
                # This sets TEXT equal to whatever your IP address is, or isn't
                try:
                    TEXT = get_ip_address("wlan0")  # WiFi address of WiFi adapter. NOT ETHERNET
                except IOError:
                    try:
                        TEXT = get_ip_address("eth0")  # WiFi address of Ethernet cable. NOT ADAPTER
                    except IOError:
                        TEXT = "NO INTERNET!"
    
    
                # Clear display.
                self._oled.fill(0)
                self._oled.show()
    
                # Create blank image for drawing.
                image = Image.new("1", (self._oled.width, self._oled.height))
                draw = ImageDraw.Draw(image)
    
                # Draw the text
                intro = "カムロボです。"
                ip = "IPアドレス:"
                draw.text((0, 46), TEXT, font=self._font14, fill=255)
                draw.text((0, 0), intro, font=self._font18, fill=255)
                draw.text((0, 30), ip, font=self._font14, fill=255)
    
                # Display image
                self._oled.image(image)
                self._oled.show()
    
                return
    
            def display_clear():
                self._oled.fill(0)
                self._oled.show()
                return
    
            if "ip" in val_disp:
                display_ip()
            else:
                # Clear display.
                display_clear()
            return
    
    def main():
        import time
    
        oled_th = OledThread()
        oled_th.start()
        q = oled_th.rcv_que
    
        q.put({"type": "oled", "time": "3000", "disp":"ip"})
        time.sleep(10)
        q.put({"type": "oled", "time": "3000", "disp":"clear"})
        time.sleep(1)
    
        oled_th.stop()
       
        return
    
    if __name__ == "__main__":
        main()
    

    단계 - 표시


    (env) $ python oled.py
    
    캠 로봇은 다음과 같은 순서에 따라 동작한다.
  • 10초 이하 표시
  • "캠 로봇입니다."
  • IP 주소
  • 표시 해제
  • 🔎주안점


    LED 제어 - 코드 제어

  • on()/off()
  • LED 켜기/LED 꺼짐
  • blink()
  • 1초 단위로 켜기/끄기 반복
  • toggle()
    매번 불려가다
  • ...->켜기->끄기->켜기->끄기->.반복
  • 버저 제어 - 사운드 지정


    다음 형식으로 지정할 수 있습니다.
  • 음명 + 8도 지정("C4": 12"E4": 만들기 등)
  • Tone("C4")
  • MIDI Note 지정(60:d(C4와 동일)
  • Tone(midi=60)
  • 주파수 지정(400)
  • Tone(frequency=400)
  • 모든 소리를 다 지목할 수 있는 건 아니에요.
    MIDI Note의 경우 57-81 범위에서 지정해야 함
    장치에 따라 다른 소리를 지정해도 같은 소리일 수 있습니다.
    내 환경에서 톤(C4)과 톤(D4)은 같은 소리로 들린다.

    디스플레이 제어 - 이미지 생성 및 그리기


    코드에 사용된 Pillow 모듈은 타사 이미지 처리 모듈입니다.
    이번 코드에서는 문자열이라도 먼저 이미지(Pillow 사용)를 만듭니다.
    oled.128x64의 단색 이미지를 이미지()에 전달합니다.

    최후


    상기 이외의 활용 방법
    https://zenn.dev/kotaproj/books/raspberrypi-tips
    총결산으로 삼다.

    좋은 웹페이지 즐겨찾기