Shujima Blog

Apple製品,技術系の話をするブログ

OMRON 環境センサ (2JCIE-BL01)の使い方

環境

ウェザーニュースのWxBeacon2でも動くと思われます.

セットアップ

モードの変更

買ってきたままのセンサを公式GitHubの通りに設定しても動きません.

qiita.com

にあるようにiPhoneまたはAndroidのアプリ「BLE Scanner」などを用いて,センサのモードを変更する必要があるようです.

上のQiita 記事では加速度センサを含まない代わりに不快指数などを含むEPモードに変更しています.

全センサの値を含めたい場合は下記記事を参照してください.

masa-flyu.hatenablog.com

理由はこちらを見なければわからないようですが,初見でこんなもの見せられてわかるわけないじゃ無いですか...

https://omronmicrodevices.github.io/products/2jcie-bl01/communication_if_manual.html

あとは公式GitHubページ

github.com

の通りに設定します.

正しく設定した状態で

$ python2 ./envsensor-observer.py

などのコマンドで実行すると,

----------------------------------------------------
sensor status : 2018-09-26 20:43:00.396593 (Intvl. 300sec)
 FA65BB20CE06 : EP : ACTIVE (2018-09-26 20:42:59.658104)

のようにEPに設定されたデバイスが発見されます.

初期設定のままだと,最初の表示までに5分かかります.

なお,前述のモード変更を行わないと,

----------------------------------------------------
sensor status : 2018-09-26 20:43:00.396593 (Intvl. 300sec)

のように,5分ごとに表示されますが,これはセンサデータを受信できていません.

公式Githubのソースを読んでみる

唯一まともであるはずの公式ライブラリですが,正直これを見て改変するのも容易じゃないという印象です. main文が長すぎますし,他から参照するのも大変でしょう. というわけで,結構長い時間をかけてソースを読んでみました.

別に大したことは書いていませんが,メモ程度にコメントを追加したりしたので一応載せておきます.

公式GitHubのソースは以下のファイルに分かれています.

  • envsensor_observer.py
  • ble.py
  • conf.py
  • csv_logger.py
  • sensor_beacon.py
  • str_util.py

それぞれの中身を読んでみます.

sensor_beacon.py

とても重要なファイルです. SensorBeaconクラスを保持しています.クラスのメンバは

    bt_address = ""
    seq_num = 0
    val_temp = 0.0
    val_humi = 0.0
    val_light = 0.0
    val_uv = 0.0
    val_pressure = 0.0
    val_noise = 0.0
    val_di = 0.0
    val_heat = 0.0
    val_ax = 0.0
    val_ay = 0.0
    val_az = 0.0
    val_battery = 0.0

    rssi = -127
    distance = 0
    tick_last_update = 0
    tick_register = 0

    flag_active = False

    sensor_type = "UNKNOWN"
    gateway = "UNKNOWN"

などがあり,センサのデータを保持しています.

ble.py

Bluetooth Low Energyパケットを解読するファイルです. 基本的に弄らなくても良いようです.

conf.py

様々な設定ができます. デバッグ中は5分待つのが大変なので,

CHECK_SENSOR_STATE_INTERVAL_SECONDS = 10

などに設定しておくと良いでしょう.

ただし電池持ちには注意です.

csv_logger.py

CSVファイルに書き込むためのファイルです. 書き込み処理の大部分はloggerモジュールに依存しているようで,中身はほとんどありません.

str_util.py

数値の形式を変換する関数の集まりです.

envsensor_observer.py

main文を内包し,環境センサの値を読むための実行ファイルです.

グローバル変数

使用されているグローバル変数は以下の通りです.

  • VER
    • バージョン用定数
  • GATEWAY
    • ホスト名
  • sensor_list[]
    • センサの情報を入れるSensorBeaconクラスのリスト.2JCIE-BL01が1つならばリスト長は1になる(温度センサ,加速度センサ,,と数えるわけではない)
  • flag_update_sensor_status
    • センサ情報をアップデートされたというフラグ.eval_sensor_state()関数内通過でTrue.

