ESP32におけるMAX3010xパルスオキシメーターセンサーのI2C初期化
MAX3010xは、心拍数と血中酸素飽和度(SpO2)の測定に広く使用される統合型パルスオキシメーターおよび心拍数モニターセンサーです。このセンサーはI2Cインターフェースを介してマイクロコントローラーと通信します。本稿では、ESP32開発ボード上でArduino環境とSparkFun MAX3010xセンサーライブラリを使用して、MAX3010xセンサーのI2C通信を初期化する方法について詳しく解説します。
ライブラリの組み込みとセンサーオブジェクトの生成
MAX3010xセンサーを使用するには、関連するライブラリをプロジェクトに含める必要があります。Arduino IDEのライブラリマネージャーから「SparkFun MAX3010x Sensor Library」を検索してインストールできます。
以下のヘッダーファイルをコードにインクルードし、センサーを制御するためのオブジェクトを生成します。
#include <Wire.h> // I2C通信用ライブラリ
#include "MAX30105.h" // SparkFun MAX3010xセンサーライブラリ
// MAX3010xセンサーオブジェクトの生成
// ライブラリはMAX30105としていますが、MAX30102も同様に扱えます。
MAX30105 pulseOximeterSensor;
Wire.hはArduinoのI2C(Two-Wire Interface)通信機能を提供します。MAX30105.hはセンサーの具体的な機能にアクセスするためのAPIを含んでいます。
I2C通信の初期設定
センサーとの通信を開始する前に、I2Cバスを適切に初期化する必要があります。SparkFunライブラリのbegin()メソッドがこの処理を担当します。
if (!pulseOximeterSensor.begin(Wire, I2C_SPEED_STANDARD)) {
Serial.println("MAX3010xセンサーの検出に失敗しました。配線と電源を確認してください。");
while (1); // センサーが検出されるまで無限ループ
}
Serial.println("MAX3010xセンサーの初期化が完了しました。");
begin()メソッドの詳細
begin()メソッドは、I2C通信の設定を行います。そのプロトタイプは以下の通りです。
boolean begin(TwoWire &i2cPort = Wire, uint32_t i2cSpeed = I2C_SPEED_STANDARD, uint8_t i2cAddress = MAX30105_ADDRESS);
このメソッドは、以下の3つのオプションパラメータを受け取ります。
i2cPort: 使用するTwoWire(I2C)ポートへの参照です。通常はデフォルトのWireを使用します。ESP32などのボードで複数のI2Cバスがある場合は、Wire1などを指定することも可能です。i2cSpeed: I2C通信速度を設定します。デフォルトはI2C_SPEED_STANDARD(100kHz) です。I2C_SPEED_STANDARD: 100 kHzI2C_SPEED_FAST: 400 kHz
I2C_SPEED_STANDARD(100kHz) の使用を推奨します。i2cAddress: MAX3010xセンサーのI2Cアドレスです。デフォルトは0x57です。通常は変更する必要はありません。
上記のコード例では、明示的にWireとI2C_SPEED_STANDARDを指定しています。これは、2番目の引数である通信速度を設定するために、最初の引数も渡す必要があるためです。もし低速(100kHz)で通信したいだけであれば、begin()メソッドに引数を渡さないことで、デフォルト設定が適用されます。
begin()メソッドの内部処理
begin()メソッドの内部では、以下のような処理が行われます。
- 指定されたI2Cポートの参照を内部変数に格納します。
_i2cPort->begin();を呼び出し、I2Cバスを初期化します。_i2cPort->setClock(i2cSpeed);を呼び出し、I2C通信速度を設定します。- センサーのI2Cアドレスを内部変数に設定します。
readPartID()メソッドを呼び出し、センサーの部品IDを読み取って、期待されるIDと一致するかを確認します。これにより、センサーが正しく接続され、通信可能であるかを検証します。readRevisionID()メソッドを呼び出し、センサーのリビジョンIDを読み取ります。
部品IDが期待値(MAX_30105_EXPECTEDPARTID = 0x15)と異なる場合、begin()メソッドはfalseを返し、センサーが見つからないことを示します。
I2Cレジスタの読み書き操作
ライブラリのbegin()メソッドやその他のセンサー設定関数は、内部的にI2Cを介したレジスタの読み書き操作を行っています。ここでは、基本的な8ビットレジスタの読み書きについて解説します。
8ビットレジスタの読み取り (readRegister8相当)
MAX3010xセンサーから1バイトのデータを読み取る関数は、以下のようなI2Cシーケンスで実装されます。
- I2C通信を開始し、センサーのアドレス(書き込みモード)を送信します。
- 読み取りたいレジスタのアドレスを送信します。
- リピートスタート条件を送信し、再度センサーのアドレス(読み取りモード)を送信します。
- センサーから1バイトのデータを受信します。
- NACK(非確認応答)を送信し、通信を終了します。
uint8_t readI2CRegister(TwoWire* i2cPort, uint8_t deviceAddress, uint8_t registerAddress) {
// 1. 読み取りたいレジスタアドレスを送信
i2cPort->beginTransmission(deviceAddress);
i2cPort->write(registerAddress);
i2cPort->endTransmission(false); // STOP条件なしで終了(リピートスタートのため)
// 2. センサーから1バイトのデータを要求
i2cPort->requestFrom(deviceAddress, (uint8_t)1);
// 3. データが利用可能であれば読み取る
if (i2cPort->available()) {
return i2cPort->read();
}
return 0; // 読み取り失敗またはデータなし
}
endTransmission(false)は、I2Cバスを解放せずにリピートスタート条件を生成するために使用されます。もしセンサーがノイズの影響などで正しく応答しない場合、requestFromがデータを受信できず、available()がfalseを返すか、read()が意図しない値を返す可能性があります。特に0が返された場合、それが実際にレジスタの値なのか、通信失敗によるものなのか判別が難しい場合があります。
endTransmission()の戻り値は、通信の状態を示します。主なステータスコードは以下の通りです。
| ステータスコード | 意味 |
|---|---|
| 0 | 送信成功 |
| 1 | データ量が送信バッファの容量を超過 |
| 2 | アドレス送信時にNACKを受信 |
| 3 | データ送信時にNACKを受信 |
| 4 | その他のエラー |
8ビットレジスタへの書き込み (writeRegister8相当)
MAX3010xセンサーのレジスタに1バイトのデータを書き込む関数は、以下のようなI2Cシーケンスで実装されます。
- I2C通信を開始し、センサーのアドレス(書き込みモード)を送信します。
- 書き込みたいレジスタのアドレスを送信します。
- 書き込みたいデータを送信します。
- STOP条件を送信し、通信を終了します。
void writeI2CRegister(TwoWire* i2cPort, uint8_t deviceAddress, uint8_t registerAddress, uint8_t dataValue) {
i2cPort->beginTransmission(deviceAddress); // センサーアドレスで通信開始
i2cPort->write(registerAddress); // 書き込み対象レジスタアドレスを送信
i2cPort->write(dataValue); // 書き込むデータを送信
i2cPort->endTransmission(true); // STOP条件を送信して通信終了
}
endTransmission(true)は、データの送信後にSTOP条件を生成し、I2C通信を終了させます。引数を省略した場合もデフォルトでtrueが指定されます。