[Python/Linux/SoC FPGA] プッシュボタンを押すと LINE に通知が飛ぶ Python アプリを、SoC FPGA に実装してみた

# もくじ
●[1.はじめに](#1はじめに)
●[2.環境](#2環境)
●[3.構成](#3構成)
●[4.準備 ~Linux 環境作成~](#4-準備-linux-環境作成)
●[5.準備 ~Linux 上で GPIO の制御方法の確認~](#5-準備-linux-上で-gpio-の制御方法の確認)
●[6.準備 ~LINE の送信~](#6-準備-line-の送信)
●[7.Python コード](#7-python-コード)
●[8.実行結果](#8-実行結果)
●[9.おわり](#9-おわりに)

## 1.はじめに
以前、[Intel® Cyclone V SoC のキット（Atlas）と PC を LAN ケーブルで繋ぎ、Python で UDP パケット通信させてみた](https://qiita.com/uchiita/items/7326091acc9a6da77622) の記事を作成した際、SoC FPGA を Python で動かすことができれば、色々面白そうと思いました。

センサーで取得したデータや FPGA 側のデータを、Python の豊富なライブラリで扱えると、夢が広がりますよね。
今回はまず手始めとして、プッシュボタンが押されたら、LINE へ通知 & LED の点灯、という Python アプリを作成してみました。
まずは成果物を見たい方は、下記リンクで飛んでみてください。
[8.実行結果](#8-実行結果)

ぜひ、最後まで読んでみてください！

## 2.環境
- 使用ボード
Terasic 社 DE0-nano-SoC（Atlas）
※Intel® Cyclone V SoC が搭載されています。

- Linux 環境
Rootfile System -> Poky
Linux Kernel version -> 5.15.70

- Python
Python version -> 3.10.7

## 3. 構成 & 動作手順
■構成
HPS 内の CPU（Cortex-A9）に Linux を実装し、こちらの環境で Python プログラムを実行します。

使用するインターフェースは、GPIO、UART、LAN ケーブルです。
- GPIO: LED とプッシュボタンの制御
- LAN ケーブル: 携帯へ LINE を送信
- UART: PC で HPS を制御及びモニタ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890170/e0cb7798-21fd-92a6-b2b3-4a65ebd02588.png)



■動作手順
手順は大まかに図の中に示した ① ~ ③ です。
①. プッシュボタンを押す
②. LED を点灯
③. LINE にプッシュボタンが押されたことを通知

## 4. 準備 ~Linux 環境作成~
下記資料を参考に Linux が起動するまでの環境を作成しました。
[Building Bootloader for Cyclone V and Arria 10](https://www.rocketboards.org/foswiki/Documentation/BuildingBootloaderCycloneVAndArria10)

## 5. 準備 ~Linux 上で GPIO の制御方法の確認~
Atlas に Linux 環境を実装し、下記フローで GPIO は制御できるのを確認しました。

~
a. 　/sys/class/gpio/ へ移動
b. 　使用したい GPIO の番号を export に書き込み
c. 　/sys/class/gpio/ 下に gpio<番号> のディレクトリが作成されていることを確認
d. 　/gpio<番号>/direction で、入出力設定
e. 　/gpio<番号>/value で、入力なら値を確認、出力なら値を設定
f. 　使用し終わったら、GPIO の番号を unexport に書き込み
g. 　gpio<番号> ディレクトリが削除されていることを確認
~

実際に GPIO を制御したログを以下に記載します。

```text:GPIO 制御ログ
■LEDの GPIO 動作確認
root@cyclone5:/sys/class/gpio#
root@cyclone5:/sys/class/gpio# ls
export        gpiochip1963  gpiochip1990  gpiochip2019  unexport
root@cyclone5:/sys/class/gpio# echo 2014 > export
root@cyclone5:/sys/class/gpio# ls
export        gpio2014      gpiochip1963  gpiochip1990  gpiochip2019  unexport
root@cyclone5:/sys/class/gpio# echo out > gpio2014/direction
root@cyclone5:/sys/class/gpio# echo 1 > gpio2014/value
root@cyclone5:/sys/class/gpio# echo 0 > gpio2014/value
root@cyclone5:/sys/class/gpio# echo 2014 > unexport
root@cyclone5:/sys/class/gpio# ls
export        gpiochip1963  gpiochip1990  gpiochip2019  unexport


■プッシュボタンの GPIO 動作確認
root@cyclone5:/sys/class/gpio# ls
export        gpiochip1963  gpiochip1990  gpiochip2019  unexport
root@cyclone5:/sys/class/gpio# echo 2015 > export
root@cyclone5:/sys/class/gpio# ls
export        gpio2015      gpiochip1963  gpiochip1990  gpiochip2019  unexport
root@cyclone5:/sys/class/gpio# echo in > gpio2015/direction
root@cyclone5:/sys/class/gpio# cat gpio2015/value
1
root@cyclone5:/sys/class/gpio# cat gpio2015/value
0
root@cyclone5:/sys/class/gpio# echo 2015 > unexport
root@cyclone5:/sys/class/gpio#
root@cyclone5:/sys/class/gpio# ls
export        gpiochip1963  gpiochip1990  gpiochip2019  unexport
root@cyclone5:/sys/class/gpio#
```

※export に登録する GPIO の番号の補足
使用したい GPIO の番号を export に書き込みますが、
その際、GPIO の番号にオフセットを足す必要があります。

本来、プッシュボタンと LED の GPIO の番号は下記です。
（export に登録するオフセットを足した値は（）の中に記載しています）
- プッシュボタンの GPIO: 54（export に登録する値 -> 2015）
- LED の GPIO: 53（export に登録する値 -> 2014）

オフセットの求め方を以下に記載していますが、
特に興味が無ければ、[6. 準備 ~LINE の送信~](https://qiita.com/drafts#6-%E6%BA%96%E5%82%99-line-%E3%81%AE%E9%80%81%E4%BF%A1) まで読み飛ばして大丈夫です。

■オフセットの求め方
トータル 85 本の GPIO は 3 グループに分かれています。
| GPIO       | Pin Number       | Address      |
|:-----------|:-------------|:-------------|
| gpio0      | [28:0]   | 0xFF70_8000  |
| gpio1      | [57:29]  | 0xFF70_9000  |
| gpio2      | [84:58]  | 0xFF70_A000  |

/sys/class/gpio/ 下の、それぞれの gpiochipXXX 内の label を確認すると、
下記のようになっていました。
gpiochip2019/label: ff708000.gpio
gpiochip1990/label: ff709000.gpio
gpiochip1963/label: ff70a000.gpio

gpiochip2019 は gpio0、gpiochip1990 は gpio1、gpiochip1963 は gpio2 を表しているのかと思います。
gpiochip"xxx" の xxx を各 gpio0~2 のそれぞれのオフセットとします。
gpio0: 1963、gpio1: 1990、gpio2: 2019

例えば、今回の LED の GPIO[53] の場合、
gpio1 に含まれるので、gpio1[24]となります（53-29（gpio0））。
24 に gpio1 のオフセットを足しますので、
24 + 1990 = 2014
となります。


（下記 label の値を確認したときのログ）
```text:label 確認ログ
root@cyclone5:/sys/class/gpio# ls
export　gpiochip1963  gpiochip1990  gpiochip2019  unexport
root@cyclone5:/sys/class/gpio# cat gpiochip1963/label
ff70a000.gpio
root@cyclone5:/sys/class/gpio# cat gpiochip1990/label
ff709000.gpio
root@cyclone5:/sys/class/gpio# cat gpiochip2019/label
ff708000.gpio
root@cyclone5:/sys/class/gpio#
```

## 6. 準備 ~LINE の送信~
LINE へメッセージを送る際には、LINE notify を使用しています。
LINE notify については、下記記事で紹介されていましたので、こちらを参考にしてみてください。
[PythonでLINE Notifyへ通知を送る](https://qiita.com/akeome/items/e1e0fecf2e754436afc8)

下記コードを使用し、LINE へメッセージ（Hello!）が送れることを確認しました。

```python:LINE_send_test.py
import requests

def send_line_notify(notification_message, package_id, sticker_id):
    line_notify_token = <LINE notify のトークン>
    line_notify_api = 'https://notify-api.line.me/api/notify'
    headers = {'Authorization': f'Bearer {line_notify_token}'}
    data = {'message': f'{notification_message}', 'stickerPackageId': package_id, 'stickerId': sticker_id}
    requests.post(line_notify_api, headers = headers, data = data)

send_line_notify('Hello!', 4, 303)
```


## 7. Python コード
GPIO の制御と LINE へのメッセージ通知を合わせ、最終的なコードは下記のようになりました。
```python:Button_LED_LINE.py
import subprocess
import requests
import datetime

print('=====Start!=====')

print('Open GPIO')
subprocess.run('echo 2015 > /sys/class/gpio/export', shell=True)
subprocess.run('echo in > /sys/class/gpio/gpio2015/direction', shell=True)

subprocess.run('echo 2014 > /sys/class/gpio/export', shell=True)
subprocess.run('echo out > /sys/class/gpio/gpio2014/direction', shell=True)

print('Wait Push the KEY...')
def send_line_notify(notification_message, package_id, sticker_id):
    line_notify_token = <LINE notify のトークン>
    line_notify_api = 'https://notify-api.line.me/api/notify'
    headers = {'Authorization': f'Bearer {line_notify_token}'}
    data = {'message': f'{notification_message}', 'stickerPackageId': package_id, 'stickerId': sticker_id}
    requests.post(line_notify_api, headers = headers, data = data)

while True:
    result=subprocess.run('cat /sys/class/gpio/gpio2015/value', shell=True, capture_output=True)
    if result.stdout == b'0\n':
        print('----------')
        t=datetime.datetime.now()
        print(t)
        print('Pushed the KEY!')
        print('LED ON!')
        subprocess.run('echo out > /sys/class/gpio/gpio2014/direction', shell=True)
        subprocess.run('echo 1 > /sys/class/gpio/gpio2014/value', shell=True)
        print('Send the LINE!')
        send_line_notify('Pushed the KEY!', 3, 222)
        print('----------')
        break;
    elif result.stdout == b'1\n':
        pass
    else:
        print('unknow')

print('Close GPIO')
subprocess.run('echo 2015 > /sys/class/gpio/unexport', shell=True)
subprocess.run('echo 2014 > /sys/class/gpio/unexport', shell=True)

print('=====Finish!=====')
```

## 8. 実行結果

実行までの流れは以下のアニメーションをご覧ください。
![terminal.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890170/d5caf899-b376-3fc7-6104-531f09fe960e.gif)

届いた LINE メッセージと LED の点灯は以下の通りです。
簡単に LINE にメッセージが送れたり、LED の制御ができると、なんだか楽しいですね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890170/302a92af-8c4c-a37e-ca7d-875281b4e1b8.png)

## 9. おわりに
手始めとして作成したプログラムですが、LINE へのメッセージや GPIO の制御など、簡単にできて楽しかったです。
やはり、Python のライブラリを活用し何か物を作成することは、とても夢があると感じます！
今後も、Python を用いて色々なことに挑戦してみたいと思いました。