タグ別アーカイブ: raspberry pi

温度センサーIC LM19を使う

ここでは温度センサーIC LM19(National Semiconductor)を使ってRaspberry piで温度監視を行う事例を紹介します。

LM19仕様

  • パッケージ:TO-92
  • 動作電源電圧:2.4V~5.5V
  • 使用温度範囲:-30℃~+130℃
  • 精度:±2.5(@30℃)

接続例

スクリーンショット 2014-01-21 4.24.02
LM19からの出力はアナログ電圧のためRaspberry piでは直接扱うことができません。そこで一旦I2Cインターフェイス基盤でアナログ電圧を読み取り、その結果をI2Cをとおして取得することにします。
ICのデータシートを参考に出力には抵抗とコンデンサをつないでいます。

Python script


import i2cinterface99
ii = i2cinterface99.i2cinterface99()
state,value = ii.analogRead(0) #read A0 Pin 
if state == True:
 volt = value / 1023.0*3.3 #3.3V電圧でインターフェイス基盤を動作している場合のAD変換式
 temp = (1.8641 - volt)/0.0117 #ボルトから温度への変換の式
 print "%d deg/C" %(temp) #摂氏表示
else:
 print "I2C error"

温度への変換式はICのデータシートに記載されている−10℃〜65℃における推奨式を元にしています。
上記スクリプトをsudo python ファイル名.py で実行します。
温度センサーの値を読み取った結果がコンソールに表示されるはずです。
I2Cインターフェイス基盤にLCDを接続していれば、LCD上に温度の値が表示されるようにしてもよいでしょう。

今回行った実験では、LM19の測定値が温度湿度センサーDHT11による測定値とほぼ同じであることが確認できました。

CIMG2384

Raspberry piにshut downボタンを付ける

Raspberry piを停止する際はコンソールからshut downを行う必要が有ります.

pi@raspberrypi ~ $sudo halt

Raspberry piの仕様から端末に繋ぐことなくスタンドアロンで運用することも多いと思います.本体をちょっと移動したいとき電源を一時的に外さなくてはならないとき、いきなり電源をオフにしてしまえばSDメモリーカードの破損、破壊の恐れが出てきます.安全に電源を落とすためには確実にShut downを行わなければなりません。

そこでイーサネット越しにリモートログインしてshut downを行っても良いのですが、Raspberry pi単体でもShut down出来ないものかと。そこでここではGPIOの一つにshut downスイッチを設け、それを常に監視し、スイッチが押されたらShut Downシーケンスに入るものとします。

スイッチにaitendoで見つけたシートキーを使います.キーが4つ有りますのでシャットダウン以外にもそれぞれ機能を持たせる事ができます。
CIMG2384
Python用GPIOインストールしているものとして進めます.
インポートしているacm1602は外付けのLCDキャラクターディスプレイを制御するものです。
GPIOピンの22〜25にシートキーをプルダウン抵抗を介して接続しておきます。
いかサンプルコードを紹介します.


import time
import acm1602
import datetime
import subprocess
import RPi.GPIO as GPIO

def pushRedButtonLong():
        lcd.cls()
        lcd.putString("Shut Down?",0)
        lcd.putString("Y)Green,N)YellowL",1)
        while 1:
                if GPIO.input(pin_yellowBtnL) == 1 or GPIO.input(pin_yellowBtnR) == 1:
                        lcd.cls()
                        lcd.putString("Cancell",0)#LCDにshut downをキャンセルした事を表示
                        time.sleep(1)
                        return
                if GPIO.input(pin_greenBtn) == 1:
                        lcd.cls()
                        lcd.putString("sudo halt",0)#LCDにshut down処理を行っている事を表示
                        ret = subprocess.check_output(["sudo","halt"]);#システムにshut downを指示する
                        exit()
def pushRedButtonShort():
        lcd.cls()
        lcd.putString("Red Button",0)
        time.sleep(1)
def pushYellowButtonLeft():
        lcd.cls()
        lcd.putString("Yellow L Button",0)
        time.sleep(1)
def pushYellowButtonRight():
        lcd.cls()
        lcd.putString("Yellow R Button",0)
        time.sleep(1)
