ESP32でGarmin foreAthlete245から心拍数データを取得する

,

手元にあるGarmin foreAthlete245から心拍数のデータをESP32 DevModuleにて取得したいと思います。

こちらの情報からUUIDは次の通り。

Service: Heart Rate UUID: 180D
Characteristic: Heart Rate Measurement UUID: 2A37
Descriptor: Client Characteristic Configuration UUID: 2902
Characteristic: Body Sensor Location UUID: 2A38

まずはGarminの設定で(左中ボタン長押し)- [光学式心拍計] – [心拍転送モード]にする必要あり。

AIの力をお借りしながら、ArduinoIDE2.3.6でプログラムおよび書き込み。初めは動作せず、どうもボードバージョンとライブラリバージョンとの相性がありそう。espのボードバージョン2.0.17、NimBLEDeviceのライブラリバージョンを1.4.3にて動作。

C++
/*
    2025-05-27 Garmin FOREATHLETE245からESP32 DEVMODULEにて心拍数取得

    ESP32 Dev Modul(2.0.17)
    NimBLEDevice(1.4.3)
    ※ 最新ボードバージョン、ライブラリバージョンでの組み合わせでは動作しないようである
*/
#include <NimBLEDevice.h>

// 取得した心拍数を格納
uint8_t heartRate = 0;
bool deviceConnected = false;

// 心拍数測定 characteristic UUID
#define HEART_RATE_SERVICE_UUID "180D"
#define HEART_RATE_MEASUREMENT_UUID "2A37"

static NimBLEAdvertisedDevice* advDevice = nullptr;
static NimBLEClient* pClient = nullptr;

// 通知(Notify)を受け取った時の処理
void notifyCallback(NimBLERemoteCharacteristic* pRemoteCharacteristic,
                    uint8_t* pData, size_t length, bool isNotify) {
  if (length > 1) {
    heartRate = pData[1];  // 2バイト目が心拍数
    Serial.print("Heart Rate: ");
    Serial.println(heartRate);
  } else {
    Serial.println("NO DATA");
  }
}

// スキャンでデバイスを見つけた時の処理
class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks {
  void onResult(NimBLEAdvertisedDevice* advertisedDevice) override {
    if (advertisedDevice->isAdvertisingService(NimBLEUUID(HEART_RATE_SERVICE_UUID))) {
      Serial.println("Found Heart Rate Device!");
      advDevice = advertisedDevice;
      advertisedDevice->getScan()->stop();
    }
  }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE Client...");
  // NimBLE 初期化
  NimBLEDevice::init("ESP32_Device");
  // スキャン実施...
  NimBLEScan* pScan = NimBLEDevice::getScan();
  pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
  pScan->setActiveScan(true);
  pScan->start(5, false);  // 5秒スキャン
}

void loop() {
  if (advDevice && !deviceConnected) {
    pClient = NimBLEDevice::createClient();
    if (pClient->connect(advDevice)) {
      Serial.println("Connected to device");
      NimBLERemoteService* pService = pClient->getService(HEART_RATE_SERVICE_UUID);
      if (pService) {
        NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(HEART_RATE_MEASUREMENT_UUID);
        if (pChar && pChar->canNotify()) {
          if (pChar->subscribe(true, notifyCallback)) {
            Serial.println("Subscribed to heart rate notifications");
            deviceConnected = true;
          } else {
            Serial.println("Failed to subscribe");
          }
        } else {
          Serial.println("Heart rate characteristic not found or notify not supported");
        }
      } else {
        Serial.println("Heart rate service not found");
      }
    } else {
      //! データが取得できなかった時のデバッグ情報...
      Serial.println("Failed to connect");
      Serial.print("Device name: ");
      Serial.println(advDevice->getName().c_str());
      Serial.print("Address: ");
      Serial.println(advDevice->getAddress().toString().c_str());
      Serial.print("RSSI: ");
      Serial.println(advDevice->getRSSI());
    }
    advDevice = nullptr;  // 次のループでは再接続しない
  }
  // その他の処理をここに記述可能
  delay(100);
}

ただ、Garminの設定を都度変更しないたダメなので、ちょっと普段使いには向いてなさそうです。


PAGE TOP