IT女子のラズベリーパイ入門奮闘記

第35回「WebIOPiでIoT!(6)プログラミング応用編~アナログ入力編」

raspberrypi35_main

WebIOPiシリーズ6回目、いよいよアナログ値の取得に挑戦します!1回目の冒頭でも触れていましたが、ようやく最終目標にたどり着きました!
今回は新しい部品が2種類登場!写真左側に写っているのがアナログ入力の光センサー、残りの2つは「A/Dコンバータ」という部品です。これらを組み合わせて、光センサーの値をラズベリーパイで受け取ってみましょう!

 

光センサーとA/Dコンバータ

CDSセル(光センサー)
CdS(硫化カドミウム)を使用した光センサーで、 光の強さに応じて電気抵抗が低下する抵抗器です。 人の目の特性に近い特性(緑色の光に対して高感度)を持っていますので、 各種明るさセンサーに最適です。

今回の主役である光センサーは、赤川シホロ先生の連載「はじめての電子工作超入門」で何度か登場している部品と同じものになります。光センサーについては、こちらのページで予習しておきましょう。

第5回 光センサーでArduinoへの入力を試してみる! – はじめての電子工作超入門
光センサー(通称CdSセル)は、暗いところにおくとセンサー自体の抵抗値が大きくなり、明るいところに置くと抵抗値が低くなる性質を持っています。
光センサーは抵抗器と同じくプラスマイナスの向きはありませんのでそのまま接続できます。

それでは早速使ってみよう!……と思って調べたところ、このままでは使えない事が判明。
なぜかというと、この光センサーはアナログ用。ラズベリーパイのGPIOはデジタルの入出力のため、このままでは値を受け取る事ができません。
そんなときの強い味方が、「A/Dコンバータ」という部品です!

A/Dコンバータとは
A/Dコンバータとは、アナログ信号をデジタル信号に変換するために用いる電子回路のことである。
例えば音声などのような、連続的な変化を持っている信号は、アナログデータである。これをA/Dコンバータによって0と1の信号に変換することで、初めてデジタル機器で情報の保存や再生、編集といった作業が可能となっている。

「A/D」は「アナログ/デジタル」の略ですね。今回の例で言うと、光センサのアナログ信号を、ラズベリーパイが認識できるデジタル信号に変換してから渡す、というような動きになります。

 

どちらもブレッドボードに直接差し込んで使用できます。ピンの本数は違いますが、基本的な使い方は同じです。今回はMCP3208-CI/Pを使って進めていきます。

 

A/Dコンバータの配線

たくさんのピンと見慣れない名前……配線にはかなり苦戦してしまいました。

MCP3204/3208 データシート

raspberrypi35_img03

図3


データシートでは、それぞれのピンの名前が図解されています。部品の向きは、表面にプリントされている文字列の向きか、半円状にくり抜かれた部分がある方を基準に確かめましょう。

MCP3204/3208 データシート(日本語版)
各ピンの機能については、日本語版のデータシートの方がわかりやすいです。

raspberrypi35_img05

図4

図の左側、1~8番ピン(CH0~7)はアナログ入力用。ここでは、光センサーを接続する側です。
右側の上から2つは、どちらも電源と接続します。16番の「VDD」は、よく見慣れた電力供給用のピンですね。15番の「VREF」は、「基準電圧入力」とピン機能表に書かれています。調べてみると、アナログ入力から取得した値は絶対値ではないため、基準となる値(最大値)と比較して計算して求めるそうです。その基準値を可変に対応させるための入力値、といったところでしょうか(例えば、アナログ入力から0V〜5Vの値を取得したい場合は、VREFには5Vを入力します)。今回の場合は3.3VでOKですね。
14番の「AGND」はアナログ用GND、9番の「DGND」はデジタル用GNDです。これはそのまま、GNDのピンにつなぎましょう。

ここまではなんとなく分かりましたが、問題は「CLK」「DOUT」「DIN」「CS/SHIN」の4つ。調べてみると、「SPI」と呼ばれるシリアルバスで使われるピン名でした(よく見るとデータシートにも「SPI」と書いてありますね)。

raspberrypi35_img06

図5 SPIバスでの接続の例(Wikipedia – SPIより)

シリアル・ペリフェラル・インタフェース – Wikipedia
シリアル・ペリフェラル・インタフェース(Serial Peripheral Interface, SPI)は、コンピュータ内部で使われるデバイス同士を接続するバスである。パラレルバスに比べて接続端子数が少なくて済むシリアルバスの一種で、比較的低速なデータ転送を行うデバイスに利用される。
ラズベリーパイ公式サイトのSPIのドキュメントには、ピン名について記載があります。