def pushGreenButton():
        lcd.cls()
        lcd.putString("Green Button",0)
        time.sleep(1)


print "start"
GPIO.setmode(GPIO.BCM)
pin_redBtn=25
GPIO.setup(pin_redBtn,GPIO.IN)
pin_yellowBtnL=24
GPIO.setup(pin_yellowBtnL,GPIO.IN)
pin_yellowBtnR=23
GPIO.setup(pin_yellowBtnR,GPIO.IN)
pin_greenBtn=22
GPIO.setup(pin_greenBtn,GPIO.IN)

lcd = acm1602.acm1602()
lcd.set_cursor('off')
try:
        redPushCount=0
        while 1:
                time.sleep(0.1)
               
               if GPIO.input(pin_redBtn) == 1:
                        redPushCount += 1
                        if redPushCount > 30:# 3秒長押し検出
                                pushRedButtonLong()
                                redPushCount = 0
                elif redPushCount > 0:
                        pushRedButtonShort() #短押しの場合
                        redPushCount = 0
                if GPIO.input(pin_yellowBtnL) == 1:
                        pushYellowButtonLeft()
                if GPIO.input(pin_yellowBtnR)==1:
                        pushYellowButtonRight()
                if GPIO.input(pin_greenBtn) == 1:
                        pushGreenButton()
except KeyboardInterrupt:
        print 'user terminated'
        GPIO.cleanup()

ここでは赤ボタンを長押ししたら、Shut downするかCancellするかどうかがLCDに表示されます.そこで緑ボタンを押せばShut down処理を始めます。
その他のキーの長押しはそのキーの名称をLCDに表示します.
ここまでで赤ボタン長押しし、そのあと緑ボタンを始めればShutDown処理に移行するようになりました。このようにユーザーの確認をワンクッション置く事で、間違ってShut downするのを防ぐことができます.
ここですぐに電源を抜いてしまうと、Shut down処理の途中ではSDメモリーカードにアクセスしていますので、アクセス中の電源断は運が悪ければSDカードの破損、再起動不能になってしまいます.
アクセスランプが消灯している事を確認してから電源を抜くようにしても良いのですが、筐体によってはボード上のLEDが見えない場合もあります。そこで接続してあるLCDにShut Down処理が完了したことを表示する事にします.

Raspberry pi(Raspbian)ではシャットダウン時に行うスクリプトを書くことができます.
/etc/default/halt
を編集してください.

# Default behaviour of shutdown -h / halt. Set to "halt" or "poweroff".
HALT=poweroff
/home/pi/shutDownLCD.py
/etc/default/halt

ここでは上のようにしました。ユーザーディレクトリに”shutDownLCD.py”というスクリプトを置いてます.これは単純にLCDに”Shut down”しましたよ〜という表示を行うだけのものです.LCDに表示する代わりに終了の音楽をならしても良いしLEDを明滅しても良いですし、ご自身の環境や趣味に合わせてスクリプトを用意してください.
厳密には電源を落としても大丈夫な状態になるのはここで指定したスクリプトが終わってからひと呼吸おいた後になるのですが、電源断のある程度の目安になる事でしょう.

今回は外部表示装置としてLCDキャラクターディスプレイACM1602を使いました。I2CインターフェイスをもつこのLCDはRaspberry piと接続したところ、ちょっと使いこなしが難しい点も幾つか見つかりました。(詳細は別記事にて)
I2Cインターフェイス基板LCD付きを使えばもっと簡単にLCDを制御できる事でしょう。

CIMG2383

動体検出にPIRセンサーを

動体検出には幾つかの方法が有ります。まずカメラ画像の中から動いている物体を検出するものがあります。これは人でも車でもカメラが見ることのできる動いているものを検出します.カメラの性能にも依存しますので例えば暗い時のようにカメラの映像にノイズが入ってしまう場合にはご検出を頻繁に起こしてしまうでしょう。

