ソフトウェア開発って、他人が過去に作ったものに対する発見を重ねるみたいなところがあって、人工的に作られた過去の遺物を発見してうまく解釈するという、なんかよく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 やコンポジットビデオ出力できるから、これで十分すぎる感じがする。

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

使いかた

マスターの場合

割込みを利用はしてますが、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) 用のライブラリ

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の仕様にあわせる

追記:最近別のアダプタに変えました [tech] RaspberyPi 用の WiFi アダプタ探し。または GW-USNANO2A が切れまくるのをなおす | Sat, Nov 15. 2014 - 氾濫原

BUFFALO 無線LAN子機 コンパクトモデル 11n技術・11g/b対応 WLI-UC-GNM -

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ぐらいまで短かくてもいいと思うけど、だいぶ改善してストレスが減ったのでとりあえずよしとする。

  1. トップ
  2. raspberrypi
  3. Raspberry Pi + Wi-Fi アダプタが異常に遅い問題を解決
  1. トップ
  2. tech
  3. Raspberry Pi + Wi-Fi アダプタが異常に遅い問題を解決

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円ぐらい。

  1. トップ
  2. raspberrypi
  3. Raspberry Pi の実測消費電力 (電流量)
  1. トップ
  2. tech
  3. Raspberry Pi の実測消費電力 (電流量)

NEC Aterm WR9500N[HPモデル] PA-WR9500N-HP -

5.0 / 5.0

評判の良いこれを買った。セットアップはあまり迷うところもなく終わった。

ただ、USB ディスク共有だけなんかおかしくて、smb://192.168.0.1/Hitachi-1 みたいな感じで、全部指定して接続をかけないと失敗する (Mac OS X)

  1. トップ
  2. tech
  3. 無線ルーターを変えた

初回はうまくいったけど、時間が経ってからもう一度やろうとしたらうまくいかなくなった。Mac の smb 接続が不安定になったりとかいろいろした結果、以下のような状態っぽいことがわかった (推測)

  • スパースバンドルのサイズが大きいとディレクトリエントリが多くなる
  • ルーターがメモリサイズ的にそのサイズのエントリを扱えなくなる
  • 死ぬ

なので、小さいスパースバンドルにするかなんとかしたらうまくいくかもしれないけど、もう諦めた…… 時間の無駄だった。

初回うまくいったのはよくわからない。Mac 側になんらかの形でエントリのキャッシュがあったのかもしれない。なんでもいいけど…

買ってから気付いたけど、USB ポートがついていて、USB HDD を繋いで SMB 共有ができるようになっていた。

せっかくなので、今まで有線USBでバックアップしていたのを、無線経由でバックアップするように変えた。

Time Machine をオフに

まず既存 Time Machine を OFF にする。

現状のバックアップディスクをバックアップ

HDDを新しく買ってくるならこんなことしなくていいんだけど、買いたくないので、一旦現状のバックアップディスクを、別にディスクに .dmg としてバックアップした。

バックアップディスクとなるスパースバンドルファイルをつくる

ディスクユーティリティでスパースバンドルファイルをつくる

  • サイズはとりあえずデフォルトのまま
    • 手元のディスク容量よりも大きいサイズには作れないため
  • フォーマットは Mac OS 拡張 (ジャーナリング)
  • イメージフォーマットはスパースバンドル・ディスクイメージ

作成したらアンマウントして、NASに繋げるディスクにコピーする。最初のバックアップまでは USB で直接繋いで作業する。

コピーしてからもう一度そのイメージをディスクユーティリティで開いて、「イメージのサイズを変更」を行い、2TB ほど容量を確保する。

バックアップをスパースバンドルイメージに復元

スパースバンドルファイルに対して、既存のバックアップの dmg を復元する。死ぬほど時間がかる。

NAS に繋げてバックアップ開始

ディスクをNASに繋げ、共有フォルダを開き、イメージをマウントする。

イメージをマウントして、マウント済みディレクトリに対し、以下のコマンドを実行。GUI を使わないので強制的に設定できる。

sudo tmutil setdestination /Volumes/Time\ Machine 