main内の変数

  • flag_scanning_started
    • main開始からBLEソケットが開くまでの間Falseその後ずっとTrue
  • args
    • プログラム起動時に渡される引数("-d"とか)をarg_parse()関数によって整理したもの.
    • args.debug
  • debug
    • プログラム起動時にdebugモードを指定されたときにmain文内でTrue.以降変化なしで,プログラム全体で使用される.
  • influx_client
  • csv_path
  • loghndl
    • CSVログファイルへのハンドル
  • form
    • CSVファイルの書式
  • log
  • sock
    • BLEソケットのハンドル
  • timer
    • eval_sensor_state()関数を走らせるタイマースレッドのインスタンス.conf.CHECK_SENSOR_STATE_INTERVAL_SECONDS 秒後から開始される.なお,eval_sensor_state()関数内にも同じコードがあり,30秒ごとに自分を呼び出す.
  • old_filter
    • bluezのBLE端末検索条件を変更する前に以前の状態を記憶しておく変数.プログラム終了時に書き戻す.
  • flt
    • 新規に取得したフィルタ

parse_events(sock, loop_count=10)

BLE通信で受信したあらゆるパケットについて,bleモジュールを利用して解析します.

OMRON製品かつデバイスの名前(Local Name)が"EP"または"IM"など,複数の条件を満たしたパケットの場合はble.verify_beacon_packet()がTrueとなります.

これを利用して,環境センサからのパケットを判別し,発見したパケットが,どのセンサから送られたものかを調べ,sensor_listを更新します .

handling_data(sensor)

センサのデータを各出力先(クラウドCSV)に渡します.

eval_sensor_state()

この関数自体がCHECK_SENSOR_STATE_INTERVAL_SECONDS(標準で5分)の間隔で定期的に実行されます.

センサからの信号がタイムアウトに設定した時刻以内に来ているかを確認し,来ていなければタイムアウト処理をします.

その後,"flag_update_sensor_status"フラグを立てて,main関数内で,状態をprintさせます.

print_sensor_state()

main関数から呼ばれる,print用の関数です.

return_number_packet

return_string_packet(pkt)

find_sensor_in_list(sensor, List)

List(SensorBeaconクラスの配列)の中からsensor( SensorBeaconクラス)と同じセンサを探索します.

判断にはアドレスを用いており,一致するアドレスのセンサがあれば,その要素の番号,無ければ-1を返します.

init_fluentd()

fluentdをセットアップします

create_influx_database()

influxをセットアップします

arg_parse()

"envsensor_observer"実行時の引数(--debugなど)をチェックします

main

mainです.

# main文だけ抜き出している.


