Max30102 + M5StickC
Post date: 2020/06/21
1. なぜ血中酸素濃度測定
在宅ワークのためのホコリセンサーのつもりで Max30105 (particle sensor) を買おうとしていたのだが、何かの間違いで Max30102 (pulse oximeter, heart-rate sensor) を買ってしまった。Amazon でいろいろ見て回るうちに、一番安いのを買ったのかもしれない(が、よく覚えていない)。
せっかくなので、脈拍と血中酸素濃度の測定器を作成することにした。
- Max30102 data sheet (maxim integrated)
- Max30102 高感度パルス酸素濃度計および心拍数センサー、ウェアラブル型ヘルスケア用 (maxim integrated)
- アプリケーションノート 6845 Guidelines for SpO2 Measurement Using the Maxim® MAX32664 Sensor Hub (maxim integrated)
2. RaspberryPi で測定
RaspberryPi Zero WH につないで、測ってみた。
こちらのサイト(Raspberry Piと心拍センサ(MAX30102)で脈を見てみよう)のソースを動かしてみたところ、5V をつなげばそのまま動いた。
- 3V3 -> VIN は NG (
$ i2cdetect -y 1
で出ない) - 5V -> VIN, GPIO0->SDA, GPIO2->SCL, GND->GND, GPIO4 -> INT で接続
血中酸素濃度も図れるようなので感心していたら、家族から「それ最近品薄で問題になっているやつ」との指摘あり。
M5StickC につないで、表示ありで動くようにすることにした。
3. M5StickC での苦難
Amazon の販売ページにMeasure Heart Rate and SpO2 with MAX30102という記事へのリンクあったので、それを利用
- Arduinoに Sparkfun_MAX3010x library をいれる
- 5V, G0->SDA, G26->SCL, GND でつなぐ
M5.begin();
Wire.begin(0, 26);
- 仕様
- 脈拍と SPO2 値を表示
- IR データのグラフを表示
問題発生:
particleSensor.begin()
が1回で応答しないので、while ループに変更while(!particleSensor.begin()) { delay(1); }
- library で計算させた HeartRate がどうもおかしい (時間間隔決め打ち?) ので、ピークをカウントして BPM (Beat Per Minute) を計算
- なぜかやや大きめ (70 - 80) に出るが、良しとする
- SPO2 値がおかしい (ほとんど正常値 (96-99) にならない)
- SPO2 値は上記 RaspPiZero 版では結構まともに出ていたので、もしかして library がおかしい (なんてことあるのか)?
仕方がないので、SPO2値の計算方法を調べてみるが、とりあえず2波長のAC成分とDC成分から計算すればよいことがわかる。
※本当は、Maker Faire Kyoto Online に参加しようと 5/2 当日朝から頑張っていたのだが、15:30 にぎりぎり間に合わず断念。
- パルスオキシメーターの原理 (KONIKA MINOLTA)
4. 解決の兆し
その後、忙しいので放っておいたが、Amazon の販売ページに新しいコメントを発見(同等品と思われるこちらにも記述あり)。やはり library がおかしい (それも、red と IR のデータが逆?点滅の順がおかしい?)。
詳しくはこちら (MAX30102でパルスオキシメーターを作る)。
記事通りに、led=1 (red only)
としてみると、
- IRのみ灯火 (見えない)
- 指をあてると
- getRed() はデータが出る
- getIR() はデータが出ない。
ことがわかる。つまり、
led=1
は IR onlygetRed()
は IR灯火時のセンサ読み取り値getIR()
は Red 灯火時のセンサ読み取りの値
であることが確認されてしまう。ライブラリのソースを見ると順番に読み取っているだけのようなのだが、面倒なのでアドホックに対応。
- プログラム中の
getRed()
をgetIR()
に変更 - プログラム中の
getIR()
をgetRed()
に変更
これでとりあえず動くようになった。
※ただ、私のはうまく読み取れるのだが。。。。
Red, IR, Green の順に4 byte x 3 づつ読み出し -> 18 bit 抜き出し
修正方法?:
- 読み出し順を IR, Red, Green にしてやればOKなのでは??
- Max30102には Green はないはず。。。
- data sheet によれば Red, IR の順
- LEDの発光順を変えれば良い?(できるのか?)
- Max 30102 には SpO2 Subsystem があるので、そこからとってくればよいのでは?
- SpO2 mode, HR mode がある
...
static const uint8_t SLOT_RED_LED = 0x01;
static const uint8_t SLOT_IR_LED = 0x02;
static const uint8_t SLOT_GREEN_LED = 0x03;
...
void MAX30105::setup(...) {
...
//Multi-LED Mode Configuration, Enable the reading of the three LEDs
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
enableSlot(1, SLOT_RED_LED);
if (ledMode > 1) enableSlot(2, SLOT_IR_LED);
if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED);
...
}
uint16_t MAX30105::check(void)
{
...
while (toGet > 0)
{
...
byte temp[sizeof(uint32_t)]; //Array of 4 bytes that we will convert into long
uint32_t tempLong;
//Burst read three bytes - RED
temp[3] = 0;
temp[2] = _i2cPort->read();
temp[1] = _i2cPort->read();
temp[0] = _i2cPort->read();
//Convert array to long
memcpy(&tempLong, temp, sizeof(tempLong));
tempLong &= 0x3FFFF; //Zero out all but 18 bits
sense.red[sense.head] = tempLong; //Store this reading into the sense array
if (activeLEDs > 1)
{
...
sense.IR[sense.head] = tempLong;
}
if (activeLEDs > 2)
{
...
sense.green[sense.head] = tempLong;
}
toGet -= activeLEDs * 3;
}
5. そしてこんどこそ
「みんなのM5Stack自慢大会」の告知を SwitchScience で発見し、何とかネタにしようと30分考えた。
- SPO2 > 95 (つまり、健康) だったら
- LED 点灯
- SOLAR パワーで、100円招き猫が手を振る
ということで、プログラム小修正と、実験開始。しかし、なかなか動くようで、動かないようで。。。。
とりあえず「Heboi」ジャンルで、facebook に投稿 (2020-06-20)。
6. 残った謎
- なぜ Sparkfun_MAX3010x を使うと getRed() と getIR() が逆になるのか
- MAX30102 のデータシート
- p.21, Table 9. Multi-LED Mode Control Registers
- 001 : LED1 (Red), 010: LED2 (IR)
- p.25, Figure 3
- Red -> IR の順に点灯
- p.21, Table 9. Multi-LED Mode Control Registers
- MAX30102 のデータシート
- VIN に 5V かけて大丈夫なのか
- 製品記事には 1.8V ~ 2.5V と書いてある (ボード裏のジャンパーは I2C のプルアップレベル 1.8V/3.3V (3.3Vを選択))
- RaspberryPi の i2cdetect では、5V 見つかる / 3.3V 見つからず
- Sparkfun の MAX30105 ボードは 5V (MAX30105 Particle and Pulse Ox Sensor Hookup Guide)
- INT を使って制御するべきか
- データシートから
- 温度補償
- SPO2モード:Table 5 Events Sequence を再現しないといけないのではないか
- 頑張ると nRF Toolbox for BLE でデータが読めるらしい