ESP32-C3는 BLE 5.0이 내장되어 있다. BLE관련 여러 서비스 중 표준 HRM 서비스를 테스트 해보자.
HR 수집기는 데이터를 사용할 수 있을 때마다 HRM 특성으로부터 알림을 받는다. 측정값은 Bluetooth LE 패킷당 23바이트의 데이터로 전송되고 첫 번째 바이트는 Flag 라고 하며 데이터 형식에 대한 정보를 제공 한다.
- HR 데이터 형식 : HR 값이 UINT8 또는 UINT16 형식인지 나타내는 1비트.
- 센서 접촉(SC) : SC 기능 지원 여부, 지원 여부, 지원 여부를 나타내는 2비트.
- Energy Expended(EE) : HRM 특성에서 Energy Expended가 있음을 나타내는 1비트.
- RR-간격(RR) : RR-간격 측정이 HRM 특성에 존재하는지 여부를 알려주는 1비트.
- RFFU : 향후 사용을 위해 예약된 3비트
HR Value
HR 데이터의 형식이 UINT8인지 UINT16인지에 따라 각각 1바이트 또는 2바이트로 값을 읽을 수 있다. 이는 플래그 뒤에 오는 바이트부터 시작하여 수신된 데이터 패킷에서 검색할 수 있다. HR 값은 분당 심박수(bpm)이다.
RR 간격
HRM 특성의 전송 간에 여러 RR 간격이 측정될 수 있으므로 RR 간격은 특성의 하위 필드에 표시. 서브 필드의 개수는 패킷의 전체 길이, HR 값의 형식, EE 존재 여부에 따라 결정. 그러므로,
- HR 값의 형식이 UINT8 인 경우 단일 HR 측정으로 알릴 수 있는 RR 간격 값의 최대 수
- EE가 있는 경우 최대 RR 간격 수는 8
- 그렇지 않으면 최대 RR 간격 수는 9
- HR 값의 형식이 UINT16 인 경우
- EE가 있는 경우 최대 RR 간격 수는 7
- 그렇지 않은 경우 최대 RR 간격 수는 8.
참고: RR 간격의 분해능은 1/1024초.
HRM 서비스를 생성하고 Charactistic을 등록해서 HRM 표준 데이터를 전송하는 ESP32-C3 BLE 코드
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include "math.h"
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
int gBleConFlag = 0;
BLECharacteristic heartRateMeasurementCharacteristics(BLEUUID((uint16_t)0x2A37), BLECharacteristic::PROPERTY_NOTIFY);
BLECharacteristic sensorPositionCharacteristic(BLEUUID((uint16_t)0x2A38), BLECharacteristic::PROPERTY_READ);
BLEDescriptor heartRateDescriptor(BLEUUID((uint16_t)0x2901));
BLEDescriptor sensorPositionDescriptor(BLEUUID((uint16_t)0x2901));
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string value = pCharacteristic->getValue();
if (value.length() > 0) {
Serial.println("*********");
Serial.print("New value: ");
for (int i = 0; i < value.length(); i++)
Serial.print(value[i]);
Serial.println();
Serial.println("*********");
}
}
void onConnect(BLEServer* pServer) {
Serial.println("Connected!");
gBleConFlag = 1;
//pBpmNotifyTask->start();
};
void onDisconnect(BLEServer* pServer) {
//pBpmNotifyTask->stop();
gBleConFlag = 0;
Serial.println("Disconnected!");
}
};
#define hrmServiceId BLEUUID((uint16_t)0x180D)
#define deviceInformationServiceId BLEUUID((uint16_t)0x180A)
#define batteryLevelServiceId BLEUUID((uint16_t)0x180F)
BLECharacteristic *_measurementCharacteristics = NULL;
BLECharacteristic *_sensorLocationCharacteristics = NULL;
BLECharacteristic *_controlPointCharacteristics = NULL;
BLECharacteristic *_batteryLevelCharacteristics = NULL;
void ble_init()
{
BLEDevice::init("HRM1");
BLEServer *pServer = BLEDevice::createServer();
//pServer->setCallbacks(this);
BLEService *hrmService = pServer->createService(hrmServiceId);
_measurementCharacteristics = hrmService->createCharacteristic(
BLEUUID((uint16_t)0x2A37), BLECharacteristic::PROPERTY_NOTIFY);
// Client Characteristic Configuration
_measurementCharacteristics->addDescriptor(new BLE2902());
// Body Sensor Location
/*
0x00 Other
0x01 Chest
0x02 Wrist
0x03 Finger
0x04 Hand
0x05 Ear Lobe
0x06 Foot
0x07–0xFF Reserved for Future Use*/
byte sensorLocationValue[1] = { 0x01 }; // Chest
BLECharacteristic *sensorLocation = hrmService->createCharacteristic(BLEUUID((uint16_t)0x2A38), BLECharacteristic::PROPERTY_READ);
sensorLocation->setValue(sensorLocationValue, 1);
// Characteristic User Description
BLEDescriptor userDescription(BLEUUID((uint16_t)0x2901));
userDescription.setValue("Demo 50-60 bpm");
_measurementCharacteristics->addDescriptor(&userDescription);
// Device Information Service - Did not help by it self.
BLEService *deviceInformationService = pServer->createService(deviceInformationServiceId);
BLECharacteristic *firmware = deviceInformationService->createCharacteristic(BLEUUID((uint16_t)0x2A26), BLECharacteristic::PROPERTY_READ);
firmware->setValue("1.2.3");
BLECharacteristic *hardware = deviceInformationService->createCharacteristic(BLEUUID((uint16_t)0x2A27), BLECharacteristic::PROPERTY_READ);
hardware->setValue("4.5.6");
BLECharacteristic *manufacturer = deviceInformationService->createCharacteristic(BLEUUID((uint16_t)0x2A29), BLECharacteristic::PROPERTY_READ);
manufacturer->setValue("ACME");
BLEService *batteryLevelService = pServer->createService(batteryLevelServiceId);
_batteryLevelCharacteristics = batteryLevelService->createCharacteristic(BLEUUID((uint16_t)0x2A19), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
_batteryLevelCharacteristics->addDescriptor(new BLE2902());
sensorLocation->setCallbacks(new MyCallbacks());
deviceInformationService->start();
hrmService->start();
batteryLevelService->start();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();
}
void setup() {
Serial.begin(115200);
ble_init();
}
byte flags = 0b00111110;
byte bpm;
byte heart[8] = { 0b00001110, 60, 0, 0, 0 , 0, 0, 0};
byte hrmPos[1] = {2};
int randomGen(int min, int max)
{
int range;
range = max-min + 1;
return rand()%range + min;
}
void loop() {
if(gBleConFlag)
{
heart[1] = (byte)bpm;
int energyUsed = 3000;
heart[3] = energyUsed / 256;
heart[2] = energyUsed - (heart[2] * 256);
Serial.print("BPM: ");
Serial.println(bpm);
heartRateMeasurementCharacteristics.setValue(heart, 8);
heartRateMeasurementCharacteristics.notify();
sensorPositionCharacteristic.setValue(hrmPos, 1);
bpm = (byte)randomGen(80,180);
Serial.println(bpm);
}
delay(2000);
}
코드 다운로드 후 nRF ToolBox 앱에서 확인하면 정상적인 심박수가 출력 된는 것을 확인 할 수 있다.
반응형