11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Raspberry Pi+PythonでLoRaWAN Deviceにチャレンジしてみる(1)

Last updated at Posted at 2017-10-25

#はじめに

某所より、「今度Fukuoka City LoRaWANってゆう実証実験ば福岡市内を中心にやりますけん、おたくも都合ば付けてやらんですか?(注:筆者脳内訳)」というお誘いが当社にあったそうで、ふたつ返事で絶賛参加することになったようです。

そこで部署内の誰がこの件を担当するか、という事になり、「ローラ?なにそれ美味しいの?もしかして『オッケー』が口癖の女性タレント!?」状態で予備知識ゼロな私に、何故かRaspberry Piを触ったことがあるというだけで、白羽の矢が立ってしまいました。

という訳(?)で、ラズパイとLoRaWANモデムを繋いでPythonから通信し、既存DWHへデータを格納できるかチャレンジしたいと思います。

まず今回は、Raspberry PiとLoRaWAN Modemを接続し、最初はシリアルコンソール越しに、次にPythonプログラムからシリアル通信できるか確認したいと思います。

#最終目標的なトポロジ
下図のようにAWSを利用することで、アプリケーションサーバを構成したいと思います。
Topology.png

#用意したもの

  • Raspberry Pi 3 Model B (Complete Starter Kit
  • LoRaWAN Modem (RigingHF社製RHF3M076) ※運営事業者(NTTネオメイト様)からお借りしました。

#開発環境

  • Raspbian GNU/Linux 9 (stretch)
  • Python 3.5.3

#ラズパイとモデム接続

ラズパイのUSBポートにモデムを接続して、ラズパイを起動します。
※事前にネットワーク周りは設定済みです。
IMG_0582.JPG

#シリアル通信できるようにする
Raspbianでは、デフォルトでSerialが無効化されていますので、有効化します。

$ sudo raspi-config

5 Interfacing Opsionsを選択
2017-10-24_17h34_41.png

P6 Serialを選択
2017-10-24_17h35_51.png

<はい>(または<Yes>)を選択
2017-10-24_17h36_39.png

<了解>(または<Ok>)を選択
2017-10-24_17h37_32.png

#モデムを認識しているか確認

この時点でOSがモデムを認識しているか確認してみます。

モデムを接続していない場合

$ lsusb
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

モデムを接続している場合

$ lsusb
Bus 001 Device 005: ID 0483:5740 STMicroelectronics STM32F407
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

どうやらRHF3M076は、OS上ではSTM32F407というProduct名で認識されるようです。
恐らくSTマイクロ社製の同チップが乗ってるのでしょう。

更にSerial通信用のポート(デバイスファイル)が出来ているか確認してみます。

$ ls -l /dev/tty*
・・・
crw-rw---- 1 root dialout 166,  0 10月 23 11:22 /dev/ttyACM0
・・・

モデムを外した場合、上記デバイスが消えるので、この /dev/ttyACM0 で間違いなさそうです。

#モデムにシリアル端末から接続してみる

シリアル端末プログラムの準備

CUI環境でシリアルコンソールを使いたいので Minicomをインストールします。

$ sudo apt-get install minicom

インストールが完了したら初期設定します。

$ sudo LANG=c minicom -s

Serial port setupを選択
2017-10-25_10h50_40.png

A - Serial Device行のデバイス名を/dev/ttyACM0に変更
2017-10-25_10h52_37.png

Screen and keyboardを選択
2017-10-25_10h53_53.png

デフォルトではコンソールから入力したコマンドが表示されない(エコーバックしない)ため、ローカルエコーするよう変更
Q - Local echoYesに変更([Q]キーを押すごとにYesNoが切り替わる)
2017-10-25_10h54_56.png

Save setup as dflを選択(設定値をデフォルト値として保存)
2017-10-25_10h55_46.png

Exitを選択(Exit from minicomを選択すると、Minicom自体も終了)
2017-10-25_10h56_44.png

Exitを選択し、以下の表示に切り替わればモデムへコンソール接続された状態となります。

Welcome to minicom 2.7

OPTIONS: I18n
Compiled on Apr 22 2017, 09:14:19.
Port /dev/ttyACM0, 14:33:04

Press CTRL-A Z for help on special keys

ちなみにコンソール接続中、[ctrl]+[A]、[Z]キーでコマンドサマリが表示され、更に[Q]キーを押すとMinicomを終了(コンソール切断)することができます。

また再度コンソール接続するには、次のコマンドを入力します。

$ sudo LANG=c minicom

#ATコマンドで操作してみる
##改行コードの変更(TeraTermの場合)

RHF3M076では、ATコマンドの終端には必ず改行コード(<LF>(\n)、または<CR><LF>(\r\n))が必要です。
※ちなみにモデムからのレスポンスメッセージには、行終端に<CR><LF>(\r\n)が付いていました。

そこでモデムをATコマンドで操作する前に、Raspberry Pi にSSH接続しているターミナル(今回はTeraTerm)で改行コードを設定しなおす必要があります。

設定(S) - 端末(T)を選択
2017-10-25_11h28_54.png

受信、送信の改行コードを、下図のように設定
2017-10-25_11h30_46.png

##コマンドを入力してみる
それではコンソールからAT(エンターキー)と入力してみます。

AT
+INFO: Input timeout, start parse
+AT: OK

何故か最後のエンターキーを押す前に、レスポンスが返ってきました。
他のATコマンドを試そうとしても、最初のAT2文字を打った時点で、必ずレスポンスが返ってきます。
また+AT: OKメッセージの前に、+INFO: Input timeout, start parseというメッセージが返ってきていますので、直感的にこれが原因だろうと推測しました。

そこで色々調べたところ、これは「UART受信タイムアウト機能」というものらしく、モデムがAT文字を受信した直後から予め設定されたタイムアウト値(ミリ秒)に達すると、強制的に受信終了する機能だそうです。
(このタイムアウト設定値はAT+UART=TIMEOUTコマンドで確認できます。)

このままだと手動でATコマンドを試すことができないので、この機能を一時的に無効にするために次のATコマンドを入力します。
但し、コンソールから直接入力すると上記の二の舞になりますので、まずはエディタ等でコマンドを入力し、それをコンソールにコピペすることにします。

AT+UART=Timeout,0

うまくタイムアウト機能を無効化できれば、次のようなレスポンスが返ってきます。

+INFO: Input timeout, start parse
+UART: TIMEOUT is disabled

これで、ATコマンドで設定や設定値を確認できるようになりました。

試しにモデムに登録されているID(DevAddr、DevEui、AppEui)を確認してみます。

AT+ID
+ID: DevAddr, 01:23:45:67
+ID: DevEui, aa:bb:cc:dd:ee:ff:aa:bb
+ID: AppEui, ab:cd:ef:ab:cd:ef:ab:cd

尚、ATコマンドの詳しいリファレンスはここからダウンロードできます。

#Pythonからモデムと通信してみる

シリアル通信でモデムをATコマンドを使って操作できるようになりましたので、次はPythonからモデムとシリアル通信してみます。

##独自モジュール作成

pySerialモジュールを利用して、モデムをATコマンドで操作できるようにする独自モジュールRHF3M076.pyを作成します。

尚、私の環境ではすでにpySerialモジュールがインストールされていましたが、もしインストールされていない場合は次のコマンドでインストールしましょう。

$ sudo pip install pySerial

そして以下が実際に作成したRHF3M076.pyのコードです。
この時点では解りやすくするため、単にIDを取得する為だけの機能に限定したモジュールにしました。

RHF3M076.py
#!/usr/bin/env python3
# coding: utf-8

import serial
import re

# Class of LoRaWAN Modem
class RHF3M076:

    # Constructor
    def __init__(self, port='/dev/ttyACM0', baud=115200, timeout=0.1):
        self._port = port
        self._baud = baud
        self._timeout = timeout
        self._crlf = '\r\n'
        self._ptnDevAddr = r'\+ID: DevAddr, (([0-9A-Fa-f]{2}[:-]){3}[0-9A-Fa-f]{2})'
        self._ptnDevEui = r'\+ID: DevEui, (([0-9A-Fa-f]{2}[:-]){7}[0-9A-Fa-f]{2})'
        self._ptnAppEui = r'\+ID: AppEui, (([0-9A-Fa-f]{2}[:-]){7}[0-9A-Fa-f]{2})'
        
        self._open()
        self._getId()

    # Open serial port
    def _open(self):
        self._modem = serial.Serial(
            port=self._port,
            baudrate=self._baud,
            bytesize=serial.EIGHTBITS,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            timeout=self._timeout,
            xonxoff=False,
            rtscts=False,
            write_timeout=None,
            dsrdtr=False,
            inter_byte_timeout=None
        )
        return()

    # Get IDs
    def _getId(self):
        cmd = 'AT+ID'
        self._write(cmd)

        lines = self._read()
        print(lines)
        for line in lines:
            res = re.match(self._ptnDevAddr, line)
            if res:
                self._DevAddr = res.group(1)
                continue
            res = re.match(self._ptnDevEui, line)
            if res:
                self._DevEui = res.group(1)
                continue
            res = re.match(self._ptnAppEui, line)
            if res:
                self._AppEui = res.group(1)

    # Send to serial port
    def _write(self, cmd):
        cmd = cmd + self._crlf
        ret = self._modem.write(cmd.encode())
        return(ret)

    # Receive from serial port
    def _read(self):
        result = []
        lines = self._modem.readlines()
        for line in lines:
            result.append(line.decode().replace(self._crlf, ''))
        return(result)

    @property
    def DevAddr(self):
        return(self._DevAddr)

    @property
    def DevEui(self):
        return(self._DevEui)

    @property
    def AppEui(self):
        return(self._AppEui)
       
    # Destructor
    def __del__(self):
        self._modem.close()

尚、_read_writeメソッドの中で文字列をencode/decodeしていますが、これはプログラムからモデムと通信する場合、改行コードの関係上bytes型でやり取りする必要がある為です。

##テスト用スクリプトを作成し、動かしてみる

モデム用モジュールをテストするスクリプトを書きます。

main.py
#!/usr/bin/env python3
# coding: utf-8

from RHF3M076 import RHF3M076

def main():
    modem = RHF3M076()

    print('DevAddr: ' + modem.DevAddr)
    print('DevEui: ' + modem.DevEui)
    print('AppEui: ' + modem.AppEui)

    modem = None
    return()

if __name__ == '__main__':
    main()

以下がテストスクリプトを実行してみた結果です。
Pythonとモデム間で、問題なく通信できているようです。

$ chmod +x main.py
$ ./main.py
DevAddr: 01:23:45:67
DevEui: aa:bb:cc:dd:ee:ff:aa:bb
AppEui: ab:cd:ef:ab:cd:ef:ab:cd
11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?