Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/MotionService.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Motion Service
## Introduction
The motion service exposes step count and raw X/Y/Z motion value as READ and NOTIFY characteristics.

## Service
The service UUID is **00020000-78fc-48fe-8e23-433b3a1942d0**

## Characteristics
### Step count (UUID 00020001-78fc-48fe-8e23-433b3a1942d0)
The current number of steps represented as a single `uint32_t` (4 bytes) value.

### Raw motion values (UUID 00020002-78fc-48fe-8e23-433b3a1942d0)
The current raw motion values. This is a 3 `int16_t` array:

- [0] : X
- [1] : Y
- [2] : Z
19 changes: 12 additions & 7 deletions doc/ble.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,19 @@ When the service does not exist in the BLE specification, InfiniTime implement c
The following custom services are implemented in InfiniTime:

- Since InfiniTime 0.8:
```
* Music Service : 00000000-78fc-48fe-8e23-433b3a1942d0
```

* Music Service : 00000000-78fc-48fe-8e23-433b3a1942d0


- Since InfiniTime 0.11:
```
* Navigation Service : 00010000-78fc-48fe-8e23-433b3a1942d0
```
* [Navigation Service](NavigationService.md) : 00010000-78fc-48fe-8e23-433b3a1942d0


- Since InfiniTime 0.13
* Call characteristic (extension to the Alert Notification Service): 00020001-78fc-48fe-8e23-433b3a1942d0


- Since InfiniTime 1.7:
* [Motion Service](MotionService.md) : 00030000-78fc-48fe-8e23-433b3a1942d0

## BLE services
[List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/)
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ list(APPEND SOURCE_FILES
components/ble/ImmediateAlertService.cpp
components/ble/ServiceDiscovery.cpp
components/ble/HeartRateService.cpp
components/ble/MotionService.cpp
components/firmwarevalidator/FirmwareValidator.cpp
components/motor/MotorController.cpp
components/settings/Settings.cpp
Expand Down Expand Up @@ -545,6 +546,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/ble/ServiceDiscovery.cpp
components/ble/NavigationService.cpp
components/ble/HeartRateService.cpp
components/ble/MotionService.cpp
components/firmwarevalidator/FirmwareValidator.cpp
components/settings/Settings.cpp
components/timer/TimerController.cpp
Expand Down Expand Up @@ -652,6 +654,7 @@ set(INCLUDE_FILES
components/ble/ServiceDiscovery.h
components/ble/BleClient.h
components/ble/HeartRateService.h
components/ble/MotionService.h
components/settings/Settings.h
components/timer/TimerController.h
components/alarm/AlarmController.h
Expand Down
16 changes: 14 additions & 2 deletions src/components/ble/HeartRateService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ constexpr ble_uuid16_t HeartRateService::heartRateServiceUuid;
constexpr ble_uuid16_t HeartRateService::heartRateMeasurementUuid;

namespace {
int HeartRateServiceServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
int HeartRateServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto* heartRateService = static_cast<HeartRateService*>(arg);
return heartRateService->OnHeartRateRequested(conn_handle, attr_handle, ctxt);
}
Expand All @@ -19,7 +19,7 @@ HeartRateService::HeartRateService(Pinetime::System::SystemTask& system, Control
: system {system},
heartRateController {heartRateController},
characteristicDefinition {{.uuid = &heartRateMeasurementUuid.u,
.access_cb = HeartRateServiceServiceCallback,
.access_cb = HeartRateServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &heartRateMeasurementHandle},
Expand Down Expand Up @@ -56,6 +56,8 @@ int HeartRateService::OnHeartRateRequested(uint16_t connectionHandle, uint16_t a
}

void HeartRateService::OnNewHeartRateValue(uint8_t heartRateValue) {
if(!heartRateMeasurementNotificationEnable) return;

uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value
auto* om = ble_hs_mbuf_from_flat(buffer, 2);

Expand All @@ -67,3 +69,13 @@ void HeartRateService::OnNewHeartRateValue(uint8_t heartRateValue) {

ble_gattc_notify_custom(connectionHandle, heartRateMeasurementHandle, om);
}

void HeartRateService::SubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle) {
if(attributeHandle == heartRateMeasurementHandle)
heartRateMeasurementNotificationEnable = true;
}

void HeartRateService::UnsubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle) {
if(attributeHandle == heartRateMeasurementHandle)
heartRateMeasurementNotificationEnable = false;
}
7 changes: 6 additions & 1 deletion src/components/ble/HeartRateService.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
#include <atomic>
#undef max
#undef min

Expand All @@ -18,6 +19,9 @@ namespace Pinetime {
int OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void OnNewHeartRateValue(uint8_t hearRateValue);

void SubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle);
void UnsubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle);

private:
Pinetime::System::SystemTask& system;
Controllers::HeartRateController& heartRateController;
Expand All @@ -28,10 +32,11 @@ namespace Pinetime {

static constexpr ble_uuid16_t heartRateMeasurementUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateMeasurementId};

struct ble_gatt_chr_def characteristicDefinition[3];
struct ble_gatt_chr_def characteristicDefinition[2];
struct ble_gatt_svc_def serviceDefinition[2];

uint16_t heartRateMeasurementHandle;
std::atomic_bool heartRateMeasurementNotificationEnable {false};
};
}
}
124 changes: 124 additions & 0 deletions src/components/ble/MotionService.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "MotionService.h"
#include "components/motion//MotionController.h"
#include "systemtask/SystemTask.h"

