SSブログ

極小ESP-WROOM-02 に部品をとりつけてみた [Arduino]

CLIP DISPLAY のために作った極小 ESP-WROOM-02 それに部品をとりつけてみました。まず最初に MPU-6050 と電源ブロック+バッテリーを付けてみました。


DSC_0580.JPG


苦労しましたが、無事に動きました!これで基本的な部分は出来ました。これに心拍センサーとOLEDディスプレイをつければ電気系は完成です。

ということで、着けてみました!ケーブルにもポリウレタン銅線を使ってかなり細くなりました。


DSC_0582.JPG


一号機よりも小さくスタイリッシュになりました。いやー苦労した甲斐がありました。残すはソフトと息子担当の外装デザインのみとなりました。いよいよ最後の追い込みです!
(^_^)/~









心拍センサ

心拍センサ

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



ESP-WROOM-02ピッチ変換済みモジュール《シンプル版》

ESP-WROOM-02ピッチ変換済みモジュール《シンプル版》

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス




極小 ESP-WROOM-02 計画 [Arduino]

CLIP DISPLAYの再設計に着手し、Arduino Pro Mini から ESP-WROOM-02 にマイコンを変更しました。せっかく小さくなったので、小ささを追及することにしました。計画がこちら。


ESP-WROOM-02-Mini.png^; 


DSC_0568.JPG


下にあるのは、バッテリー接続用のコネクターと3.7V から 3.3V を生成するための LDO 、チップコンデンサー、スイッチが入っている電源ブロックです。この大きさでPCからスケッチを書き込むこともできます。我ながら小さくできました!
σ(^_^)

















CLIP DISPLAY(仮) の再設計にチャレンジ [Arduino]

MFT2016 に出展予定の CLIP DISPLAY(仮) の再検討をはじめました。


CLIPDISPLAY_1.jpg


今までは Arduino Pro Mini を使っていましたが、CLIP DISPLAY(仮) の全ての機能を盛り込むとメモリ不足になってしまいます。ということで、代わりのマイコンとして ESP-WROOM-02 を選択しました。

