京セラモニタ（PMD47A）とのシリアル通信データ解析

# はじめに
- 京セラ製太陽光発電システムが稼働している関係で、エコノナビット（PMD47A）という発電状況をモニタする装置（以下、モニタ）を設置しています。
- このモニタはPCとシリアル通信ポートで繋がっていて、京セラから提供されている通信ソフトを使うと、モニタから収集したデータがCSVファイルで出力されます。
- 定期的にモニタからのデータ収集を行いたいのですが、それだとPCを常時稼働させる必要があります。
- このデータ収集をラズパイで行いたいと思い、その通信内容を把握することが今回の目的となります。

---
# UART通信のキャプチャ
- ラズパイと[秋月電子製ＵＳＢ－シリアル２ｃｈ変換モジュール](http://akizukidenshi.com/catalog/g/gM-02990/)を使って、モニターPC間のUART通信をキャプチャし、解析してみることにしました。
- 構成図
![linemon.png](https://qiita-image-store.s3.amazonaws.com/0/166435/c6371849-31a6-46ac-155d-df3e711127da.png)

- 回路図
    - CN1がシリアルポートの9ピンDSUBとなっており、RxD（２番）、TxD（３番）とも上記モジュールの受信入力に繋がるようにします。
    - ラズパイは、3.3Vの供給に使い、上記モジュールとはUSBケーブルで接続します。
![スクリーンショット 2017-05-28 18.18.57.png](https://qiita-image-store.s3.amazonaws.com/0/166435/4b94ff93-3037-bf26-9807-d64b0e95639e.png)

- プログラム
    - 2chのUART受信を行い、そのデータを標準出力するだけのものです。

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

import time
import datetime
import threading
import serial

event = threading.Event()
lock = threading.Lock()

def cap(dev, baud, ch) :
  ser = serial.Serial(dev, baud, timeout=5)
  while event.is_set() != True :
    data = ser.read()
    if data.__len__() > 0 :
      lock.acquire()
      try :
        if ch == 0 :
          print("{0:02X},".format(ord(data)))
        else :
          print(",{0:02X}".format(ord(data)))
      finally :
        lock.release()

def wait(msg) :
  code = raw_input("")
  event.set()

thread1 = threading.Thread(target=cap, args=('/dev/ttyUSB0', 38400, 0,))
thread2 = threading.Thread(target=cap, args=('/dev/ttyUSB1', 38400, 1,))
thread3 = threading.Thread(target=wait, args=('>>> ',))
thread1.start()
thread2.start()
thread3.start()
thread1.join()
thread2.join()
thread3.join()
```

---
# 解析結果
- プロトコル
    - 下記の流れでやり取りされる。（全データを送り終わるまで、５と６が繰り返される）
        1. PCからの要求電文
        2. モニタからのACK電文
        3. モニタからの開始電文
            - この電文から、全データ数を抽出できる。
        4. PCからのACK電文
        5. モニタからのデータフレーム
        6. PCからのACK電文
        7. モニタからの終了電文
        8. PCからのACK電文
    - とりあえず、全データを取得するには、下記の電文フォーマットを把握できれば問題なさそう。
        - PCからの要求電文（1）
        - モニタからの開始電文（3）
        - PCからのACK電文（4,6）
        - モニタからのデータフレーム（5）

1. PCからの要求電文構成
    - STX（02h）から始まり、ETX（03h）で終わる電文。
        - STX, CMD1000FFFF50, ETX
        - 何度かキャプチャして比較したが、毎回同じだった。固定文字列でよさそう。

1. PCからのACK電文構成
    - STX（02h）、ACK（06h）、ETX（03h）の3バイト。
    
1. モニタからの開始電文構成
    - STX（02h）から始まり、ETX（03h）で終わる電文。
        - STX, CMD01000000214FCB, ETX
        - この電文から、全データ数214Fh（8527byte）が得られる。
        - 何度かキャプチャして比較したが、毎回同じだった。
        - 8527byte固定でよいかもしれないが、電文から取得すべきと思われる。
    - 具体例

    ```
02434D44303130303030303032313446434203
```
 
2. モニタからのデータフレーム構成
    - フレームの正常性確認は、BCCを含めたn+2バイトを加算して、下位８ビットがFF（16進値）となるかどうかで判定。
        - データ長が１バイト、ペイロードがnバイト、BCCが１バイト。
    - 具体例
        - データ長＝21h（33byte）
        - BCC＝93h
        - チェックサム（16進）＝21+45+43+4E+56+00+01+00+68+21+4F+00+68+00+14+00+7C+00+03+00+23+00+E5+00+02+06+90+0E+05+00+02+06+90+93＝5FFh

    ```
2145434E5600010068214F00680014007C0003002300E5000206900E050002069093
```

1. データ・レイアウト
    - データフレームのペイロードを抽出して、順に並べたもの。
    - ３０分毎のデータ、日毎のデータ、月毎のデータが下表に示すようなレイアウトで配置されている。

|項番|種別|アドレス範囲|備考|
|--:|:--|:-:|:--|
|1|３０分毎のデータの日付|007C〜00E4|年月日の3バイトが、35日分|
|2|３０分毎の発電データ|00E5〜0E04|10倍した値が2バイト（16進値）で1データ、48データが1日分で35日。未取得の時間帯は、7530hとなっている。|
|3|３０分毎の買電ー売電データ|0E05〜1B24|10倍した値が2バイト（16進値）で1データ、48データが1日分で35日。未取得の時間帯は、7530hとなっている。|
|4|日毎のデータの日付|1B48〜1C73|年月日の3バイトが100日分|
|5|日毎の発電データ|1C74〜1D3B|1日分が2バイト（16進値）で、100日分|
|6|日毎の買電ー売電データ|1D3C〜1E03|1日分が2バイト（16進値）で、100日分|
|7|月毎のデータの日付|1E04〜1EB7|年月日の3バイトが、60ヶ月分|
|8|月毎の発電データ|1EB8〜1F2F|1月分が2バイト（16進値）で、60ヶ月分|
|9|月毎の買電ー売電データ|1F30〜1FA7|1月分が2バイト（16進値）で、60ヶ月分|