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 以外の他の電源を回路に接続するとよくないかもしれないです。

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

  1. トップ
  2. raspberrypi
  3. Raspberry Pi のローレベル周辺機器の電源を特定プロセスが動いているときだけオンにする
  1. トップ
  2. tech
  3. 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" : { ... }
}

ngResource での対応

いくつかハマりポイントがある

  • AngularJS は POST 時のデフォルト Content-Type が application/json
  • ngResource は直接配列のJSONが返ってくることを前提にしている
    • そして付属するデータをうまく返す方法がない

いろいろやってみると以下のようになった。

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'
		}
	}
});
  • 自力で transformResponse, transformRequest で ngResource が要求しているフォーマットに変更してやる
  • リストに付随するデータはスタティックに持たせてしまう (リクエスト直後に読み出すことを想定)
  • 自力で application/x-www-form-urlencoded なリクエストを作ってやる
  • 冗長に書いてるけど PUT と POST はメソッドが違うだけ

これを使う場合、

var entries = Entry.query(function () {
    $scope.hasMore = Entry.hasMore;
});

...

var entry = entries[0];

entry.title = "FooBar";
entry.$update();

みたいになる。だいぶアホっぽいし、この部分のコードがカオスになるけど、一応使えるようにはなる。

もっといい方法があったら教えてください……

  1. トップ
  2. angularjs
  3. AngularJS の ngResource を既存APIの仕様にあわせる
  1. トップ
  2. tech
  3. AngularJS の ngResource を既存APIの仕様にあわせる

そこそこ使いやすい感じなのを学習しながら書いてみた。

使いかた

マスターの場合

割込みを利用はしてますが、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_state を見て、I2C_STATE_IDLE であることを確認してから
  • 読む場合
    • i2c_state を見て、I2C_STATE_IDLE であることを確認してから
    • cli してから

が必要だと思います。通信中のデータはコピーして別で持っておけばいいんですが、もったいないし、そんなに問題にならなそうだしお手軽なのでこんな感じです。

実装にあたって

I2C は仕様書の日本語訳で公開されている。本家?はi2c-bus.org かな。

ポイントとしては

  • 送信・受信の切替えにあたっては必ず再度アドレスの送信が必要
  • Repeated START というのは単に STOP + START 相当のことを START だけでできるようにしているに過ぎない (バスを連続して占有し続けられるというだけ)
  • NACK は ACK を返さない状態のことを言ってる
    • エラーと区別はない
    • 転送終了とか、受信終了とかの意味付けされてるけど、応答がないのでもう何もしない、って感じ
  • START コンディション、STOP コンディション、クロックは常にマスターが生成する

マルチマスターまわりとゼネラルコール(マルチキャスト)まわりはいまいち理解できてない (今のところ必要性を感じてない)。

I2C 上のプロトコル

I2C 自体は任意長のバイトの送受信しか定義してない (言及はあるけど) ので「特定のアドレスのデータを読みだしたい」みたいな場合は、その上にプロトコルを乗せる必要がある。デファクトスタンダードっぽいのは

  1. アドレスを1バイト送信する
  2. Repeated START (たぶん STOP + START でも同じだけど…)
  3. データをnバイト受信する

というもののようだ。ステートフルなので、読み出される側は読まれようとしているアドレスを記憶しておく必要があり、1バイト読まれるごとにインクリメントする必要がある。送信と受信は別々にアドレスを指定する必要があるので、この場合2度アドレス送信が行われる。

AVR での実装

ポイントは

  • 割込みがかかったら何か必要なことをして TWINT を 1 にする、というのをくりかえす
    • TWINT をクリアしないと無限に割込みが入り続けるし、SCL がローのままになるのでバスが解放されず一切I2Cできなくなる
    • すべきこと (何をしたら次どういう状態になるか) はデータシートにモードごとに書いてある
  • データシートにある "TWINT Flag is set" な状態というは TWINT が 0 の状態のことで、TWINT Flag を clear するというのは TWINT に 1 を書くということらしい。論理逆なの?
  • TWINT Flag が set されている間 SCL はローになる
    • ソフトウェア処理に時間がかかった場合自動でクロックストレッチングされる
  • MR, MT, SR, ST ({Master,Slave} {Receiver,Transmitter}) の定義を先に読んどかないと意味不明

