評判の良いこれを買った。セットアップはあまり迷うところもなく終わった。
ただ、USB ディスク共有だけなんかおかしくて、smb://192.168.0.1/Hitachi-1 みたいな感じで、全部指定して接続をかけないと失敗する (Mac OS X)
評判の良いこれを買った。セットアップはあまり迷うところもなく終わった。
ただ、USB ディスク共有だけなんかおかしくて、smb://192.168.0.1/Hitachi-1 みたいな感じで、全部指定して接続をかけないと失敗する (Mac OS X)
初回はうまくいったけど、時間が経ってからもう一度やろうとしたらうまくいかなくなった。Mac の smb 接続が不安定になったりとかいろいろした結果、以下のような状態っぽいことがわかった (推測)
なので、小さいスパースバンドルにするかなんとかしたらうまくいくかもしれないけど、もう諦めた…… 時間の無駄だった。
初回うまくいったのはよくわからない。Mac 側になんらかの形でエントリのキャッシュがあったのかもしれない。なんでもいいけど…
買ってから気付いたけど、USB ポートがついていて、USB HDD を繋いで SMB 共有ができるようになっていた。
せっかくなので、今まで有線USBでバックアップしていたのを、無線経由でバックアップするように変えた。
まず既存 Time Machine を OFF にする。
HDDを新しく買ってくるならこんなことしなくていいんだけど、買いたくないので、一旦現状のバックアップディスクを、別にディスクに .dmg としてバックアップした。
ディスクユーティリティでスパースバンドルファイルをつくる
作成したらアンマウントして、NASに繋げるディスクにコピーする。最初のバックアップまでは USB で直接繋いで作業する。
コピーしてからもう一度そのイメージをディスクユーティリティで開いて、「イメージのサイズを変更」を行い、2TB ほど容量を確保する。
スパースバンドルファイルに対して、既存のバックアップの dmg を復元する。死ぬほど時間がかる。
ディスクをNASに繋げ、共有フォルダを開き、イメージをマウントする。
イメージをマウントして、マウント済みディレクトリに対し、以下のコマンドを実行。GUI を使わないので強制的に設定できる。
sudo tmutil setdestination /Volumes/Time\ Machine
これでバックアップを再開するとネットワーク経由でバックアップが開始される。履歴も引き継がれ、差分バックアップになる。
追記:最近別のアダプタに変えました 500 Can't connect to lowreal.net:443 (certificate verify failed)
アダプタは 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 の実測電流値が気になったので安定化電源に繋いで適当に計ってみた。
アナログで読んだのでだいたい。USB Wi-Fi は指したまま起動。ほかのイーサネットポートやHDMIポートには何も指していない。
自分で接続した周辺機器をオン (AVR + RS232ドライバ + 気圧計 + I2C文字液晶) にすると、約650mA だけど、ほぼLEDだと思う。
スペック上、このモデルは 700mA 使うことになってるけど、意外と少なかった。CPU 全力 + HDMI + イーサネットポートとかだと 700mA になるのかな?
700mA (3.5W) で1年起動させっぱなしで、電気料金が高くとも 30円/kWh だとすると、(3.5 * 24 * 265) / 1000 * 25 ≒ 668円ぐらい。
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 以外の他の電源を回路に接続するとよくないかもしれないです。
ぜんぜんよくわからないんですが、こういう回路はハイサイドにしたほうが安全なのかな。応答速度はあまり必要ではないし……
AngularJS には ngResource という拡張があって、サーバに対する API 経由の CRUD 的操作を JavaScript のオブジェクトとしてラッピングできる。具体的には例えば
var Entry = $resource('/entry/:id');
var entry = Entry.get({ id : 0 }, function () {
entry.title = "yuno";
entry.$save(); // XHR (async)
}); とかできる。ちょっとかっこいいけど、既存APIで使おうとすると、些細なフォーマットの違いで案の定使えなかったりする。どうしても使ってみたいけど、サーバサイドAPIの仕様まで変えたくない場合、若干無理矢理な方法である程度なら対応させることができる。
前提として以下のような仕様だとする
エントリリスト取得
GET /api/entries
# Response
{
"ok" : true,
"has_more" : true,
"entries" : [ ... ]
} データの新規作成
POST /api/entries
Content-Type: application/x-www-form-urlencoded
title=xxx&body=yyy
# Response
{
"ok": true
"entry" : { ... }
} データの編集
PUT /api/entries?id=0
Content-Type: application/x-www-form-urlencoded
title=xxx&body=yyy
# Response
{
"ok": true
"entry" : { ... }
} いくつかハマりポイントがある
いろいろやってみると以下のようになった。
var Entry = $resource('/api/entries', { id : '@id' }, {
'query': {
method:'GET',
isArray: true,
transformResponse : function (data, headers) {
data = angular.fromJson(data);
if (!data.ok) throw "API failed";
Entry.hasMore = data.has_more;
return data.entries;
}
},
'save': {
method:'POST',
transformResponse : function (data, headers) {
data = angular.fromJson(data);
if (!data.ok) throw "API failed";
return data.entry;
},
transformRequest: function (data, headers) {
var ret = '';
for (var key in data) if (data.hasOwnProperty(key)) {
var val = data[key];
ret += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
}
return ret;
},
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
}
},
'update' : {
method: 'PUT',
transformResponse : function (data, headers) {
data = angular.fromJson(data);
if (!data.ok) throw "API failed";
return data.entry;
},
transformRequest: function (data, headers) {
var ret = '';
for (var key in data) if (data.hasOwnProperty(key)) {
var val = data[key];
ret += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
}
return ret;
},
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
}
}
}); これを使う場合、
var entries = Entry.query(function () {
$scope.hasMore = Entry.hasMore;
});
...
var entry = entries[0];
entry.title = "FooBar";
entry.$update(); みたいになる。だいぶアホっぽいし、この部分のコードがカオスになるけど、一応使えるようにはなる。
もっといい方法があったら教えてください……
そこそこ使いやすい感じなのを学習しながら書いてみた。
割込みを利用はしてますが、APIとしてはブロックする同期なものしか用意してません。
uint8_t data[7];
uint8_t ret;
i2c_set_bitrate(100);
// Set target slave address
i2c_master_init(0x60);
ret = i2c_master_write((uint8_t*)"\x04", 1);
if (ret) goto error;
ret = i2c_master_read(data, 8);
if (ret) goto error;
i2c_master_stop();
error:
i2c_master_stop(); スレーブの場合、特定のメモリ領域を登録すると、そこに対して read も write もできる、という感じの設計になっています。
// Slave memory map (must be smaller than 254 (0xfe) bytes) uint8_t data[9]; // Enter to slave receive mode with data and size. // After this operation, data will be changed automatically by TWI interrupt. i2c_slave_init(0x65, data, 10); // Access (set or get) to I2C data block data[0] = 0x10;
// Or more readable code with struct
struct {
uint8_t foo_flag1;
uint8_t foo_flag2;
uint16_t bar_value1;
uint16_t bar_value2;
uint16_t bar_value3;
uint16_t bar_value4;
} data;
i2c_slave_init(0x65, &data, 10); 割込みでいつのまにかデータが変化する感じなので、マルチバイトデータの読み書きではデータが化ける可能性があります。
マルチバイトデータの読み書きを行う場合、
が必要だと思います。通信中のデータはコピーして別で持っておけばいいんですが、もったいないし、そんなに問題にならなそうだしお手軽なのでこんな感じです。
I2C は仕様書の日本語訳で公開されている。本家?はi2c-bus.org かな。
ポイントとしては
マルチマスターまわりとゼネラルコール(マルチキャスト)まわりはいまいち理解できてない (今のところ必要性を感じてない)。
I2C 自体は任意長のバイトの送受信しか定義してない (言及はあるけど) ので「特定のアドレスのデータを読みだしたい」みたいな場合は、その上にプロトコルを乗せる必要がある。デファクトスタンダードっぽいのは
というもののようだ。ステートフルなので、読み出される側は読まれようとしているアドレスを記憶しておく必要があり、1バイト読まれるごとにインクリメントする必要がある。送信と受信は別々にアドレスを指定する必要があるので、この場合2度アドレス送信が行われる。
ポイントは
基本がわかればあとはそんなに難しくなく、試行錯誤したらできる感じだった。ただ、割込みの中で余計なことをすると、うまく次の割込みが入らなくなるということがあったりするので、動作を観測するのが難しい。LED チカらせてデバッグするしかないことがある。
Linux の I2C まわりを調べていて出てくる SMBus とかいうのはPCの電源管理とかで使われるプロトコルで、基礎プロトコルとしてI2Cを利用している。I2C レイヤーの上に SMBus というレイヤーがあるイメージ。
ソフトウェア開発って、他人が過去に作ったものに対する発見を重ねるみたいなところがあって、人工的に作られた過去の遺物を発見してうまく解釈するという、なんかよくSFモノにある、失なわれた技術を発掘していくみたいな、そういう感じ。
ソフトウェアは書いた通りにしか動かない。ソフトウェアのどこかの部品が壊れて(バグって)いたら必ず、なにかしら観測可能な状態に落としこめるような環境が整っている。
ハードウェア部分は、人間が工夫して設計しているとはいえ、基本は物理現象(電子の動き)を利用していて、それが自分には不思議で面白い。ソフトウェアレイヤーをいじっている限りでは物理現象を利用しているという感覚は一切なく「他人がどういう意図で設計したか?」という、物理というよりは精神なことを考えていることが多い。
ハードウェア、回路レベルの設計は全体的にパズル的で、動いているのが不思議な感じがする。ソフトウェアでこういうパズル的なことをやろうとしたらテストを書かないと殺されると思うけど、頑張ってだいたいうまく動いてる感じで感心すると同時に、こんなので大丈夫なのかと心配になる。
しかし実際には精度良くハードウェアは動くように設計されていて、そういう頑張ってる感じのパズル的に動くロジックの大量の組合せでCPUが構成されて、その上で機械語が実行されていて、機械語を生成するための言語があって、Cから機械語を生成するためのツールチェインがあって、それらを利用してさらに抽象化されたレイヤーで書けるLLがあって、なんだか考えていると、LLでコードを書くのはジェンガの上のほうでコードを書いている感じに思えてくる。
GPIO の操作はいろいろやる方法があるみたいだけど、LL からだと sysfs への IO を行うのが一番簡単っぽい。以下のような感じでかなり簡単に書ける。この程度だとライブラリを使う必要はない。
pin の数値は、RPi Low-level peripherals で書いてあるような GPIO n の n の部分を指定する。(物理的なピンの並びとは関係ない)
この例だと 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
無線のPCモールスUSBインターフェイスって結構高価なのが多いんだけど、Raspberry Pi だと GPIO 経由で普通にキーイングできるし、単体で HDMI やコンポジットビデオ出力できるから、これで十分すぎる感じがする。
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
して以下のように
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 を普通に繋ぐだけ)
なんとなく i2c-dev.h とかで定義されているAPIを呼ばないと使えないのかなあと思っていたけど、デバイスファイルの読み書きと ioctl だけで普通に使うことができた。
I2C 的には START -> SLAVE + W -> DATA送信 -> STOP が行われる。
I2C 的には START -> SLAVE + W -> DATA送信 -> ReSTART -> SLAVE + R -> DATA受信 -> STOP が行われる。
たぶん一度 write/read するたびに Repeated Start が送られるのかな。
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
RFC (rfc:1945) は1996年 (HTTP 1.0と同じRFCで説明されてる)
最初の HTTP。GET メソッドしかなかった。レスポンスヘッダもない (ボディーだけ)。
RFC (rfc:1945) は1996年
RFC (rfc:2068 → rfc2616) は1997年
HTTP 1.0 に Keep Alive とかを追加したやつ。
RFC (rfc:2817, rfc:2818) は2000年
HTTP 1.0 または 1.1 を TLS (暗号化層) にのっけたもの (暗号化通信)
(SSL は TLS の元になったプロトコルの名前)
RFC (rfc:6455) は2011年。HTTP を拡張したもの、と言っていいか微妙だけど、HTTP でセッションを初期化して 101 Switching Protocols で専用プロトコルに切り替わる関連技術的なもの。
Google が作ったプロトコル。現在は SPDY Protocol - Draft 3.1 が最新。
SPDY を元に提唱されてる新しい HTTP のバージョン。SPDY を HTTP 2.0 として標準化しよう、って感じかな。現状では SPDY と一緒。
Google が作ってるあたらしいプロトコル。上記までのような HTTP の互換レイヤーの話ではなく、その下のレイヤーのプロトコル。
技術の発展とともに TCP がネックになることが増えてきたので、UDP を使って TCP+TLS レイヤーを実装しなおすという感じ。
未来における WebSocket の立ち位置がよくわからない。HTTP 2.0 になったとして (SPDY 実装はすすんでいるし、たぶんなるだろう)、WebSocket はどうなるだろう。HTTP 2.0 上で WebSocket を使うのか、あるいは HTTP 2.0 のセッションを JS からコントロールできるような API が整備されるのか。
Raspberry Pi だ! ππだ!! Raspberry Pi は約5000円ぐらいで買うことができるカードサイズの Linux パソコンです。当然スペックとしてはしょぼいのですが、ホームサーバ用途とかには十分な感じです。
まぁそれだけでも安くて便利なボードなのですが、キモはさらに GPIO (General Purpose IO) がついているところです。基板にピンが立っていて、そのピンを Linux 側から制御できるので、マイコンの延長として使うことができます。普通、マイコンといえども性能が良かったり複雑なインターフェイスをつけようとすると、結構コストがかかるので、多少複雑なことをしたい場合、このような安い Linux コンピュータで比較的富豪的に諸問題を解決するのは個人レベルではかなりコストパフォーマンスが良い気がします。
これです。クリアケース付きです。本体の他に
が必要です。
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 だけ繋げて起動します。
reboot が終わったら、とりあえず pi/raspberry でログインしてみて
sudo shutdown -h now
して終了させます。
さっさと 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 はいらないので外します。
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 が入っていて、まぁそのまま頑張るほうがよさそうです。
デフォルトでは有効になっていないらしいので有効にします。
/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
確認して、関連ツールを入れておきます。
仕様を確認しておくと
となっています。ハードウェア部分はミスると普通に壊れるので、気をつけて作業する必要があります。
手元に、ワンチップで簡単そうなのは 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桁は信用できない値です。