Gitlab信号機を作る話 -リセットボタンを付ける-

前回、Gitlab信号機にて開発プロジェクトのステータス状態を表すLED信号を作りました。
[Gitlab Pipeline信号機を作る話 -設計とブレッドボード編-(2022年6月2日)](https://qiita.com/wataru775/items/8933d3662ac5eb90998b)

信号機なので点灯状態での放置でも良いですが、消しておきたいこともあります。
なので、今回は全灯を消灯するリセットボタンを設置します。

ボタンの設置自体は昔は難しかったですが今は超簡単です。

# 設計する
はじめに今回ボタンを追加するGPIOピンを決めて配線図を作成します。

## GPIOピン計画
信号機のLEDに使っているGPIOピンは12,27,22,23を使っています。
なので、近いGPIOピン…
GPIO24を使います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/c570eebf-3b1b-2d02-302c-888423209160.png)

記事を書きながら思いましたが出力の横というのも配線ミスすると危ないのでGPIO5とか離れた方がいいかもしれませんね。反省


## 配線図を描く
利用するGPIOピンが決まりましたので次に回路図を描きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/4d05200a-8eb3-f8bc-2dca-401d98796ee8.png)

今回 2極のタクトスイッチを使ったのでFrizingのパーツで2極のタクトスイッチが欲しいな…


## ブレッドボード図を描く
次にブレッドボード図で接続する配線を描いておきます…
LED出力は無視してください。次の設計しています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/8b9bee4d-67d5-864f-492c-f42c39388102.png)
右のタクトボタンが対象です。
GNDとGPIO24の間にスイッチがあればOKです。

## 配線する
次にブレッドボード図を参考に配線を行います…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/6a5d1e5a-c009-4c83-d1b8-ddfbd1b48588.png)

タクトスイッチが4極ではなく2極を使いました。
ってか4極より2極の方が分かり易いです


# プログラムを書く
コマンドで確認したかったのですが、ややこしくなりそうでしたのでプログラムを書きます。

全ソースは後ろに書いています。抜粋にしました。

## セットアップする
pull_up_downの値はボタン押下でHiとするかLowにするかのプルアップ抵抗を定義しています。
GPIO.PUD_UPを定義するとボタンを押下した時にHi状態とします。
GPIO.PUD_DOWNはボタンを押下した時にLow(押していない場合Hi)します。

```python
SW_Reset = 24 # リセットボタン
 
GPIO.setup(SW_Reset, GPIO.IN, pull_up_down=GPIO.PUD_UP)
```

## イベント追加
ボタンを押下のイベントを定義します。
```python
GPIO.add_event_detect(SW_Reset, GPIO.RISING, callback=reset_button_callback,bouncetime=2000)
```
GPIO.RISINGはエッジです。
立ち上がりエッジ(RISING)/ 立ち下がりエッジ(FALLING) / 両方(BOTH)を指定します。

bouncetimeはチャタリング防止（イベントが検出後、単位時間[ms]イベントを実行しない)を指定します。
なければ予想外にボタンイベントが発生しますので微調整します。

callbackはこのイベントが検出された時に走る処理を指定します。(例では reset_button_callback)

## callback処理を書く
宣言とイベントを追加しましたのでボタンを押された時の処理(callback)を以下に書きます。

```python
def reset_button_callback(channel):
    print("Button was pushed!")
    GPIO.output(LED_Pending, False)
    GPIO.output(LED_Running, False)
    GPIO.output(LED_Success, False)
    GPIO.output(LED_Fail, False)
```
先にありました、チャタリング確認とかタイミングを調べるための出力とLEDの全消灯処理を書いています。

# 動作確認
LEDが点灯している時に消灯となれば動作はできます…

# 終いに
今回は前回の忘形見、リセットボタンを実装しました。

サンプルをネットで探していましたが、結構 無限ループ(while/forなど)でやっているところが多いイメージですね。
Gitlab信号機ではmqtt購読処理がloop_foreverになっております。
なのでfor loopなどで0.5秒間隔で確認するとかの処理はできません。
ちゃんとイベント検出で行いました。

次回は…
今、ブレッドボード上の信号機はLEDの明るさ調整以外は開発は終わっています。
なので、ブレッドボードから旅立ってもいいかななどと思っています。
そもそも、信号機なので4色な部分が考えた方が良いですかねw

# 信号機全ソース
```python
#! /usr/bin/python3
import RPi.GPIO as GPIO
import paho.mqtt.client as mqtt
import json
import time
 
host = '192.168.1.201'
port = 1883
topic = 'pipeline'
 
LED_Pending = 17 # 黄色
LED_Running = 27 # 青
LED_Success = 22 # 緑
LED_Fail = 23 # 赤
 
SW_Reset = 24 # リセットボタン
 
GPIO.setmode(GPIO.BCM)
 
GPIO.setup(LED_Pending, GPIO.OUT)
GPIO.setup(LED_Running, GPIO.OUT)
GPIO.setup(LED_Success, GPIO.OUT)
GPIO.setup(LED_Fail, GPIO.OUT)
 
GPIO.setup(SW_Reset, GPIO.IN, pull_up_down=GPIO.PUD_UP)
 
def reset_button_callback(channel):
    print("Button was pushed!")
    GPIO.output(LED_Pending, False)
    GPIO.output(LED_Running, False)
    GPIO.output(LED_Success, False)
    GPIO.output(LED_Fail, False)
 
def on_message(client, userdata, msg):
    print(str(msg.payload,'utf-8'))
    message = json.loads(str(msg.payload,'utf-8'))
 
    GPIO.output(LED_Pending, False)
    GPIO.output(LED_Running, False)
    GPIO.output(LED_Success, False)
    GPIO.output(LED_Fail, False)
 
    if message['status'] == 'pending':
        GPIO.output(LED_Pending, True)
    elif message['status'] == 'running':
        GPIO.output(LED_Running, True)
    elif message['status'] == 'success':
        GPIO.output(LED_Success, True)
    elif message['status'] == 'fail':
        GPIO.output(LED_Fail, True)
 
    # time.sleep(1)
 
def run():
    client = mqtt.Client()
    client.connect(host, port=port, keepalive=60)
 
    client.subscribe(topic)
    client.on_message = on_message
 
    client.loop_forever()
 
if __name__ == '__main__':
    try:
        GPIO.add_event_detect(SW_Reset, GPIO.RISING, callback=reset_button_callback,bouncetime=2000)
        run()
    finally:
        GPIO.cleanup(LED_Pending)
        GPIO.cleanup(LED_Running)
        GPIO.cleanup(LED_Success)
        GPIO.cleanup(LED_Fail)
```