ソフトウェア開発って、他人が過去に作ったものに対する発見を重ねるみたいなところがあって、人工的に作られた過去の遺物を発見してうまく解釈するという、なんかよくSFモノにある、失なわれた技術を発掘していくみたいな、そういう感じ。
ソフトウェアは書いた通りにしか動かない。ソフトウェアのどこかの部品が壊れて(バグって)いたら必ず、なにかしら観測可能な状態に落としこめるような環境が整っている。
ソフトウェア開発って、他人が過去に作ったものに対する発見を重ねるみたいなところがあって、人工的に作られた過去の遺物を発見してうまく解釈するという、なんかよく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 やコンポジットビデオ出力できるから、これで十分すぎる感じがする。
そこそこ使いやすい感じなのを学習しながら書いてみた。
割込みを利用はしてますが、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 というレイヤーがあるイメージ。
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();
みたいになる。だいぶアホっぽいし、この部分のコードがカオスになるけど、一応使えるようにはなる。
もっといい方法があったら教えてください……
追記:最近別のアダプタに変えました 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円ぐらい。
評判の良いこれを買った。セットアップはあまり迷うところもなく終わった。
ただ、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
これでバックアップを再開するとネットワーク経由でバックアップが開始される。履歴も引き継がれ、差分バックアップになる。
とりあえず、2種類のテストがあり、どちらも十分なサポートがされている。
基本的にangular-seedというのを元に作ればいいんだけど、e2e (end to end) テストについては protractor というのを使うのが新しいようなので、今からはじめるならそちらを使ったほうが良い。
node で完結する、ロジックの単体テスト。主に controller とか filter をテストする。controller で DOM を直でいじっていると実行できない。
サーバサイドとかとの通信とかは全てモックにしなければならない。Angular の DI の仕組みで、モックオブジェクトを外部から注入して単体テストを完結させる。
いろいろ面倒くさいけど、これを書くようにすることで controller / directive の使いわけとかを意識せざるを得なくなるので良い気がする。
selenium を使った結合テスト。
protractor は Angular JS 用の e2e テストライブラリ。簡単に selenium-standalone をセットアップするところから、テスト用のユーティリティまでのセット。ドキュメント の通りにやれば OS X では全く苦もなく selenium 環境を作りテストを開始できる。
どこが「Angular JS用」なのかというと、ページロードとか、イベント発火とかで、いちいち自分で wait() を書く必要がなく、Angular 準拠の部分は自動で処理待ちをするので、かなり楽をできる。
ので、パターンを網羅したロジックは書きたいなら karma で完結するように書いたほうがいい。
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 メソッドを定義している。
12月10日に受験して、12月24日の合格発表と同時に申請を出し、免許日は1月9日で、それから2日後には不在票 (書留にしたので) が入ってた。思ったより早かった。営業日的には7日ぐらいかな。
今まで持っていたやつはラミネート加工されたやつだったけど、新しい形式になっていわゆるカードサイズになったのと、英語表記が併記されるようになった (電波の性質上、国際的に使われうる免許だからかな) のと、ホログラム (富士山と桜?) っぽいものが全面に入るようになったみたい。
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 だと面倒な点がいくつかあります
node の fork / exec (execFile) は *nix の fork / exec とは全く違うので、細かいプロセスの制御ができません。特に exec 単体相当はないので、現在のプロセス自体を置き換えるということができません。そんなわけで1つ別プロセスを経由している関係で、無駄に1プロセスを消費しています。
オブジェクトのファイナライザがないので、node-glint では process.on('exit') で起動したサーバを終了しています。ただし、普通に外部プロセスを起動すると、そのプロセスの終了を待つ挙動になり、exit されません。が、spawn() のオプションに detach: true を指定した上で、unref() を呼んであげることで、外部プロセスの終了に関わらず起動元の node プロセスを exit させることができます。
一応形にはなりましたが、細かい挙動の検証がめんどうなので npm にあげてません。特に現在の実装はシグナルまわりのハンドリングがいい加減です。node マスターの皆様におかれましては、よりよい方法をご教示頂ければと思う所存です。
特筆するほど難しいことはないけど書いておく。基本的に 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 だとどこの環境でも簡単に復元可能なので、どっちもどっちかなという感じ。
いろいろでてきてる。
Type A と Type B があり、Type A はイーサネットポートと組込みUSBハブがなくメモリが少ない分少しだけ安い。
本体は Raspberry Pi と同じぐらいの価格だけど、
という特徴がある。内蔵フラッシュメモリがあるので、(それにおさまるなら) SDカードがいらない分、Raspberry Pi よりコスト面でも有利。一方で欠点は
価格的に上であげたものの1.2〜1.5倍ぐらいする。現時点ではまだ日本未発売。1月中旬らしいのでそろそろかな。2A のAC アダプタとケーブルが一部付属っぽいので、(それらが必要な人なら) そんなに割高とはいえないかもしれない。
Linux が動いてるけど、Arduino 互換を謳ってる。ピン互換があって Arduino 用にでまわっているシールドがつかえるのと、開発環境も 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 はいいかもしれない。(自分で互換のブートローダーを書きこんでもいいけど)
続きをかきました。
よくわからなかったので以下のようにしましたが、まっとうな方法には思えないので正しいやりかたをおしえていただきたく申し上げます……
一応、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 的なものはどうやって書くんですか
Raspberry Pi せっかくあるので、一応作りたい目標をもっていろいろ試してみてる。現時点での思惑を書いておく。
無線機の周辺を拡張して、PCと連携しやすくしたり便利にしたり、というのが目的。具体的には
ある程度拡張性を持たせつつこれらを収めたい。無線機の近くで使うので、ある程度ノイズ対策が必要かもしれない。
12V が必要なので、ACアダプタとして 12V を使い、降圧して 5V, 3.3V を作りたい。
ぐらい確保できたら十分そう。PC用の電源だと、100Vから直接これらの電圧を全部調達できるけど、コストはともかくオーバースペックでデカすぎる。
12V 2A ぐらいのACアダプタ (24W) から 5V はスイッチングである程度高容量を高効率でとりだして、3.3V は 5V からリニアレギュレーターで安定化させる感じにする。
http://akizukidenshi.com/catalog/g/gM-02038/
5V 3A のスイッチングDC/DCコンバーターで、変換効率は最低でも73%。
ATmega168P をつかって実装。TWI (I2C) がついてるので簡単。内蔵8 M クロックで動作させて、アンテナのリレーのコントールをI2C経由及びボタンで行い、LED でどのアンテナが有効かを表示する。
リレーは2〜6つほどを同時に駆動する必要があり、この時最大で 400mA ほど流れる。かなり余裕があり、3.3V でも十分スイッチできるパワーMOS-FET でスイッチングする。
電源は I2C を Raspberry Pi と直接繋げたいので 3.3V 供給
基本的に I2C 対応、3.3V 品なので、そのままバスに繋ぐだけ。ごちゃごちゃやりたいのでブレッドボードを内蔵してそこに差す
無線機とのインターフェイスのために RS-232 変換をする。
クソコード を書いたはいいが、釈然としなかった。 "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');
});
みたいなあ
こういう謎の構造体を返すのってわっかりにくいので気持ち悪いなあて思いました。全部メソッドになっているべきなのでは????
directive のプロパティの説明がどこにあるかわかりにくいけど、 http://docs.angularjs.org/api/ng.$compile#description_comprehensive-directive-api_directive-definition-object にある
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 というのを使った。応答速度の関係でものによっては動かないとかあるみたいだけど手元にあったのでうまく動いた。
AI の挙動不審
AI (Auto Information) でデータが自動でくるようになるけど、その状態でこちらから読み出しコマンドを出したり、書き込みコマンドを出したりすると、それらのコマンドがうまく読みとってくれなかったり、あるいは出力が混ざったりする。
解決方法でいまいち良いのがない
コマンドが成功していることが確認できるまで送り続ける方法もあるが、コマンドごとに確認方法が違うのでコーディングが大変
確実に動くけど
例えば読み出しコマンドを発行すると、250ms ぐらいでようやくレスポンスがあるので (何やってんだ?) 、4個値を読み出すだけで1秒かかってしまう。
AI はそのままにするのがやはりよさそう。コマンドを送って確実に操作するよりも情報を早く受けとるほうが重要だと思う。
とはいえ、コマンドを送ったのに反映されないのもイラつくので、かなり執拗に送信確認をする必要がある。
として、返答があるのを250msぐらい待って、セットできていなかったらリトライする。
机の横に棚が欲しかったけど、ちょうどいいのがないので作った。
机の横に防湿庫を置いていて、その上のスペースがあいている感じだったので、
という感じでやった。どちらも 1x8材 で作った。
高さをあわせる用のやつは 1x8 6 feet のものを自分でノコギリで切って作った。だいぶヘタクソな感じだけど、寸法的にはちょうどのものが作れた。事前に設計図的なのを書かず、場当たり的に作ってしまった
その上にのっける棚は 1x8 12feet の板から全部の板をとったけど、こちらはホームセンターでカットしてもらった。一旦 SketchUp でモデリングしてから、必要なサイズを事前に計算したので比較的スムーズに発注・組み立てができた。
組み立てはコーススレッドをぶちコムだけなので、と思っていたけどトルク強すぎて板が割れたり結構大変だった。サイズをぴったりにはならないので鉋か荒いヤスリが必要だなーとか、店頭で良く見てから買わないとかなり反った材料を買ってしまうなーとか、気付きはいろいろあった。
あとは 120 -> 240 でやすりをかけて、ニスをひたすら塗るだけだけど、どう考えてもこの一連の作業が一番辛い。ただ手を抜くと悲しい感じではあるのでわりと頑張った。
一連の作業を行うと翌日激しい筋肉痛になる。金で解決できるなら金で解決するほうが安いというこのご時世ではありますが、たまに DIY をやると、いろいろ気付くことや思うことがあるし、物理的に形あるものを作るのも、ヘタクソではありつつも愛着が沸いておもしろいものであります。
ディスクユーティリティを使って FAT でフォーマッットをすると、フォーマットしようとしているディスクが2G未満だと自動的に FAT16 になってしまうようだ。
世の中にはFAT32だけをサポートするという変わったシステムもあるので、そのような環境において 2GB 未満のメディアを使おうとすると、これは困る。しかし以下のように diskutil コマンドからならば普通にフォーマットをかけられる。
$ diskutil list # /dev/disk2 とか、フォーマットしたいディスクをよくよく確認する $ diskutil partitionDisk /dev/{{disk}} 1 MBRFormat "MS-DOS FAT32" SD 100%
OWON SDS7102 というのを買った。OWON は中国の測定器メーカーで、かなり安いオシロスコープを出しているけど、ググったりしてみると割と評判が良く、英語圏でも使ってみたりハックしたりしている記事がヒットする。
オシロスコープの選定基準は主に
があるけど、買ったモデルは、帯域 100MHz、サンプリングレート1G samples/sec、2ch のもので、中堅クラスぐらいのもの。帯域が100MHz でも、矩形波にすると25MHz程度までしか観測できないので、マイコンプログラミング程度でもこれぐらいのスペックが必要になり、なかなか厳しいものがある。デジタル回路を見る場合2chは必要…
これ以上のスペックものは正直高すぎて手が出ない。購入価格は正規代理店経由のもので 52000円ぐらい。円安になりつつあって一時期より値段があがっていたけど、消費税もあがると買うことにした。
今までオシロスコープを所持したことがないので、他のオシロスコープとの比較はできない。
届いてあけた感想としては「画面がでかい!」「奥行がない!」という感じだった。ちょうどいい大きさ。
使いこなせるかな、思っていたけど、予想よりは全然操作が簡単で、すぐトリガをかけて単発現象を観測したりはできるようになった。
見えないものが見えるようになるというのはすごくテンションがあがる。可能性が一気に広がったように感じる。
現時点では完全に使いこなせているとは言えないので、あまり思いつかないけど、
というのがあった。LAN ポートもついていて、こっちの連携はすぐにうまくいった。
普通にルーターと接続してアドレスとかを設定すると、設定したポートで listen するようになってるのでコマンドを送るだけ。
なので自分で Ruby で実装を書いてみた。bmp を得られるけど、png で欲しいので ImageMagick で変換までしている。
プロトコルフォーマットはググると出てくる。ただ、bin なフォーマットは仕様書とだいぶ違っていて、結構めんどうくさい。
というのがあって、解析したコードがあったりする。けど、とりあえず必要ないし、そこまで実装してない。