nkolban / esp32-snippets

Sample ESP32 snippets and code fragments
https://leanpub.com/kolban-ESP32
Apache License 2.0
2.36k stars 710 forks source link

Using BLE for Heart Rate Monitors, newer belts don't work #837

Closed Vulture-in-the-woods closed 5 years ago

Vulture-in-the-woods commented 5 years ago

Hi Neil.

First of all thank you for your library and the subsequent work you've been putting in it.

I've been using your library together with the ESP32 and a 64x32 RGB LED matrices to show my Heart rate while I work out. The idea is based on a sketch by Andreas Spiess, which I use as a test/benchmark file. I've been using it toghter with a Polar H7 pulse belt and everything has been working like a charm, so far.

Now I've gotten two new puls pelts, the Polar H10 and the Wahoo Tickr. Both of them show the same GATT with the Service UUID 0x180D and CharUUID 0x2A37 Neither connects to the ESP32, that is, they get to the "- Created client" step, but not the "- Connected to server" which is given by these lines of code:

BLEClient* pClient = BLEDevice::createClient(); Serial.println(" - Created client");

// Connect to the remove BLE Server. pClient->connect(pAddress); Serial.println(" - Connected to server");

I've tried changing the CharUUID in case the sensor had 0x2A38 or 0x2A39 instead for what ever reason, but nothing helps.

I can find both belts on my Iphone SE, and connect to them via apps like the Wahoo Fitness app, Polar Beat and Endomondo.

I can also see them via the nRF Connect app and verify that the serviceUUID infact is 0x180D

The common denominator for these belt are that they have some sort of extra capacity, primarily memory function

My questions are:

What is the difference which prohibits me from connecting the ESP32 to the pulse belt? Which measures could be taken to make the BLE connection work in order for the belts to connect to the ESP32?

Best Regards Erik

bluetooth_andreass_spiess.zip

chegewara commented 5 years ago

Hi, if you are using latest ble arduino version then try this: pClient->connect(pAddress, 1);

Vulture-in-the-woods commented 5 years ago

Hi chegewara

Thank you for your input.

Unfortunately that did not compile. I have update the library.

//Erik

error message: .....Arduino\libraries\ESP32_BLE_Arduino-master\src/BLEClient.h:37:45: note: initializing argument 2 of 'bool BLEClient::connect(BLEAddress, esp_ble_addr_type_t)'

bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC); // Connect to the remote BLE Server

exit status 1 invalid conversion from 'int' to 'esp_ble_addr_type_t' [-fpermissive]

chegewara commented 5 years ago

pClient->connect(pAddress, (esp_ble_addr_type_t)1); or pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM);

Sorry, i didnt remember that enum value: BLE_ADDR_TYPE_RANDOM

Vulture-in-the-woods commented 5 years ago

pClient->connect(pAddress,(esp_ble_addr_type_t)1) compiles nicely, but I'm not at the lab right now, I will test it in a few hours, thanks.

But I didn't get why? what is BLE_ADDR_TYPE_RANDOM and what difference does it do? Sorry for being daft, I'm a Master student doing Microwave Hardware design, programming is a useful tool for me, but not the core of my abilities/understanding.

chegewara commented 5 years ago

It just tells what kind of address should expect. Some time ago i thought i will add this option to fix some issues with connecting to sensors/servers. I know it is confusing and for backward compatibility i left this parameter with value that was hardcoded earlier.

esp-idf stack is evaluating and some options and/or functionalities are introduced, so ive been trying to follow it.

Vulture-in-the-woods commented 5 years ago

It works.!

Thank you Chegewara.

I have tested several pulse belts, and for some the line "pClient->connect(pAddress);" work well

and for others the line "pClient->connect(pAddress, (esp_ble_addr_type_t)1);" works

I see no difference on using pClient->connect(pAddress, (esp_ble_addr_type_t)1); or pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM);

but neither is generic for the four different HRM devices I've tested.

But for all practical purposes it works, and I am gratefull.

//Erik

chegewara commented 5 years ago

pClient->connect(pAddress, (esp_ble_addr_type_t)1); and pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM); are equal. BLE_ADDR_TYPE_RANDOM is equal 1 in enumerator definition.

me-chiel commented 2 years ago

I wonder if any of you can help. I've got the Polar H9 and with the code it finds the service, and connects, and then crashes due to a stack overflow error. I'm rather new to this, and tried the other connect options you proposed... the result ending in the same stack overflow error.