これでバックアップを再開するとネットワーク経由でバックアップが開始される。履歴も引き継がれ、差分バックアップになる。

NEC Aterm WR9500N[HPモデル] PA-WR9500N-HP -

5.0 / 5.0

  1. トップ
  2. tech
  3. NEC Aterm WR9500N で Time Capsule 的バックアップ (履歴引き継ぎ)

とりあえず、2種類のテストがあり、どちらも十分なサポートがされている。

基本的にangular-seedというのを元に作ればいいんだけど、e2e (end to end) テストについては protractor というのを使うのが新しいようなので、今からはじめるならそちらを使ったほうが良い。

karma での unit テスト

node で完結する、ロジックの単体テスト。主に controller とか filter をテストする。controller で DOM を直でいじっていると実行できない。

サーバサイドとかとの通信とかは全てモックにしなければならない。Angular の DI の仕組みで、モックオブジェクトを外部から注入して単体テストを完結させる。

いろいろ面倒くさいけど、これを書くようにすることで controller / directive の使いわけとかを意識せざるを得なくなるので良い気がする。

protractor による end to end テスト

selenium を使った結合テスト。

protractor は Angular JS 用の e2e テストライブラリ。簡単に selenium-standalone をセットアップするところから、テスト用のユーティリティまでのセット。ドキュメント の通りにやれば OS X では全く苦もなく selenium 環境を作りテストを開始できる。

どこが「Angular JS用」なのかというと、ページロードとか、イベント発火とかで、いちいち自分で wait() を書く必要がなく、Angular 準拠の部分は自動で処理待ちをするので、かなり楽をできる。

karma か protractor か

  • karma のテストは早い
  • protractor (selenium) は遅い

ので、パターンを網羅したロジックは書きたいなら karma で完結するように書いたほうがいい。

  1. トップ
  2. angularjs
  3. AngularJS のテスト
  1. トップ
  2. tech
  3. AngularJS のテスト

protractor (webdriver) を使った場合、外から executeAsyncScript を使うと文字列でページ側で実行できる。

けど、文字列で渡すとか、シンタックスチェックもかからないし、ありえないので、定義自体は普通に書きたい。ので以下のような関数を定義する。

var PageObject  = function () {
	this.exec = function (func) {
		var args = Array.prototype.slice.call(arguments, 0);
		args[0] = '('+ (func.toString()) + ').apply(null, arguments);';
		browser.executeAsyncScript.apply(browser, args);
	};

	this.createEntry = function (data) {
		this.exec(function (data, callback) {
			angular.injector(['myApp']).invoke(function (Entry) {
				var entry = new Entry();
				for (var key in data) if (data.hasOwnProperty(key)) entry[key] = data[key];
				entry.$save(callback);
			});
		}, data);

		return data;
	};
};

この例では、定義した exec 関数を使って、ページ側の ngResource で定義したクラスを使い、テスト用のデータを生成する createEntry メソッドを定義している。

  1. トップ
  2. angularjs
  3. AngularJS のテストでページ側のスクリプトを実行する
  1. トップ
  2. tech
  3. AngularJS のテストでページ側のスクリプトを実行する

  • 1つのページに複数のコントローラーを定義し、それぞれを連携させる方法がわからない
    • scope はどうなる?
  • スコープまわりがよくわかってない
    • $on、$apply の伝搬がよくわかってない
  • DI まわりがよくわかってない
    • 何を service にして何を provider にするのかとか
  1. トップ
  2. angularjs
  3. AngularJS でまだわからないこと
  1. トップ
  2. tech
  3. AngularJS でまだわからないこと

12月10日に受験して、12月24日の合格発表と同時に申請を出し、免許日は1月9日で、それから2日後には不在票 (書留にしたので) が入ってた。思ったより早かった。営業日的には7日ぐらいかな。

