🔆
ラズパイ - FastAPIとシリアルLEDで電光掲示板をつくったよ
FastAPIとシリアルLEDを使って、電光掲示板を作りました。
きっかけ、作り方について紹介します。
💡きっかけ
参加している勉強会のLTのネタとして作りました。
⚙概要
動作の流れ
以下の流れです。
- PC/スマホより、描画したい文字列を送信する
- [ラズベリーパイ]文字列を x * 32の画像に変換する(xは、文字数により変わる)
例 : - [ラズベリーパイ]作成された画像を読み出し、シリアルLEDに送信する
文字列が表示される
🔧パーツ一覧
no | 部品名 | 個数 | 備考 | |
---|---|---|---|---|
1 | ラズベリーパイ | 1 | 今回は4Bで確認 | |
2 | シリアルLED(16x16) | 4 | BTF-LIGHTING WS2812B ECO RGB合金ワイヤー 16X16cm 265ピクセル LED | |
3 | ACアダプター 5V 4A | 1 | ACアダプター 5V 4A出力プラグ外径5.5mm | 3A以上は必須 |
4 | DCジャック変換プラグ | 1 | DCジャック変換プラグ | |
5 | ジャンパー線 | 適量 | - |
接続
各モジュールの接続
シリアルLEDの接続
- 正面から見たシリアルLED
- 背面から見たシリアルLED(接続)
接続表
ws281x | 接続先 | 備考 |
---|---|---|
[A]①3pin - 5V | - | つながない |
[A]①3pin - GND | ラズベリーパイ-GND | - |
[A]①3pin - DIN | [pi]① ラズベリーパイ-[PHY]12([BCM]18) | [PHY]は物理ピン, [BCM]はプログラムで指定 |
[A]②2pin - 5V | 外部電源-5V | DCジャック変換プラグを介して、ACアダプタに接続 |
[A]②2pin - GND | 外部電源-GND | DCジャック変換プラグを介して、ACアダプタに接続 |
[A]③3pin - 5V | ws281x - [B]③3pin - 5V | 専用のコネクタで接続する |
[A]③3pin - GND | ws281x - [B]③3pin - GND | 専用のコネクタで接続する |
[A]③3pin - DOUT | ws281x - [B]③3pin - DIN | 専用のコネクタで接続する |
[B]④3pin - 5V | ws281x - [C]④3pin - 5V | 専用のコネクタで接続する |
[B]④3pin - GND | ws281x - [C]④3pin - GND | 専用のコネクタで接続する |
[B]④3pin - DOUT | ws281x - [C]④3pin - DIN | 専用のコネクタで接続する |
[C]④3pin - 5V | ws281x - [D]④3pin - 5V | 専用のコネクタで接続する |
[C]④3pin - GND | ws281x - [D]④3pin - GND | 専用のコネクタで接続する |
[C]④3pin - DOUT | ws281x - [D]④3pin - DIN | 専用のコネクタで接続する |
- 専用コネクタは、シリアルLEDに付属されている
ラズベリーパイの接続部分
💻環境
開発環境
- ラズベリーパイ
- 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)
apt
日本語を表示するためにフォントをインストールします。
$ sudo apt-get install fonts-ipafont
pip
adafruit-circuitpython-neopixelというをライブラリを使用することで、シリアルLEDを制御することができます。
本モジュールを使用する場合は、sudo権限が必要です。
インストールが紹介されている公式サイトは、下記となります。
adafruit-circuitpython-neopixelをインストールします。
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install adafruit-circuitpython-neopixel
また、fastapi, pillow, opencv, webcolorsをインストールします。
(env) $ pip install fastapi, uvicorn
(env) $ pip install pillow
(env) $ pip install opencv-python==4.5.1.48
(env) $ pip install webcolors
📝手順
3つのファイルに分けて作成します。
各ファイルの役割は以下となります。
ファイル名 | 役割 | 備考 |
---|---|---|
main.py | fastapiの実行 | - |
schemes.py | スキーマ PC <-> ラズベリーパイ間のリクエスト定義 | - |
utl.py | 画像の作成, 表示処理 | - |
全コードは、下記に配置してあります。
schemes.py : スキーマ
以下の仕様になっています。
POST /regist
項目 | 型 | 説明 |
---|---|---|
job_id | string (Job Id) | 識別子(ファイル名に使用) |
message | string (Message) | 表示する文字列 |
color | string (Color) | 色の指定(red, green, ...) |
fontsize | integer (Fontsize) | 文字サイズ |
POST /show
項目 | 型 | 説明 |
---|---|---|
job_id | string (Job Id) | 識別子(ファイル名に使用) |
bright | number (Bright) | 明るさ |
interval | number (Interval) | 更新間隔 |
POST /do
項目 | 型 | 説明 |
---|---|---|
job_id | string (Job Id) | 識別子(ファイル名に使用) |
message | string (Message) | 表示する文字列 |
color | string (Color) | 色の指定(red, green, ...) |
fontsize | integer (Fontsize) | 文字サイズ |
bright | number (Bright) | 明るさ |
interval | number (Interval) | 更新間隔 |
main.py : API処理
表示時間がかかるため、各処理をbackground_tasksに渡しています。
@app.post("/do")
async def do_item( item: ItemDo,
background_tasks: BackgroundTasks = None):
background_tasks.add_task(do_job, job_id=item.job_id, msg=item.message, color=item.color,\
fontsize=item.fontsize, bright=item.bright, interval=item.interval
)
return item
utl.py : 画像の作成、画像の表示
画像の作成 - regist_job
pillowを使用しています。背景を黒にして文字を描画します。
colorにて、"primary"(or "rainbow")を指定した場合、カラフルな文字になります。
import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont
import webcolors
# フォント
ttfontname = "/usr/share/fonts/truetype/fonts-japanese-gothic.ttf"
# 表示位置
top_shift = 0
# 色種別
rain_colors = ["red", "green", "blue", "cyan", "white", "magenta", "yellow"]
def regist_job(job_id: str, msg: str, color: str, fontsize: int) -> None:
# 画像サイズ,背景色を設定
canvasSize = (32*(len(msg)+2), 32)
backgroundRGB = webcolors.name_to_rgb("black")
# 文字を描く画像の作成
img = PIL.Image.new('RGB', canvasSize, backgroundRGB)
draw = PIL.ImageDraw.Draw(img)
# 用意した画像に文字列を描く
x_off, y_off = fontsize, 0
font = PIL.ImageFont.truetype(ttfontname, fontsize)
for cnt, moji in enumerate(msg):
textWidth, textHeight = draw.textsize(moji, font=font)
textTopLeft = (x_off, top_shift)
if color in ["rainbow", "primary"]:
rgb = webcolors.name_to_rgb(rain_colors[cnt%len(rain_colors)])
else:
rgb = webcolors.name_to_rgb(color)
draw.text(textTopLeft, moji, fill=rgb, font=font)
x_off += textWidth
# crop
img = img.crop((0, 0, x_off+32, 32))
img.save((job_id + ".png"))
画像の表示 - show_job
pngファイルを開いて、スクロールします。
gifを指定すると、アニメーションできるようにもなっています。
def show_job(job_id: str, bright: float, interval: float) -> None:
global stop_flag_pixel
global is_running_pixel
if is_running_pixel:
stop_flag_pixel = True
time.sleep(1)
is_running_pixel = True
# 消灯
pixels.fill((0, 0, 0))
pixels.show()
# 明るさの指定
pixels.brightness = 0.200 * bright
img_path = job_id + ".png"
cap = cv2.VideoCapture(img_path)
while True:
ret, frame = cap.read()
if ret == False:
break
h, w, _ = frame.shape
# スクロール表示
for srt in range(w-32):
# 中断
if stop_flag_pixel:
stop_flag_pixel = False
return
show32x32_pixel(cv2.cvtColor(frame[:,srt:srt+32,:], cv2.COLOR_BGR2RGB))
if 0.0 < interval:
time.sleep(interval)
time.sleep(0.1)
# LED消灯
pixels.fill((0, 0, 0))
pixels.show()
stop_flag_pixel = False
is_running_pixel = False
LEDの細かな仕様は、前記事 - ラズパイを使って、シリアルLEDを制御するを参照してください。
実行
ラズベリーパイ側
以下を指定して実行します。
$ sudo ~/mitomoku/env_mito/bin/uvicorn main:app --reload --host 0.0.0.0 --port 8000
PC側
ブラウザ等で、ラズベリーパイIPアドレス:8000にアクセスします。
POST /doで確認にをします。
"Execute"を実行すると、文字列が流れます👏。
さいごに
モノを動かすのは楽しいです。
この他にも、ラズパイの活用方法を
としてまとめ中です。
Discussion