Backtrace:0x40093ed3:0x3ffd6a500x40092004:0x3ffd6a90 0x40091fb6:0xa5a5a5a5 |<-CORRUPTED

any idea how I could fix this? And sorry for posting on this old link. Any help would be appreciated..

chegewara commented 2 years ago

Stack canary watchpoint triggered (BTU_TASK)

You have problem with your code.

me-chiel commented 2 years ago

I wonder if any of you can help. I've got the Polar H9 and with the code it finds the service, and connects, and then crashes due to a stack overflow error. I'm rather new to this, and tried the other connect options you proposed... the result ending in the same stack overflow error.

Backtrace:0x40093ed3:0x3ffd6a500x40092004:0x3ffd6a90 0x40091fb6:0xa5a5a5a5 |<-CORRUPTED

any idea how I could fix this? And sorry for posting on this old link. Any help would be appreciated..

Stack canary watchpoint triggered (BTU_TASK)

You have problem with your code.

sadly it happens a bit random. I'll do my best to have a closer look at it later this weekend!

imaimai17468 commented 2 years ago

I am now in the same situation as me-chiel. In my case, this happens when I use getService(). We are not using a particularly large array, and there seems to be plenty of free memory space. I also increased the stack size in xTaskCreateUniversal() in the esp32 settings, but it did not improve the problem. Here is the code used and the output of the serial communication.

I would appreciate it if you could tell me how to improve this problem.

//===== header file & define & global variable =====
#include <M5Stack.h>
#include "BLEDevice.h"

#include <esp_bt_main.h>
#include "esp_pm.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define sleep_ms(ms)  vTaskDelay( (ms)/portTICK_PERIOD_MS )

static bool doConnect = false;
static bool isConnected = false;
static bool doScan = false;
static bool isScanContinue = true;
static int count = 0;

static BLEUUID serviceUUID ("0000180d-0000-1000-8000-00805f9b34fb");
static BLEUUID charUUID ("00002a37-0000-1000-8000-00805f9b34fb");

static BLEAdvertisedDevice* myh10;
static BLEClient* pClient_h10;

String h10Name = "Polar H10 68D9F22F";

int time_h10 = 0;
int h10_dot = 0;

File h10file;

#define IP5306_ADDR (117) // 0x75
#define IP5306_REG_SYS_CTL1 (0x01)
#define BOOST_ENABLE_BIT (0x20)
//===========================================

//===== class & function ====================
void poweroff(){
  uint8_t data;
  M5.Lcd.setBrightness(0);
  M5.Lcd.sleep();
  if (M5.I2C.readByte(IP5306_ADDR, IP5306_REG_SYS_CTL1, &data) == true){
    M5.I2C.writeByte(IP5306_ADDR, IP5306_REG_SYS_CTL1, (data & (~BOOST_ENABLE_BIT)));
  }
  esp_bluedroid_disable();
  esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
  gpio_deep_sleep_hold_dis();
  M5.Power.setPowerBoostKeepOn(false);
  M5.Power.setLowPowerShutdownTime(POWER::ShutdownTime::SHUTDOWN_8S);
  M5.Power.setPowerBtnEn(true);
  esp_deep_sleep_start();
}

class MyClientCallback: public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) { }
    void onDisconnect(BLEClient* pclient) {
      isConnected = false;
      Serial.print("onDisconnect: ");
      Serial.println(++count);
      M5.Lcd.println("onDisconnetct");

      if(isScanContinue){
        BLEDevice::getScan()->erase(pClient_h10->getPeerAddress());
      }
      delete myh10;
      delete pClient_h10;
    }
};

class MyAdvertisedDeviceCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      // Serial.println(advertisedDevice.toString().c_str());

      if (h10Name.equals(advertisedDevice.getName().c_str())) {
        Serial.print("Connect BLE device : ");
        Serial.println(advertisedDevice.toString().c_str());
        myh10 = new BLEAdvertisedDevice(advertisedDevice);
        BLEDevice::getScan()->stop();
        doConnect = true;
        doScan = true;
      }
    }
};