基本がわかればあとはそんなに難しくなく、試行錯誤したらできる感じだった。ただ、割込みの中で余計なことをすると、うまく次の割込みが入らなくなるということがあったりするので、動作を観測するのが難しい。LED チカらせてデバッグするしかないことがある。

備考

Linux の I2C まわりを調べていて出てくる SMBus とかいうのはPCの電源管理とかで使われるプロトコルで、基礎プロトコルとしてI2Cを利用している。I2C レイヤーの上に SMBus というレイヤーがあるイメージ。

  1. トップ
  2. tech
  3. AVR TWI (I2C) 用のライブラリ
  1. トップ
  2. avr
  3. AVR TWI (I2C) 用のライブラリ
  1. トップ
  2. arduino
  3. AVR TWI (I2C) 用のライブラリ

ソフトウェア開発って、他人が過去に作ったものに対する発見を重ねるみたいなところがあって、人工的に作られた過去の遺物を発見してうまく解釈するという、なんかよくSFモノにある、失なわれた技術を発掘していくみたいな、そういう感じ。

ソフトウェアは書いた通りにしか動かない。ソフトウェアのどこかの部品が壊れて(バグって)いたら必ず、なにかしら観測可能な状態に落としこめるような環境が整っている。

ハードウェア部分は、人間が工夫して設計しているとはいえ、基本は物理現象(電子の動き)を利用していて、それが自分には不思議で面白い。ソフトウェアレイヤーをいじっている限りでは物理現象を利用しているという感覚は一切なく「他人がどういう意図で設計したか?」という、物理というよりは精神なことを考えていることが多い。

ハードウェア、回路レベルの設計は全体的にパズル的で、動いているのが不思議な感じがする。ソフトウェアでこういうパズル的なことをやろうとしたらテストを書かないと殺されると思うけど、頑張ってだいたいうまく動いてる感じで感心すると同時に、こんなので大丈夫なのかと心配になる。

しかし実際には精度良くハードウェアは動くように設計されていて、そういう頑張ってる感じのパズル的に動くロジックの大量の組合せでCPUが構成されて、その上で機械語が実行されていて、機械語を生成するための言語があって、Cから機械語を生成するためのツールチェインがあって、それらを利用してさらに抽象化されたレイヤーで書けるLLがあって、なんだか考えていると、LLでコードを書くのはジェンガの上のほうでコードを書いている感じに思えてくる。

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
  1. トップ
  2. tech
  3. Ruby で sysfs 経由での GPIO 操作
  1. トップ
  2. raspberrypi
  3. Ruby で sysfs 経由での GPIO 操作

無線の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
  • 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 を普通に繋ぐだけ)

  1. トップ
  2. tech
  3. Raspberry Pi の GPIO でシリアル通信
  1. トップ
  2. raspberrypi
  3. Raspberry Pi の GPIO でシリアル通信

なんとなく 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

  1. トップ
  2. tech
  3. Linux 上の LL で I2C する (Ruby)
  1. トップ
  2. i2c
  3. Linux 上の LL で I2C する (Ruby)
  1. トップ
  2. raspberrypi
  3. Linux 上の LL で I2C する (Ruby)
  1. トップ
  2. ruby
  3. Linux 上の LL で I2C する (Ruby)

HTTP 0.9

RFC (rfc:1945) は1996年 (HTTP 1.0と同じRFCで説明されてる)

最初の HTTP。GET メソッドしかなかった。レスポンスヘッダもない (ボディーだけ)。

HTTP 1.0

RFC (rfc:1945) は1996年

  • メソッドに GET POST HEAD が追加された
  • ヘッダが追加された

HTTP 1.1

RFC (rfc:2068 → rfc2616) は1997年

HTTP 1.0 に Keep Alive とかを追加したやつ。

  • バーチャルホストに対応 (Host ヘッダが必須に)
  • Keep Alive が可能
    • 1つのTCPセッションで複数のファイルのやりとりができる
  • リクエストパイプラインが可能
    • リクエストを複数個レスポンスを待たずに送ることができる
    • サーバ側で対応が必要なのであまり有効活用されてない

HTTPS

RFC (rfc:2817, rfc:2818) は2000年

