Wi-SUN対応無線モジュール（BP35C0-T01）とラズパイを繋ぐ。（制作・実験編）

# はじめに
- 前回の記事[Wi-SUN対応無線モジュール（BP35C0-T01）とラズパイを繋ぐ。（検討編）](http://qiita.com/katsumin/items/38961767a66078ba7f8e)の続きです。
- ~~ただし、まだBルートサービスのユーザID・パスワードを入手できておらず、スマートメータとの通信はできないので、無線モジュールとホスト（ラズパイ、PC）との疎通確認まで行いました。~~

---

# 回路の見直しと実装
- ラズパイと無線モジュールの接続用基板は、UART信号を繋ぐだけでよいと思ってましたが、リセット回路も必要でした。
- また、FT232RLから供給できそうとしていた無線モジュールへの3.3Vですが、動作が安定しなかったので、[三端子レギュレータ](http://akizukidenshi.com/download/ds/unisonic/LP2950_2951.pdf)も追加しました。
- 結局、ラズパイ接続用として無線モジュールを実装する基板、それをPCに接続するための基板の2枚構成となりました。

![BP35C0−1_回路図.png](https://qiita-image-store.s3.amazonaws.com/0/166435/698b713e-b860-8f97-0035-7c9589e63fe3.png)

![BP35C0−2_回路図.png](https://qiita-image-store.s3.amazonaws.com/0/166435/dbf80a67-8b86-3b8a-b501-67bd1b93623b.png)

- ラズパイに実装した状態は、こんな感じ。
![IMG_0313.JPG](https://qiita-image-store.s3.amazonaws.com/0/166435/a78c257e-4153-05b5-4848-fbff47edcc1d.jpeg)

- PC接続用基板に載せた状態は、こんな感じ。
![IMG_0314.JPG](https://qiita-image-store.s3.amazonaws.com/0/166435/467f6a0e-8d8d-1b2e-e516-e767aafe4146.jpeg)

---

# ラズパイとの疎通確認
- 事前準備
    - 操作用端末には、MacBook Airを使用。
    - 確認に使った環境は、[RasPBX](http://www.raspberry-asterisk.org/)をインストールしたRaspberry Pi 2 Model B。
    - コンソール入出力としてシリアルポートを使わないように設定。
        - [こちらの記事](http://nanicananica.blog.fc2.com/blog-entry-14.html)が参考になりました。
        
        ```
root@raspbx:~# nano /boot/cmdline.txt
root@raspbx:~# cat /boot/cmdline.txt
dwc_otg.lpm_enable=0 rpitestmode=1 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
root@raspbx:~# reboot
        ```

- ラズパイにSSH接続し、screenコマンドで無線モジュールとやり取りをしてみます。
    - ラズパイのIPアドレスは、環境に合わせて読み替えてください。
    - デフォルトのrootパスワードは、raspberryです。

```
MacBook-Air:raspbx user$ ssh root@192.168.1.120
root@192.168.1.120's password: 

Welcome to RasPBX - Asterisk for Raspberry Pi
　：
root@raspbx:~# screen /dev/ttyAMA0 115200
（以下、screenコマンドの画面になります）
```

- 無線モジュールのバージョン確認（SKVERコマンド）
    
```
SKVER
EVER 1.5.2
OK
```

- 無線モジュールの固有ID取得（SKINFOコマンド）
    - ここで入手したユーザーID（xxxx）とパスワード（yyyy）がないと、コマンドリファレンスなどの詳しいドキュメントを入手できません。

```
SKINFO
EINFO xxxx:0000:0000:0000:yyyy:aaaa:bbbb:cccc 001Daaaabbbbcccc 21 FFFF 0 
OK
```

- プロダクト設定コマンド
    - WOPTコマンド・ROPTコマンド
        - データ部の表示形式（00：バイナリ表示、01：ASCII表示）
    - RUARTコマンド
        - UART設定（00：115200bps、キャラクター間インターバルなし、フロー制御なし）
 
```
WOPT
WOPT 01
ROPT
OK 01
RUART
OK 00
```

とりあえず、ラズパイと無線モジュールの疎通確認はできました。

---

# PCとの疎通確認
- 事前準備
    - 操作用端末には、MacBook Airを使用。
    - PCとは、USB接続。

- シリアルデバイスの確認
    - /dev/tty.usbserial-A4001cT2と確認できました。

```
MacBook-Air:raspbx user$ ls -l /dev/tty.*
crw-rw-rw-  1 root  wheel   19,   0  3 18 16:43 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw-  1 root  wheel   19,  16  3 19 22:39 /dev/tty.usbserial-A4001cT2
MacBook-Air:raspbx user$ 
```

- screenコマンドを使用し、無線モジュールとやり取りしてみます。

```
MacBook-Air:raspbx user$ screen /dev/tty.usbserial-A4001cT2 115200
```

- 無線モジュールのバージョン確認（SKVERコマンド）

```
SKVER
EVER 1.5.2
OK
```

- 無線モジュールの固有ID取得（SKINFOコマンド）

```
SKINFO
EINFO xxxx:0000:0000:0000:yyyy:aaaa:bbbb:cccc 001Daaaabbbbcccc 21 FFFF 0 
OK
```

- プロダクト設定コマンド
 
```
WOPT
WOPT 01
ROPT
OK 01
RUART
OK 00
```

---

# 試しに、チャンネルスキャンしてみる。
- SKSCANコマンドを送ってみました。
    - 無線モジュールとは違うMACアドレス（EPANDESCのAddr）が表示されたので、おそらく、スマートメータではないかと。
    - ただ、同じコマンドを再発行したところ、応答がありませんでした。応答をガードする時間帯でもあるんでしょうか。

```
SKSCAN 0 FFFFFFFF 4 0
OK
EVENT 1F xxxx:0000:0000:0000:yyyy:aaaa:bbbb:cccc 0
EEDSCAN
0 21 0C 22 09 23 0A 24 09 25 08 26 0A 27 0A 28 0E 29 0E 2A 0B 2B 0F 2C 14 2D 28 
2E 21 2F 0E 30 0E 31 0E 32 0A 33 12 34 08 35 10 36 0C 37 0D 38 0C 39 0F 3A 0F 3B
 0D 3C 0E

SKSCAN 3 FFFFFFFF 4 0
OK
EVENT 20 xxxx:0000:0000:0000:yyyy:aaaa:bbbb:cccc 0
EPANDESC
  Channel:3B
  Channel Page:09
  Pan ID:####
  Addr:################
  LQI:D1
  Side:0
EVENT 22 xxxx:0000:0000:0000:yyyy:aaaa:bbbb:cccc 0
SKSCAN 3 FFFFFFFF 4 0
OK
EVENT 22 xxxx:0000:0000:0000:yyyy:aaaa:bbbb:cccc 0
```
---

# アンテナについて
- メーカー推奨のW1063M（1枚目の写真に写っているアンテナです）のほか、秋月電子で購入した[安いアンテナ](http://akizukidenshi.com/catalog/g/gP-03703/)も試してみました。
    - 結果としては、両方とも使えました。（試したのはアクティブスキャンだけですが）
    - ちなみに、W1063Mは1100円、秋月電子のアンテナは500円でした。大きさは、W1063Mの３分の１くらいです。
    - おすすめできるものではありませんが、小さく収まるので、秋月電子のアンテナをしばらく使ってみようと思っています。

---

# スマートメータとの通信
- Bルートサービスが開通（Bルート認証IDとパスワードが届いた）したので、少し試してみました。
- 参考となる資料（以下、自分用のメモとして、必要なものだけ抜粋）
    - [ECHONET Lite 通信ミドルウェア仕様](https://echonet.jp/spec_v112_lite/)
        - スマートメータと通信するための電文フォーマットが規定されています。

|EHD1/EHD2|TID|SEOJ|DEOJ|ESV|OPC|EPC|PDC|EDT|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|1B/1B|2B|3B|3B|1B|1B|1B|1B|1B〜|

- 電文要素の詳細
    - EHD1/EHD2
        - 0x1081: ECHONET Lite規格・形式1（規定電文形式）
    - TID
        - 0x0001: 規定がないようなので、とりあえず
    - SEOJ/DEOJ: 送信元/送信先ECHONET Liteオブジェクト
        - 0x028801: 住宅・設備関連機器クラス・低圧スマート電力量メータ
        - 0x05FF01: 管理・操作関連機器クラス・コントローラ（無線モジュール＋ラズパイが相当します）
    - ESV: ECHONET Lite サービス
        - 0x62: プロパティ読み出し要求
        - 0x72: プロパティ値読み出し応答
        - 0x73: プロパティ値通知
    - OPC: 処理プロパティ数（EPC〜EDTをセットとした個数）
    - EPC: ECHONET Lite プロパティ
        - 0xE1: 積算電力量単位
        - 0xE7: 瞬時電力(W)
        - 0xEA: 定時正方向積算電力量(kWh)
        - 0xEB: 定時逆方向積算電力量(kWh)
    - PDC: EDT長（バイト数）
    - EDT: プロパティ値データ

- [ECHONET 機器オブジェクト詳細規定（低圧スマート電力量メータクラス規定）](https://echonet.jp/spec_object_rh/)

|EPC|PDC|EDT|
|:-:|:-:|:-:|
|0xE1|0x01|積算電力量単位|
|1B|1B|1B|

|EPC|PDC|EDT|
|:-:|:-:|:-:|
|0xE7|0x04|瞬時電力|
|1B|1B|4B|

|EPC|PDC|EDT|||||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|0xEA/0xEB|0x0B|年|月|日|時|分|秒|積算電力量|
|1B|1B|2B|1B|1B|1B|1B|1B|4B|
                 
- サンプルコード
    - SKJOINに成功すると、３０分ごとに積算電力量がスマートメータから送られてきます。
    - このサンプルは、その定時積算電力量を受け取って標準出力するだけのものです。

```python:bp35c0_rcv.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function

import sys
import serial
import time

# シリアルポート初期化
serialPortDev = '/dev/ttyAMA0'
ser = serial.Serial(serialPortDev, 115200)

while True:
    line = ser.readline()
    print(line, end="")

    if line.startswith("ERXUDP") :
        cols = line.strip().split(' ')
        res = cols[9]   # UDP受信データ部分
        tid = res[4:4+4]
        seoj = res[8:8+6]
        deoj = res[14:14+6]
        ESV = res[20:20+2]
        OPC = res[22:22+2]
        if seoj == "028801" :
            if ESV == "72" :
                # スマートメーター(028801)から来た応答(72)なら
                # 瞬時電力計測値
                EPC = res[24:24+2]
                if EPC == "E7" :
                    # 内容が瞬時電力計測値(E7)だったら
                    hexPower = line[-8:]    # 最後の4バイト（16進数で8文字）が瞬時電力計測値
                    intPower = int(hexPower, 16)
                    print(u"瞬時電力計測値:{0}[W]".format(intPower))
                else :
                    print(u"Other EPC: {0}".format(EPC))
            elif ESV == "73" :
                # スマートメーター(028801)から来たプロパティ通知(73)なら
                # 積算電力量計測値
                iOPC = int(OPC, 16)
                print(u"OPC: {0}".format(iOPC))
                for index in range(iOPC) :
                    offset = index * 26

                    # EA(EPC:正方向定時積算電力量)
                    offset = offset + 24
                    len = 2
                    EPC = res[offset:offset+len]
                    offset = offset + len
                    len = 2
                    PDC = res[offset:offset+len]
                    offset = offset + len
                    len = 4
                    intYear = int(res[offset:offset+len],16)
                    offset = offset + len
                    len = 2
                    intMonth = int(res[offset:offset+len],16)
                    offset = offset + len
                    len = 2
                    intDay = int(res[offset:offset+len],16)
                    offset = offset + len
                    len = 2
                    intHour = int(res[offset:offset+len],16)
                    offset = offset + len
                    len = 2
                    intMin = int(res[offset:offset+len],16)
                    offset = offset + len
                    len = 2
                    intSec = int(res[offset:offset+len],16)
                    offset = offset + len
                    len = 8
                    intValue = int(res[offset:offset+len],16)
                    if EPC == "EA" :
                        print(u"正方向定時積算電力量: {0:04d}/{1:02d}/{2:02d} {3:02d}:{4:02d}:{5:02d} {6:.1f}[kWh]".format(intYear,intMonth,intDay,intHour,intMin,intSec,float(intValue)/10))
                    elif EPC == "EB" :
                        print(u"逆方向定時積算電力量: {0:04d}/{1:02d}/{2:02d} {3:02d}:{4:02d}:{5:02d} {6:.1f}[kWh]".format(intYear,intMonth,intDay,intHour,intMin,intSec,float(intValue)/10))
                    else :
                        print(u"Other EPC: {0}".format(EPC))
            else :
                print(u"Other ESV: {0}".format(ESV))
        else :
            print(u"Other SEOJ: {0}".format(seoj))
    else :
        print(u"Not ERXUDP")
```

- 実行例

```
ERXUDP xxxx:0000:0000:0000:zzzz:dddd:eeee:ffff xxxx:0000:0000:0000:yyyy:aaaa:bbbb:cccc 0E1A 0E1A ggggddddeeeeffff 1 0 0026 1081000102880105FF017302EA0B07E1031A10000000000AA9EB0B07E1031A100000000004DE
OPC: 2
正方向定時積算電力量: 2017/03/26 16:00:00 272.9[kWh]
逆方向定時積算電力量: 2017/03/26 16:00:00 124.6[kWh]
```