オムロン環境センサ と ラズパイ で ラジコンカー − GOBERNABLES de Pi −
今年のみんなのラズパイコンテストでOMRON製の環境センサの無償提供が実施されましてー
【再】応募者に、ラズパイ3、拡張基板、環境センサを無償提供します!
「みんなのラズパイコンテスト」の特別企画です。コンテストへの応募が無償提供の条件です。
ぜひ、こちらから応募ください! → https://t.co/i5UM8d1Jes
— 日経Linux | ラズパイマガジン (@nikkei_Linux) 2018年7月17日
即効でアイディア送ってGET!ありがとうございまーす!
今年もラズパイコンテストの季節がやってきた。センサ届きました。ありがとうございます。#オムロン #ラズパイ pic.twitter.com/ymHs86OPXp
— HomeMadeGarbage (@H0meMadeGarbage) 2018年8月3日
“センサ観測値(加速度とか)を用いて、ラジコン作ります!”ってアイディアを送ったのだけど、この時点ではセンサのスペックよくわからない状態(事前に調べとけよ)。。。
本当にセンサが届いたとき、「やっべ。。。」と思いつつ焦って調査&ラジコン製作スタートしましたとさ。このときばかりはトランキーロじゃいられなかったゼ。。
目次
構成
環境センサの加速度と照度の測定値をラズパイに送って、モータとLEDを制御します。
部品
- オムロン製 環境センサ 2JCIE-BL01
- Raspberry Pi Zero W
-
I2Cモータードライバ・モジュール DRV8830
- 白色LED
車のヘッドライト用。ダイソーの自転車用LEDライトから取り出しましたww白色LEDが手元にないのでダイソーの自転車用LEDライトから取った。108円で5個ならコスパ悪くないよね。1kと30ohmのチップ抵抗も載ってるし pic.twitter.com/UUbrR5hXFn
— HomeMadeGarbage (@H0meMadeGarbage) 2018年9月7日
- ダイソーの電池式モバイルバッテリー
電源として使用。単三電池2本を5Vにしてくれます。108円でこれはいいよね。今度分解してみよう。。 - ダイソーの黄色い車
-
ボールキャスター
車の前方に使用。
センサをコントローラとして、車を走らせたりライトをつけたりします。
環境センサ 2JCIE-BL01
この環境センサは温度、湿度、気圧、照度などの各種環境値を測定してBLEを介して、スマホアプリで測定値みたりプロットされたグラフを見れるという代物。
GitHubでラズパイで値を取得できるpythonサンプルコードも公開されております。
https://github.com/OmronMicroDevices/envsensor-observer-py
環境センサの設定
上記のpythonサンプルコードを使用するためには環境センサをBroadcasterモード(デフォルトではBeaconモード)にする必要があります。
また今回は車のリモコンとしてセンサを使用するので、測定間隔もデフォルトの300秒から最速の1秒に変更します。
センサの設定を変更するためにスマホアプリBLE Scannerを使用しました。
動作モード変更
スマホアプリ BLE Scannerを起動するとBLEデバイスのスキャンが開始します。環境センサは”Env”という名前で表示されますのでCONNECTをクリックします。
2JCIE-BL01 ユーザーズマニュアル p. 26-28 によると、センサのモードはService UUID:0x3040のCharacteristics UUID: 0x3042で変更します。
BLE ScannerでCUSTOM SERVICEの”0C4C3040〜”をクリックして”0C4C3042〜”にデータを書き込みます。書き込むデータは以下の表 (p. 27)のとおりです。
ここでは、ADV_IND 送信間隔を最短の500msec (0x0320)にしてBeacon ModeをGeneral Broadcaster 1 (0x02) [マニュアル p. 28参照]にして、その他は初期値のままにします。
ちなみにBeacon Mode : General Broadcaster 2では加速度は送信されませんでした。
ここで注意が必要でデータを書き込む際は上の表の通りLow Byteから書いて後にHigh Byteを書き込みます。よって今回は以下を書き込みます。
“2003A0000A0032000200” (書き込みタイプはByte Arrayを選択)
ADV_IND 送信間隔500msec (0x0320)にしたいので最初にLow Byte : 02、そしてHigh Byte : 03を書きます。これ最初気づかずに0320で書き込んでいて全然測定値更新されずオムロンさんに問合せちゃった。。。。
こんな私に対しても丁寧に対応くださったオムロン様、本当にありがとうございます!!!!
測定間隔変更
センサの測定値の更新間隔を最速の1secにします。
BLE ScannerでCUSTOM SERVICEの”0C4C3010〜”をクリックして”0C4C3011〜”にデータを書き込みます。書き込むデータは以下の表 (p. 15)のとおりです。
ここでは、測定間隔を最短の1sec (0x0001)にしますのでLow Byteから
“0100”(書き込みタイプはByte Arrayを選択)
を書き込みます。0001じゃないことに注意!Low Byte → High Byteで書き込みます。
以上で環境センサの設定はOKです。
参考
- https://qiita.com/komde/items/7209b36159da69ae79d2
BLE Scannerでの2JCIE-BL01の設定方法が詳しく書いています
サンプルコードでセンサ動作確認
オムロン公式のpythonサンプルコードを動かしてみます。
ここではRaspberry Pi Zero Wを使用しました。OSはRASPBIAN STRETCH WITH DESKTOP ver. 4.14です。
pythonでBLEを使用するためのライブラリをインストール
1 |
$ sudo apt-get install python-bluez |
https://github.com/OmronMicroDevices/envsensor-observer-py でZipをDLしてラズパイの適当なフォルダに解凍して、解凍したフォルダで以下を実行
1 |
$ sudo python envsensor-observer-py/envsensor_observer.py -d |
デバッグモードでプログラムが実行されターミナルに測定値が表示されます。
環境センサの照度と加速度の最速通信(1秒毎)やっと出来たー!オムロンさんありがとうございます????⤵#オムロン #omron #ラズパイ #環境センサ #RaspberryPiZeroW pic.twitter.com/YgpkElOliY
— HomeMadeGarbage (@H0meMadeGarbage) 2018年9月3日
↑ちょっとわかりにくいですがセンサの設定変更がきちんとなされて1秒毎に測定値が送られています。
コントローラ製作
コントローラはダンボールで製作しましたww。Nintendo Laboで鍛えられたダンボール工作力を駆使しています。
百均で買った木の箱とダンボールで作ったバネと環境センサが入る直方体ダンボール箱を用意。
んで、この3つを木工用ボンドで接着して先端に環境センサをはめて出来上がり!
はいそうです。9/14に発売予定の「Nintendo Labo Toy-Con 03: Drive Kit」を参考に作りました。パクりちゃう。。サンプリングや。。。
車製作
ヘッドライトとして左右にLEDをつけました。ライト部に穴を開けてグルーガンで固定しました。
シャーシはガッツリ加工して、後輪にギアドモータを接続。タイヤは元のものを使用。前方にはボールキャスターをつけて右折・左折に対応します。
後輪のグリップが甘いので輪ゴム巻いてます。
Raspberry Pi Zero WとモータドライバDRV8830を載っけて配線します。
モバイルバッテリーも詰め込んでボディーを被せます。LEDの配線もZero Wに接続します。なんかギュウギュウだけど。。。まぁ。。完成!
ラズパイ設定
モータドライバ 用I2Cの設定
モータドライバDRV8830にI2Cでデータを書き込んで回転方向や印加電圧を指定します。回転方向や電圧は環境センサの加速度で変更します。
I2C有効化
まずはラズパイのI2Cを有効にします。Raspberry Piの設定を開きインターフェースのタグでI2Cの有効を選択しRebootします。
Python用I2Cライブラリインストール
Python-smbusをインストールします。
1 2 |
$ sudo apt-get update $ sudo apt-get install python-smbus |
OS RASPBIAN STRETCH WITH DESKTOP ver. 4.14では初めからインストールされてました。
コードの書き方は以下を参考にしました。
http://www.raspberry-projects.com/pi/programming-in-python/i2c-programming-in-python/using-the-i2c-interface-2
I2CモータードライバDRV8830
マニュアルを参考にI2Cデータ書き込みを実施します。
I2Cアドレス設定
2個のモジュールを制御する必要がありますので、それぞれ異なるI2Cアドレスを設定します。
本モジュールは基板上のジャンパA1, A0のステートによって以下の表のようにアドレスを指定することができます。
表のアドレスxは読み込み時には1、書き込み時は0をしてします。ここでは書き込みのみ使用します。左モータ用モジュールのジャンパを両方GND (アドレス:0x60)、右モータ用モジュールのジャンパを両方Open (アドレス:0x64)としました。
書き込みI2Cデータ
アドレス0x00に8bitの情報を書き込むことでモータを制御します。
各ビットの設定は以下の通り
ヘッドライトLED用PWM出力
ヘッドライトLEDの輝度は環境センサの照度で制御します。PWM制御でLEDの輝度を設定します。センサの照度が低下するとLEDの輝度を上げ、センサ照度が上がるとLED輝度を下げます。
コードの書き方は以下を参考にしました。
http://blog.robotakao.jp/blog-entry-122.html
pythonコード
公式pythonサンプルコードを元にプログラム修正しました。
https://github.com/OmronMicroDevices/envsensor-observer-py/blob/master/envsensor-observer-py/envsensor_observer.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
#!/usr/bin/python # -*- coding: utf-8 -*- import sys import os import argparse import requests import socket import datetime import threading import struct import sensor_beacon as envsensor import conf import ble import math import RPi.GPIO as GPIO import time import smbus # constant VER = 1.0 # ystem constant GATEWAY = socket.gethostname() # Global variables influx_client = None sensor_list = [] flag_update_sensor_status = False #LED PWM設定 GPIO.setmode(GPIO.BCM) GPIO.setup(4, GPIO.OUT) #GPIO4 p4 = GPIO.PWM(4, 100) #Setting GPIO4 Frequency at 100Hz p4.start(0) #モータ I2C設定 bus = smbus.SMBus(1) motorL = 0x64 motorR = 0x60 #モータドライバ書き込み用関数 def writeMotorResister(address, rot, vset): vdata = vset << 2 | rot bus.write_word_data(address, 0x00, vdata) def parse_events(sock, loop_count=10): global sensor_list pkt = sock.recv(255) parsed_packet = ble.hci_le_parse_response_packet(pkt) if "bluetooth_le_subevent_name" in parsed_packet and \ (parsed_packet["bluetooth_le_subevent_name"] == 'EVT_LE_ADVERTISING_REPORT'): for report in parsed_packet["advertising_reports"]: if (ble.verify_beacon_packet(report)): sensor = envsensor.SensorBeacon( report["peer_bluetooth_address_s"], ble.classify_beacon_packet(report), GATEWAY, report["payload_binary"]) #照度, 加速度表示 light = sensor.val_light print "val_light = ", light accX = sensor.val_ax accY = sensor.val_ay accZ = sensor.val_az print "Acc = ", accX, accY, accZ #LED輝度算出 lightLed = 100 *(1.0 - light / 50.0) if lightLed < 0: lightLed = 0 print "lightLed = ", lightLed #LED点灯 p4.ChangeDutyCycle(lightLed) #モータ #モータ回転方向 if accX > 0: rot = 1 #全進 elif accX < 0: rot = 2 #後進 else: rot = 0 #停止 #モータスピード計算 speed = math.sqrt(accX * accX + accY * accY) if accY > 0: #右折 speedL = speed - accY speedR = speed else: #左折 speedL = speed speedR = speed - abs(accY) #モータスピード正規化 speedL = int(speedL / 30) speedR = int(speedR / 30) if speedL > 37: speedL = 37 if speedR > 37: speedR = 37 print "Speed = ", speedL, speedR #モータドライバ書き込み writeMotorResister(motorL, rot, speedL) writeMotorResister(motorR, rot, speedR) else: pass else: pass return # check timeout sensor and update flag def eval_sensor_state(): global flag_update_sensor_status global sensor_list nowtick = datetime.datetime.now() for sensor in sensor_list: if (sensor.flag_active): pastSec = (nowtick - sensor.tick_last_update).total_seconds() if (pastSec > conf.INACTIVE_TIMEOUT_SECONDS): if debug: print "timeout sensor : " + sensor.bt_address sensor.flag_active = False flag_update_sensor_status = True timer = threading.Timer(conf.CHECK_SENSOR_STATE_INTERVAL_SECONDS, eval_sensor_state) timer.setDaemon(True) timer.start() def print_sensor_state(): print "----------------------------------------------------" print ("sensor status : %s (Intvl. %ssec)" % (datetime.datetime.today(), conf.CHECK_SENSOR_STATE_INTERVAL_SECONDS)) for sensor in sensor_list: print " " + sensor.bt_address, ": %s :" % sensor.sensor_type, \ ("ACTIVE" if sensor.flag_active else "DEAD"), \ "(%s)" % sensor.tick_last_update print "" # Utility function ### def return_number_packet(pkt): myInteger = 0 multiple = 256 for c in pkt: myInteger += struct.unpack("B", c)[0] * multiple multiple = 1 return myInteger def return_string_packet(pkt): myString = "" for c in pkt: myString += "%02x" % struct.unpack("B", c)[0] return myString def find_sensor_in_list(sensor, List): index = -1 count = 0 for i in List: if sensor.bt_address == i.bt_address: index = count break else: count += 1 return index # main function if __name__ == "__main__": try: flag_scanning_started = False debug = True # reset bluetooth functionality try: if debug: print "-- reseting bluetooth device" 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) # 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: # parse ble event parse_events(sock) 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) #モータドライバ書き込み writeMotorResister(motorL, 0, 0) writeMotorResister(motorR, 0, 0) #LED STOP p4.stop() GPIO.cleanup() print "Exit" |
モータ動作
コントローラを前に傾けてセンサのx軸の加速度が+になるとモータは前進、-になると後進します。
回転スピードは傾きを大きくして加速度が大きくなるほど早くなります。ここでは書き込み電圧2.97V (0x25)以上にならないようにしています。
またコントローラを横に傾けてy軸の加速度が+になると右折、-になると左折します。
#環境センサ コントローラが出来た。1秒毎の測定だからラグあるけど。#Nintendo #ラボ作品 のおかげでダンボール力上がってるw#オムロン #omron #ラズパイ #RaspberryPiZeroW #RaspberryPi #ダンボーラー #ワンカッパー pic.twitter.com/fOpMni5hU6
— HomeMadeGarbage (@H0meMadeGarbage) 2018年9月5日
ヘッドライト動作
環境センサの窓が照度センサになっており、窓を覆って暗くするとLEDヘッドライトが点灯します。
ヘッドライト#ラズパイ #RaspberryPi #LED #環境センサ #オムロン #omron pic.twitter.com/MmzOPeDs3P
— HomeMadeGarbage (@H0meMadeGarbage) 2018年9月7日
参考
ラズパイ起動時にpythonコードを自動スタートする方法は以下を参考にしました。
Pythonプログラムを起動時に実行する方法
動作
以上でやっとこ完成!
センシング間隔が1秒のためタイムラグあるけど動いてるよね 😀
できたー!#ラズパイ #RaspberryPi #LED #環境センサ #オムロン #omron #ラジコン pic.twitter.com/3hGFdiX7ic
— HomeMadeGarbage (@H0meMadeGarbage) 2018年9月8日
#環境センサ で #ラジコン
GOBERNABLES de Pi #ラズパイ #RaspberryPi #LED #オムロン #omron pic.twitter.com/CXTL6erL7H— HomeMadeGarbage (@H0meMadeGarbage) 2018年9月8日
終わりに
多くにおいて人間は環境に抗うことはできず、順応もしくは耐えるしか道はありません。
でも一歩踏み出す勇気さえあれば環境を変え物事を動かすことができるのです。
ま・さ・に・
GOBERNABLES de Pi!
追記
2018/12/16
今年は受賞はなりませんでしたが、早めに投稿したので『スタートダッシュ賞』をいただけました!やったー!
ラズパイコンテスト2018 スタートダッシュ賞頂きました!
効いたよね早めのサブミト pic.twitter.com/qXrfj4ynKN— HomeMadeGarbage (@H0meMadeGarbage) 2018年12月16日