今まで持っていたやつはラミネート加工されたやつだったけど、新しい形式になっていわゆるカードサイズになったのと、英語表記が併記されるようになった (電波の性質上、国際的に使われうる免許だからかな) のと、ホログラム (富士山と桜?) っぽいものが全面に入るようになったみたい。

  1. トップ
  2. tech
  3. 1アマ (第1級アマチュア無線技士) 免許がきた
  1. トップ
  2. ham
  3. 1アマ (第1級アマチュア無線技士) 免許がきた

Perl には Test::TCP というのがあって、テスト中、空いてるポートで何かしらのサーバーを起動して使うということができます。Ruby においては Glint というライブラリがあって、同じことができます。

node.js の場合、node-test-tcp というのがあって、node の net.Server で動くサーバに関しては簡単に同じことができます。が、memcached とか外部プロセスを起動させようとするとちょっと困るのと、done() を呼ばないと終了しないので、何かいい方法はないかなと思ったので書いてみました。

test-tcp だとカブるので glint のほうの名前を仮りています。

つかいかた

glint(
	function (port) {
		// ここは外部プロセスで実行される
		// ただし文字列化して関数が渡されるので外のスコープの変数は使えない。
		// node.js の exec, execFile はいわゆる exec ではないので1つプロセスが余分にできる。我慢するしかない。
		console.log('starting memcached with port: ' + port);
		require('child_process').execFile('memcached', ['-p', port]);
	},

	function (error, server) {
		if (error) throw error;

		// server は起動が確認済み
		// server.port でポート番号がとれる。
		// server.kill() でこのプロセスだけ殺せる

		console.log(server);
		var s = net.connect(server.port, function () {
			s.write("version\r\n");
		});
		s.on('data', function (data) {
			console.log(data.toString('UTF-8'));
			s.end();
		});

		// この関数を抜けても exit されるまで server は (明示的にkillしない限り) kill されない
	}
);

しくみ

他のモジュールとやってることは一緒なのですが、node.js だと面倒な点がいくつかあります

  • fork / exec はいわゆる *nix の fork/exec ではない
  • オブジェクトファイナライザ的な仕組みがない
    • 特定のオブジェクトが破棄されるタイミングで処理を行うことができない

node の fork / exec (execFile) は *nix の fork / exec とは全く違うので、細かいプロセスの制御ができません。特に exec 単体相当はないので、現在のプロセス自体を置き換えるということができません。そんなわけで1つ別プロセスを経由している関係で、無駄に1プロセスを消費しています。

オブジェクトのファイナライザがないので、node-glint では process.on('exit') で起動したサーバを終了しています。ただし、普通に外部プロセスを起動すると、そのプロセスの終了を待つ挙動になり、exit されません。が、spawn() のオプションに detach: true を指定した上で、unref() を呼んであげることで、外部プロセスの終了に関わらず起動元の node プロセスを exit させることができます。

まとめ

一応形にはなりましたが、細かい挙動の検証がめんどうなので npm にあげてません。特に現在の実装はシグナルまわりのハンドリングがいい加減です。node マスターの皆様におかれましては、よりよい方法をご教示頂ければと思う所存です。

  1. トップ
  2. tech
  3. node.js で Perl における Test::TCP または Ruby における Glint

特筆するほど難しいことはないけど書いておく。基本的に 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 だとどこの環境でも簡単に復元可能なので、どっちもどっちかなという感じ。

  1. トップ
  2. tech
  3. Raspberry Pi の SD カードを Mac でバックアップ
  1. トップ
  2. raspberrypi
  3. Raspberry Pi の SD カードを Mac でバックアップ

いろいろでてきてる。

Raspberry Pi

Type AType B があり、Type A はイーサネットポートと組込みUSBハブがなくメモリが少ない分少しだけ安い。

BeagleBoard Black

本体は Raspberry Pi と同じぐらいの価格だけど、

  • CPU がより高速
  • オンボードなフラッシュメモリがある (SDカードもつけれる)
  • 消費電力がより低い
  • GPIO が多い

という特徴がある。内蔵フラッシュメモリがあるので、(それにおさまるなら) SDカードがいらない分、Raspberry Pi よりコスト面でも有利。一方で欠点は

  • 入手性
    • Raspbery Pi はかなり安定して手に入る感じだけど、BeagleBoard Black はそうでもない感じ
  • 情報源
    • やはりちょっと少ない

