環境
ウェザーニュースのWxBeacon2でも動くと思われます.
セットアップ
モードの変更
買ってきたままのセンサを公式GitHubの通りに設定しても動きません.
にあるようにiPhoneまたはAndroidのアプリ「BLE Scanner」などを用いて,センサのモードを変更する必要があるようです.
上のQiita 記事では加速度センサを含まない代わりに不快指数などを含むEPモードに変更しています.
全センサの値を含めたい場合は下記記事を参照してください.
理由はこちらを見なければわからないようですが,初見でこんなもの見せられてわかるわけないじゃ無いですか...
https://omronmicrodevices.github.io/products/2jcie-bl01/communication_if_manual.html
あとは公式GitHubページ
の通りに設定します.
正しく設定した状態で
$ 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
- influxクラスのインスタンス
- csv_path
- loghndl
- CSVログファイルへのハンドル
- form
- CSVファイルの書式
- log
- loggingクラスのインスタンス
- 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)
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"