2015年8月28日金曜日

node.jsとwebsocket

node.js自体のinstall方法は色々な方が書かれているのでここでは飛ばします。
今回はさくらVPS上でubuntu14.04LTSでサーバーを運用しています。

javascript初心者なので何かとつまらないところで躓いてますが、なんとか通信が動くようになりました。

最初にhttpsで接続し、upgradeでwebsocketに切り替える動作になるので、サーバー認証、クライアント認証はhttps接続時に行います。
最初に躓いたのは、自己認証局の証明書(いわゆるオレオレ証明書です)でMacのSafari(8.0.8)からwebsocketに接続しようとすると接続がハネられてしまうことに気が付かなかったため数日間悩みました。Chromeで接続するとちゃんと接続できたので、Safariの仕様のようです。wiresharkでみてみるとサーバー側からはリクエストが出ていますがSafariからクライアント証明書を送っていないようです。
最終的にはSafariではなくRaspberryPi上の自作プログラムから接続するので、このまま進めます。

最初に必要なモジュールを読み込みます。
var WebSocketServer = require('ws').Server;
var https = require('https');
var fs = require('fs');

後で変更しそうな設定値を記述したconfigファイルを用意して読み込みます。
var config = require('./config');

httpsのセキュリティ条件をoptsで設定します。
  var opts = {
    key: fs.readFileSync(config.keyFile),
    cert: fs.readFileSync(config.certFile),
    ca: fs.readFileSync(config.caFile),
    ciphers: "ALL:!EXP:!ADH:!LOW:!SSLv2:!SSLv3:!DES:!3DES:!RC4",
    honorCipherOrder: true,
    secureProtocol: "TLSv1_2_method",
    requestCert: true,
    rejectUnauthorized: true
  };

クライアント認証をするには最後のrejectUnauthorized:trueが必要です。
これがないとやりとりはするけど認証されてない相手でも通信を許してしまいます。
あとdebug時にwiresharkで暗号通信の中身を確認したい場合はciphers:の先頭のALL:をkRSA:にする必要があります。これをしないと暗号が解けないので中身が見えません。

  // connect from house controller
  var houseControllerServer = https.createServer(opts);
  var wss = new WebSocketServer({server:houseControllerServer});
  var houseControllerConnections = [];

  houseControllerServer.listen(config.houseControllerPort, function() {
    console.log("house controller listing on port %d", config.houseControllerPort);
  });
指定されたportでアクセスが来るのを待ちます。

  wss.on('connection', function (ws) {
    // connectionイベントの処理
    console.log('connect from house controller');
    houseControllerConnections.push(ws); // 接続先を記録しておきます。
  // ここにwebsocket接続時の処理を追加します。

    ws.on('close', function () {
      // closeイベントの処理
      console.log('disconnect from hosue controller');
      // closeする接続先を記録から除いています。
      houseControllerConnections = houseControllerConnections.filter(function (conn, i) {
        return (conn === ws) ? false : true;
      });
     // ここにdisconnect時の処理を追加します。
    });

    ws.on('message', function (message) {
      // mesageイベントの受信処理
      var OU = ws._socket.getPeerCertificate().subject.OU;
      var msg = JSON.parse(message);
      // ここに通信相手からのメッセージの処理を追加します。
      console.log(msg);
    });
  });

  //送信はイベント処理ではないのでfunctionです。
  function sendWebsocket(message) {
    houseControllerConnections.forEach(function (con, i) {
      // 登録されている通信相手にメッセージを送信します。
      con.send(JSON.stringify(message));
    });
  }

以上で接続、終了、受信、送信の処理となります。


2015年8月21日金曜日

websocket

