2014年 01月 20日

Raspberry Pi を中心にやってみようとしているところ

Raspberry Pi せっかくあるので、一応作りたい目標をもっていろいろ試してみてる。現時点での思惑を書いておく。

概要

無線機の周辺を拡張して、PCと連携しやすくしたり便利にしたり、というのが目的。具体的には

  • 無線機の Wi-Fi 化
    • API サーバになってLAN内のコンピュータから無線機の情報を取得・設定する
  • アンテナ切替情報の取得・設定
  • そのたもろもろ

全体

  • Raspberry Pi
    • I2C バス
      • アンテナ切替コントローラー (AVR)
      • センサー計 (温度など)
    • シリアルポート
      • 無線機コントロール (RS-232C 変換)
  • WebSocket サーバー
    • 周辺機器の中央集権サーバー
      • 無線機コントローラー
      • アンテナ情報
      • センサーなど

ある程度拡張性を持たせつつこれらを収めたい。無線機の近くで使うので、ある程度ノイズ対策が必要かもしれない。

ブロックごとの設計

電源部
  • アンテナ切り替えリレー用に 12V (最大 400mA 程度) が必要
  • Raspberry Pi が 5V 700mA が必要
  • ほか周辺機器分はそんなに食うものがないので、かなり余裕を持っても 5V 500mA ぐらいあればよさそう
    • Raspberry Pi の GPIO が 3.3V で接続機器もあわせたいので 3.3V も必要

12V が必要なので、ACアダプタとして 12V を使い、降圧して 5V, 3.3V を作りたい。

  • 12V 500mA (6W)
  • 5V 1A (5W)
  • 3.3V 500mA (1.65W)

ぐらい確保できたら十分そう。PC用の電源だと、100Vから直接これらの電圧を全部調達できるけど、コストはともかくオーバースペックでデカすぎる。

12V 2A ぐらいのACアダプタ (24W) から 5V はスイッチングである程度高容量を高効率でとりだして、3.3V は 5V からリニアレギュレーターで安定化させる感じにする。

http://akizukidenshi.com/catalog/g/gM-02038/

5V 3A のスイッチングDC/DCコンバーターで、変換効率は最低でも73%。

  • アダプタ: 12V 2A
    • 12V 500mA
    • 12V 1.5A (18W) -> 5V 2.6A (18 * 0.73 = 13.14W)
      • 5V 1A
      • 3.3V 500mA を 5V からリニアレギュレーターで作ろうとすると、電圧差はそのまま熱になるので ( (5 - 3.3) * 0.5 = 0.85W が無駄になる)、単に 5V 500mA を消費すると考えることができる。
アンテナ切替コントローラー

ATmega168P をつかって実装。TWI (I2C) がついてるので簡単。内蔵8 M クロックで動作させて、アンテナのリレーのコントールをI2C経由及びボタンで行い、LED でどのアンテナが有効かを表示する。

リレーは2〜6つほどを同時に駆動する必要があり、この時最大で 400mA ほど流れる。かなり余裕があり、3.3V でも十分スイッチできるパワーMOS-FET でスイッチングする。

電源は I2C を Raspberry Pi と直接繋げたいので 3.3V 供給

センサー類

基本的に I2C 対応、3.3V 品なので、そのままバスに繋ぐだけ。ごちゃごちゃやりたいのでブレッドボードを内蔵してそこに差す

RS-232 変換

無線機とのインターフェイスのために RS-232 変換をする。

2014年 01月 15日

Raspberry Pi の SD カードを Mac でバックアップ

特筆するほど難しいことはないけど書いておく。基本的に dd を使う方法が最速だと思うけど、dd コマンドはうっかり使うと死ぬ感じなので、こういうことは GUI で行いたい。なので、ディスクユーティリティを使用してバックアップを作る。

SD カードをさしこむとディスクユーティリティにでてくるので、各パーティションをアンマウントして (boot だけ自動でマウントされるはず)、カード (この例では 32.02GB APPLE SD Card...) を選択して、ツールバーの「新規イメージ」を選択する。

新規イメージでは、デフォルトで「圧縮」が選択されているはずなので、そのまま適当なディレクトリに保存する。

