📝 はじめに
前回作成した OLED ディスプレイ表示機能に、GPIO ボタンによるモード切り替え機能を追加します。ボタンを押すとセンサーデータ表示とシステム情報表示を切り替えられるようになります。
対象読者
- Raspberry Pi で GPIO を使った入力制御をしたい人
- 物理ボタンで IoT デバイスを操作したい人
前提条件
- 前回の記事(OLED ディスプレイ表示)が完了していること
- GPIO の基本を理解している
🎯 背景・動機
OLED ディスプレイにセンサーデータを表示できるようになりましたが、IP アドレスやホスト名も確認したい場面があります。物理ボタンを追加して表示モードを切り替えられるようにしました。
🛠️ 手順
1. ハードウェア接続
タクトスイッチを GPIO 21 に接続します。
| タクトスイッチ | Raspberry Pi |
|---|---|
| 片側 | GPIO 21 |
| 反対側 | GND |
2. GPIO ボタン制御の実装
import RPi.GPIO as GPIO
class SensorClient:
def __init__(self, button_pin=21):
self.button_pin = button_pin
self.display_mode = 0 # 0: センサーデータ, 1: システム情報
self.button_available = False
self.last_button_state = GPIO.HIGH
def init_button(self):
"""GPIO ボタンを初期化(ポーリング方式)"""
try:
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.button_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
self.button_available = True
return True
except Exception as e:
print(f'GPIO 初期化失敗: {e}')
return False
def check_button(self):
"""ボタン状態をチェック(ポーリング)"""
if not self.button_available:
return
current_state = GPIO.input(self.button_pin)
# HIGH → LOW の遷移を検出(ボタン押下)
if self.last_button_state == GPIO.HIGH and current_state == GPIO.LOW:
self.toggle_display()
self.last_button_state = current_state
def toggle_display(self):
"""ディスプレイモードを切り替え"""
self.display_mode = 1 - self.display_mode # 0 ↔ 1 を切り替え
print(f'表示モード変更: {self.display_mode}')
ポーリング方式を選んだ理由: 割り込み方式より安定動作し、デバッグが容易。100ms 間隔のチェックで十分な応答性が得られます。
3. メインループへの統合
def run(self):
"""メインループ"""
# 初期化
self.oled.init_display()
self.init_button()
loop_count = 0
while True:
# ボタンをチェック(100ms 間隔)
self.check_button()
# 10秒ごとにセンサーデータを更新
if loop_count >= 100: # 100ms × 100 = 10秒
data = self.read_sensor_data()
if data:
self.send_sensor_data(data)
self.update_display(data)
loop_count = 0
time.sleep(0.1)
loop_count += 1
def update_display(self, data):
"""表示モードに応じてディスプレイを更新"""
if self.display_mode == 0:
self.oled.show_sensor_data(data['temperature'], data['humidity'])
else:
self.oled.show_system_info(self.hostname, self.get_ip_address())
4. IP アドレス取得
import socket
def get_ip_address(self):
"""自身の IP アドレスを取得"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
except:
return "No IP"
🚨 つまずきポイント
チャタリング対策
機械式スイッチは押下時に信号が振動します。ポーリング間隔を 100ms に設定することで、追加のデバウンス処理なしでも安定動作します。
Docker コンテナでの GPIO 使用
compose.yml で /dev/gpiomem をマウントし、privileged: true を設定する必要があります。
📈 まとめ
- ポーリング方式で安定動作: 割り込みよりもシンプルで信頼性が高い
-
状態遷移の検出:
HIGH → LOWの変化を検出してボタン押下を判定 - メインループとの統合: センサー読み取りと並行してボタンをチェック