Intel Galileo

価格的に上であげたものの1.2〜1.5倍ぐらいする。現時点ではまだ日本未発売。1月中旬らしいのでそろそろかな。2A のAC アダプタとケーブルが一部付属っぽいので、(それらが必要な人なら) そんなに割高とはいえないかもしれない。

Linux が動いてるけど、Arduino 互換を謳ってる。ピン互換があって Arduino 用にでまわっているシールドがつかえるのと、開発環境も Arduino のものが使える。

気になるのは

  • グラフィック出力がない (HDMI とか)
  • 消費電力 (詳しいスペックがわからないけど、2A のアダプタが付属ってことは結構あるのかな)

Arduino

Arduino は AVR というマイコンを使って動いているので、Linux は載っていない。本当にローレベルなことしかできないし、CPU クロックも20MHz程度。2000円〜3000円ぐらいで買えるみたいだけど、できることと、実装方法の苦楽の差を考えると Raspberry Pi や BeagleBoard Black のほうが圧倒的にコストパフォーマンスは高い。

個人の見解のまとめ

最初に手をとるなら Raspberry Pi がよさそう。もっと要求がでてきたら BeagleBoard Black がよさそう。Intel Galileo はなんともいえない。

さらに別の回路を複雑に繋げたり、役割分担をさせたくなったら、Arduino 的なものを導入してもよさそう。

しかし AVR 使うなら、普通にチップ買えば1個あたり50円〜300円程度 (チップによる) なので、個人的にはそのほうがいいと思う。ただ、Arduino はオールインワンな感じの開発環境がキモなので、それが欲しいなら Arduino はいいかもしれない。(自分で互換のブートローダーを書きこんでもいいけど)

  1. トップ
  2. tech
  3. Raspberry Pi / BeagleBoard Black / Intel Galileo / Arduino の違いのメモ

続きをかきました。

[tech] Angular JS で View を伴う Service 的なことをしたいとき、あるいは Directive に Controller をつけたいとき。 | Tue, Jan 21. 2014 - 氾濫原

よくわからなかったので以下のようにしましたが、まっとうな方法には思えないので正しいやりかたをおしえていただきたく申し上げます……

  • factory で空のオブジェクトをかえしとく
  • view に紐付けた controller で factory で作ったオブジェクトを初期化する
  • 作った Service を依存に加えて使う

一応、controller 間は直接依存しない (Service を mock にさしかえれば動く) のでユニットテストは書けると思うけど、よくわかりません……

myApp.factory('DialogService', function () { return { } });
myApp.controller('DialogControl', function ($scope, $q, DigalogService) {
	DialogService.open = function (title, message) {
		$scope.deferred = $q.defer();
		$scope.show = true;
		return $scope.deferred.promise;
	};

	$scope.close = function () {
		$scope.show = false;
		$scope.deferred = null;
	};

	$scope.ok = function () {
		$scope.deferred.resolve(true);
	};
});


// User
myApp.controller('FooController', function ($scope, DialogService) {
	DialogService.open('foo', 'bar').then(function () {
		...
	});
});

そもそも "View independent business logic: Services" と書いてあるので、定義からして Service はビューを持ってはいけないようです。

じゃあビューを伴う Service 的なものはどうやって書くんですか

  1. トップ
  2. tech
  3. Angular JS でビューを伴う Service ってどう書けばいいんですか

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 変換をする。

  1. トップ
  2. avr
  3. Raspberry Pi を中心にやってみようとしているところ
  1. トップ
  2. raspberrypi
  3. Raspberry Pi を中心にやってみようとしているところ
  1. トップ
  2. ham
  3. Raspberry Pi を中心にやってみようとしているところ

クソコード を書いたはいいが、釈然としなかった。 "View independent business logic: Services" とドキュメントに書いてある通りなので、やはり他のやりかたのほうがよい。のでよくよく読んだところ、やはり View にかかわる部分は Directive に集約されるようだ…… しかし使いかたが非常に難しい。