ESP-WROOM-02 のメモリと処理能力は Arduino Pro Mini とは比較にならないくらい高いです。(参考記事:http://makers-with-myson.blog.so-net.ne.jp/2016-04-13) また大きさも Arduino Pro Mini よりも小さく、文句ない選択です。


DSC04217.JPG


あと試作をしていて困ったのがケーブルです。意外と機内配線のケーブルが体積を占め、全てのパーツを納めるのに苦労しました。その体積をできるだけ小さくするために、ポリウレタン銅線を使うことにしました。いわゆるエナメル線です。



2UEW エナメル線 2種 ポリウレタン銅線 0.2mm 20m

2UEW エナメル線 2種 ポリウレタン銅線 0.2mm 20m

  • 出版社/メーカー: サンコー電商
  • メディア:




これは"はんだ付け"をすると表面のポリウレタンが溶けて結線でき、"はんだ"されていない部分は絶縁されたままという優れものです。何より、とにかく細いので省スペースで実装できそうです。

一番の悩みは電源。前回の試作はバッテリーを内蔵しましたが、これではバッテリ交換ができません。バッテリーは別モジュールにしようかと考えています。
( ̄_ ̄ )。o0





Arduino Pro Mini 328 3.3V 8MHz

Arduino Pro Mini 328 3.3V 8MHz

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



ESP-WROOM-02 Wi-Fiモジュール

ESP-WROOM-02 Wi-Fiモジュール

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



2UEW エナメル線 2種 ポリウレタン銅線 0.2mm 20m

2UEW エナメル線 2種 ポリウレタン銅線 0.2mm 20m

  • 出版社/メーカー: サンコー電商
  • メディア:



MPU6050 の Digital Motion Processor の性能を試してみた(5) [Arduino]

前回、MPU6050 のオフセットを解消するためのフィルターを考えてみました。今回はその性能を試してみたいと思います。


DSC04208.JPG


フィルターなしの場合は、動きを与えたあとにオフセットが発生していました。


MPU6050DMPnotConvergenceToZero2.png


今回、64サンプルのフィルターを使ってデータを取得してみました。センサーを左右に大きく振ってみました。


W64Filtered_accel.png


フィルターのおかげでオフセットはキャンセルされています。うまくいきました。このデータから速度のデータを算出してみました。


W64Filtered_speed.png


だいたい1.2m/sec。左右の振り幅は1mくらいなので、感覚的になんとなく合っています。次に距離を計算してみました。左右に大きく30回振りましたので、だいたい30m位のはずです。


W64Filtered_distance.png


悪くないデータになりましたが、静止状態でもずるずると距離が伸びてしまっています。加速度データの正負の値が一致していないので、差し引きで速度が残ってしまっています。これ位はしょうがないレベル?悩む。
(。-`ω´-)ンー





MPU-6050搭載三軸加速度三軸ジャイロセンサモジュール

MPU-6050搭載三軸加速度三軸ジャイロセンサモジュール

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



ESPr Developer(ESP-WROOM-02開発ボード)

ESPr Developer(ESP-WROOM-02開発ボード)

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス







MPU6050 の Digital Motion Processor の性能を試してみた(4) [Arduino]

前回の検討で、MPU6050をCalibrationをしてもオフセットが残ってしまう問題が残りました。何か解決策を考えなければなりません。


DSC04208.JPG


一般的なやり方は、センサーの出力値からローパスフィルターを使ってDC成分を得て除去するというもの。Android のページでも紹介されています。

SensorEvent
https://developer.android.com/reference/android/hardware/SensorEvent.html


具体的には下記のようなアルゴリズムです。

 // alpha は 0.5 ~ 0.8 で調整
 lpf_signal = alpha * x[i-1] + (1 - alpha) * x[i]; // low pass filter
 hpf_signal = x[i] - lpf_signal; // distill high pass value using low pass value


しかし、これを実際のセンサーの出力(10msec間隔)に適用すると、ローパスフィルタの値がセンサーの出力値とほぼ重なってしまい、正しい加速度が算出できません。(赤がローパスフィルターの波形で、その下に青のセンサーからの出力波形が隠れています)

general_lowpass_filter.png


理想的には数十サンプルの平均値をとるようなローパスフィルターを実現したいところです。でも配列はメモリを食うので使いたくないし・・・。

ということで、下記のようなアルゴリズムを考えてみました。


#include 
#include 
#include "I2Cdev.h"
#include "MPU6050DMP.h"
extern "C" {
#include "user_interface.h"
}

#define INT_PIN 4

MPU6050DMP mpu6050;
bool dmpReady = false;
uint16_t  packetSize = 0;

volatile bool mpuInterrupt = false;
void dmpDataReady() {
    mpuInterrupt = true;
}

unsigned long getCurrentTimeMS() {
  return ESP.getCycleCount() / 80000L;
}

void setup() {

  Wire.begin(12, 14);  
  Wire.setClock(400000);
  Serial.begin(115200);
  pinMode(INT_PIN, INPUT);
  WiFi.disconnect();
  WiFi.mode(WIFI_OFF);
  WiFi.forceSleepBegin();

  if (mpu6050.testConnection()) {
    Serial.println("MPU6050 OK");
    attachInterrupt(INT_PIN, dmpDataReady, RISING);
  } else {
    Serial.println("MPU6050 NG");
    return;
  }
  
  mpu6050.initialize();
  // offset values for my sensor
  mpu6050.setXAccelOffset(565);
  mpu6050.setYAccelOffset(-5);
  mpu6050.setZAccelOffset(1432);
  mpu6050.setXGyroOffset(17);
  mpu6050.setYGyroOffset(550);
  mpu6050.setZGyroOffset(464);

  Serial.println("");
  Serial.println("");

  if (mpu6050.dmpInitialize() == 0) {
    Serial.println("MPU6050DMP OK");
    mpu6050.setDMPEnabled(true); 
    packetSize = mpu6050.dmpGetFIFOPacketSize();
    dmpReady = true;    
  } else {
    Serial.println("MPU6050DMP NG");
  }
}

uint16_t fifoCount  = 0;
uint8_t  fifoBuffer[64];
unsigned long preTime = 0;

unsigned int loop_counter = 0;

#define WSIZE 16 // change this value for your purpose

long sum_x = 0;
long sum_y = 0;
long sum_z = 0;
int adj_x = 0;
int adj_y = 0;
int adj_z = 0;


void loop() {
  
  if (!dmpReady) return;
  
  while (!mpuInterrupt);
  mpuInterrupt = false;

  while (fifoCount < packetSize) {
    fifoCount = mpu6050.getFIFOCount();
  }
  
  uint8_t mpuIntStatus = mpu6050.getIntStatus();
  
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
    mpu6050.resetFIFO();
    return;
  } else if ((mpuIntStatus & 0x02) == false) {
    return;
  }
  
  mpu6050.getFIFOBytes(fifoBuffer, packetSize);
  fifoCount -= packetSize;

  unsigned long curTime = getCurrentTimeMS();
  unsigned long duration = curTime - preTime;

  Quaternion q;
  VectorInt16 aa;
  VectorInt16 aaReal;
  VectorFloat gravity;
  
  mpu6050.dmpGetQuaternion(&q, fifoBuffer);
  mpu6050.dmpGetAccel(&aa, fifoBuffer);
  mpu6050.dmpGetGravity(&gravity, &q);
  mpu6050.dmpGetLinearAccel(&aaReal, &aa, &gravity);
  
  if (loop_counter < 2000) {
    if (loop_counter % 80 == 0)
      Serial.println("");
    ++loop_counter;
    Serial.print(".");
    return;
  } else if (loop_counter < 2000 + WSIZE) {
    sum_x += aaReal.x;
    sum_y += aaReal.y;
    sum_z += aaReal.z;
    if (loop_counter % 80 == 0)
      Serial.println("");
    ++loop_counter;
    Serial.print("*");
    return;
  } else {
    adj_x = sum_x / WSIZE;
    adj_y = sum_y / WSIZE;
    adj_z = sum_z / WSIZE;
    
    sum_x = sum_x - adj_x + aaReal.x;
    sum_y = sum_y - adj_y + aaReal.y;
    sum_z = sum_z - adj_z + aaReal.z;  
    
    /* 
    // High-pass values
    aaReal.x -= adj_x;
    aaReal.y -= adj_y;
    aaReal.z -= adj_z; 
    */   
  }
  
  Serial.print("areal\t");
  Serial.print(aaReal.x);
  Serial.print("\t");
  Serial.print(aaReal.y);
  Serial.print("\t");
  Serial.print(aaReal.z);
  Serial.print("\t");
  Serial.print(adj_x);
  Serial.print("\t");
  Serial.print(adj_y);
  Serial.print("\t");
  Serial.print(adj_z);
  Serial.print("\t");
  Serial.print(duration);
  Serial.println("");
  preTime = curTime;
}



アイディアは単純です。指定サンプル分の積算値を保持しておき、そこから平均値を出します。計算が終わったら、積算値から平均値を一つ分を減算して、直近の得られた値を積算値に加えるというものです。

このアルゴリズム適用した結果を以下に示します。赤がサンプル数分の平均値で、青がセンサーからの出力です。なかなか良い感じのデータが得られました。


16サンプル平均
16ave_lowpass_filter.png


32サンプル平均
32ave_lowpass_filter.png


64サンプル平均
64ave_lowpass_filter.png


128サンプル平均
128ave_lowpass_filter.png


このフィルターの欠点はオリジナルの波形に対して少し位相が遅れてしまうところです。位相の遅延を最低限にしつつ加速度の値をとれそうなのは、64か32あたりかな・・・。

適正なサンプル数は速度や距離を実測して決めたいと思います。
(^^)





MPU-6050搭載三軸加速度三軸ジャイロセンサモジュール

MPU-6050搭載三軸加速度三軸ジャイロセンサモジュール

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



ESPr Developer(ESP-WROOM-02開発ボード)

ESPr Developer(ESP-WROOM-02開発ボード)

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス







MPU6050 の Digital Motion Processor の性能を試してみた(3) [Arduino]

引き続きMPU6050のDMPの性能テストです。前回までのテストで、MPU6050のDMP(Digital Motion Processor)は重力の影響を排除してくれることは分かりました。


DSC04208.JPG


問題はXYZ軸の加速度が静止状態でゼロでない値に収束してしまうことです。このままでは静止していても、計算上は、どんどん速度が上がってしまいます。


MPU6050DMPnotConvergenceToZero.png

Google師匠に聞いてみると、キャリブレーションが効果がありそうです。


Arduino Sketch to automatically calculate MPU6050 offsets
http://www.i2cdevlib.com/forums/topic/96-arduino-sketch-to-automatically-calculate-mpu6050-offsets/


ということで、上記サイトにあるキャリブレーション用のスケッチを試してみることにしてみました。


MPU6050_calibration_v1.1.zip


フォーラム中でも話題になっていましたが、このキャリブレーションスケッチ、一向に値が収束しません。仕方ないので収束条件を緩めるように変更しました。


int acel_deadzone=30;     //Acelerometer error allowed, make it lower to get more precision, but sketch may not converge  (default:8)
int giro_deadzone=5;     //Giro error allowed, make it lower to get more precision, but sketch may not converge  (default:1)



これでも値がなかなか収束しません。どうも、かなり厳密にセンサーを平行に保たないと収束しないようです。何度も試行錯誤して、ようやく結果が得られました。


XAccelOffset: 82
YAccelOffset: 333
ZAccelOffset: 1443
XGyroOffset: -3
YGyroOffset: 716
ZGyroOffset: 475
※センサーによって値はまったく異なるので注意してください。


実際に試してみると、確かに静止状態では前よりも収束していますが(これでも若干のオフセットがあります)、センサーに動きを与えると、また大きなオフセットが発生してしまいました。

MPU6050DMPnotConvergenceToZero2.png


これでは速度、距離を算出することはできません。どうやら、オフセットを除去するためのフィルターを考える必要がありそうです。
σ(。-`ω´-)






MPU-6050搭載三軸加速度三軸ジャイロセンサモジュール

MPU-6050搭載三軸加速度三軸ジャイロセンサモジュール

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



ESPr Developer(ESP-WROOM-02開発ボード)

ESPr Developer(ESP-WROOM-02開発ボード)

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



Arduino 9軸モーションシールド

Arduino 9軸モーションシールド

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス




MPU6050 の Digital Motion Processor の性能を試してみた(2) [Arduino]

引き続き MPU6050 の DMP の性能テストです。今回は斜めの状態で、DMPの値がどのように収束していくか試してみます。スケッチは前回使ったものと同じものです。

まず、左に傾けてみました。

DSC04209.JPG


平面の静止状態とあまり変わりません。すごいですね。

MPU6050_back_up.png


次に右に傾けてみました。

DSC04210.JPG


傾向は変わらないようです。

MPU6050_front_up.png


今度は手前に傾けてみました。

DSC04211.JPG


ここまで来ると想像通りの振る舞いですね。

MPU6050_left_up.png


せっかくなので、奥にも傾けてみましょう。

DSC04212.JPG


予想通りの結果です。

MPU6050_right_up.png


これで平行に置かなくても値は収束することが分かりました。しかし、若干オフセットがかかっているのが気になります。加速度では変化なくても、速度にするとどんどん加速もしくは減速してしまいます。

やっぱり、キャリブレーションは必要そうですね。チャレンジしてみますか・・・。
σ( ̄・ω・ ̄)





サインスマート(SainSmart) MPU-6050 3軸ジャイロスコープ モジュール for Arduino

サインスマート(SainSmart) MPU-6050 3軸ジャイロスコープ モジュール for Arduino

  • 出版社/メーカー: サインスマート(SainSmart)
  • メディア: エレクトロニクス



ESP-WROOM-02ピッチ変換済みモジュール《フル版》

ESP-WROOM-02ピッチ変換済みモジュール《フル版》

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



図解入門よくわかる最新センサーの基本と仕組み (How‐nual Visual Guide Book)

図解入門よくわかる最新センサーの基本と仕組み (How‐nual Visual Guide Book)

  • 作者: 高橋 隆雄
  • 出版社/メーカー: 秀和システム
  • 発売日: 2011/03/23
  • メディア: 単行本




MPU6050 の Digital Motion Processor の性能を試してみた(1) [Arduino]

MPU6050 ので速度と距離を測定するため、MPU6050 の Digital Motion Processor (DMP) がどの程度の精度が出るのか性能を測定をして見ることにしました。


DSC04208.JPG


DMPで取れる座標には REAL ACCEL とWORLD ACCEL の2種類あります。REAL ACCEL と WORLD ACCES の違いはこちらを参照してください。どちらがいいのか調べてみたら、下記のスレッドのやりとりで、WORLD ACCEL は精度が低いからやめろとありました。

MPU-6050 Accelerometer Calibration and DMP
http://www.i2cdevlib.com/forums/topic/75-mpu-6050-accelerometer-calibration-and-dmp/

ということで、静止状態でリセットした時の REAL ACCEL の出力データの様子を、下記のスケッチで試すことにしてみました。

#include <Wire.h>
#include "I2Cdev.h"
#include "MPU6050DMP.h"
extern "C" {
#include "user_interface.h"
}

#define INT_PIN 4

MPU6050DMP mpu6050;
bool dmpReady = false;
uint16_t  packetSize = 0;

volatile bool mpuInterrupt = false;
void dmpDataReady() {
    mpuInterrupt = true;
}

unsigned long getCurrentTimeMS() {
  return ESP.getCycleCount() / 80000L;
}

void setup() {

  Wire.begin(12, 14);  
  Wire.setClock(400000);
  Serial.begin(115200);
  pinMode(INT_PIN, INPUT);

  if (mpu6050.testConnection()) {
    Serial.println("MPU6050 OK");
    attachInterrupt(INT_PIN, dmpDataReady, RISING);
  } else {
    Serial.println("MPU6050 NG");
    return;
  }
  
  mpu6050.initialize();
  mpu6050.setXGyroOffset(220);
  mpu6050.setYGyroOffset(76);
  mpu6050.setZGyroOffset(-85);
  mpu6050.setZAccelOffset(1688);

  if (mpu6050.dmpInitialize() == 0) {
    Serial.println("MPU6050DMP OK");
    mpu6050.setDMPEnabled(true); 
    packetSize = mpu6050.dmpGetFIFOPacketSize();
    dmpReady = true;    
  } else {
    Serial.println("MPU6050DMP NG");
  }
}

uint16_t fifoCount  = 0;
uint8_t  fifoBuffer[64];
unsigned long preTime = 0;

void loop() {
  
  if (!dmpReady) return;
  
  while (!mpuInterrupt);
  mpuInterrupt = false;

  while (fifoCount < packetSize) {
    fifoCount = mpu6050.getFIFOCount();
  }
  
  uint8_t mpuIntStatus = mpu6050.getIntStatus();
  
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
    mpu6050.resetFIFO();
    return;
  } else if ((mpuIntStatus & 0x02) == false) {
    return;
  }
  
  mpu6050.getFIFOBytes(fifoBuffer, packetSize);
  fifoCount -= packetSize;

  unsigned long curTime = getCurrentTimeMS();
  unsigned long duration = curTime - preTime;

  Quaternion q;
  VectorInt16 aa;
  VectorInt16 aaReal;
  VectorFloat gravity;
  
  mpu6050.dmpGetQuaternion(&q, fifoBuffer);
  mpu6050.dmpGetAccel(&aa, fifoBuffer);
  mpu6050.dmpGetGravity(&gravity, &q);
  mpu6050.dmpGetLinearAccel(&aaReal, &aa, &gravity);
  Serial.print("areal\t");
  Serial.print(aaReal.x);
  Serial.print("\t");
  Serial.print(aaReal.y);
  Serial.print("\t");
  Serial.print(aaReal.z);
  Serial.print("\t");
  Serial.print(duration);
  Serial.println("");
  preTime = curTime;
}