するとコピーがはじまる。基本、SDカードそのものの読み出し速度に律速される。手元のカード (Class 10) の場合、25MB/sec ぐらいで読み出されて、圧縮で 8〜16MB/sec で書き込みがある。32GB だと、32 * 1024 / 25 / 60 で、22分ほどかかる。最終的に 18GB になった。

Mac では Linux で使われる extN には対応していないので、Raspberry Pi の ext4 のパーティションはマウントできない。コピー中、パーティションのタイプ自体は認識されているけど、ext2 として認識されている。

圧縮された .dmg ができるので、復元 (あるいはクローン) もディスクユーティリティを使う必要がある (Mac 上でマウントして復元、または hdiutil convert してから)。dd + gzip だとどこの環境でも簡単に復元可能なので、どっちもどっちかなという感じ。

2014年 01月 09日

Raspberry Pi + Wi-Fi アダプタが異常に遅い問題を解決

追記:最近別のアダプタに変えました 500 Can't connect to lowreal.net:443 (certificate verify failed)

 -

3.0 / 5.0

アダプタは WLI-UC-GNM というやつをつけているんだけれど、ping が平均 100ms ぐらい (1ms〜250ms ばらつきがある) で、速度も 300kB/sec (2.4Mbps) ぐらいしかでない状態だった。

SSH して作業をしているので、ちょいちょいひっかかってストレスがかかるのと、奇妙な感じなので直したくていろいろ調べていたけど、ようやく解決したので記録しておく。

解決法

先に解決方法を書くと、/etc/network/interfaces に以下を書けばいいだけだった。

wireless-power off 

経緯

いろいろググってみると、ドライバに対してオプションをわたしたりとかしている例がでてくるけど、別のチップの話なのでそのまま適用できない感じだった。ただ、パワーマネジメントまわりでよくないことが起こることがある、みたいなのはこの時点で頭に入った。

iwconfig の出力を眺めると、以下のように Power Management:on になっていた。なので、iwconfig 側から Power Management を off にできないか調べたら解決方法にあるようなオプションがあることがわかった。

$ iwconfig
wlan0     IEEE 802.11bgn  ESSID:"SNEG"  
          Mode:Managed  Frequency:2.462 GHz  Access Point: XX:XX:XX:XX:AE:CE   
          Bit Rate=43.3 Mb/s   Tx-Power=20 dBm   
          Retry  long limit:7   RTS thr:off   Fragment thr:off
          Power Management:on
          Link Quality=63/70  Signal level=-47 dBm  
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:41   Missed beacon:0

lo        no wireless extensions.

eth0      no wireless extensions.

オプションを適用させると、Power Management:off になり、ping は平均36ms程度まで、速度は 3MB/sec (24Mbps) まで改善した。数msぐらいまで短かくてもいいと思うけど、だいぶ改善してストレスが減ったのでとりあえずよしとする。

Raspberry Pi の実測消費電力 (電流量)

Raspberry Pi の実測電流値が気になったので安定化電源に繋いで適当に計ってみた。

アナログで読んだのでだいたい。USB Wi-Fi は指したまま起動。ほかのイーサネットポートやHDMIポートには何も指していない。

  1. 起動直後 約400mA (2W)
  2. USB Wi-Fi がチカチカしはじめてから 約500mA (2.5W)
  3. ruby -e 'loop {}' (CPU 99%) 約600mA (3W)

自分で接続した周辺機器をオン (AVR + RS232ドライバ + 気圧計 + I2C文字液晶) にすると、約650mA だけど、ほぼLEDだと思う。

スペック上、このモデルは 700mA 使うことになってるけど、意外と少なかった。CPU 全力 + HDMI + イーサネットポートとかだと 700mA になるのかな?

700mA (3.5W) で1年起動させっぱなしで、電気料金が高くとも 30円/kWh だとすると、(3.5 * 24 * 265) / 1000 * 25 ≒ 668円ぐらい。

2014年 01月 08日

Raspberry Pi のローレベル周辺機器の電源を特定プロセスが動いているときだけオンにする

GPIO ピンからとれる電源は普通に電源回路直結なので、Raspberry Pi 自体が起動していようがしていまいが、電源ケーブルさえ接続されていれば供給されています。まぁこれはいいんですが、Raspberry Pi 本体が起動していないとき、もっというとそれらを扱うデーモンが起動していないときに電源供給されてもエコじゃないので、なんとかしました。