これまでは自宅のNAT内のRaspberryPiのホームコントロールシステムを外からアクセス出来るようにするためにNAT内から外のサーバーに対してTLSでsocketを1本通しておいて、外部サーバーの443portにアクセスしてきたものをclient認証をした後にTLSを逆流させてRaspberryPiのapache2に繋ぐという仕組みを作って運用していました。
これはこれで簡単でいいのですが、apache2をRaspberryPiで運用していたのでどうしても重たくなっていました。
少し世間一般的な解決方法を取り入れようと考えて、色々と最近の流行を調べたりしているのですが、webの世界はフレームワークの標準とかの変化が激しすぎてなかなかついていけません。とりあえず始めないと始まらないので、まずは手始めにサーバーサイドをnode.jsにしてRaspberryPi上のホームコントロールサーバーにwebsocketのプロトコルを話せるような機能を追加してみました。
websocketはwikipediaの説明を読めば大体理解できたので対向のwebsocketサーバーにnode.jsのサンプルプログラムを利用してそのままTLS化して繋いで動作確認をしていきます。
まだ他の部分が安定して動作してないので安定してきたら公開しようかと思います。
最終的にはセンサー、HAコントロール、etcの機能をESP8266を使ったWiFiモジュールで実現し、RaspberryPi2上のホームコントロールサーバーで情報集約してsecure websocketでNAT越しにさくらVPSで借りているサーバー上のnode.jsに接続、そこからiPhone上のWebAppに繋ぐということを実現しようかと考えています。
まだjavascriptを勉強し始めたところなので道のりが長そうですが........

2015年8月14日金曜日

OS-XでESP8266開発環境その2

前回作ったSDKの環境でbuildしていきます。
# cd ~/ESP8266/esp_iot_sdk_v1.2.0
# cp -r examples/at/ .
# cd at
scriptの属性を実行可能に修正
# chmod +x gen_misc.sh
# ./gen_misc.sh
gen_misc.sh version 20150511

Please follow below steps(1-5) to generate specific bin(s):
STEP 1: choose boot version(0=boot_v1.1, 1=boot_v1.2+, 2=none)
enter(0/1/2, default 2):
1 <---最初にfirmware updateで使用したイメージと同じものを選択
boot mode: new

STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)
enter (0/1/2, default 0):
1 <---ここも最初にfirmware updateで使用したイメージと同じものを選択
generate bin: user1.bin

STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)
enter (0/1/2/3, default 2):
2 <---defaultを選択
spi speed: 40 MHz

STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)
enter (0/1/2/3, default 0):
0 <---defaultを選択
spi mode: QIO

STEP 5: choose spi size and map
    0= 512KB( 256KB+ 256KB)
    2=1024KB( 512KB+ 512KB)
    3=2048KB( 512KB+ 512KB)
    4=4096KB( 512KB+ 512KB)
    5=2048KB(1024KB+1024KB)
    6=4096KB(1024KB+1024KB)
enter (0/2/3/4/5/6, default 0):
2 <---ここも最初にfirmware updateで使用したイメージと同じものを選択
spi size: 1024KB
spi ota map:  512KB + 512KB


start...

DEPEND: xtensa-lx106-elf-gcc -M -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -ffunction-sections -fdata-sections -DICACHE_FLASH -DAT_UPGRADE_SUPPORT -I include -I ./ -I ../../include/ets -I ../include -I ../../include -I ../../include/eagle user_main.c
xtensa-lx106-elf-gcc -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -ffunction-sections -fdata-sections  -DICACHE_FLASH -DAT_UPGRADE_SUPPORT   -I include -I ./ -I ../../include/ets -I ../include -I ../../include -I ../../include/eagle  -o .output/eagle/debug/obj/user_main.o -c user_main.c
xtensa-lx106-elf-ar ru .output/eagle/debug/lib/libuser.a .output/eagle/debug/obj/user_main.o 
xtensa-lx106-elf-gcc  -L../lib -nostdlib -T../ld/eagle.app.v6.new.1024.app1.ld -Wl,--no-check-sections -u call_user_start -Wl,-static -Wl,--start-group -lc -lgcc -lhal -lphy -lpp -lnet80211 -llwip -lwpa -lmain -ljson -lupgrade -lsmartconfig user/.output/eagle/debug/lib/libuser.a -lat -Wl,--end-group -o .output/eagle/debug/image/eagle.app.v6.out 

!!!
Support boot_v1.2 and +
Generate user1.1024.new.2.bin successully in folder bin/upgrade.
boot.bin------------>0x00000
user1.1024.new.2.bin--->0x01000
!!!

これで../bin/upgradeに
user1.4096.new.4.S 逆アセンブルコード
user1.4096.new.4.bin 書き込みイメージ
user1.4096.new.4.dump シンボル情報
が出来上がります。

# cd ../bin

基板のIO0のスイッチをUART書き込み側に倒してリセットします。

# esptool.py -p /dev/cu.usbserial-A101JG84 write_flash -fs 32m 0x00000 boot_v1.4\(b1\).bin 0x1000 upgrade/user1.1024.new.2.bin                                   
Connecting...
Erasing flash...
Writing at 0x00000800... (100 %)
Erasing flash...
Writing at 0x00040800... (100 %)