4回データを測定をしてみました。縦軸の単位は G(9.8m/s^2) になります。0.5 は0.5G の意味です。


MPU6050DMP1.png

MPU6050DMP2.png

MPU6050DMP3.png

MPU6050DMP4.png


だいたいの傾向が見えてきました。データの間隔は10msecなので、17秒くらいで収束をしているようです。20秒静止していれば問題ないでしょう。

XYZ軸いずれもノイズ成分は Peak to Peak で 0.1G 程度あるようです。結構大きいですね。またZ軸(グレー)はオフセットがかかっているようなので調整が必要そうです。

次は斜め静止状態でどのようなデータが取れるか試してみたいと思います。
(^_^)/~









ESPr Developer(ESP-WROOM-02開発ボード)

ESPr Developer(ESP-WROOM-02開発ボード)

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



Make: Sensors: Projects and Experiments to Measure the World with Arduino and Raspberry Pi

Make: Sensors: Projects and Experiments to Measure the World with Arduino and Raspberry Pi

  • 作者: Tero Karvinen
  • 出版社/メーカー: Maker Media, Inc
  • 発売日: 2014/06/02
  • メディア: ペーパーバック




ESP-WROOM-02 と心拍センサーをバッテリーで動かしてみた。 [Arduino]

今日は火星がきれいに見えたので、帰宅早々天体望遠鏡を引っ張り出し、息子と天体観測をしました。スーパーマーズは逃しましたが、火星の運河を見ることができました。そのうち天体写真もチャレンジしたいなぁ。

