定期的に実行し、信号が得られなかったときは情報なしで記録
#!/usr/bin/python # coding: UTF-8 ''' ble信号をスキャンしてデータをcsvファイルに書き出す - scan関数は、引数で指定する時間、信号を待ち受け、得られた結果を返す - 信号がなかったら空のデータを書き出す - 信号あった時は、その中からaddrTypeがrandomなものを拾い出し、それを書き出す - sleepは必要ないかもしれない - Ctrl-Cを押すとファイルを閉じ終了 - ble検出モジュールbluepyのマニュアルURL: bluepy manual page: http://ianharvey.github.io/bluepy-doc/ ''' import time import numpy as np from datetime import datetime, timedelta import sys sys.path.append('/usr/local/lib/python2.7/dist-packages/bluepy') from bluepy.btle import Scanner, DefaultDelegate # initial try: scanner = Scanner().withDelegate(DefaultDelegate()) post_interval = 0.5 # interval of scanning except : print "no device" sys.exit() # file for output outfile = open("bleTest.csv","w") while True: try: devices = scanner.scan(post_interval) if len(devices)==0: print ",,,%s" %(datetime.now().strftime("%H:%M:%S") ) else: for dev in devices: if dev.addrType=="random": dateNow = datetime.now() timeStr = dateNow.strftime("%H:%M:%S") outfile.write("%s (%s), %d, %s\n" % (dev.addr, dev.addrType, dev.rssi, dateNow)) # file output print "Device %s (%s), RSSI=%d dB, %s" % (dev.addr, dev.addrType, dev.rssi, timeStr) time.sleep(post_interval) except KeyboardInterrupt: outfile.close() except : print "no device" sys.exit() break
#!/usr/bin/python # coding:utf-8 ''' gpsデータとbleデータを取得し、サーバに送信する - 送信先URLはpostUrlで指定 - gpsデータ取得モジュールはpythonに標準で入っている - gpsデータを取得するOS上のモジュールgpsdはインストールしておくこと - bleデータ取得にはbluepyモジュールを使う - bluepyモジュールをインストールした場所をpathに加える - それぞれのモジュールは並行して動かせていない、順々におこなうので、その分、時間はずれる ''' import sys sys.path.append('/usr/local/lib/python2.7/dist-packages/bluepy') import requests import json import struct import uuid from gps import * from time import * import time import threading from datetime import datetime from pytz import timezone from bluepy.btle import Scanner, DefaultDelegate postUrl = "http://8tops.yamanashi.ac.jp/~toyoki/busloc/post_test.php" gpsd = None #seting the global variable def isNaN(num): return num != num class GpsPoller(threading.Thread): ''' GPSデータ取得のためのクラス ''' def __init__(self): threading.Thread.__init__(self) global gpsd #bring it in scope gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info self.current_value = None self.running = True #setting the thread running to true def run(self): global gpsd while gpsp.running: gpsd.next() #this will continue to loop and grab EACH set of gpsd info to clear the buffer if __name__ == '__main__': # instance for BLE scanner = Scanner().withDelegate(DefaultDelegate()) ble_get_interval = 0.5 # interval of scanning BLEs try: devices = scanner.scan(ble_get_interval) haveBleDevice = True except : haveBleDevice = False # macアドレスの16進表示 mac_address = ":".join( [hex( fragment )[2:].zfill( 2 ) for fragment in struct.unpack( "BBBBBB", struct.pack( "!Q", uuid.getnode() )[2:] )] ) # 送信用基本データ ("name"は今のところダミー) dt = { "name": "RaspberryPI 1", "mac": mac_address, } # create the instance for gps device gpsp = GpsPoller() # create the thread try: gpsp.start() # gpsデータ検索窓口 while True: # GPSデータ取得 if gpsd.utc != "" and (not isNaN(gpsd.fix.latitude)): dt["lat"] = gpsd.fix.latitude dt["lng"] = gpsd.fix.longitude t_utc = timezone('UTC').localize(datetime.strptime(gpsd.utc,"%Y-%m-%dT%H:%M:%S.%fZ")) now = datetime.strftime(t_utc.astimezone(timezone('Asia/Tokyo')), "%Y-%m-%d %H:%M:%S") dt['time'] = now else: dt["time"] = datetime.now().strftime("%H:%M:%S") # BLEデータ取得 if haveBleDevice : bleDevices = scanner.scan(ble_get_interval) if len(bleDevices) != 0: for dev in bleDevices: if dev.addrType=="random": dt["bleAddr"] = dev.addr dt["bleRssi"] = dev.rssi print dev.addr # データ送信 res = requests.post( postUrl, data=json.dumps(dt)) time.sleep(5) #set to whatever except (KeyboardInterrupt, SystemExit): #when you press ctrl+c # print "\nKilling Thread..." gpsp.running = False gpsp.join() # wait for the thread to finish what it's doing #print "Done.\nExiting."
(Googleドライブ上の文書に書いた。) https://docs.google.com/document/d/1rQnKdOOaYGrYmnsv2qv9Os72lCca2pc-YrkzYSDOj9o
<?php header("Content-type: application/json; charset=utf-8"); $ymd = $_GET['file']; if(isset($_GET['file'])) { $FP = fopen($_GET['file'], "r"); } else { $FP = fopen("data/postedData" . $ymd . ".csv", "r"); // デフォルト値 } if($FP == false) exit(); $dataArray = []; while( ($row = fgetcsv($FP)) !== false) { $singleData = []; foreach($row as $col) { // 各項を=で分解して前半はキー、後半は値とする $value = explode("=",$col); if(count($value)==2) $singleData[$value[0]] = $value[1]; } $dataArray[] = $singleData; } $jsonTree = json_encode($dataArray); if(isset($_GET['callback'])) $jsonTree = $_GET['callback'] . "(". $jsonTree . ")"; echo $jsonTree; ?>
データは各行、「日時, 緯度, 経度」の3カラムからなっている場合。
<?php header("Content-type: application/json; charset=utf-8"); $ymd = $_GET['file']; if(isset($_GET['file'])) { $FP = fopen($_GET['file'], "r"); } else { $FP = fopen("data/postedData" . $ymd . ".csv", "r"); } if($FP == false) exit(); $dataArray = []; while( ($row = fgetcsv($FP)) !== false) { $dataArray[] = array("time"=>$row[0], "lat"=>$row[1], "lng"=>$row[2]); } $jsonTree = json_encode($dataArray); if(isset($_GET['callback'])) $jsonTree = $_GET['callback'] . "(". $jsonTree . ")"; echo $jsonTree; ?>
1行を読み込んでみてイコールを含むかどうか判断して2通りの処理を行う。
<?php header("Content-type: application/json; charset=utf-8"); $ymd = $_GET['file']; if(isset($_GET['file'])) { $FP = fopen($_GET['file'], "r"); } else { $FP = fopen("data/postedData" . $ymd . ".csv", "r"); } if($FP == false) exit(); // ファイル内容の形式を検出する $row = fgetcsv($FP); if(strpos($row[1],"=")) { $withIndex = true; } else { $withIndex = false; } rewind($FP); $dataArray = []; if($withIndex) { // インデックス付きcsvファイルの場合 while( ($row = fgetcsv($FP)) !== false) { $singleData = []; foreach($row as $col) { // 各項を=で分解して前半はキー、後半は値とする $value = explode("=",$col); if(count($value)==2) $singleData[$value[0]] = $value[1]; } if($singleData["lat"]!="") $dataArray[] = $singleData; } } else { // 単純なcsvファイルの場合 while( ($row = fgetcsv($FP)) !== false) { if($row[1] == "") continue; $dataArray[] = array("time"=>$row[0], "lat"=>$row[1], "lng"=>$row[2]); } } $jsonTree = json_encode($dataArray); if(isset($_GET['callback'])) $jsonTree = $_GET['callback'] . "(". $jsonTree . ")"; echo $jsonTree; ?>
基本的なページの記述(サーバサイドスクリプト)はhtml, phpで書き、jsonデータをよみこんで描画する部分はJavaScriptで記述する。
実行例: http://8tops.yamanashi.ac.jp/~toyoki/busloc/showBusLocations.php
松浦くんのデータ表示: http://8tops.yamanashi.ac.jp/~toyoki/busloc/showBusLocations4matsu.php
(json形式データ処理には上記3番目のものを使った。したがってファイル形式の違いを主導で選択しなくても良い。)
<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.01 traditional//en"> <head>ファイル名一覧
ファイルの種別
phpを含む上記htmlでshowBuslocations.jsという名前で読み込むJavaScript。
サーバ側にファイルとしてjson形式のデータを置いてテストしてみるのもよい。(もちろん、json形式ではなく、csv形式のデータを読み込んで処理することも可能である。jQueryにはcsvを便利に扱う関数もある。)
// global variables var tinyIcon = "http://8tops.yamanashi.ac.jp/~toyoki/icons/blue.png"; var normalIcon = "http://maps.google.co.jp/mapfiles/ms/icons/blue-dot.png"; var baseUrl = 'http://8tops.yamanashi.ac.jp/~toyoki/busloc/'; var posMarker = []; var busPosition = []; var map; var maxDataNumber= 2000; // 地図表示に時間がかかるのでデータ数の上限を決めておく // show bus positions on Google Map function showBusLocations() { var urlStr = baseUrl + $("#scriptName").val() + "?callback=?" + "&file=" + $("#filename").val(); $.getJSON(urlStr, function(data) { if(data==null) alert("データがみつかりません"); var clat=0.0; var clng=0.0; var dataCount=0; busPosition = []; for(var idx in data) { if(data[idx].time=="" || data[idx].lat=="") continue; dataCount++; /* $("#show_results").append(data[idx].time +"," + data[idx].lat + "," + data[idx].lng + "
"); */ clat += parseFloat(data[idx].lat); clng += parseFloat(data[idx].lng); busPosition.push({"time":data[idx].time, "lat": parseFloat(data[idx].lat), "lng": parseFloat(data[idx].lng)}); if(dataCount>maxDataNumber) { alert("データ数が" + maxDataNumber + "を超えましたので、それ以上のデータを読み込みませんでした。"); break; } } // 描画 if(busPosition.length==0) { alert($("#filename").val() + "からは位置情報がひとつも得られませんでした。"); } clat /= dataCount; clng /= dataCount; if(map == null) { var mapOptions = { zoom:16, center: new google.maps.LatLng(clat,clng) } map = new google.maps.Map(document.getElementById("map-canvas"),mapOptions); } if(posMarker.length !=0) { for(var i in posMarker) posMarker[i].setMap(null); posMarker = []; } map.setCenter(new google.maps.LatLng(clat,clng)); var iconObj = {url: tinyIcon, anchor: new google.maps.Point(4,4)} for( var idx in busPosition) { posMarker.push(new google.maps.Marker({ position: new google.maps.LatLng(busPosition[idx].lat, busPosition[idx].lng), icon: iconObj, map: map, title: busPosition[idx].time })); } }); }