using namespace Pinetime::Controllers;

namespace {
// 0002yyxx-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
return ble_uuid128_t{
.u = {.type = BLE_UUID_TYPE_128},
.value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x03, 0x00 }
};
}

// 00020000-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t BaseUuid() {
return CharUuid(0x00, 0x00);
}

constexpr ble_uuid128_t motionServiceUuid {BaseUuid()};
constexpr ble_uuid128_t stepCountCharUuid {CharUuid(0x01, 0x00)};
constexpr ble_uuid128_t motionValuesCharUuid {CharUuid(0x02, 0x00)};

int MotionServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto* motionService = static_cast<MotionService*>(arg);
return motionService->OnStepCountRequested(conn_handle, attr_handle, ctxt);
}
}

// TODO Refactoring - remove dependency to SystemTask
MotionService::MotionService(Pinetime::System::SystemTask& system, Controllers::MotionController& motionController)
: system {system},
motionController {motionController},
characteristicDefinition {{.uuid = &stepCountCharUuid.u,
.access_cb = MotionServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &stepCountHandle},
{.uuid = &motionValuesCharUuid.u,
.access_cb = MotionServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &motionValuesHandle},
{0}},
serviceDefinition {
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &motionServiceUuid.u,
.characteristics = characteristicDefinition
},
{0},
} {
// TODO refactor to prevent this loop dependency (service depends on controller and controller depends on service)
motionController.SetService(this);
}

void MotionService::Init() {
int res = 0;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);

res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0);
}

int MotionService::OnStepCountRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
if (attributeHandle == stepCountHandle) {
NRF_LOG_INFO("Motion-stepcount : handle = %d", stepCountHandle);
uint32_t buffer = motionController.NbSteps();

int res = os_mbuf_append(context->om, &buffer, 4);
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} else if(attributeHandle == motionValuesHandle) {
int16_t buffer[3] = { motionController.X(), motionController.Y(), motionController.Z() };

int res = os_mbuf_append(context->om, buffer, 3 * sizeof(int16_t));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
return 0;
}

void MotionService::OnNewStepCountValue(uint8_t stepCount) {
if(!stepCountNoficationEnabled) return;

uint32_t buffer = stepCount;
auto* om = ble_hs_mbuf_from_flat(&buffer, 4);

uint16_t connectionHandle = system.nimble().connHandle();

if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
}