さて、ずっと気になっていた ESP-WROOM-02 の 110mAh バッテリーによる駆動。ようやく試してみました。


DSC04084.JPG


接続を図示します。バッテリからの出力が3.7VでESP-WROOM-02への入力が3.3Vです。


ESP-WROOM-02-09086.png


3.7Vから3.3Vを生成する低消費電力の小型LDOがなかなか無くて探し回りましたが、なんとか秋葉原で調達することができました。


DSC04086.JPG


さて実際に動くか試してみましょう。



おぉ、きちんと動きました。しかもLEDの抵抗なしで動くとは・・・。余力がまだありそうです。正直、ESP-WROOM-02の起動時の突入電流でまともに駆動できないのではと思ったのですが杞憂だったようです。

残す課題は MPU6050 の追加のみとなりました。バッテリーとFLASHの容量が足りるかなー?
σ(ー. ー )





ESP-WROOM-02ピッチ変換済みモジュール《フル版》

ESP-WROOM-02ピッチ変換済みモジュール《フル版》

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



心拍センサ

心拍センサ

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



ブレッドボード用電源ボード3.3V

ブレッドボード用電源ボード3.3V

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス




ESP-WROOM-02で心拍センサーを動かしてみた [Arduino]

ESP-WROOM-02で心拍センサーを動かしてみました。


