Raspberry Piとセンサーでヘルス情報の取得(心拍数編)

2017年12月24日

IoT テクノロジー プログラミング 日記

先日「サバフェス2016」に参加して、発表に間に合わなかったので、自社内でちゃんと完成させたくて、以下の様な成果物にたどり着きました。

心拍数で緊張度計測

ストレス社会と言われる現代で、IT企業で仕事をしていると、どのくらいのストレスを感じているのかをリアルに計測したいと思います。 開発名は「ドキドキ計測ツール」です。 と言っても、心拍センサーの計測を行うだけですが、ADコンバータの組み込みから慣れないPythonコーディングの作業結果をブログに記します。 サバフェスでいただいたセンサーは「心拍センサー」です。
・RaspberryPiを使用 ・心拍数センサーを利用(ADコンバータ含む) ・myThingsを利用

RaspberryPiとセンサーの構築

RasberryPiは「Rasbian-OS」を普通にインストールして、センサーデータはSPI経由で取得するようにします。

1、RaspberryPi初期設定

こちらのページの手順でサクッとOSインストールとWEBアクセスの完了

2、心拍数センサーとADはコンバータを接続

初めてADはコンバータを扱ったんですが、とりあえず別サイトでの配線を参考にして接続させてみました。

3、センサーデータの取得はPythonで行いたいので、まずはインストール

Python環境の構築 $ sudo apt-get install python-dev $ curl https://bootstrap.pypa.io/ez_setup.py -o - | sudo python $ curl https://bootstrap.pypa.io/get-pip.py -o - | sudo python<!--nextpage--> $ git clone git://github.com/doceme/py-spidev $ cd py-spidev $ sudo python setup.py install <h2>sensor.py</h2> #!/usr/bin/python <h1>-<em>- coding: utf-8 -</em>-</h1> import spidev import time import sys import datetime <h1>SPIバスへのアクセスを開く</h1> spi = spidev.SpiDev() spi.open(0,0) def ReadChannel(channel): adc = spi.xfer2([1,(8+channel)&lt;&lt;4,0]) data = ((adc[1]&amp;3) &lt;&lt; 8) + adc[2] return data<!--nextpage--> <h1>--</h1> <h1>センサーを読み込み1</h1> aa = ReadChannel(0) <h1>センサーを読み込み2</h1> bb = ReadChannel(1) <h1>センサーを読み込み3</h1> cc = ReadChannel(2) d = datetime.datetime.today() dt = d.strftime("%Y%m%d") tm = d.strftime("%H%M%S") csv = str(dt) + "," + str(tm) + "," + str(aa) + "," + str(bb) + "," + str(cc) + ",\n"<!--nextpage--> <h1>出力</h1> <h2>print csv</h2> これで、アクセス毎の値が取得できます。

4、次にリアルタイムにグラフ表示

見た目でわかるようにしたいので、NodeJSのSocket.IOを使いたいます。 グラフ表示には「ccchart.js」を使いたいと思います。 $ wget http://ccchart.com/js/ccchart.js 表示用のHTML元ソース <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>RealChart</title> <script src="ccchart.js"></script> <script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/javascript"> var socket = io.connect(); socket.on('chart', function(data, fn) { document.getElementById("str").innerHTML = data.str; // set-data if(data.str){ var datas = data.str.split(","); chartdata.data[0].push(datas[1]); chartdata.data[1].push(datas[2]); chartdata.data[2].push(datas[3]); chartdata.data[3].push(datas[4]); } // max-length var title = [ chartdata.data[0][0],chartdata.data[1][0],chartdata.data[2][0],chartdata.data[3][0] ]; var maxLength = 30; if(chartdata.data[0].length>maxLength){ chartdata.data[0] = chartdata.data[0].splice(chartdata.data[0].length - maxLength -1); chartdata.data[1] = chartdata.data[1].splice(chartdata.data[1].length - maxLength -1); chartdata.data[2] = chartdata.data[2].splice(chartdata.data[2].length - maxLength -1); chartdata.data[3] = chartdata.data[3].splice(chartdata.data[3].length - maxLength -1); chartdata.data[0].unshift(title[0]); chartdata.data[1].unshift(title[1]); chartdata.data[2].unshift(title[2]); chartdata.data[3].unshift(title[3]); } ccchart.wsCloseAll();//一旦クリア ccchart.init('hoge', chartdata); }); </script> </head> <body><!--nextpage--> <canvas id="hoge"></canvas> <div id="str"></div> <script> var chartdata = { "config": { "title": "WebSocket Line Chart", "subTitle": "WebSocketで列データをリアルタイム受信し追記描画する", "type": "line", "lineWidth": 1, "maxY": 250, "minY": 0, "useVal": "yes", "useMarker": "arc", "xScaleSkip": 3, "maxWsColLen": 18, "colorSet": ["#DDA0DD","#3CB000","#A0DDDD"], "xLines": [ {"useRow":1,"color":"rgba(250,250,250,0.7)" } ] },<!--nextpage--> "data": [ ["Time"], ["data-1"], ["data-2"], ["data-3"] ] }; // set empty-data var maxLength = 30; for(var i=0;i<maxLength;i++){ chartdata.data[0].push(""); chartdata.data[1].push(0); chartdata.data[2].push(0); chartdata.data[3].push(0); } ccchart.wsCloseAll();//一旦クリア ccchart.init('hoge', chartdata); </script> </body> </html> 起動用NodeJSモジュール var http = require('http'); var socketio = require("socket.io"); var fs = require('fs'); var exec = require('child_process').exec;<!--nextpage--> // set-server var server = http.createServer(function(req,res){ // get-fileName var urls = req.url.split("?")[0].split("/"); var fileName = urls[urls.length-1]; if(fileName.match(/&#46;js$/)){ res.writeHead(200,{'Content-Type':'text/javascript'}); res.end(fs.readFileSync(fileName,'utf-8')); } else if(fileName.match(/&#46;css$/)){ res.writeHead(200,{'Content-Type':'text/css'}); res.end(fs.readFileSync(fileName,'utf-8')); } else if(fileName.match(/&#46;html$/)){ res.writeHead(200,{'Content-Type':'text/html'}); res.end(fs.readFileSync(fileName,'utf-8')); } else{ res.writeHead(200,{'Content-Type':'text/html'}); res.end(fs.readFileSync('index.html','utf-8')); } }).listen(80); var io = socketio.listen(server); console.log("Running...");<!--nextpage--> //round-check setInterval(function(){ exec("python sensor.py",function(err,stdout,stderr){ io.sockets.emit("chart",{str:"--"+stdout}); }); },100);

5、実行

$ node index.js Running... この状態でブラウザを使って、対象サーバー(Raspberry Pi)にアクセスすれば、以下の様な画面が現れます。

解説

画面下部の数値は、pythonで返された値をそのまま表示しています。 100ms毎に値を取得してグラフを描画しているので、センサーを指でつまむと値が変わって計測ができます。 何も触れていない時は周辺の光を受信して意味不明な値を取得してしまうんですが、指でつまむと、安定して値が取れるので、見ていて面白いですね。

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。