回路図

ちょっと余計な線がありますが、キモは FET だけです。伸びてる GND と VCC を周辺回路に繋ぐと、GPIO 25 (22pinから出てる) の論理によってオン・オフします。

手元では 2SK2796L を使ってます (3.3V で直接駆動できるので)

コード側

単に GPIO 25 ピンのハイ・ローを切り替えるだけです。Ruby の場合以下のように

#!/usr/bin/env ruby
# coding: utf-8
 
module GPIO
	def self.export(pin)
		File.open("/sys/class/gpio/export", "w") do |f|
			f.write(pin)
		end
	end
 
	def self.unexport(pin)
		File.open("/sys/class/gpio/unexport", "w") do |f|
			f.write(pin)
		end
	end
 
	def self.direction(pin, direction)
		[:in, :out].include?(direction) or raise "direction must be :in or :out"
		File.open("/sys/class/gpio/gpio#{pin}/direction", "w") do |f|
			f.write(direction)
		end
	end
 
	def self.read(pin)
		File.open("/sys/class/gpio/gpio#{pin}/value", "r") do |f|
			f.read.to_i
		end
	end
 
	def self.write(pin, val)
		File.open("/sys/class/gpio/gpio#{pin}/value", "w") do |f|
			f.write(val ? "1" : "0")
		end
	end
end
 
GPIO.export(25)
GPIO.direction(25, :out)
GPIO.write(25, true)
at_exit do
    GPIO.write(25, false)
end
sleep 3 # 周辺機器が安定するまで適当な時間

...

at_exit でローにするようにしてるだけです。

備考

回路図だとわかりにくい感じだけど、これは GND 側のスイッチ (ローサイドスイッチ) で、VCC は繋がりっぱなしなので、Raspberry Pi 以外の他の電源を回路に接続するとよくないかもしれないです。

ぜんぜんよくわからないんですが、こういう回路はハイサイドにしたほうが安全なのかな。応答速度はあまり必要ではないし……

2014年 01月 05日

Ruby で sysfs 経由での GPIO 操作

GPIO の操作はいろいろやる方法があるみたいだけど、LL からだと sysfs への IO を行うのが一番簡単っぽい。以下のような感じでかなり簡単に書ける。この程度だとライブラリを使う必要はない。

pin の数値は、RPi Low-level peripherals で書いてあるような GPIO n の n の部分を指定する。(物理的なピンの並びとは関係ない)

  • export で指定した GPIO ピンを使えるようにする
  • direction で方向を指定する (入力か、出力か)
  • read/write で 0/1 を読み書きする

この例だと GPIO 25 (22pin) で指定時間ごとに論理が反転する。

#!/usr/bin/env ruby
# coding: utf-8

module GPIO
	def self.export(pin)
		File.open("/sys/class/gpio/export", "w") do |f|
			f.write(pin)
		end
	end

	def self.unexport(pin)
		File.open("/sys/class/gpio/unexport", "w") do |f|
			f.write(pin)
		end
	end

	def self.direction(pin, direction)
		[:in, :out].include?(direction) or raise "direction must be :in or :out"
		File.open("/sys/class/gpio/gpio#{pin}/direction", "w") do |f|
			f.write(direction)
		end
	end

	def self.read(pin)
		File.open("/sys/class/gpio/gpio#{pin}/value", "r") do |f|
			f.read.to_i
		end
	end

	def self.write(pin, val)
		File.open("/sys/class/gpio/gpio#{pin}/value", "w") do |f|
			f.write(val && val.nonzero? ? "1" : "0")
		end
	end
end

GPIO.export(25)
GPIO.direction(25, :out)

led = true
loop do
	GPIO.write(25, led)
	led = !led
	sleep 0.5
end
2013年 12月 31日

Raspberry Pi の GPIO でシリアル通信

GPIO に UART があるけど、デフォルトではシリアルコンソールとして使うことが想定されていて、カーネルメッセージとかが流れる。これを無効にして、普通のシリアルポートとして使う。

/etc/inittab を書きかえる。ttyAMA0 の行をコメントアウトする

#Spawn a getty on Raspberry Pi serial line
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