赤外線の変化を検出して動体検出とするものが有り、これが焦電センサーまたはPIRセンサーで行うことができます。防犯ライトなどにも多く使われている一般的なセンサーです.人間や動物など熱を持つ物体は赤外線を放出しており、その赤外線をセンサーが検出します.赤外線の検出値に変化が有れば何らかの物体が動いたという事でセンサーからの出力を行います.カメラのように画像によることがありませんので暗闇でも検出することができます.一方風なども物体と捉える事が出来るため、風の強い日はその赤外線の変化さえも検出してしまいます.

ここでは安価に入手できるPIRセンサーを紹介します.
CIMG2383
PIR センサー A708-1H
AITENDOで購入
仕様概要:動作電圧0.8-8v、 出力電圧 h:3.3 l:0v、 検出距離5-7m
参考にRaspberry piで接続する場合の例を紹介します.
必要な回路部品はすべてセンサーモジュールに実装されています.接続はPIRの出力をRaspberry piのいずれかのGPIOにつなげるだけです.

スクリーンショット 2013-12-15 13.06.30
あえて書くまでもないかもしれませんが、一応サンプルコードを紹介します.
PythonのGPIOはインストールしている前提です.


#!/usr/bin/python3
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
io_pin=17
GPIO.setup(io_pin,GPIO.IN)
while True:
        gp17=GPIO.input(io_pin)
        if gp17 == 1:
                print ("Move!");
        else:
                print("No move");
        time.sleep(1)

電源電圧が3.3VであるRaspberry piはGPIOの入力電圧も電源電圧を超えないものとしなければなりません。このPIRセンサーの出力は3.3Vという事でそのまま接続できますので大変便利ですね。

IOError: [Errno 5] Input/output error

Raspberry piでデバイスとI2C通信をしている時に
IOError: [Errno 5] Input/output error
に出会う時が有ります.基本的な設定が済んでいるものとして、このようなエラーが出る場合はいくつかの原因が考えられます。

1)信号線が長い、ノイズの影響を受けているなどで、信号そのものの波形が劣化している場合

 この場合は具体的にはオシロスコープを使って波形を確認した方が良いでしょう。オシロスコープが無い場合は信号線を出来るだけ短くすること、プルアップ抵抗が適切なものが入っているか確認する事と通信スピードを落としてみるのも有効です。

2)相手が準備できていないのにコマンドを送っている、データをリクエストしている場合
 コマンドを送る前にWaitを入れることになります.デバイスによってはコマンドを受け付けた後にいくら処理に時間がかかってそのあとにリクエストを受け付けると言ったシーケンスを明記しているものも有ります。詳しくはそれぞれのデバイスのリファレンスを参照の上で最適化するようにします。

3)初期化シーケンスをコマンド送信ごとに送ってしまっている
 本来であればSPI通信を始める場合の初期化処理は一度で良いのですが、スクリプトになっていとそれを外から呼び出した時に何度も短い間隔で呼び出して気づかない事も有ります。初期化は誰がやっているか、どのタイミングでやっているか確認し、スクリプトの最適化を行います.

シリアルインタフェイス I2CとSPIの選択

センサーや表示装置などのデバイスモジュールは多くの種類があり小型になり、また手頃な価格で入手できるようになりました。これらをマイコンまたはRaspberry piなどの小型Linux機で扱うのに容易なシリアルインターフェイスで制御できるものも多く有ります.

シリアルインターフェイスにはUART,I2C,SPI,Single wireなどがあります。UARTは主に一対一の通信に用いられます.Raspberry piでRaspbianをOSとして使っている場合はこのUARTはシリアルコンソール用と割り当てられています。
SPI通信ではマスターとスレーブを定義し、一つのマスターに対して複数のスレーブを持つことができます.スレーブの切り替えはSS(Slave select、CS:chip select, CE:Chip enableとも呼ばれる)信号で行います.Raspberry piには二つのCE信号線が有ります。
I2C通信では二つの信号線、データ線SDAとクロック線SCLによって通信します。(この他にモジュールはVccとGNDも接続します )SPIではMOSI,MISO,SCLK,SSの信号線が必要だったのと比較して信号線が少なくて済みますね。I2Cも複数のモジュールをスレーブとしてつなげることができます.同時に複数のデバイスをつなげた場合はそれそののデバイスが持つアドレスによってデータのやり取りの相手を指定します.同じバスで使うデバイスの持つアドレスが競合しないように気をつけなければなりませんが、標準の7ビットアドレスモードで使う場合は最大で112個のデバイスを扱うことができるのです.