SPI – Raspberry Pi Documentation
SCLK – Serial CLocK
CE – Chip Enable (often called Chip Select)
MOSI – Master Out Slave In
MISO – Master In Slave Out
MOMI – Master Out Master In
MIMO – Master In Master Out

以上の情報から、A/Dコンバータとラズベリーパイのピンをつき合わせてみると、次のようになります!

A/Dコンバータ側 ラズベリーパイ側
CLK クロックの略。 SCLK – Serial CLocK GPIO11
DOUT デジタルアウトの略。ADコンバータから見た「出力」 MISO – Master In Slave Out GPIO9
DIN デジタルインの略。ADコンバータから見た「入力」 MOSI – Master Out Slave In GPIO10
CS/SHIN チップセレクト/シャットダウンの略。 CE – Chip Enable (often called Chip Select) GPIO8

というわけで、早速配線です!

raspberrypi35_img07

CH0に光センサを接続しました。抵抗の強さは、実際に値を取得しながら調節してみてください。

ジャンパーワイヤーの本数が多く、ピンの名前も似ていて間違えやすいので、慎重に接続しましょう。配線が間違っていると、A/Dコンバータが非常に熱くなってしまう場合があります(配線を間違えたまましばらく使っていて、熱でブレッドボードが溶けてしまいました……)。

 

SPIの準備

ラズベリーパイでA/Dコンバータを使用するには、「SPI」を有効にする必要があります。ラズベリーパイ公式サイトのドキュメントでは、raspi-configから設定する方法が紹介されていますが、「Raspberry Pi Configuration」という設定画面を利用する方法もあります(OS:Raspbian Jessie/2015年9月24日リリース版にて確認)。

raspberrypi35_img08

上部のメニューの中から[Preferences]-[Raspberry Pi Configuration]を選択します。

raspberrypi35_img09

「Interfaces」タブをクリックし、「SPI」の選択状態を「Enable」に設定します。

raspberrypi35_img10

メッセージが表示されます。SPIを有効化するためには再起動が必要となるので、ここではYESをクリックして再起動しましょう。

一画面でいろいろな設定が確認できるので、コマンドから操作するよりも簡単で便利ですね!

 

WebIOPiの準備

ここからはWebIOPi!WebIOPi公式サイトの、以下のページを参考に進めました。

Tutorial : Using devices
MCP3000 series SPI Analog-to-Digital Converter
Python Example – ADC (Analog-to-Digital Converter) – WebIOPi

まずは、MCP3208を使うための設定です。WebIOPiの設定ファイルを開きましょう。

sudo nano /etc/webiopi/config

[DEVICES]に、下記のように追記します。

mcp0 = MCP3208

使用するチャンネルを指定する場合は、

mcp0 = MCP3208 chip:0

このように、チャンネルの番号を追記します。未指定の場合のデフォルトは0です。

使用していないデバイスが有効になっていると、エラーが発生してしまう場合があるので、不要なものはここでコメントアウトしておきましょう(前回の温度センサを使ったプログラムを試している場合は「temp2」をコメントアウトしてください)。

それではプログラムを書いていきましょう!
WebIOPi公式サイトに、MCP3208を使用したサンプルソースが紹介されていました。アナログ値を取得する関数は3種類用意されていました。

analogRead(channel)
Returns the integer value of the given analog channel.
REST API : GET /devices/name/analog/channel/integer
name (str) : device name from configuration file
channel (int) : analog channel number

analogReadFloat(channel)
Returns the float value of the given analog channel, from 0.0 to 1.0.
REST API : GET /devices/name/analog/channel/float
name (str) : device name from configuration file
channel (int) : analog channel number

analogReadVolt(channel)
Returns the voltage value of the given analog channel.
REST API : GET /devices/name/analog/channel/volt
name (str) : device name from configuration file
channel (int) : analog channel number

これらの関数を使って、光センサの値を表示するプログラムを作成してみました。

/home/pi/webiopi_sample/python/script.py

import webiopi
def loop():
    mcp = webiopi.deviceInstance("mcp0")
    cds1 = mcp.analogRead(0)
    cds2 = mcp.analogReadFloat(0)
    cds3 = mcp.analogReadVolt(0)
    print("read: %f float: %f volt: %f" % (cds1,cds2,cds3))
    webiopi.sleep(5)

