Raspberry Pi だ! ππだ!! Raspberry Pi は約5000円ぐらいで買うことができるカードサイズの Linux パソコンです。当然スペックとしてはしょぼいのですが、ホームサーバ用途とかには十分な感じです。
まぁそれだけでも安くて便利なボードなのですが、キモはさらに GPIO (General Purpose IO) がついているところです。基板にピンが立っていて、そのピンを Linux 側から制御できるので、マイコンの延長として使うことができます。普通、マイコンといえども性能が良かったり複雑なインターフェイスをつけようとすると、結構コストがかかるので、多少複雑なことをしたい場合、このような安い Linux コンピュータで比較的富豪的に諸問題を解決するのは個人レベルではかなりコストパフォーマンスが良い気がします。
これです。クリアケース付きです。本体の他に
- 4GB Class4 以上 SD カード
- microUSB 5V 0.7A以上のACアダプタ (容量が多いほうがよい)
- USB Wi-Fi アダプタ
が必要です。
SD カードの速度
Raspberry Pi 側は UHS-1 とか SDXC には対応していないので、仕様上 Class10 の SDHC 32GB というのが最大限富豪な選択になります。。
http://elinux.org/RPi_SD_cards を見ると、だいたい Read Write ともに 20MB/sec 程度でればいい方みたい (異常に早い報告もあるけどよくわからない)。
イメージの書きこみ
オンラインインストールのものではなく、普通にイメージを落としてきて Mac から書きこみました。特別別途ソフトがいるとかはないので楽です。
diskutil list diskutil unmountDisk /dev/disk99 sudo dd bs=1m if=2013-09-25-wheezy-raspbian.img of=/dev/rdisk99 sudo diskutil eject /dev/rdisk99
指定するのはあくまでディスクであって、パーティションではないところに注意が必要です (/dev/disk2s1 とかはパーティション)。df で出てくるのはマウント済みのパーティション一覧で紛らわしいので、diskutil list を使ったほうが良いです。dd で指定するディスクファイルは r (raw) をつけて指定します。
dd の指定をミスると指定ディスクの内容が消えて悲しいのでこわい。
dd でイメージ作ってるけど、初期設定でパーティションを拡張するようになっているので、でかいカードを使っておいても損はしない。
初期設定
最初はキーボードと HDMI だけ繋げて起動します。
- Expand Filesystem を実行して、SDカード領域全域を使えるようになる。これは一瞬で終わります。
- Internationalization Options → Locale で en_US.UTF-8 をスペースで選択して、デフォルトを en_US.UTF-8 にする
- Internationalization Options → Timezone を Asia/Tokyo にする。
- Advanced Options → SSH を Enable にする。
- Finish して reboot をする。
reboot が終わったら、とりあえず pi/raspberry でログインしてみて
sudo shutdown -h now
して終了させます。
Wi-Fi 設定
さっさと SSH したいのでネット設定します。
念のため終了して電源ケーブルを抜いてから USB Wi-Fi をつけます。
でもう一度電源ケーブルをさして起動
IP は固定で指定します (SSH しやすいように)
$ sudo vi /etc/network/interfaces auto lo iface lo inet loopback iface eth0 inet dhcp allow-hotplug wlan0 iface wlan0 inet static address 192.168.0.250 netmask 255.255.255.0 gateway 192.168.0.1 wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf # WiFi アダプタが異常に遅い現象が出るのでオフ wireless-power off iface default inet dhcp
めんどうなのでパスは直書き。
$ sudo su # wpa_passphrase ESSID password >> /etc/wpa_supplicant/wpa_supplicant.conf # exit
でもって
$ sudo ifdown wlan0 $ sudo ifup wlan0
以下のようなエラーが出た場合、less /var/log/daemon.log を見るとエラってる部分がわかるはず。
wpa_supplicant: /sbin/wpa_supplicant daemon failed to start
うまくいった場合何も表示されない。daemon.log を見てうまくいっているか確認し、ifconfig を実行する以下のように IP アドレスが割当られます。
pi@raspberrypi ~ $ ifconfig eth0 Link encap:Ethernet HWaddr b8:27:eb:01:5c:f2 UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:22 errors:0 dropped:0 overruns:0 frame:0 TX packets:22 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2434 (2.3 KiB) TX bytes:2434 (2.3 KiB) wlan0 Link encap:Ethernet HWaddr b0:c7:45:a9:9e:91 inet addr:192.168.0.250 Bcast:192.168.0.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:176 errors:0 dropped:0 overruns:0 frame:0 TX packets:112 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:36200 (35.3 KiB) TX bytes:17924 (17.5 KiB)
これで SSH できるようになったはずなので、手元のマシンからログインします。
$ ssh pi@192.68.0.250 pi@192.168.0.250's password: Linux raspberrypi 3.6.11+ #538 PREEMPT Fri Aug 30 20:42:08 BST 2013 armv6l The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Thu Sep 26 07:18:58 2013 pi@raspberrypi ~ $
~/.ssh/authorized_keys に手元のマシンのキーを追加して公開鍵認証にしとく。これで、もはやキーボードと HDMI はいらないので外します。
dns-sd 化
IP アドレス固定にしているので必須ではないけど、手元のヒストリとかが綺麗になるし、便利なのでやっておく
$ sudo apt-get install avahi-daemon $ sudo insserv avahi-daemon
で avahi-daemon を入れて、起動スクリプトを登録する。
以下のファイルを作って SSH をアナウンスするようにする。
$ sudo vim /etc/avahi/services/multiple.service <?xml version="1.0" standalone='no'?> <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> <service-group> <name replace-wildcards="yes">%h</name> <service> <type>_device-info._tcp</type> <port>0</port> <txt-record>model=RaspberryPi</txt-record> </service> <service> <type>_ssh._tcp</type> <port>22</port> </service> </service-group>
それで avahi-daemon を再起動
$ sudo /etc/init.d/avahi-daemon restart
これで名前でアクセスできるようになる。
$ dns-sd -B _ssh._tcp $ ssh -c arcfour pi@raspberrypi.local
アップデート
まずこれをやらないとこわいのでやっておきます。upgrade で結構時間がかかります。1時間ぐらい。
$ sudo apt-get update $ sudo apt-get upgrade
ここまできたらあとは普通に自分の環境をつくります。
環境作り
zsh は重いのでいれず、手元の dotfiles は .bashrc も入れてあるのでそれを使います。
$ ssh-keygen # して key を github に登録 $ cd $ git clone git@github.com:cho45/dotfiles.git $ cd dotfiles $ ruby setup.rb $ sudo apt-get install screen libncurses5-dev vim $ screen -S main $ curl -L http://cpanmin.us | perl - --sudo App::cpanminus ### 以下は時間がかかりすぎるのでやめた $ mkdir tmp $ cd tmp $ wget ftp://ftp.vim.org/pub/vim/unix/vim-7.4.tar.bz2 $ tar xjvf vim-7.4.tar.bz2 $ cd vim-7.4 $ ./configure --prefix=/usr/local/vim7 --enable-multibyte --enable-gpm --enable-cscope --with-features=huge --enable-fontset --disable-gui --without-x --disable-xim --disable-perlinterp $ make # かなり時間がかかる $ sudo make install
raspi はさすがに遅いので vim のコンパイルとかはしないほうが良いみたいです。
perl はデフォルトで 5.14.2 が入っているので十分で、cpanm さえ入れればよさそうです。ruby は 1.9.4p194 が入っていて、まぁそのまま頑張るほうがよさそうです。
I2C できるように
デフォルトでは有効になっていないらしいので有効にします。
/etc/modules に以下を追加
i2c-dev
/etc/modprobe.d/raspi-blacklist.conf の以下の部分のコメントアウトする
# blacklist i2c-bcm2708
でリブートして
$ cat /var/log/dmesg | grep i2c $ sudo apt-get install i2c-tools
確認して、関連ツールを入れておきます。
I2C 確認のため MPL115A2 (気圧計) を読む
仕様を確認しておくと
- 3.3V
- プルアップ抵抗は実装済み
- 3.3V ピンから 50mA しかとれない
- 5V ピンは供給電源から本体の電流(700mA)を引いたぶんだけ使える。1A のアダプタなら 300mA
- しかしレギュレータで 3.3V 安定化するか、入出力部分はレベル変換が必要
となっています。ハードウェア部分はミスると普通に壊れるので、気をつけて作業する必要があります。
手元に、ワンチップで簡単そうなのは MPL115A2 という気圧計しかないのでこれを頑張って読むことにしました。3.3V で使えるし、消費電力もほとんどないので簡単そうです。
http://elinux.org/RPi_Low-level_peripherals を参考に結線すると i2cdetect にでてきます。このアドレスは仕様書に書かれてるのと一緒です。
$ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
sudo してるのは /dev/i2c* のパーミッションが一般ユーザに許可されていないからですが、面倒なので pi ユーザも許可するように変えます。/dev/i2c* は i2c グループが所有者になっているのでi2c グループに pi ユーザを追加します。そのうち必要になりそうなのも追加しとく。
sudo usermod -G i2c,spi,gpio,dialout pi
いっかい exit でログインしなおさないと反映されないっぽいです。
でもって MPL115A2 の仕様書通りに値を読みだして計算します。今回は Ruby で書いてみました。
#!/usr/bin/env ruby | |
# coding: utf-8 | |
class I2CDevice | |
# ioctl command | |
# Ref. https://www.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/i2c.h | |
I2C_RETRIES = 0x0701 | |
I2C_TIMEOUT = 0x0702 | |
I2C_SLAVE = 0x0703 | |
I2C_SLAVE_FORCE = 0x0706 | |
I2C_TENBIT = 0x0704 | |
I2C_FUNCS = 0x0705 | |
I2C_RDWR = 0x0707 | |
I2C_SMBUS = 0x0720 | |
I2C_UDELAY = 0x0705 | |
I2C_MDELAY = 0x0706 | |
attr_accessor :address | |
def initialize(address) | |
@address = address | |
end | |
def i2cget(address, length=1) | |
i2c = File.open("/dev/i2c-1", "r+") | |
i2c.ioctl(I2C_SLAVE, @address) | |
i2c.write(address.chr) | |
ret = i2c.read(length) | |
i2c.close | |
ret | |
end | |
def i2cset(*data) | |
i2c = File.open("/dev/i2c-1", "r+") | |
i2c.ioctl(I2C_SLAVE, @address) | |
i2c.write(data.pack("C*")) | |
i2c.close | |
end | |
end | |
class ACM1602NI < I2CDevice | |
MAP = Hash[ | |
[ | |
"。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚".split(//).map {|c| | |
c.force_encoding(Encoding::BINARY) | |
}, | |
(0xa1..0xdf).map {|c| | |
c.chr | |
} | |
].transpose | |
] | |
def initialize | |
super(0x50) | |
initialize_lcd | |
end | |
undef i2cget | |
def initialize_lcd | |
# function set | |
i2cset(0, 0b00111100) | |
sleep 53e-6 | |
# display on/off control | |
i2cset(0, 0b00001100) | |
sleep 53e-6 | |
clear | |
end | |
def clear | |
i2cset(0, 0b00000001) | |
sleep 2.16e-3 | |
end | |
def put_line(line, str) | |
str.force_encoding(Encoding::BINARY) | |
str.gsub!(/#{MAP.keys.join('|')}/, MAP) | |
str = "%- 16s" % str | |
# set ddram address | |
i2cset(0, 0b10000000 + (0x40 * line)) | |
sleep 53e-6 | |
i2cset(*str.unpack("C*").map {|i| [0x80, i] }.flatten) | |
sleep 53e-6 | |
end | |
# Usage: | |
# lcd.define_character(0, [ | |
# 0,1,1,1,0, | |
# 1,0,0,0,1, | |
# 1,1,0,1,1, | |
# 1,0,1,0,1, | |
# 1,1,0,1,1, | |
# 1,0,0,0,1, | |
# 1,0,0,0,1, | |
# 0,1,1,1,0, | |
# ]) | |
def define_character(n, array) | |
raise "n < 8" unless n < 8 | |
raise "array size must be 40 (5x8)" unless array.size == 40 | |
array = array.each_slice(5).map {|i| | |
i.inject {|r,i| (r << 1) + i } | |
} | |
i2cset(0, 0b01000000 + (8 * n)) | |
sleep 53e-6 | |
i2cset(*array.map {|i| [0x80, i] }.flatten) | |
sleep 53e-6 | |
end | |
end | |
class MPL115A2 < I2CDevice | |
def initialize | |
super(0x60) | |
coefficient = i2cget(0x04, 8).unpack("n*") | |
@a0 = fixed_point(coefficient[0], 12) | |
@b1 = fixed_point(coefficient[1], 2) | |
@b2 = fixed_point(coefficient[2], 1) | |
@c12 = fixed_point(coefficient[3], 0) / (1<<9) | |
p [@a0, @b1, @b2, @c12] | |
end | |
def fixed_point(fixed, int_bits) | |
msb = 15 | |
deno = (1<<(msb-int_bits)).to_f | |
if (fixed & (1<<15)).zero? | |
fixed / deno | |
else | |
-( ( (~fixed & 0xffff) + 1) / deno ) | |
end | |
end | |
def calculate_hPa | |
i2cset(0x12, 0x01) # CONVERT | |
sleep 0.003 | |
data = i2cget(0x00, 4).unpack("n*") | |
p_adc = (data[0]) >> 6 | |
t_adc = (data[1]) >> 6 | |
p_comp = @a0 + (@b1 + @c12 * t_adc) * p_adc + @b2 * t_adc | |
hPa = p_comp * ( (1150 - 500) / 1023.0) + 500; | |
end | |
end | |
mpl = MPL115A2.new | |
loop do | |
puts "%d hPa" % mpl.calculate_hPa | |
sleep 0.5 | |
end |
校正値が入っていてそれを考慮して計算しないといけないので多少面倒だけど、比較的すぐ書けました。
やった〜! 天気図を見た感じではだいたいあってる感じです。センサー自体の精度が ±1kPa なので、下1桁は信用できない値です。