LoginSignup
38

More than 3 years have passed since last update.

移動中の心拍数(位置情報付き)をモバイル回線経由で記録する。丸1日動かせるくらいの低消費電力で。

Last updated at Posted at 2019-08-24

はじめに

山登りやサイクリングで移動中に、心拍、標高、気温、気圧などのデータを定期的に取得してGPS位置情報付きで保存できたら、楽しそうだと思いませんか?
集めたデータを見返し、同じペースでも気圧が低いと心拍数が上がりやすいな、などの気づきを得たり、
どの地点でペースが上がりすぎていたか可視化し、次回以降のペース配分に活かしたり。

そこで第一弾として、移動中の心拍数を位置情報付きで記録するシステムを作りました。
下記エントリーにインスパイアされています。
M5Stack+心拍センサー+GPSで移動中の心拍数を記録する – Ambient

ここではマイコンとスマホをテザリングで接続しています。
しかし、山登りやサイクリングは長丁場ですから、バッテリーが丸一日は持ってほしい。テザリングでは消費電力が気になりますね。
そこでBLE通信を使います。スマホとマイコンのBLE接続は、LINE Thingsを利用して簡単に実現できます。

※ Arduino系統に触るのは今回が初めてです。指摘や情報提供いただけるとたいへん喜びます。

概要

構成図

動いているところ

マジックテープを指に巻き、心拍センサの受光部を押し当てています。
GPSセンサが点滅して衛生信号の受信を示しています。
ezgif.com-resize.gif

結果

移動中、1分おきに計測した心拍数を地図上にマッピングできました。
image.png
脈が速いほど、マーカーの色が暖色になります。
スタート直後(水色マーカー)は脈が遅く、その後ダッシュしてみたためオレンジ色に。
以降は歩いているため緑色で落ち着いています。
高層ビル街なのでGPSは多少誤差がありますね。

消費電力

13時~21時にかけてスマホとESP32をBLEで繋ぎっぱなしにしましたが、スマホのバッテリの減り具合は普段とほとんど変わらず。
マイコンの電源に使用したモバイルバッテリの残目盛り(4段階)は全く減っていませんでした。
10000mAhあれば一週間は持ちそうです。

BLEで消費電力を抑える目的は達成です。

使ったもの

ハードウェア

サービス

作り方

ESP32側

ESPr® Developer 32に書き込んだコードです。

https://gist.github.com/TakeshiMasukawa/2165c5b4a05bbc90914293620053dc0e

以下、手順の解説です。

LINE Thingsを利用したBLE通信

LINE Things Messageを使ってみよう #linethings #linedc - Qiitaを参考にしました
BLE通信に関わるコードはESP32の場合のgistをベースにしています。

変更点は以下の通り。

  • 削除したコード
    • ボタン押下時にスマホにNotify送信する処理
    • LEDを光らせる処理
  • 追加したコード
    • 1分おきにセンサデータをスマホにNotify送信する処理

送信処理のスケジューリングにはTickerを使います。

Tickerのインクルード

ESP32HeartRateWithGPS.ino
// Schedule notify
#include <Ticker.h>

Tickerのセットアップ

setup()
6000msごとにデータ送信処理をコール

ESP32HeartRateWithGPS.ino
  // Schedule notify
  ticker.attach_ms(60000, sendData);

データ送信処理

BLE Notifyに乗せられるユーザデータのペイロードは20Byte。
GPSデータだけでいっぱいになってしまうため、データを3回に分けて送ります。
順番は次のとおりです。

  1. データ送信開始のシグナルを送る
    • サーバはこのシグナルを受け取ったタイミングをデータの記録時刻とします。
  2. GPS信号が正しく受信できていた場合、位置情報を"{緯度},{経度}"の形式で送る
  3. 1分間の心拍数を送る

センサデータは最初にまとめて取得しますが、Notifyは5秒ずつ間隔を空けて送信し、サーバ側の負荷を分散します。