デバッグ実行コマンド
sudo webiopi -d -c /etc/webiopi/config

デバッグ実行時、下記のようなエラーが表示された場合は、SPIが無効になっている可能性がありますので、もう一度設定を確認しましょう。

No such file or directory: '/dev/spidev0.0'

raspberrypi35_img11

こちらが実行結果です。analogRead、analogReadFloat、analogReadVoltの3種類関数を使って、5秒間隔で値を取得し、表示しています。
6行目までは、室内の明るさをそのまま取得しています。7~11行目は、ライトの光を当てて明るくした状態です。急激に値が大きくなっています。12~17行目は、光センサの上を指でふさいで光を遮った状態です。今度は逆に、値が小さくなっているのが分かります。

ツールとして利用するには、使いたい場所の明るさに合わせて基準値を決めて、条件分岐をさせるようにプログラムを組むと良さそうですね!

 

応用プログラムで『目覚ましラジオ』もつくれる!

/home/pi/webiopi_sample/python/script.py

import webiopi
import subprocess

PLAY = False
RADIO_URL = 'http://yp.shoutcast.com/sbin/tunein-station.pls?id=172098';

def setup():
    webiopi.sleep(15)

def loop():
    global PLAY, RADIO_URL
    mcp = webiopi.deviceInstance("mcp0")
    cds1 = mcp.analogRead(0)
    cds2 = mcp.analogReadFloat(0)
    cds3 = mcp.analogReadVolt(0)
    print("read: %f float: %f volt: %f" % (cds1,cds2,cds3))

    if((cds1>1000) and (PLAY==False)):
        cmd = "mplayer -playlist " + RADIO_URL + " > /dev/null &"
        subprocess.Popen(cmd, shell=True)
        PLAY = True
        print("radio start!!!!!")
    elif((cds1>=1000) and (PLAY==True)):
        subprocess.Popen("sudo killall mplayer", shell=True)
        PLAY = False
        print("radio stop!!!!!")
    webiopi.sleep(1)

def destroy():
    subprocess.Popen("sudo killall mplayer", shell=True)

前回作った温度センサとイルミネーションライトのツールのように、明るさの度合いに応じて条件分岐するようなプログラムを組むと、簡単にツール化することができます。

今回は、部屋の明るさに応じて自動的にインターネットラジオ流してくれるツールを作成してみました。明るくなると再生され、暗くなると自動で停止してくれます。Pythonのみで動作するので、HTMLファイル用意しなくてもOKです。

まず、使いたい場所の平均的な明るさをあらかじめ調べておいて、明るさの判定に使う基準値を決めます。
上記のプログラムソースは、デスクの上で動かすことを想定して、ディプレイの前に置いたときの明るさを基準として使用しました。analogRead関数で取得した値は、ディスプレイをONにしたときは1300程度、OFFにすると600程度だったので、基準値は1000としています。

ラジオの再生には「mplayer」パッケージを使用しています。mplayerについては第13回「ラズベリーラジオ前編 – インターネットラジオ受信と遠隔操作」で詳しく解説しています。

mplayerインストールコマンド
sudo apt-get install mplayer

ラズベリーパイのHDMIを使用している場合、音声の出力先もHDMIが自動で選択されてしまうことがあります。ステレオジャックを指定するには、下記のコマンドを実行しましょう。

音声の出力先をステレオジャックに設定
sudo amixer cset numid=3 1

今回のようなツールの場合は、自動起動の設定をしておくと便利です。自動起動時に認識されていないデバイスがあるとエラーが発生してしまうようです。最初の「setup:」で15秒ほどスリープを入れてあげるとうまく動作しました。

自動起動を設定するコマンド
sudo update-rc.d webiopi defaults
自動起動を解除するコマンド
sudo update-rc.d webiopi remove

 

まとめ

念願のアナログ値の取得に成功!
今回はWebIOPiよりも配線に一苦労……抵抗器の強さや置き方など、ちょっとした違いで値の取れ方が変わってしまったり、安定するまでかなり苦戦してしまいました。とはいえ、ラズベリーパイでアナログ値を扱えるようになったので、今後は「やりたいこと」をベースに電子工作がたのしめそうです。
第6回まで続いたWebIOPiシリーズも、今回で一区切り。とはいえ、お気に入りのツールなので、新しい部品を扱うときにはまた活躍してもらおうと思います!

出場ロボット解剖計画
円山ナカノ

プロフィール:プログラミング暦通算4年、最近IT業界に舞い戻ってきたプログラマーです。女子です。