/boot/cmdline.txt を書きかえる。デフォルトだと以下のようになっているので

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
  • console=ttyAMA0,115200 を削除 (シリアルコンソールのオプション)
  • kgdboc=ttyAMA0,115200 を削除 (カーネルデバッグ用のオプション ref. kgdboc

して以下のように

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

で reboot すると普通にユーザレベルで自由に使えるようになる。

3.3V なので、RS-232C レベルにするなら 3.3V でも使えるドライバICを使う必要がある。ICL3232 だと100円ぐらい。

回路

FET らへんは GPIO 25 (22pin) をオンにしているときだけスイッチを入れるための仕組みなので RS-232C のためには必須ではない (GND を普通に繋ぐだけ)

2013年 12月 28日

Linux 上の LL で I2C する (Ruby)

なんとなく i2c-dev.h とかで定義されているAPIを呼ばないと使えないのかなあと思っていたけど、デバイスファイルの読み書きと ioctl だけで普通に使うことができた。

書きこみ

  • /dev/i2c-1 とかを rw+ で open
  • ioctl で I2C_SLAVE コマンドを使ってスレーブアドレスを指定する
  • write する

I2C 的には START -> SLAVE + W -> DATA送信 -> STOP が行われる。

読みこみ

  • /dev/i2c-1 とかを rw+ で open
  • ioctl で I2C_SLAVE コマンドを使ってスレーブアドレスを指定する
  • 読みこみたいアドレスを write する
  • read す

I2C 的には START -> SLAVE + W -> DATA送信 -> ReSTART -> SLAVE + R -> DATA受信 -> STOP が行われる。

たぶん一度 write/read するたびに Repeated Start が送られるのかな。

Ruby での例

Ruby でやる場合以下のような感じでいけた。デバイスによってはうまくいかないかもしれない。

class I2CDevice
	# ioctl command
	# Ref. https://www.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/i2c.h
	I2C_SLAVE       = 0x0703

	attr_accessor :address

	def initialize(address)
		@address = address
	end

	def i2cget(address)
		i2c = File.open("/dev/i2c-1", "r+")
		i2c.ioctl(I2C_SLAVE, @address)
		i2c.write(address.chr)
		ret = i2c.read(1).ord
		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

2013年 12月 26日

Raspberry Pi がきたのでセットアップして I2C デバイスを読むところまで

Raspberry Pi だ! ππだ!! Raspberry Pi は約5000円ぐらいで買うことができるカードサイズの Linux パソコンです。当然スペックとしてはしょぼいのですが、ホームサーバ用途とかには十分な感じです。

まぁそれだけでも安くて便利なボードなのですが、キモはさらに GPIO (General Purpose IO) がついているところです。基板にピンが立っていて、そのピンを Linux 側から制御できるので、マイコンの延長として使うことができます。普通、マイコンといえども性能が良かったり複雑なインターフェイスをつけようとすると、結構コストがかかるので、多少複雑なことをしたい場合、このような安い Linux コンピュータで比較的富豪的に諸問題を解決するのは個人レベルではかなりコストパフォーマンスが良い気がします。

Raspberry Pi Type B 512MB ケースセット (clear) -

5.0 / 5.0

これです。クリアケース付きです。本体の他に

  • 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 だけ繋げて起動します。

  1. Expand Filesystem を実行して、SDカード領域全域を使えるようになる。これは一瞬で終わります。
  2. Internationalization Options → Locale で en_US.UTF-8 をスペースで選択して、デフォルトを en_US.UTF-8 にする
  3. Internationalization Options → Timezone を Asia/Tokyo にする。
  4. Advanced Options → SSH を Enable にする。
  5. Finish して reboot をする。

reboot が終わったら、とりあえず pi/raspberry でログインしてみて

 sudo shutdown -h now

して終了させます。

Wi-Fi 設定

さっさと SSH したいのでネット設定します。

念のため終了して電源ケーブルを抜いてから USB Wi-Fi をつけます。

 -

3.0 / 5.0

でもう一度電源ケーブルをさして起動

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
view raw rb.rb hosted with ❤ by GitHub

校正値が入っていてそれを考慮して計算しないといけないので多少面倒だけど、比較的すぐ書けました。

やった〜! 天気図を見た感じではだいたいあってる感じです。センサー自体の精度が ±1kPa なので、下1桁は信用できない値です。