ESP32HeartRateWithGPS.ino
void sendData() {
  Serial.println("send start");
  notifyCharacteristic->setValue("start");
  notifyCharacteristic->notify();
  const int BPM = pulseSensor.getBeatsPerMinute();;
  if (gps.location.isValid())
  {
    char coordinate[20];
    sprintf(coordinate,"%f,%f",gps.location.lat(),gps.location.lng());
    delay(5000);
    notifyCharacteristic->setValue(coordinate);
    notifyCharacteristic->notify();
  }
  delay(5000);
  char sendData[10];
  sprintf(sendData,"%d;",BPM);
  notifyCharacteristic->setValue(sendData);
  notifyCharacteristic->notify();
}

心拍センサ

Ambient 心拍モニターを参考にしました。

防水処理

雨や汗からセンサ基盤を守るために、♡マークがある側に付属の保護シールを、反対側にはホットグルーを盛ります。

こちら側に保護シールを貼り、
image.png

反対側にはホットグルーを盛って、付属のマジックテープシールを貼ります。
image.png

ESP32との接続

アナログ通信(ADC)を利用します。
次のように接続しました。

用途 センサ側 ESP32側
電源 (3~5V) 赤い線 3V3 (3.3V)
GND 黒い線 GND
通信 紫の線 IO34 (ADC1_CH6)

ESP32のアナログ入力についてはESP-WROOM-32に関するTIPSを参照ください。
WiFiやBLEをONにすると、アナログ入力に対応しているピンのうちADC2回路を使うものが全て使えなくなるので注意しましょう。
私はここでだいぶハマりました。

入力に使用するピンを指定します。

ESP32HeartRateWithGPS.ino
#define HR_PIN 34

生データをシリアルプロッタで見るとこんな具合です。

Screenshot from Gyazo

これを心拍数に変換するためにライブラリPulseSensorPlaygroundを利用します。

ソースコード

ライブラリは、公式版 (ESP32未対応)ではなく、Ambientによる改良版を利用します。
公式版では"Added support for ESP32 #103"というPRが2019/06にマージされていますが、大嘘です。
コンパイルが通るようになっただけで、getBeatsPerMinute()が心拍数を返してくれません (2019/08現在)。

  • PulseSensorPlaygroundのインクルード

includeの前にUSE_ARDUINO_INTERRUPTSをdefineします。
こうしないとコンパイルが通りません。

ESP32HeartRateWithGPS.ino
// Heart rate
#define USE_ARDUINO_INTERRUPTS true
#include <PulseSensorPlayground.h>
  • セットアップ

setup()内で生データをライブラリに渡すよう設定します。

ESP32HeartRateWithGPS.ino
  // Heart rate library settings
  pulseSensor.analogInput(HR_PIN);
  pulseSensor.setSerial(Serial);
  pulseSensor.setOutputType(OUTPUT_TYPE);
  pulseSensor.setThreshold(THRESHOLD);
  pulseSensor.begin();

あとはデータが欲しいタイミングでpulseSensor.getBeatsPerMinute()すると、直近の分間BPMを取得できます。

GPSセンサ

GPSの受信

センサに電源を投入するとランプが光ります。さらにGPS信号の受信に成功するとランプが点滅します(冒頭の動画参照)。
屋内だとなかなか点滅しないことがあります。その場合は信号を受信しやすい窓際に置いてみてください。
屋外で使用する場合はまず大丈夫です。

ESP32との接続

UARTによるシリアル通信を使用します。
通信にはESP32に標準搭載されているハードウェアシリアルを使います。
詳しくはArduino-ESP32 Serial通信 - Qiitaを参照ください。

今回はUART2を使いました。
UART0はArduino IDEからのコード書き込みと競合します。またUART1はピンをプログラムで指定する必要があり、面倒です。

次のように接続します。

