zshでネットワーク経由のファイル転送

zsh(とtar)を使ってネットワーク経由のファイル転送を効率的に行う方法についてです。
ARMボード(RaspberryPiとかPCduino等)を使っていると、scpなどでホスト間で大きなファイル転送をする際に、CPUネックになりがち。rsyncやscpするときに暗号をarcfour等にする*1ことでCPU使用率を低減する方法もありますが、そもそもLAN内等で暗号化しなくても良い場合もあります。こういうとき、単一のファイルであれば、

受信側(host1): nc -l 12345 >file
送信側(host2): nc host1 12345 <file

とncを使えばOK。(ポートがファイアウォールで塞がれていないことが前提ですが。)


複数のファイルだと

受信側(host1): nc -l 12345 | tar vxf -
送信側(host2): tar vcf - files/* | nc host1 12345

のように転送する方法がありますが、これだとパイプ経由でのデータコピーのためにncによってCPU使用率が上がってしまい、単発の場合と比べて性能が悪くなってしまいます。


bashであれば、送信側についてはncを経由せずに、

tar vcf - files >/dev/tcp/host1/12345

とすることで直接TCPソケットに書き出せます。が、受信側は知る限りこういった方法がありません。


zshであれば、標準でついてくるnet/tcpモジュールを使って、送受信側とも直接TCPソケットに読み書きさせることができます。

ただ、net/tcpが提供する各コマンドはシェルスクリプトTCPサーバを書くことを念頭に作られているようで、今回の目的にはローレベルのztcpコマンドを操作しないといけないようです。手動では若干面倒なので、.zshrc内にあらかじめ以下の関数を作っておきます。

function file_recv {
        if [ $# = 0 ]; then
                echo file_recv listen_port
                return
        fi
        (
        autoload -U tcp_open; tcp_open >/dev/null 2>/dev/null # define ztcp
        ztcp -l "$1"
        fd_listen=$REPLY
        echo "Waiting on port $1 (fd $fd_listen) ..."
        ztcp -a $fd_listen || echo failed.
        fd_accept=$REPLY
        echo "Connected. (fd $fd_accept)"
        tar vxf - <&$fd_accept
        ztcp -c $fd_listen
        ztcp -c $fd_accept
        )
}
function file_send {
        if [ $# -lt 3 ]; then
                echo file_send host port files ...
                return
        fi
        (
        autoload -U tcp_open; tcp_open >/dev/null 2>/dev/null # define ztcp
        ztcp "$1" "$2"
        shift 2
        tar vcf - "$@" >&$REPLY
        ztcp -c $REPLY
        )
}

あとは、

受信側(host1): file_recv 12345
送信側(host2): file_send host1 12345 files/*

とすればOKです。送信側は上記bashを使ってもOKです、その方が事前に準備要らないですからね。*2


しかしなんというか、ここまでするなら、tarも使わずsendfile(2)とかsplice(2)等を使ってユーザ空間でのコピーも避けられるようなさらに効率的な転送コマンドを用意する方がいい気がしてきた。


あと、tar vcf - ... の部分に任意のコマンドを指定できるようにした方が利用シーンが広がって有用かもしれない(効率のいいncとして使う)。

*1:参考: http://d.hatena.ne.jp/rx7/20101025/p1

*2:どちらから接続するかを入れ替えれば、"受信側は上記関数の準備不要"とするということもできるはず。

近接センサでRaspberryPi LCDのバックライトを点灯

Raspberry Pi 2に7インチタッチディスプレイをつけて、パッと見たい情報(天気予報とか時計とかバス時刻表とか)を表示するのに使っているのですが、常時つけっぱなしになっているのが気になっていました。周囲に人がいるときだけオンになったらいいなと思い、近接センサで一定範囲内(だいたい1.5m以内)に反応があった場合にバックライト点灯→しばらく反応がないと消灯するようにしました。


バックライトの制御コマンド

発売当初はバックライトON/OFFが制御できませんでしたが、最近のカーネルなら/sys/class/backlight/rpi_backlight/bl_power に0/1をwriteすることで ON/OFFができるようになっています(輝度変更は効きませんが)。また、XのDPMS (Energy Star)とも連動してON/OFFされます。今回は一定時間で消灯したいのと、タッチしたらONになって欲しいので、DPMSを使います。

Xが起動している状態で、

$ xset dpms force on
$ xset dpms force off

でバックライトのON/OFFが可能です。また、

$ xset dpms 120 120 120

とすると120秒間操作しないと自動でOFFになります。現在の状態確認はxset qを使います。

$ xset q
...
DPMS (Energy Star):
  Standby: 120    Suspend: 120    Off: 120
  DPMS is Enabled
  Monitor is Off

近接センサの接続

近接時の検出は以下の測距センサを使いました。距離に応じた電圧がアナログ出力されるものです。
シャープ測距モジュール GP2Y0A02YK: センサ一般 秋月電子通商 電子部品 ネット通販

Raspberry Piには直接アナログ入力することができないため、一旦Arduino Pro Miniを通して一定距離内に入った時にデジタルで信号を出力するようにしています。動画のRasPiの後ろで近接時にLEDが点灯しているのがArduino Pro Miniです。(別にデジタル変換はArduinoである必要は全くなく、ただのコンパレータでいいのですが、手持ちがなかったので。。)


上記のように接続してGPIOの17を読めば、近接検出ができます。GPIOへのアクセスは

# echo 17 > /sys/class/gpio/export

を実行しておき、

# cat /sys/class/gpio/gpio17/value

のようにすれば0または1として値を読み出すことができます。

近接センサでバックライト制御

以下のスクリプトでGPIOを定期的に監視し、近接時にxsetでバックライトの点灯させます。なお、点灯中にforce onしても無操作タイマーが更新されず、すぐに自動消灯してしまったりするので、近接中は定期的に時間を再設定することでタイマーをリスタートしておきます。

#!/usr/bin/perl
use Time::HiRes "sleep";
open my $in, "/sys/class/gpio/gpio17/value";
while (true) {
    seek $in, 0, 0;
    my $val = <$in>;
    if ($val == 1) {
        `xset dpms force on`;
        `xset dpms 120 120 120`;
        print "ON\n";
        sleep 60;
    } else {
        sleep 0.1;
    }
}

スマートメーターの瞬時電力や履歴をWebブラウザで見る

前の記事の続き。
スマートメーターからリアルタイムに消費電力を取得する - Okiraku Programming


前回の記事ではArduinoで電力を取れるようにしたわけですが、ログを取って統計処理したり、Webでリアルタイムに見れるようにしようとすると、やっぱりLinuxの方が便利なので、Linuxマシン(今回はPCduino3を使いましたが、RaspberryPiでも全く同様にできるはず)に移植しました。


前回の記事のスケッチを移植・改造した電力取得・ログ記録プログラム(C++) → WebSocketサーバ(Node.js) → ブラウザ上で動的にグラフ描画(Highcharts) という構成です。こんな感じのメーターがリアルタイム(5秒ごとくらい)にピコピコ動きます。

履歴も秒オーダー/分オーダー/時〜日オーダーで変化が見れたりします。

PCduino3に接続

接続は、電源(3.3V, GND)とUART(これも3.3Vレベル)ピンの計4本をBP35A1に繋ぎます。誤って5Vに繋ぐとBP35A1が壊れると思われますので注意。


ソース

https://gist.github.com/NeoCat/73f8ed86db468dd005a2

  • smart_meter.cpp: 前回のスケッチを適当にLinuxに移植したもの。前回はシリアル通信速度を9600bpsにしましたが、デフォルトの115200bpsで問題ありません。/tmp/power.log に10分毎の時刻・平均電力を記録するほか、/tmp/power.sockというソケット経由でリアルタイムの瞬時電力値(2分間のヒストリ付き)と過去2時間分の分毎の平均電力をとれます(-cオプションでソケットから読み出し)。ややマルチスレッドのロック処理を端折ってるので高頻度で接続を繰り返すと稀に変なデータが出てくるかも。あとログファイルは無限に追記されていくので、別途logrotateなどをかます必要があります。
  • power_ws.js: WebSocketサーバ。 node.js用のコードです。別途websocket.ioをnpmで導入する必要があります。
  • power.html: WebSocketサーバに接続してHighchartsでリアルタイムにグラフ描画します。ローカルファイルをブラウザで開くだけで実行できます。ファイル中でWebSocketサーバのIPアドレス指定が必要です。

実行

## smart_meter.cpp内にID、パスワードを設定
% g++ -Os smart_meter.cpp -o smart_meter -pthread
## ttyS1(シリアル)を有効化
% sudo sh -c 'echo 3 > /sys/devices/virtual/misc/gpio/mode/gpio0'
% sudo sh -c 'echo 3 > /sys/devices/virtual/misc/gpio/mode/gpio1'
% ./smart_meter &
## https://nodejs.org/en/download/ から node.js 4.2.4 ARMv7 バイナリをダウンロードして/usr/local/以下にインストール。
% npm install websocket.io
% node power_ws.js &

あとはpower.htmlにIPアドレスを指定して、Webブラウザで開けばOKです。

スマートメーターからリアルタイムに消費電力を取得する

少し前からスマートメーターの導入が始まっています。東電は2020年度末までに一般家庭などへの導入を完了させるそう。
http://www.tepco.co.jp/smartmeter/index-j.html


東電の場合、電力メーター情報発信サービス(Bルートサービス)を申し込むと、優先的にスマートメーターに交換してくれます。特に費用はかかりません。スマートメーターが設置されるとでんき家計簿から30分毎の電力使用状況グラフ(0.1kWh単位)を見ることができるようになりますが、加えて上記サービスに申し込むと、スマートメーターと家庭内のHEMS機器との間(これをBルートと言います)で通信を行うことが可能になり、リアルタイムで詳細な電力使用状況(瞬時電力値(W)、瞬時電流値(A)など)が取得できるようになります。


利用にあたってHEMS機器やサービスの導入に費用がかかるのがネックですが、HEMSに使われている無線通信方式(Wi-SUN)やプロトコル(ECHONET Lite)はオープンな規格であり対応する通信モジュールも一般販売されているため、自作の機器でもBルートで通信を行って電力使用状況の取得ができるはずです。


というわけで瞬時電力値の取得に挑戦してみました。


作ったのは、7セグにリアルタイムに瞬時電力(W)が表示されるというもの。シリアルでPCからも値を読み取れます。

まずはBルートサービスに申し込み

自宅の場合、スマートメーターへの交換は申し込んで1週間ほど(週末を指定したので)でやってくれました。要立会い、作業は15分くらい、そのうち5分くらい停電しました。交換後、翌々週くらいにBルート接続用のIDとパスワードが送られてきました。トータル3週間ほど。

使用した機器

その間にWi-SUN関係の通信機器をこちらなどで購入しました。→ https://www.zaikostore.com/zaikostore/itstoreWireless?cid=3#c2

 (DT101EACV101の方が簡単に使えますが、購入できない場合は BP35A7A でもOKと思います。)


BP35A1は電圧が3.3Vで、5Vには対応していませんので、USBシリアルやArduinoも3.3Vに合わせる必要があります(または適切に分圧やレベル変換が必要です)。


シリアル対応7セグは手持ちの古い版なので、真似する場合は適当に別のものに置き換えた方がいいでしょう。(その場合、下記のスケッチの dig というキーワードが出てくる部分の書き換えも必要でしょう。)


なおPCで使う場合は、WSR35A1-00 というUSBシリアルとBP35A1を一体化したUSBデバイスも売られているので、これが一番簡単でしょう。


Wi-SUN通信モジュールBP35A1のデータシート等は以下にあります。
http://micro.rohm.com/jp/download_support/wi-sun/

追記

Arduinoではなく、RaspberryPiなどのLinuxボードを使う場合はこちらの記事もみてください。
スマートメーターの瞬時電力や履歴をWebブラウザで見る - Okiraku Programming

事前準備

BP35A1はDT101EACV101を噛ませ、さらに XBeeの載る「C基板」上に乗せました。
C基盤にはArduino Pro MiniのUSBシリアル接続と同配置になるようピンヘッダに配線し、PCと簡単につなげるようになっています。


BP35A1とArduinoは直接シリアル通信で利用できます。ただ、Arduino Pro Miniは一系統しかハードウェアシリアル(UART)を持っていないので、ここにBP35A1を繋いでしまうとPCとシリアル通信ができなくなり、デバッグ等が困難になってしまいます。そこで、BP35A1はArduinoのソフトウェアシリアルを使って接続することにします。*1

ソフトウェアシリアルの欠点として、半二重なので送信と受信が同時にできない、通信速度が高速だと不安定になりやすいという問題があります。BP35A1のデフォルト設定は115200bpsなのですが、3.3V/8MHzのArduinoでは文字が化けたりして正常に動作しませんでした。そこで,事前に通信速度の設定を9600bpsにし、さらに文字間にウェイト400μsを入れるように変更しておきます。


これは、BP35A1をPCにUSBシリアルの3.3V, GND, TX<->RXの4本で接続して、ターミナルソフトを115200bps設定で起動します。
Arduino IDEのシリアルモニターでも改行コードをCRにすれば通信できますので、これが手っ取り早いでしょう。

WUART 43

というコマンドを実行すればシリアル設定はOKです。一度設定すると内蔵のフラッシュに保存され、電源を再投入すると以後は9600bpsでの通信になります。


PCに直接繋いで利用する場合はこのシリアル設定は不要です。また、設定変更後にデフォルトの設定に戻したい時は WUART 00 を実行すればOKです。

配線

こんな感じでブレッドボード上に配線しました。

スケッチ

ArduinoのスケッチはGist上に置いてあります。
https://gist.github.com/NeoCat/72390f357d3ee2ce339d

Bルート用のID・パスワードをスケッチの先頭のID, PASSWORDに設定し、Arduinoに書き込みます。

//#define DEBUG

と書かれた部分のコメントアウトを外すと、詳細な通信状況がシリアルに出力されるようになります。


概ね以下の処理を行っています。

  • setup
    1. 初期設定(バージョン取得、ASCII出力モードへの設定、ID・passwordの設定)
    2. PANのスキャン(スマートメーターとの通信チャンネルやIPv6アドレスなどの取得)
    3. スマートメーターとの通信セッションを確立(ID・passwordによるPANA認証。以降、自動的に暗号化通信になります)
    4. ECHONET Lite (UDP port 3610)を使ってスマートメーターの状態を確認
      • EPC 0x80 (動作状態) = 0x30: ON、 0x31: OFF
      • EPC 0x88 (異常状態) = 0x41: 異常あり、 0x42: 異常なし
  • loop
    1. 3秒ごとにスマートメーターから瞬時電力(EPC 0xe7)を取得,シリアルおよび7セグに表示


ちなみに自宅のスマートメーターは最初なぜか異常状態になっており、セッションの確立は出来るものの、EPC 0x88 = 0x41 (異常あり)を返し、かつ瞬時電力や累積電力なども取得しようとすると"非対応"のエラーを返してきていました。電力会社のスマートメーター推進室ということろに問い合わせてEPC等の状態を伝えたところ、遠隔でスマートメーターの通信機能のログ確認やリブートを行ってくれ、その結果無事に瞬時電力等が取れるようになりました。言ってみるものですね。

参考資料

スケッチ

*1:Arduino LeonardoであればUSB用とハードウェアシリアルが分かれているので簡単だったかも。

twicliの☆を♡にする

Twitter公式では、お気に入り(fav: ☆アイコン)が、いいね(like: ♡アイコン)に変更されました。


twicliでも♡アイコンを使いたい方は、以下のユーザスタイルシートを適用することで変更できます。


手順は、+タブ→設定とクリックして、ユーザスタイルシート の欄に以下をコピペし、保存ボタンを押せばOKです。

.fav > img { opacity: 0; z-index: 9; position: absolute; }
.fav > span:before { background-image: url("images/icon_like_empty.png"); background-size: contain; width: 16px; height: 16px; display: inline-block; content: "."; color: transparent; line-height: 0; }
.fav > img[src*="loading"] + span:before { background-image: url("images/icon_like_loading.png"); }
.fav > img[src*="full"] + span:before { background-image: url("images/icon_like_full.png"); }

MicroView + FM Tuner

OLEDディスプレイ搭載ArduinoMicroView (Uno互換) と、FM Tuner Si4703 評価ボードを組み合わせてFMラジオを試作しました。
電源はリチウムイオン電池LiPro Charger


動いてるところ。表示は1行目から周波数、ボリューム、感度 ([S]はステレオ受信)、放送局名*1、Radio Text(曲名等)。

タクトスイッチ(写真だとつけてませんが)か、Serialで選曲、ボリューム操作ができます。


ラジオ聞くだけなら付属のライブラリを使えば何の問題もなし。

U.S.やヨーロッパではRDS (Radio Data System)といって、放送局名や放送中の曲名、代替周波数などの情報がFM放送に重畳されており、Si4703はそのデコードもできます。が、SparkFunの配布しているArduinoライブラリだと、RDS機能の実装が手抜きでよく分からん文字列を拾ってくる。そこでデコーダーを書き直し、放送局名(PS, 最大8文字)とRadio Text(最大64文字、曲名、アーティスト名など)をきちんと抽出できるようにしました。


修正版のライブラリ、MicroView用のスケッチ(examples/uView_Radio4703)は以下に。
https://github.com/NeoCat/Si4703_FM_Tuner_Evaluation_Board/tree/master/Libraries/Arduino/Si4703_Breakout/examples

*1:U.S.だと曲名等も8文字ずつを時分割で送ってたりする曲もあります。

Open vSwitchやNamespaceの関係図をコマンド一つで描く

LinuxでSDNっぽいことをOpen vSwitchでやろうとすると、BridgeやらNamespaceやらInterfaceやらが増えまくって全貌の把握が困難になってきます。また、なぜかpingが通らないときなど、あっちこっちでtcpdumpして回るのは大変です。

そこで、コマンド一つでOpen vSwitchやNamespaceの関係図を描き、ついでに全interfaceをtcpdumpしてpingがどこを通っているかも色で示すツールを作りました。


今のところ単一ホスト内のみなので複数nodeあるときは各nodeで実行する必要がありますが。

使い方

ソースは https://github.com/NeoCat/ovsimager にあります。sudo できるユーザかrootで

 $ sudo gem install ovsimager
 $ sudo yum install graphviz
 $ ovsimager

とインストールして実行すると、interfaces.png というファイルに関係図が出力されます。


以下はOpenStack Neutron DVR(all-in-one構成)でinstanceにFloating IPを振った状態です。
Open vSwitch Virtual SwitchとLinuxBridge内のInterface、およびNamespaceとの関係(routingに使われうる、IPが振られたインターフェイスのみ)が点線で描かれます。vethやOpen vSwitch間のpeerは実線で繋がります。


pingをトレースするときは、400byteのpayloadのpingを打ちながら、

 $ sudo ovsimager -d

とします。すると、全インターフェイスtcpdumpが数秒間起動され、400byteのping (vxlanも含む) が通った場所を記録、図にpingの宛先・送信元IPアドレスが追加され、色もつきます。(黄: ICMP Echo Request/Response両方が通過、ピンク: Requestのみ通過、赤:Responseのみ通過)


なお ovsimager 自身に ping を実行させることもできます。

 $ sudo ovsimager -d -f 10.0.0.1 -t 192.0.2.1

pingのトレース時は全interfaceでtcpdumpを同時実行するとか割と荒っぽいので、大規模な環境で実行しないほうがいいと思います。。が、学習用にはいいかな、と。