HTTP 1.0 または 1.1 を TLS (暗号化層) にのっけたもの (暗号化通信)

(SSL は TLS の元になったプロトコルの名前)

WebSocket

RFC (rfc:6455) は2011年。HTTP を拡張したもの、と言っていいか微妙だけど、HTTP でセッションを初期化して 101 Switching Protocols で専用プロトコルに切り替わる関連技術的なもの。

  • ブラウザでソケットのようにサーバと自由に送受信ができるようにするのが目的
  • サーバプッシュが可能
  • 普通 JS と一緒に使う

SPDY

Google が作ったプロトコル。現在は SPDY Protocol - Draft 3.1 が最新。

  • レンテンシを低くするのが目的
  • HTTPS のように TLS (暗号化層) にのっかってるので、常に暗号化通信
  • HTTP は SPDY プロトコル上にのっかる感じに
  • サーバープッシュが可能
  • サーバーヒント
    • 予め必要そうなファイルをサーバから送りつけることができる

HTTP 2.0

SPDY を元に提唱されてる新しい HTTP のバージョン。SPDY を HTTP 2.0 として標準化しよう、って感じかな。現状では SPDY と一緒。

QUIC

Google が作ってるあたらしいプロトコル。上記までのような HTTP の互換レイヤーの話ではなく、その下のレイヤーのプロトコル。

  • レンテンシを低くするのが目的
  • TCP の代替

技術の発展とともに TCP がネックになることが増えてきたので、UDP を使って TCP+TLS レイヤーを実装しなおすという感じ。

わからないところ

未来における WebSocket の立ち位置がよくわからない。HTTP 2.0 になったとして (SPDY 実装はすすんでいるし、たぶんなるだろう)、WebSocket はどうなるだろう。HTTP 2.0 上で WebSocket を使うのか、あるいは HTTP 2.0 のセッションを JS からコントロールできるような API が整備されるのか。

  1. トップ
  2. tech
  3. SPDY とか新しいプロトコルがよくわからないので簡単にまとめた
  1. トップ
  2. http
  3. SPDY とか新しいプロトコルがよくわからないので簡単にまとめた

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桁は信用できない値です。

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

25日発表ってアナウンスされてたと思うんだけど、なんとなく見にいったら24日に発表されてた。合格日は速報の日と同じらしいので、さっさと書いて出してきた。

25日にハガキが届いた。はやい。

  1. トップ
  2. ham
  3. 1アマ合格

というのを作ってみた。

AVR に限らないけど、マイコンで時間を測るにはCPUクロックを数えるわけですが、欲しい時間に対して分周比とかを求めるのが面倒なのでかいた。

F_CPU は CPU クロック数、Seek Freq. のほうに欲しい周波数または時間間隔を入れて、Calculate を押すと、各分周比において CTC でいくつを設定すればいいか、あるいはオーバーフローでいけるかどうかとかを出す。

16MHz で 1msec を測りたい場合、

Pre-scaler:1, 16bit Timer CTC:16000
Pre-scaler:8, 16bit Timer CTC:2000
Pre-scaler:64, 8bit Timer CTC:250
Pre-scaler:256, no result for 62.5
Pre-scaler:1024, no result for 15.625

このようにでる。この場合は 8bit タイマーで 64 分周して CTC を 250 に設定したらよい。

  1. トップ
  2. tech
  3. AVR のタイマー計算機
  1. トップ
  2. avr
  3. AVR のタイマー計算機
  1. トップ
  2. arduino
  3. AVR のタイマー計算機

買ったSDカードあるいは持っているSDカードの速度がどんなものか測りたいとき、Mac だといまいち良いツールがなかったけど、いつのまにか良さそうなのがでてた。

ただ、SDカードの領域を埋めつくすまで書きこもうとするのでかなり時間がかかるのがイマイチな感じ。意図としてはカード全体がちゃんと動いてるかどうかをチェックという感じだと思う。実際、途中から速度が大幅に変わったりする。

以下のスクリーンショットは手元にあった microSD カード (カード上の表記は Class10) を測定中のもの。最初は 6MB/s だったけど、途中から 11MB/s ぐらいになった。Class10 は綺麗な状態での書き込み10MB/sを保証するので、仕様には適合している。