センサーモジュールによっては同じ機能のものでもI2C用とSPI用と2種類有るものや、一つのモデルで設定でI2CとSPIを切り替えられるものも有ります.またI2CのみまたはSPIのみに対応しているデバイスもあります。

自分が実現したい仕様に対して、用いるデバイスがどちらのインターフェイスを持っているか、それと制御するマイコンまたはLinuxボードのインターフェイスの制約条件とを勘案してよりベターな組み合わせを模索する必要が有ります

Raspberry piの日本語化

sshで接続しているとあまり関係がありませんが、ローカルコンソールで使っている場合、日本語の文字化けが起ります。
その回避法として、jfbterm を使う方法が有ります.

Raspi-configではDefault localeとしてja_JP.UTF-8を選択しているとします.

sudo aptitude -y install jfbterm
jfbtermをインストールします.これは一度だけ行います.

シェルでjfbtermを実行すると、日本語の文字化けの無いterminalが起動します.
raspberry piを立ち上げるごとにjfbtermを起動する必要が有り面倒な面も有りますが、普段はsshなどでリモートログインで利用する場合は関係ありません.運用次第と言ったところでしょうか。

Raspberry piにFTPを導入しよう

Raspberry piを運用しているとSDメモリーカードの破損に備えてデータのバックアップを行った方が良いでしょう。
その際に考えられる方法としてUSBメモリーへの保存もありますが、マウントなどの処理が案外面倒なものです.ここではFTPを使ってネットワーク越しに別のPCから必要なデータを吸い上げることを考えます.

そこでFTPの導入です.
Raspberry pi でftpを実現するためにはvsftpdを使います

–Vsftpdインストール

$ sudo apt-get update
$ sudo apt-get install vsftpd
*2.3.5-3 がインストールされました。

vsftpd.conf を編集しましょう

$ sudo service vsftpd stop
$ sudo cp /etc/vsftpd.conf /etc/vsftpd.conf-ORG
$ sudo vi /etc/vsftpd.conf

—- 変更箇所だけ
anonymous_enable=NO #匿名ユーザー(anonymous)のログイン禁止

local_enable=YES #ローカルユーザーを許可

write_enable=YES #書き込み許可

local_umask=022 #ファイル作成時のパーミッション755

—- 以上

–Vsftpdの起動
$ sudo service vsftpd start
もしくは
$ sudo /etc/init.d/vsftpd start

同じネットワーク上の別のPCからFTPクライアントを使ってRapberry piに接続します.
ユーザー名:pi
パスワードはraspberry piにログインする時につかっているもの。
これでディレクトリーhome/piに接続できるでしょう

Raspberry piに送信メールサーバをインストールする

Raspberry piでGPIOを使って外部センサーからの情報を入力した際に、その結果をメールで通知することがあります.またUSBカメラを使った動体検出をトリガーとして報知システムを考えた場合にもメール送信を検討する事でしょう。

ここではRaspberry piでメール送信を行うための設定に付いて紹介します.
メールサーバーとしてpostfixをインストールします.

pi@raspberrypi ~ $ sudo apt-get install postfix

つぎに設定ファイルの作成です
/etc/postfix/main.cf に次の内容を追記します。ここではレンタルサーバーのリレーSMTPとして使います。gmailを使う事も出来ますのでご自分の使いやすい方を選んでください。

inet_protocols = ipv4
sender_canonical_maps = regexp:/etc/postfix/canonical
relayhost=[smtp.gmoserver.jp]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/relay_password
smtp_sasl_security_options = noanonymous 

/etc/postfix/relay_password を作成し上記サーバーのアカウント情報を記述します。

smtp.gmoserver.jp hoge@sample.com:foovar

お約束ごとですがhoge@sample.comの部分に実際のメールアカウント名を、foovarの部分にパスワードを記述します
次に /etc/postfix/canonical を作成します