iimsApp.directives.directive('dialog', function ($q) {
    return {
        restrict: 'E',
        transclude : true,
        scope : {
            Dialog : '=name'
        },
        controller : function ($scope) {

            $scope.Dialog = {
                open : function (title) {
                    $scope.deferred = $q.defer();
                    $scope.title = title;
                    $scope.show  = true;
                    return $scope.deferred.promise;
                }
            };

            $scope.close = function () {
                $scope.show = false;
            };

            $scope.ok = function () {
                $scope.close();
                $scope.deferred.resolve();
            };

            $scope.cancel = function () {
                $scope.close();
                $scope.deferred.reject();
            };
        },
        templateUrl : '/static/js/app/dialog.html'
    };
});

こんな感じで dialog という directive を定義して、

<div ng-controller="FooCtrl">
    <dialog name="FooBarDialog">
        {{ message }}
    </dialog>
</div>

というふうにすると、FooCtrl のスコープから FooBarDialog というプロパティで見えるようになる。つまり

        $scope.message = "Hello!";
        $scope.FooBarDialog.open('OKK!!!!!').then(function () {
            alert('ok');
        }, function () {
            alert('cancel');
        });

みたいなあ

難しポイント

  • restrict: 'E'
    • この directive は要素として使うよってこと
  • directive の scope の定義がむずかしい
    • プロパティ名には自分のスコープで見える名前、値には謎の書式で親スコープから見える名前が書いてある属性名を書く
  • controller に渡される $scope
    • 親スコープなどからは分離されているスコープが与えられる。directive が何度も出現することもあるからね!
  • transclude: true の意味。これは dialog 要素内に内包される変数が、外側のスコープをさすようにするもの。例では $scope.message を外側のスコープで代入している

こういう謎の構造体を返すのってわっかりにくいので気持ち悪いなあて思いました。全部メソッドになっているべきなのでは????

directive のプロパティの説明がどこにあるかわかりにくいけど、 http://docs.angularjs.org/api/ng.$compile#description_comprehensive-directive-api_directive-definition-object にある

  1. トップ
  2. tech
  3. Angular JS で View を伴う Service 的なことをしたいとき、あるいは Directive に Controller をつけたいとき。

RS-232 TTL 変換をフォトカプラでやる。

グラウンドを分離したいと、ノイズ相互の流入を防ぎたいのでRS-232変換を専用ドライバーICではなく、フォトカプラで行うようにした。無線機側とグラウンドを共通にするとインターフェアがでやすいらしい。

面倒な点は、RS-232 側の両電源を別に用意しなくてはならないところだけど、ググってみるとRTSにきてる電圧と RX の電圧をうまいこと利用している例があったので、それに従うことにした。一応RTS CTSをショートさせて電圧を計ってできそうなことを確かめた。

回路

これのコピーです

RS-232 が正負電圧で論理表現をするのでそこが面倒なところ、正電圧はRTSできてるのでいいけど、負電源をとれるピンはないので、TXのアイドル (負電圧がかかってる) を使ってる。47uFの電解コンデンサは電源を安定させるためについてる。

TX のアイドル時にコンデンサに電荷をためといて、RX が正じゃないときはダイオードを通じて RX のレベルを負に保つようになっている。コンデンサの前に 680Ω の抵抗がついているのは正のときにおかしくならないようにかな……

あとは電圧をクリップするダイオードがついていたり、フォトカプラのスペックにあわせるための抵抗がついてたり。

RS-232とTTLは論理が逆だけど、フォトカプラがオープンコレクタなので自動で反転して丁度いい。

手元で試した限りだと、ボーレート 19200 でもうまくうごいた。

挙動を確認する

その後オシロスコープを買ったので再度挙動を確認してみた。

これは Linux → Rig 方向のもの。この方向の負電圧は 47uF に蓄えられているものが使われる (と思う) ので、連続して送信を行うと、だんだん電圧レベルがさがってしまう。最後のほうは 3V 未満になってしまっているので、RS-232 レベル的には動かない。

200uF にすると、だいぶマシになる。適切なのを選択する必要がありそう。

