nyabot’s diary

電気猫の夢を見るお話

WebIOPiでRaspberry Piに繋いだフルカラーLEDを操作する

ロボットの目にRGBフルカラーLEDをつけたので、スマホからLEDを操作できるようにしたい。
ラズパイでWebIOPiが動作するように設定してあるので、操作画面とスクリプトを作る。

できあがったものはこんな感じ↓

作りかた

発光色に応じたボタンをクリックすることで、RGBの三色のうちの任意の1〜3色をONにし、他をOFFにする。
ON-OFFの切り替えだけで出せるカラーは、単色で発光させたときのred, green, blueに加えて、2色を同時に発光させたyellow, cyan, magenta、3色を発光させたwhite、計7色となる。
より細かな色合いを出すのは処理が複雑になるし、7色もあれば十分楽しいので、今回はこの7色+消灯で8つの状態を切り替えられるようにする。

まずはラズパイのGPIOを操作するスクリプトから。
これは、WebIOPi起動時に読み込むスクリプトファイルに記述する。

import webiopi
from time import sleep
from distutils.util import strtobool

# GPIOライブラリの取得
GPIO = webiopi.GPIO

# LEDのRGBに対応するGPIO。ここでは23,24,25を使う
LED_R = 23
LED_G = 24
LED_B = 25
LED_STATE = GPIO.LOW

# デバッグ出力を有効に
webiopi.setDebug()

# WebIOPiの起動時に呼ばれる関数
def setup():
    webiopi.debug("Script with macros - Setup")
    # GPIOのセットアップ
    GPIO.setFunction(LED_R, GPIO.OUT)
    GPIO.setFunction(LED_G, GPIO.OUT)
    GPIO.setFunction(LED_B, GPIO.OUT)
    GPIO.digitalWrite(LED_R, LED_STATE)
    GPIO.digitalWrite(LED_G, LED_STATE)
    GPIO.digitalWrite(LED_B, LED_STATE)

# WebIOPiにより繰り返される関数
def loop():
    webiopi.sleep(5)

# WebIOPi終了時に呼ばれる関数
def destroy():
    webiopi.debug("Script with macros - Destroy")
    # GPIO関数のリセット(入力にセットすることで行う)
    GPIO.setFunction(LED_R, GPIO.IN)
    GPIO.setFunction(LED_G, GPIO.IN)
    GPIO.setFunction(LED_B, GPIO.IN)

# Javascriptから呼ぶマクロ
@webiopi.macro
def setLED(gpio, led_state):
    global LED_STATE
    LED_STATE = strtobool(led_state)#渡された文字列をbool値に変換して使用する
    GPIO.digitalWrite(int(gpio), LED_STATE)
    return LED_STATE

次に、Javascript。WebIOPiの操作画面のHTMLから読み込むファイル。

function setLED(color){
  const gpio_r = 23;
  const gpio_g = 24;
  const gpio_b = 25;
  let state_r, state_g, state_b;

  // RGBのうちどれを点灯させるか色ごとに指定する
  if (color == 'white') {
    state_r = 'on';
    state_g = 'on';
    state_b = 'on';
  } else if (color == 'red') {
    state_r = 'on';
    state_g = 'off';
    state_b = 'off';
  } else if (color == 'green') {
    state_r = 'off';
    state_g = 'on';
    state_b = 'off';
  } else if (color == 'blue') {
    state_r = 'off';
    state_g = 'off';
    state_b = 'on';
  } else if (color == 'yellow') {
    state_r = 'on';
    state_g = 'on';
    state_b = 'off';
  } else if (color == 'cyan') {
    state_r = 'off';
    state_g = 'on';
    state_b = 'on';
  } else if (color == 'magenta') {
    state_r = 'on';
    state_g = 'off';
    state_b = 'on';
  } else if (color == 'black') {// 消灯
    state_r = 'off';
    state_g = 'off';
    state_b = 'off';
  }

  webiopi().callMacro('setLED', [gpio_r, state_r]);
  webiopi().callMacro('setLED', [gpio_g, state_g]);
  webiopi().callMacro('setLED', [gpio_b, state_b]);

  // ボタンのクラスを付け替える
  const led_btns = document.getElementsByClassName('btn_led');
  for (let led_btn of led_btns) {
    led_btn.classList.remove('btn_led--active');
  }
  document.getElementById('led_' + color).classList.add('btn_led--active');
}

そして、HTML。操作画面のボタンを作り、スクリプトと紐付けする。

<div class="btn_led_wrap">
  <span class="btn_led btn_led--active" id="led_black" onClick="setLED('black')"></span>
  <span class="btn_led" id="led_white" onClick="setLED('white')"></span>
  <span class="btn_led" id="led_red" onClick="setLED('red')"></span>
  <span class="btn_led" id="led_yellow" onClick="setLED('yellow')"></span>
  <span class="btn_led" id="led_green" onClick="setLED('green')"></span>
  <span class="btn_led" id="led_cyan" onClick="setLED('cyan')"></span>
  <span class="btn_led" id="led_blue" onClick="setLED('blue')"></span>
  <span class="btn_led" id="led_magenta" onClick="setLED('magenta')"></span>
</div>

ページを読み込んだタイミングでは、消灯状態(black)をアクティブにしておく。
本当はspanじゃなくてbuttonとかでマークアップした方がわかりやすいのかもだけど、フォーカスとか面倒だからspanとかdiv使ってしまう。
あと当然このHTMLファイルでjsとcssを読み込んでおく。

最後に、CSS。ボタンの配置と装飾をする。

.btn_led_wrap {
  position: fixed;// 配置方法や場所はよしなに
  bottom: 20px;
  width: 100%;
  display: flex;
  justify-content: center;
}

.btn_led {
  box-sizing: border-box;
  height: 36px;
  width : 36px;
  margin: 4px;
  border : 10px solid #fff;
  border-radius: 50%;
  transition: all 0.1s;
}

.btn_led--active {
  border-width: 0;
}

#led_white { background-color: rgb(230,230,230);}
#led_red { background-color: rgb(255,100,100);}
#led_yellow { background-color: rgb(255,255,100);}
#led_green { background-color: rgb(100,255,100);}
#led_cyan { background-color: rgb(100,255,255);}
#led_blue { background-color: rgb(100,100,255);}
#led_magenta { background-color: rgb(255,100,255);}
#led_black { background-color: rgb(60,60,60);}

各色のドットをクリックすると対応する色でLEDが点灯し、選択したボタンがいい感じに大きくなる。

今回はとてもスムーズに実現できたけど、ただLEDを操作するだけでこんなに色々書くことになるあたり、ロボットって大変だなぁと思いました。

つづく。