ble_gattc_notify_custom(connectionHandle, stepCountHandle, om);
}
void MotionService::OnNewMotionValues(int16_t x, int16_t y, int16_t z) {
if(!motionValuesNoficationEnabled) return;

int16_t buffer[3] = { motionController.X(), motionController.Y(), motionController.Z() };
auto* om = ble_hs_mbuf_from_flat(buffer, 3 * sizeof(int16_t));

uint16_t connectionHandle = system.nimble().connHandle();

if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
}

ble_gattc_notify_custom(connectionHandle, motionValuesHandle, om);
}

void MotionService::SubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle) {
if(attributeHandle == stepCountHandle)
stepCountNoficationEnabled = true;
else if(attributeHandle == motionValuesHandle)
motionValuesNoficationEnabled = true;
}

void MotionService::UnsubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle) {
if(attributeHandle == stepCountHandle)
stepCountNoficationEnabled = false;
else if(attributeHandle == motionValuesHandle)
motionValuesNoficationEnabled = false;
}
39 changes: 39 additions & 0 deletions src/components/ble/MotionService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once
#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
#include <atomic>
#undef max
#undef min

namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class MotionController;
class MotionService {
public:
MotionService(Pinetime::System::SystemTask& system, Controllers::MotionController& motionController);
void Init();
int OnStepCountRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void OnNewStepCountValue(uint8_t stepCount);
void OnNewMotionValues(int16_t x, int16_t y, int16_t z);

void SubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle);
void UnsubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle);

private:
Pinetime::System::SystemTask& system;
Controllers::MotionController& motionController;

struct ble_gatt_chr_def characteristicDefinition[3];
struct ble_gatt_svc_def serviceDefinition[2];

uint16_t stepCountHandle;
uint16_t motionValuesHandle;
std::atomic_bool stepCountNoficationEnabled {false};
std::atomic_bool motionValuesNoficationEnabled {false};
};
}
}
18 changes: 17 additions & 1 deletion src/components/ble/NimbleController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController)
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController)
: systemTask {systemTask},
bleController {bleController},
dateTimeController {dateTimeController},
Expand All @@ -39,6 +40,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
batteryInformationService {batteryController},
immediateAlertService {systemTask, notificationManager},
heartRateService {systemTask, heartRateController},
motionService{systemTask, motionController},
serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
}

Expand Down Expand Up @@ -81,6 +83,7 @@ void NimbleController::Init() {
batteryInformationService.Init();
immediateAlertService.Init();
heartRateService.Init();
motionService.Init();

int rc;
rc = ble_hs_util_ensure_addr(0);
Expand Down Expand Up @@ -215,6 +218,19 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
event->subscribe.prev_notify,
event->subscribe.cur_notify,
event->subscribe.prev_indicate);

if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) {
heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
}
else if(event->subscribe.prev_notify == 0 && event->subscribe.cur_notify == 1) {
heartRateService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
motionService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
}
else if(event->subscribe.prev_notify == 1 && event->subscribe.cur_notify == 0) {
heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
}
break;

case BLE_GAP_EVENT_MTU:
Expand Down
5 changes: 4 additions & 1 deletion src/components/ble/NimbleController.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "NavigationService.h"
#include "ServiceDiscovery.h"
#include "HeartRateService.h"
#include "MotionService.h"

namespace Pinetime {
namespace Drivers {
Expand All @@ -43,7 +44,8 @@ namespace Pinetime {
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController);
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController);
void Init();
void StartAdvertising();
int OnGAPEvent(ble_gap_event* event);
Expand Down Expand Up @@ -95,6 +97,7 @@ namespace Pinetime {
BatteryInformationService batteryInformationService;
ImmediateAlertService immediateAlertService;
HeartRateService heartRateService;
MotionService motionService;

uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
Expand Down
Loading