/.*/ hoge@sample.com

ここでもお約束事ですね。hoge@sample.comをご自分の使う環境に合わせて書き換えてください。

つぎは先ほど作成したrelay_passwordからhash を作成します

sudo postmap hash:/etc/postfix/relay_password

最後にpostfix の再起動を行いましょう

sudo service postfix restart

エラーメッセージの無き事を確認してください.

mailコマンドを使う場合にはapt-get install でmailutilsをインストールしてください。

IMG_1899

Raspberry piで大気圧センサーMPL115A1を使う

ここではRaspberry piで秋月電子で買える大気圧センサーモジュールを使ってみます.
I2Cインターフェイスに対応したMPL115A2もありますが今回はSPIインターフェイスに対応したMPL115A1を使います

IMG_1899

スクリーンショット 2013-12-14 6.13.23

事前にRaspberry piでSPI通信を行うための設定を済ませておきます.

今回はPythonでプログラムを組んでみました。
このデバイスではデバイスごとのばらつきを補正するための補正係数を内部に持っています。それら補正係数を読み出した上で大気圧を算出します。他のセンサーと比較して複雑な計算処理が必要になるという事で使いこなしは少々面倒です.Google検索で見つかる同様の使いこなしについての記事を見渡してもその計算はいろいろと工夫されているのがうかがえます.ここでは出来るだけデバイスのアプリケーションノートにそった形で作っています.


import spidev
import time
import math
#main
spi_ch = 0
spi = spidev.SpiDev()
spi.open(0,spi_ch)
spi.max_speed_hz=(100000)

msb = spi.xfer2([0x88,0x00])
lsb = spi.xfer2([0x8a,0x00])
a0=(msb[1]<<8)|lsb[1]
if a0 >> 15:
     a0 = ~a0
a0int= (a0>>3)& 0b111111111111
a0fract = (a0 & 0b111)/8.0
a0val = a0int + a0fract*(0.1** int(math.log10(a0fract)+1))
print a0val    
if a0>>15:
     a0=-a0val
else:
     a0 = a0val
print 'a0',a0

msb = spi.xfer2([0x8c,0x00])
lsb = spi.xfer2([0x8e,0x00])
b1 = (msb[1]<<8)|lsb[1]
if b1 >>15:
     b1 = ~b1
b1int= (b1>>13)& 0b11
b1fract = (b1 & 0b1111111111111)/8191.0
b1val = b1int + b1fract*(0.1** int(math.log10(b1fract)+1))
if b1 >> 15:
     b1=-b1val
else:
     b1=b1val
print 'b1',b1

msb = spi.xfer2([0x90,0x00])
lsb = spi.xfer2([0x92,0x00])
b2 = (msb[1]<<8)|lsb[1]
if b2 >>15:
     b2 = ~b2
b2int= (b2>>14)& 0b1
b2fract = (b2 & 0b11111111111111)/16383.0
b2val = b2int + b2fract*(0.1** int(math.log10(b2fract)+1))
if b2>>15:
     b2=-b2val
else:
     b2 =b2val
print 'b2',b2

msb = spi.xfer2([0x94,0x00])
lsb = spi.xfer2([0x96,0x00])
c12 = (msb[1]<<8)|lsb[1]
c12 >>= 2
c12value = c12 / 4194304.0
c12 = c12value
print 'c12',c12

print
values=[]
for i in range(10):
     spi.xfer2([0x24,0x00])
     time.sleep(0.003)
     padcm=spi.xfer2([0x80,0x00])
     padcl=spi.xfer2([0x82,0x00])
     padc = ((padcm[1]<<8) | padcl[1])
     tadcm=spi.xfer2([0x84,0x00])
     tadcl=spi.xfer2([0x86,0x00])
     tadc = ((tadcm[1]<<8)| tadcl[1])
     padc >>= 6
     tadc >>= 6
     c12x2 = c12*tadc
     a1 = b1 + c12x2
     a1x1 = a1*padc
     y1 = a0 +a1x1
     a2x2 = b2*tadc
     pcomp =y1+a2x2
     pressureValue = (pcomp *65.0/1023.0+50)*10 # hPa
     print "%d:pressure:%0.2f hPa"%(i,pressureValue)
     values.append(pressureValue)