DSC04081.JPG


ESP-WROOM-02のADCは0Vから1Vの範囲しか測定できないので、3.3Vの心拍センサーの出力をだいたい1/3に分圧して入力しています。


ESP-WROOM-0220with20PulseSensor.png


スケッチを設計するポイントは、

・ シグナル値の読み込みに analobRead() 関数ではなく sysytem_adc_read() 関数を使用
・ 割り込みの設定に、timer0_isr_init()、timer0_attachInterrupt()、timer0_write() 関数を使用
・ 時間取得に、ESP.getCycleCount() 関数を使用

の3点です。


extern "C" {
  #include "user_interface.h"
}

#define N 10

volatile int BPM;
volatile int Signal;
volatile int IBI = 600;
volatile boolean Pulse = false;
volatile boolean QS = false;

volatile int Rate[N];
volatile unsigned long CurrBeatTime = 0;
volatile unsigned long LastBeatTime = 0;
volatile int P = 500;
volatile int T = 500;
volatile int Threshold = 512;
volatile int Amplifier = 100;

int PulseSensorPin = 17;
int FadePin = 4;
int FadeRate = 0;

void timer0_ISR (void) {
  noInterrupts();
  Signal = system_adc_read();
  CurrBeatTime = getCurrentTime(); // msec
  unsigned long interval = CurrBeatTime - LastBeatTime;
  
  // hold bottom
  if ((Signal < Threshold) && (interval > (IBI*3) / 5)) {
    if (Signal < T) {
      T = Signal;
    }
  }
   
  // hold peak
  if (Signal > Threshold && Signal > P) {
    P = Signal;
  }
  
  if (interval > 250 /* ms */) {
    
    // check if Signal is over Threshold
    if ((Signal > Threshold) && !Pulse && (interval > (IBI*3) / 5)) {
      Pulse = true;
      IBI = interval;
      
      if (Rate[0] < 0) { // first time
        Rate[0] = 0;
        LastBeatTime = getCurrentTime();
        setupTimer(10);
        noInterrupts();
        return;
      } else if (Rate[0] == 0) {  // second time
        for (int i = 0; i < N; ++i) {
          Rate[i] = IBI;
        }
      }
      
      word running_total = 0;     
      for (int i = 0; i < N-1; ++i) {
        Rate[i] = Rate[i+1];
        running_total += Rate[i];
      }
      
      Rate[N-1] = IBI;
      running_total += IBI;
      running_total /= N;
      BPM = 60000 / running_total;
      QS = true;
      LastBeatTime = getCurrentTime();
    }
  }
  
  // check if Signal is under Threshold
  if ((Signal < Threshold) && Pulse) {
    Pulse = false;
    Amplifier = P - T;
    Threshold = Amplifier / 2 + T; // revise Threshold
    P = Threshold;
    T = Threshold;
  }
  
  // check if no Signal is over 2.5 sec
  if (interval > 2500 /* ms */) {
    Threshold = 512;
    P = 500;
    T = 500;
    LastBeatTime = getCurrentTime();
    for (int i = 0; i < N; ++i) {
      Rate[i] = -1;
    }
  }
  setupTimer(10);
  interrupts();
}