Leaving...

基板のIO0のスイッチを通常モードに戻してリセットします。
# jerm -b 115200 -r rntn /dev/cu.usbserial-A101JG84
を起動してATコマンドでバージョンを確認
AT+GMR
AT version:0.30.0.0(Jul  3 2015 19:35:49)
SDK version:1.2.0
compile time:Jul 25 2015 10:44:06
OK
ちゃんと更新されて動いているようです。

2015年8月7日金曜日

OS-XでESP8266開発環境その1

ともの技術メモさんを参考にArduinoIDE ESP8266の環境をinstallします。
linux版ですが非常にわかりやすく書いてくれています。
OS-Xでもほぼ同じ手順で行けます。

以下はOS-X yosemiteでの環境構築です。

Arduino IDE for ESP8266
のInstalling with Boards Managerに書かれているとおりの手順です。

Aruduino website
からARDUINO 1.6.5のMac OS X 10.7 Lion or newerをdownloadします。
ダウンロードしたzipファイルをダブルクリックして開くとArudiono.appが展開されます。
Arudiono.appをApplicationフォルダにコピーし、ファイルを右クリックで開きます。
メニューバーのArudino->Preferences...を開きます。
Additional Boards Manager URLs:に
    http://arduino.esp8266.com/package_esp8266com_index.json
を設定しOKボタンを押してPreferences...を閉じます。
メニューバーのツール->ボード->Board Manager..を開きます。
一番下にesp8266 by ESP8266 Communiyというものがあるので選択し、Installボタンを押します。
ダウンロードが完了したら閉じて、Aruduinoを終了します。

~/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9/
にxtensa-lx106のツール一式が出来上がっています。
このままでもいいのですが、diretoryが深すぎて使いにくいので
# cp -pr ~/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9 /usr/local/ESP8266
とします。(このへんは好みの問題です)
shellのPATH設定に/usr/local/ESP8266/binを追加します。

次にBRILLIANTSERVICE TECHNICAL BLOGさんを参考にESP8266のSDKとツール等を展開するためのdirectoryを掘ります。
まずは最新firmwareをbuild出来る環境を作って確認していきます。

# mkdir ~/ESP8266
# cd ~/ESP8266
# curl -o esp_iot_sdk_v1.2.0_15_07_03.zip https://raw.githubusercontent.com/esp8266/esp8266-wiki/master/sdk/esp_iot_sdk_v1.2.0_15_07_03.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 11.7M  100 11.7M    0     0  2088k      0  0:00:05  0:00:05 --:--:-- 2992k
# unzip esp_iot_sdk_v1.2.0_15_07_03.zip
Archive:  esp_iot_sdk_v1.2.0_15_07_03.zip
..............
  inflating: release_note.txt  

buildに必要なincludeファイルを入れます。
# curl -o includes.tgz https://raw.githubusercontent.com/esp8266/esp8266-wiki/master/include.tgz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  956k  100  956k    0     0   316k      0  0:00:03  0:00:03 --:--:--  316k
# cd esp_iot_sdk_v1.2.0
# tar zxvf ../includes.tgz
x include/
..........
x include/wctype.h

libc.a,libhal.aを入れます。
# curl -o lib/libc.a https://raw.githubusercontent.com/esp8266/esp8266-wiki/master/libs/libc.a
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2689k  100 2689k    0     0   854k      0  0:00:03  0:00:03 --:--:--  854k
# curl -o lib/libhal.a https://raw.githubusercontent.com/esp8266/esp8266-wiki/master/libs/libhal.a
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  339k  100  339k    0     0   593k      0 --:--:-- --:--:-- --:--:--  594k

書き込みのためのツールを入れます。
# cd ..
#  git clone https://github.com/themadinventor/esptool esptool-py 
Cloning into 'esptool-py'...
remote: Counting objects: 192, done.
remote: Total 192 (delta 0), reused 0 (delta 0), pack-reused 192
Receiving objects: 100% (192/192), 66.80 KiB | 0 bytes/s, done.
Resolving deltas: 100% (93/93), done.
Checking connectivity... done.
# cd esptool-py 
# python setup.py install
running install
.......
Installing esptool.py script to /usr/local/bin
......
Finished processing dependencies for esptool==0.1.0
# cd ..

ここまででbuild出来る環境が出来上がりました。
次回は実際にbuildして書き込んで確認します。