ハマったところ

ハマったのは、USB シリアル変換ケーブルと、無線機のRX TXの配線が逆な点だった。これは普通にドライバーIC使ったときもハマったので比較的すぐ抜けれたけど、毎回ハマる…

あとはTTLレベルのRXをRS232ドライバICに面倒くさがって繋がったままやってたら動かなかったのもハマった (電源が入ったままだったので GND に落ちていたっぽい)。変なことしないほうがよい

あと、もっと簡略化した回路(TXとRXを繋ぐ部分、負電源部分を削除したもの)もググるとでてくるけど、それだと今回はレベルが安定しなくて、エコーする現象が起きてだめだった。

フォトカプラは FOD817B300 というのを使った。応答速度の関係でものによっては動かないとかあるみたいだけど手元にあったのでうまく動いた。

参考文献

  1. トップ
  2. tech
  3. 絶縁型 RS-232 ⇔ TTL レベル変換
  1. トップ
  2. ham
  3. 絶縁型 RS-232 ⇔ TTL レベル変換

AI の挙動不審

AI (Auto Information) でデータが自動でくるようになるけど、その状態でこちらから読み出しコマンドを出したり、書き込みコマンドを出したりすると、それらのコマンドがうまく読みとってくれなかったり、あるいは出力が混ざったりする。

解決方法でいまいち良いのがない

考えた解決案

  • AI はするが、コマンドは何度か送りつける
  • 一旦 AI を止めて、コマンドを送りつけて AI を再開する
  • AI を一切やめて必要な情報だけをポーリングする
AI はするが、コマンドは何度か送りつける
  • 3回程度送りつけても結局失敗することがある

コマンドが成功していることが確認できるまで送り続ける方法もあるが、コマンドごとに確認方法が違うのでコーディングが大変

一旦 AI を止めて、コマンドを送りつけて AI を再開する
  • まず、AI を止めるためのコマンドが認識されないことがある
  • AI を止めてもすぐに止まるわけではないっぽい
AI を一切やめて必要な情報だけをポーリングする

確実に動くけど

  • 情報が反映されるのが非常に遅くなる

例えば読み出しコマンドを発行すると、250ms ぐらいでようやくレスポンスがあるので (何やってんだ?) 、4個値を読み出すだけで1秒かかってしまう。

AI はそのまま

AI はそのままにするのがやはりよさそう。コマンドを送って確実に操作するよりも情報を早く受けとるほうが重要だと思う。

とはいえ、コマンドを送ったのに反映されないのもイラつくので、かなり執拗に送信確認をする必要がある。

  • 書きこみするコマンドを3回送りつける
  • 読みだしする

として、返答があるのを250msぐらい待って、セットできていなかったらリトライする。

  1. トップ
  2. tech
  3. FT-450D の CAT の挙動
  1. トップ
  2. ham
  3. FT-450D の CAT の挙動

机の横に棚が欲しかったけど、ちょうどいいのがないので作った。

机の横に防湿庫を置いていて、その上のスペースがあいている感じだったので、

  • 机の高さにあわせるため、防湿庫に1段作る
  • その上に載るように棚をつくる

という感じでやった。どちらも 1x8材 で作った。

高さをあわせる用のやつは 1x8 6 feet のものを自分でノコギリで切って作った。だいぶヘタクソな感じだけど、寸法的にはちょうどのものが作れた。事前に設計図的なのを書かず、場当たり的に作ってしまった

その上にのっける棚は 1x8 12feet の板から全部の板をとったけど、こちらはホームセンターでカットしてもらった。一旦 SketchUp でモデリングしてから、必要なサイズを事前に計算したので比較的スムーズに発注・組み立てができた。

組み立てはコーススレッドをぶちコムだけなので、と思っていたけどトルク強すぎて板が割れたり結構大変だった。サイズをぴったりにはならないので鉋か荒いヤスリが必要だなーとか、店頭で良く見てから買わないとかなり反った材料を買ってしまうなーとか、気付きはいろいろあった。