void setupTimer(int m /* msec */) {
  timer0_isr_init();
  timer0_attachInterrupt(timer0_ISR);
  timer0_write(ESP.getCycleCount() + 80000L * m); // 80MHz/1000 == 1msec
}

unsigned long getCurrentTime() {
  return ESP.getCycleCount() / 80000L;
}

void setup() {
  pinMode(FadePin, OUTPUT);
  analogWriteRange(255);
  Serial.begin(115200); 
  noInterrupts();
  setupTimer(10);
  interrupts();
  LastBeatTime = getCurrentTime(); // msec
}

void loop() {
  if (QS) {
    FadeRate = 255; 
    Serial.print("BPM: ");
    Serial.println(BPM);
    QS = false;
  }
  
  FadeRate -= 15;
  FadeRate = constrain(FadeRate, 0, 255);
  analogWrite(FadePin, FadeRate);
  delay(20);
}


さて、きちんと動くかなー?



システムから時間をとっているせいか、Arduno Pro Mini で使ったアルゴリズムよりも精度よく測定しているような気がします。次はいよいよバッテリー駆動にチャレンジかなぁ。
σ(^_^)





ESP-WROOM-02ピッチ変換済みモジュール《フル版》

ESP-WROOM-02ピッチ変換済みモジュール《フル版》

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス



心拍センサ

心拍センサ

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス







この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。