用途 センサ側 ESP32側
電源 (3.8V~12V) 5V VOUT
GND GND GND
通信 TXD IO16 (U2RXD)

GPSセンサからのデータ送信のみ利用するため、センサのRXDおよび1PPSピンは使用しません。
HW初心者すぎて、TXDとTXD、RXDとRXDを繋いで「データが取れない!」と頭を抱えていたのは秘密です。

生データはこんな具合です。(画像はDevice Plusさんからの借り物です)
image.png

これを緯度、経度に変換するためライブラリTinyGPS++を利用します。

ソースコード

  • TinyGPS++のインクルード
ESP32HeartRateWithGPS.ino
// GPS
#include <TinyGPS++.h>
  • シリアル通信セットアップ
    • UART2用にSerial2という変数が用意されています。

setup()

ESP32HeartRateWithGPS.ino
  // Set up serial for GPS
  Serial2.begin(9600);// GPS
  • センサデータをライブラリに渡す

loop()

ESP32HeartRateWithGPS.ino
  // Send GPS raw data to library
  while (Serial2.available() > 0) {
    if (gps.encode(Serial2.read())) {
      break;
    }
  }

あとはデータが欲しいタイミングで、gps.location.lat()で緯度を、,gps.location.lng()で経度を取得できます。

サーバ側

LINE Things 自動通信機能のバックエンドとして使用したコードです。

https://github.com/TakeshiMasukawa/Sensor-data-with-GPS-coordinates-tracker

LINE Things Messageを使ってみよう #linethings #linedc - Qiitaのコードをベースにしています。

以下、各部を解説します。

Ambientとの接続

Ambientのnode.jsライブラリの使い方は次を参照ください。
node.jsライブラリー ambient-lib – Ambient

受信データのデコード

ESP32からは文字列データが送られます。
ascii変換されており、そのままでは扱えないのでデコードします。

handleEvent()

server.js
    // 受信データをデコード
    const buffer = new Buffer.from(thingsData.bleNotificationPayload, 'base64');
    const data = buffer.toString('ascii');

受信データのキャッシュ&送信

ESP32からは3回に分けてデータが送られてきます。
それぞれをAmbientへの送信データにセットし、全て受信したら送信します。
1回目の受信時刻をデータ取得時刻としてセットしています。
緯度、経度データはlatlngにセットするとAmbientに認識してもらえます。

handleEvent()

server.js
    if (data === "start") {
      // データ送信開始信号
      sendData.created = Date.now();
    } else if (data.includes(',')) {
      // GPSデータ
      const cordinates = data.split(',');
      sendData.lat = cordinates[0];
      sendData.lng = cordinates[1];
    } else if (data.includes(";")) {
      // 心拍データ
      const BPM = data.split(';')[0];
      sendData.d1 = BPM;
      await ambientSendAsync(sendData);
      // 次の受信に備えてデータをリセット
      sendData = {};
      ...
    }

LINEメッセージ送信機能

心拍数が上がりすぎている場合は、LINEメッセージで警告する機能をつけてみました。
アウトドア活動のアシスタントになってくれます。

handleEvent()

server.js
      if (parseInt(BPM) > heartRateLimit) {
        message = `心拍数が上がりすぎています。ペースを落としましょう。BPM: ${BPM}`;
      }

登山やサイクリングで、ペースを一定に保つのに使えそうです。

image.png

今後の展望

今回はGPSセンサでの緯度経度と併せて、心拍数のみを記録しました。
他にも、GPSセンサの標高を取ってみたり、気圧センサを追加してみれば、登山の記録としてさらに一段深い考察ができそうです。
温度・湿度センサを組み合わせれば、メッセージで熱中症の警告を送るなどと、より高機能なアシスタントになってくれるでしょう。

実はこの製作はお盆休みの富士登山計画に向けて始めたのですが、実装が間に合わなかった上に雨天中止になってしまいました。
次に登山するときはぜひこれを携行して、記録をつけてみようと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38