あとは 120 -> 240 でやすりをかけて、ニスをひたすら塗るだけだけど、どう考えてもこの一連の作業が一番辛い。ただ手を抜くと悲しい感じではあるのでわりと頑張った。

一連の作業を行うと翌日激しい筋肉痛になる。金で解決できるなら金で解決するほうが安いというこのご時世ではありますが、たまに DIY をやると、いろいろ気付くことや思うことがあるし、物理的に形あるものを作るのも、ヘタクソではありつつも愛着が沸いておもしろいものであります。




  1. トップ
  2. ham
  3. 週末 DIY

ディスクユーティリティを使って FAT でフォーマッットをすると、フォーマットしようとしているディスクが2G未満だと自動的に FAT16 になってしまうようだ。

世の中にはFAT32だけをサポートするという変わったシステムもあるので、そのような環境において 2GB 未満のメディアを使おうとすると、これは困る。しかし以下のように diskutil コマンドからならば普通にフォーマットをかけられる。

$ diskutil list
# /dev/disk2 とか、フォーマットしたいディスクをよくよく確認する
$ diskutil partitionDisk /dev/{{disk}} 1 MBRFormat "MS-DOS FAT32" SD 100%
  1. トップ
  2. tech
  3. Mac で強制的に FAT32 でフォーマットする

超薄型 1Gsサンプリング100MHzFFT機能付カラーポータブルデジタルオシロスコープフルセット SDS7102 -

5.0 / 5.0

OWON SDS7102 というのを買った。OWON は中国の測定器メーカーで、かなり安いオシロスコープを出しているけど、ググったりしてみると割と評判が良く、英語圏でも使ってみたりハックしたりしている記事がヒットする。

オシロスコープの選定基準は主に

  • 帯域
  • サンプリングレート
  • チャンネル数

があるけど、買ったモデルは、帯域 100MHz、サンプリングレート1G samples/sec、2ch のもので、中堅クラスぐらいのもの。帯域が100MHz でも、矩形波にすると25MHz程度までしか観測できないので、マイコンプログラミング程度でもこれぐらいのスペックが必要になり、なかなか厳しいものがある。デジタル回路を見る場合2chは必要…

これ以上のスペックものは正直高すぎて手が出ない。購入価格は正規代理店経由のもので 52000円ぐらい。円安になりつつあって一時期より値段があがっていたけど、消費税もあがると買うことにした。

所感

今までオシロスコープを所持したことがないので、他のオシロスコープとの比較はできない。

届いてあけた感想としては「画面がでかい!」「奥行がない!」という感じだった。ちょうどいい大きさ。

使いこなせるかな、思っていたけど、予想よりは全然操作が簡単で、すぐトリガをかけて単発現象を観測したりはできるようになった。

見えないものが見えるようになるというのはすごくテンションがあがる。可能性が一気に広がったように感じる。

よくないところ

現時点では完全に使いこなせているとは言えないので、あまり思いつかないけど、

  • USB host 側として、FAT32 しかファイルシステムをサポートしてない
  • USB slave 側として、Mac につなげるとなぜか Mac がフリーズする
  • 電源ケーブルが硬く、ソケットが左向きについているので、結構邪魔
  • 電源ケーブルの接続がなんか心もとない

というのがあった。LAN ポートもついていて、こっちの連携はすぐにうまくいった。

  1. トップ
  2. tech
  3. オシロスコープを買った
  1. トップ
  2. ham
  3. オシロスコープを買った

普通にルーターと接続してアドレスとかを設定すると、設定したポートで listen するようになってるのでコマンドを送るだけ。

なので自分で Ruby で実装を書いてみた。bmp を得られるけど、png で欲しいので ImageMagick で変換までしている。

プロトコルフォーマットはググると出てくる。ただ、bin なフォーマットは仕様書とだいぶ違っていて、結構めんどうくさい。

というのがあって、解析したコードがあったりする。けど、とりあえず必要ないし、そこまで実装してない。

  1. トップ
  2. tech
  3. SDS7102 のスクリーンショットを LAN からとる
  1. トップ
  2. ham
  3. SDS7102 のスクリーンショットを LAN からとる