# main function
if __name__ == "__main__":
    try:
        flag_scanning_started = False

        # 引数の受け取り(--debug)
        # process command line arguments
        debug = False
        args = arg_parse()
        if args.debug:
            debug = True


        # reset bluetooth functionality
        try:
            if debug:
                print "-- reseting bluetooth device"
            # ble通信の開始
            ble.reset_hci()
            if debug:
                print "-- reseting bluetooth device : success"
        except Exception as e:
            print "error enabling bluetooth device"
            print str(e)
            sys.exit(1)

        # クラウド(influxDB)の設定(デフォルトではオフ)
        # initialize cloud (influxDB) output interface
        try:
            if conf.INFLUXDB_OUTPUT:
                if debug:
                    print "-- initialize influxDB interface"
                influx_client = InfluxDBClient(conf.INFLUXDB_ADDRESS,
                                               conf.INFLUXDB_PORT,
                                               conf.INFLUXDB_USER,
                                               conf.INFLUXDB_PASSWORD,
                                               conf.INFLUXDB_DATABASE)
                influx_client.create_database(conf.INFLUXDB_DATABASE)
                if debug:
                    print "-- initialize influxDB interface : success"
        except Exception as e:
            print "error initializing influxDB output interface"
            print str(e)
            sys.exit(1)

        # fluentdの設定(デフォルトではオフ)
        # initialize fluentd forwarder
        try:
            if conf.FLUENTD_FORWARD:
                if debug:
                    print "-- initialize fluentd"
                init_fluentd()
                # create database when using influxDB through fluentd.
                if conf.FLUENTD_INFLUXDB:
                    create_influx_database()
                if debug:
                    print "-- initialize fluentd : success"
        except Exception as e:
            print "error initializing fluentd forwarder"
            print str(e)
            sys.exit(1)

        # CSVファイルの設定
        # initialize csv output interface
        try:
            if conf.CSV_OUTPUT:
                if debug:
                    print "-- initialize csv logger"

                if not os.path.isdir(conf.CSV_DIR_PATH):
                    os.makedirs(conf.CSV_DIR_PATH)
                csv_path = conf.CSV_DIR_PATH + "/env_sensor_log.csv"
                # create time-rotating log handler
                loghndl = csv_logger.CSVHandler(csv_path, 'midnight', 1)
                form = '%(message)s'
                logFormatter = logging.Formatter(form)
                loghndl.setFormatter(logFormatter)

                # create logger
                log = logging.getLogger('CSVLogger')
                loghndl.configureHeaderWriter(envsensor.csv_header(), log)
                log.addHandler(loghndl)
                log.setLevel(logging.INFO)
                log.info(envsensor.csv_header())

                if debug:
                    print "-- initialize csv logger : success"
        except Exception as e:
            print "error initializing csv output interface"
            print str(e)
            sys.exit(1)

        # 
        # initialize bluetooth socket
        try:
            if debug:
                print "-- open bluetooth device"
            sock = ble.bluez.hci_open_dev(conf.BT_DEV_ID)
            if debug:
                print "-- ble thread started"
        except Exception as e:
            print "error accessing bluetooth device: ", str(conf.BT_DEV_ID)
            print str(e)
            sys.exit(1)

        # set ble scan parameters
        try:
            if debug:
                print "-- set ble scan parameters"
            ble.hci_le_set_scan_parameters(sock)
            if debug:
                print "-- set ble scan parameters : success"
        except Exception as e:
            print "failed to set scan parameter!!"
            print str(e)
            sys.exit(1)

        # start ble scan
        try:
            if debug:
                print "-- enable ble scan"
            ble.hci_le_enable_scan(sock)
            if debug:
                print "-- ble scan started"
        except Exception as e:
            print "failed to activate scan!!"
            print str(e)
            sys.exit(1)

        flag_scanning_started = True
        print ("envsensor_observer : complete initialization")
        print ""

        # activate timer for sensor status evaluation
        timer = threading.Timer(conf.CHECK_SENSOR_STATE_INTERVAL_SECONDS,
                                eval_sensor_state)
        timer.setDaemon(True)
        timer.start()

        # preserve old filter setting
        old_filter = sock.getsockopt(ble.bluez.SOL_HCI,
                                     ble.bluez.HCI_FILTER, 14)
        # perform a device inquiry on bluetooth device #0
        # The inquiry should last 8 * 1.28 = 10.24 seconds
        # before the inquiry is performed, bluez should flush its cache of
        # previously discovered devices
        flt = ble.bluez.hci_filter_new()
        ble.bluez.hci_filter_all_events(flt)
        ble.bluez.hci_filter_set_ptype(flt, ble.bluez.HCI_EVENT_PKT)
        sock.setsockopt(ble.bluez.SOL_HCI, ble.bluez.HCI_FILTER, flt)


        #無限ループ
        while True:
            # ble通信のイベントをチェック
            # parse ble event
            parse_events(sock)
            # "flag_update_sensor_status"フラグが立ったら
            # eval_sensor_state()内でCHECK_SENSOR_STATE_INTERVAL_SECONDSごとにフラグを立てている(デフォルトで5分ごと)
            if flag_update_sensor_status :
                # センサの情報を表示する
                print_sensor_state()
                # フラグを戻す
                flag_update_sensor_status = False

    except Exception as e:
        print "Exception: " + str(e)
        import traceback
        traceback.print_exc()
        sys.exit(1)

    finally:
        if flag_scanning_started:
            # restore old filter setting
            sock.setsockopt(ble.bluez.SOL_HCI, ble.bluez.HCI_FILTER,
                            old_filter)
            ble.hci_le_disable_scan(sock)
        print "Exit"
当ブログをご利用いただく際には免責事項をお読みください。