void notifyCallback_h10(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
  int hr = pData[1];
  int16_t ppi = 0;
  std::vector<int> times;
  for (int i = 2; i < length; i += 2) {
    ppi = pData[i] | (pData[i + 1] << 8);

    times.push_back(ppi);
  }

  for (int i = 0; i < times.size(); i++) {
    times[i] /= 1.024;
  }

  for (int i = 0; i < times.size(); i++) {
    time_h10 += times[i];
    h10_dot++;

    String data = "[H10] count : " + String(h10_dot) + ", ppi [ms] : " + String(times[i]) + ", time sum [s] : " + String(time_h10 / 1000.0, 3);
    Serial.println(data);

    String sd_data = String(h10_dot) + "," + String(times[i]) + "," + String(time_h10 / 1000.0, 3);
    h10file.println(sd_data);

    M5.Lcd.fillRect(0, 95, 1000, 50, BLACK);
    M5.Lcd.setCursor(0, 95);
    M5.Lcd.setTextSize(4);
    M5.Lcd.print(time_h10 / 1000.0, 3);
    M5.Lcd.print(", ");
    M5.Lcd.print(times[i]);
  }
}

void indicateCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
  Serial.print("Indicate callback for characteristic ");
  Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
  Serial.print(" of data length ");
  Serial.println(length);
  Serial.print("data: ");
  for (int i = 0; i < length; i++) {
    Serial.printf("%02x", pData[i]);
    Serial.print(" ");
  }
  Serial.println();
}

bool connectToServer() {
  Serial.print("connection to : ");
  Serial.println(myh10->getAddress().toString().c_str());
  pClient_h10 = BLEDevice::createClient();
  Serial.println(" - Created client (h10)");

  Serial.print("1-BLE FreeHeap: ");
  Serial.println(ESP.getFreeHeap());
  pClient_h10->setClientCallbacks(new MyClientCallback() );

  bool _connected = pClient_h10->connect(myh10);
  if(!_connected){
    Serial.println(" - failed to server (h10)");
    delete pClient_h10;
    delete myh10;
    return false;
  }
  Serial.println(" - Created to server (h10)");

  Serial.print("2-BLE FreeHeap: ");
  Serial.println(ESP.getFreeHeap());

  // service uuid
  BLERemoteService* pRemoteService_h10 = pClient_h10->getService(serviceUUID);
  if (pRemoteService_h10 == nullptr) {
    Serial.println("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient_h10->disconnect();
    return false;
  }
  Serial.print(" - Found service ( ");
  Serial.print(serviceUUID.toString().c_str());
  Serial.println(" )");

  Serial.print("3-BLE FreeHeap: ");
  Serial.println(ESP.getFreeHeap());

  // char uuid
  static BLERemoteCharacteristic* pRemoteCharacteristic;
  pRemoteCharacteristic = pRemoteService_h10->getCharacteristic(charUUID);
  if ( pRemoteCharacteristic == nullptr ) {
    Serial.print("Failed to find out characteristic UUID : ");
    Serial.println(charUUID.toString().c_str());
    pClient_h10->disconnect();
    return false;
  }
  Serial.print(" - Add Notify ( ");
  Serial.print(charUUID.toString().c_str());
  Serial.println(" )");

  Serial.print("4-BLE FreeHeap: ");
  Serial.println(ESP.getFreeHeap());

  // notify h10
  if (pRemoteCharacteristic->canNotify()) {
    Serial.println(" - Can Notify h10");
    pRemoteCharacteristic->registerForNotify(notifyCallback_h10);
  }

  isConnected = true;
  return true;
}
//===========================================

//===== setting =============================
void setup() {
  M5.begin();
  M5.Power.begin();

  esp_pm_config_esp32_t esp_pm_config_esp32;
  esp_pm_config_esp32.max_freq_mhz = RTC_CPU_FREQ_240M;
  esp_pm_config_esp32.min_freq_mhz = RTC_CPU_FREQ_XTAL;
  esp_pm_config_esp32.light_sleep_enable = true;
  esp_pm_configure(&esp_pm_config_esp32);

  // LCD settings
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setBrightness(128);
  M5.Lcd.setCursor(0, 0);

  if (!SD.begin()) {
    M5.Lcd.println("Card failed, or not present");
    while (1) { sleep_ms(1); }
  }

  // SD card
  M5.Lcd.println("check csv...");
  if ( SD.exists("/h10.csv") == true ) {
    M5.Lcd.println("csv will be destroyed. OK? ");
    M5.Lcd.println("OK -> Button A");
    M5.Lcd.println("NO -> Button B (stop)");
    while ( M5.BtnA.wasReleased() == false ) {
      M5.update();
      if ( M5.BtnB.wasReleased() ){
        while(1){ sleep_ms(1); }
      }
    }
  }
  SD.remove("/h10.csv");
  h10file = SD.open("/h10.csv", FILE_WRITE);
  M5.Lcd.println("opened h10.csv!");
  M5.Lcd.clear(BLACK);

  // display settings
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextSize(2);
  M5.Lcd.println(h10Name.c_str());
  M5.Lcd.setTextSize(4);
  M5.Lcd.println("Unknown");
  M5.Lcd.println();
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("Button A -> finish");
  M5.Lcd.println("Button B -> display off");
  M5.Lcd.println("Button C -> display on");

  // start ble
  Serial.print("BLE FreeHeap: ");
  Serial.println(ESP.getFreeHeap());
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("");
  BLEDevice::setMTU(232);
  static BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallback());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);
}
//===========================================