sum = 0.0
for i in range(len(values)):
     sum += values[i]
ave = sum/len(values)
print"average pressure: %0.2f hPa"%ave

実行するとコンソールには補正係数の計算途中も表示されます。不要な場合はコメントアウトまたは削除して構いません。測定ばらつきを吸収するために10回測定値の平均を最後に表示するようにしています.単位は天気予報でもなじみのあるヘクトパスカル(hPa)です。

上手く動作すれば、天気予報でみる気圧と同じ様な値が出てくるはずです.気圧は標高にも左右されそしてセンサーの周りの気圧変化にも非常に敏感です.センサーの近くで息を吹きかければそれも気圧の変化を及ぼします.正確な測定値を期待する場合には設置場所にも配慮する必要が有ります.

定点観測する事で気圧の変化のトレンドを知ることができます.たとえば低気圧の通過に伴ってセンサーの出力結果も値が上下するのが分かるはずです.大気圧の測定は気圧の変化のあくまで結果(現在の事象)であり、そこから未来の天気を予報をするのは難しいと思います.測定結果と同時に天候の様子を記録し、それぞれの相関を観察するのも面白いでしょう。天気が急速に変化する時には大気圧も急激に変化していきます。台風の通過では気圧が普段よりも一層低い値を示します.

Raspberry piを使えば測定結果をその都度メールで発信したり、Twitterでtweetしたり、GoogleDocのスプレッドシートに記録したりと、応用範囲はずっと広がります。ぜひ挑戦してみてください.

IMG_1900

温度センサーADT7310をRaspberry piで使う

温度センサーADT7310をRaspberry piで使ってみます.
秋月電子で売られている同タイプの温度センサーにはI2Cインターフェイスに対応したADT7410もあります。
ここではSPIに対応したADT7310を使います。
IMG_1900

スクリーンショット 2013-12-14 5.55.12

事前にRaspberry piでSPIが使えるようシステムの設定を済ませておきます。
ここではPythonで制御します

import spidev
import time
spi_ch = 1
spi = spidev.SpiDev()
spi.open(0,spi_ch)
spi.max_speed_hz=(100000)   #クロック周波数を設定
spi.xfer2([0x08,0x80])  # センサーを16ビット精度に設定
time.sleep(0.5)
spi.xfer2([0x54]) #連続モードにセット
time.sleep(1) #ADT7410が測定データをAD変換するための時間を待つ
ret = spi.xfer2([0xff,0xff]) #ダミーの2バイトをセンサーに書き込んで2バイトの温度データを読み取る
temp = ret[0]<<8 | ret[1]
temp /= 128.0      #℃に変換
print "temparature:",temp
print "stop:",spi.xfer2([0x50]) #連続モードを停止
spi.close()

無事動作すればコンソールに現在温度を表示するはずです。定時観測に使う場合はこのプログラムに測定結果をメールなどで飛ばす処理を加えたスクリプトにしてcronで定期的に呼び出すようにすると良いでしょう。
ADT7310の制御コマンドは他にも有りますが、順番など含めて動作を確認できたのが上で紹介したコマンドの流れになります.使いこなしによっては他のコマンドも使えると思います。いろいろと試してみてください。

Raspberry piにはSPIで使えるCE信号がCE0とCE1の二つしか無いと言う事はSPIデバイスを二つ使えるという事ですね。液晶デバイスなどSPIシリアルを持ったモジュールなども有りますので使いこなしてみるのも面白いでしょうね。

今回はSPIシリアルを持った温度センサーを使いました。同タイプの温度センサーモジュールにはI2Cインターフェイスを持ったものが有ります.他にも気圧センサーなどもSPIとI2Cのどちらかを選択することができます.もっと多くのモジュールをつなげたい、ほかのセンサーも同時に使いたいと言った時にはI2C対応のモジュールを検討してみると良いでしょう。(それでもI2Cアドレスが競合してしまっては使えないので気をつけましょう)