🔈
ラズパイ-ラジコン+αを自作する - その4(LED/ブザー/表示)
🏁デモ
📚記事一覧
📍本記事の範囲
- LEDのON/OFF
- ブザーのON/OFF
- 液晶の表示
について説明します。
🔧パーツ一覧
部品名 | 個数 | 内容 | 備考 |
---|---|---|---|
RaspberryPiZeroWH | 1 | ラズベリーパイゼロ本体 | 秋月電子 |
カムロボット | 1 | カムロボット本体 | Amazon |
SG90 | 2 | SG90本体 | 秋月電子 |
LED | 2 | LED本体 | - |
LED用抵抗 | 2 | 抵抗 | - |
圧電ブザー(PKM13EPYH4000) | 1 | BEEP音用 | 秋月電子 |
OLED | 1 | 表示機128x64dot | 秋月電子 |
※全パーツは、その1-コンセプトを参照のこと
接続図
LED
Pin | LED | 備考 |
---|---|---|
GND | GND | LED1/2 |
BCM16 | LED1-アノード | - |
BCM20 | LED2-アノード | - |
ブザー
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を選択
- "はい"(or "Yes")を選択
- これで有効化されます(1回行えばOKです)
モジュールのインストール
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
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
以下の順番でカムロボットが動作します。
- LED1を3秒点灯
- LED1を消灯
- LED2を3秒点灯
- LED2を消灯
ブザー制御
コード - ブザー
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
以下の順番でカムロボットが動作します。
- ドを1秒鳴らす
- レを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()
- 呼ばれるたびに...->点灯->消灯->点灯->消灯->..を繰り返す
ブザー制御 - 音の指定
以下の形式で指定することができます。
- 音名+オクターブ指定("C4" : ド、"E4" : ミなど)
- Tone("C4")
- MIDI Note指定(60 : ド (C4と同じ))
- Tone(midi=60)
- 周波数の指定(400)
- Tone(frequency=400)
表示制御 - 画像を作成して描画
コード上で使用しているPillowモジュールはサードパーティ製の画像処理モジュールです。
今回のコードでは、文字列であっても一旦画像にしています(Pillowを使用)。
oled.image()へ128x64 のモノクロ画像を渡しています。
さいごに
上記以外のラズパイの活用方法を
としてまとめ中です。
Discussion