//===== main ================================
void loop() {
  M5.update();

  // A:finish, B:diplay off, C:display on
  if ( M5.BtnA.wasReleased() || M5.Power.getBatteryLevel() == 0 ) {
    M5.Lcd.clear(BLACK);
    M5.Lcd.setTextSize(2);
    M5.Lcd.setCursor(0, 0);

    pClient_h10->disconnect();
    delete(pClient_h10);
    h10file.close();
    Serial.println("SD Filse close");
    while (isConnected == true){
      sleep_ms(1);
    }

    poweroff();
  }
  if ( M5.BtnB.wasReleased() ) {
    M5.Lcd.setBrightness(0);
  }
  if ( M5.BtnC.wasReleased() ) {
    M5.Lcd.setBrightness(128);
  }

  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("now connected to BLE Server.");
    } else {
      Serial.println("faild to connect to the server.");
    }
    doConnect = false;
  }

  if ( isConnected == false && doScan == true ) BLEDevice::getScan()->start(0);

  sleep_ms(1000);
}
//===========================================
BLE FreeHeap: 218096
Starting Arduino BLE Client application...
Connect BLE device : Name: Polar H10 68D9F22F, Address: de:e1:43:dd:bc:64, manufacturer data: 6b00230b5251, serviceUUID: 0000180d-0000-1000-8000-00805f9b34fb, serviceUUID: 0000feee-0000-1000-8000-00805f9b34fb, txPower: 4
connection to : de:e1:43:dd:bc:64
 - Created client (h10)
1-BLE FreeHeap: 133216
 - Created to server (h10)
2-BLE FreeHeap: 127088
Guru Meditation Error: Core  0 panic'ed (Unhandled debug exception). 
Debug exception reason: Stack canary watchpoint triggered (BTU_TASK) 
Core  0 register dump:
PC      : 0x40095f22  PS      : 0x00050036  A0      : 0x400945c7  A1      : 0x3ffd7b50  
A2      : 0x00050023  A3      : 0x00000000  A4      : 0x00000001  A5      : 0x400944f4  
A6      : 0x00060a23  A7      : 0x00000008  A8      : 0x80085a6e  A9      : 0x3ffbe900  
A10     : 0x00000000  A11     : 0x3ffbe900  A12     : 0x00000000  A13     : 0x00060023  
A14     : 0x00000001  A15     : 0x3ffd1820  SAR     : 0x00000008  EXCCAUSE: 0x00000001  
EXCVADDR: 0x00000000  LBEG    : 0x40090cec  LEND    : 0x40090d02  LCOUNT  : 0xffffffff  

Backtrace:0x40095f1f:0x3ffd7b500x400945c4:0x3ffd7b90 0x40094576:0xa5a5a5a5  |<-CORRUPTED
FIXMBR commented 2 years ago

I was having the same issue as @me-chiel and @imaimai17468 with Polar H10. I switched from arduino to platfrom.io and it just magically worked.

FIXMBR commented 2 years ago

As it turns out platformio uses outdated arduino-esp32 1.0, the issue happens when you use 2.0.0+ version of the library. You can simply change esp version to 1.0.6 in boards manager to make it work. I'm gonna write an issue on the official repo to get it fixed in future.

me-chiel commented 2 years ago

Oh that’s interesting! Will try this out asap! Thank you so much for this tip. I wonder if we can make it work with the newer version someday

On 25 Dec 2021, at 23:16, FIXMBR @.***> wrote:

 As it turns out platformio uses outdated arduino-esp32 1.0, the issue happens when you use 2.0.0+ version of the library.

— Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you were mentioned.

adojang commented 2 years ago

As it turns out platformio uses outdated arduino-esp32 1.0, the issue happens when you use 2.0.0+ version of the library. You can simply change esp version to 1.0.6 in boards manager to make it work. I'm gonna write an issue on the official repo to get it fixed in future.

Just wanted to say thank you for this, this fixed the issue for me.