Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Find my phone" app. #2053

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions doc/ble.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ This page describes the BLE implementation and API built in this firmware.
- [BLE Services](#ble-services)
- [CTS](#cts)
- [ANS](#ans)
- [BLE Clients](#ble-clients)
- [IAC](#iac)
- [Getting Information](#getting-information)
- [Firmware Version](#firmware-version)
- [Battery Level](#battery-level)
Expand Down Expand Up @@ -113,6 +115,16 @@ The following custom services are implemented in InfiniTime:

![ANS sequence diagram](./ble/ans_sequence.png "ANS sequence diagram")

## BLE clients

### IAC

InfiniTime implements an Immediate Alert Service client, which can be used to send alerts to the companion app.
This is useful for "Find my Phone" functionality.

More documentation about this service can be found here.

[Immediate Alert Service](https://www.bluetooth.com/specifications/specs/immediate-alert-service-1-0/)
---

### Getting Information
Expand Down
7 changes: 6 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/Notifications.cpp
displayapp/screens/Twos.cpp
displayapp/screens/HeartRate.cpp
displayapp/screens/FindMyPhone.cpp
displayapp/screens/FlashLight.cpp
displayapp/screens/List.cpp
displayapp/screens/CheckboxList.cpp
Expand Down Expand Up @@ -459,6 +460,7 @@ list(APPEND SOURCE_FILES
components/ble/BatteryInformationService.cpp
components/ble/FSService.cpp
components/ble/ImmediateAlertService.cpp
components/ble/ImmediateAlertClient.cpp
components/ble/ServiceDiscovery.cpp
components/ble/HeartRateService.cpp
components/ble/MotionService.cpp
Expand Down Expand Up @@ -528,6 +530,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/ble/BatteryInformationService.cpp
components/ble/FSService.cpp
components/ble/ImmediateAlertService.cpp
components/ble/ImmediateAlertClient.cpp
components/ble/ServiceDiscovery.cpp
components/ble/NavigationService.cpp
components/ble/HeartRateService.cpp
Expand Down Expand Up @@ -608,6 +611,7 @@ set(INCLUDE_FILES
displayapp/Apps.h
displayapp/screens/Notifications.h
displayapp/screens/HeartRate.h
displayapp/screens/FindMyPhone.h
displayapp/screens/Metronome.h
displayapp/screens/Motion.h
displayapp/screens/Timer.h
Expand Down Expand Up @@ -647,6 +651,7 @@ set(INCLUDE_FILES
components/ble/BatteryInformationService.h
components/ble/FSService.h
components/ble/ImmediateAlertService.h
components/ble/ImmediateAlertClient.h
components/ble/ServiceDiscovery.h
components/ble/BleClient.h
components/ble/HeartRateService.h
Expand Down Expand Up @@ -839,7 +844,7 @@ if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
# add_definitions(-DCLOCK_CONFIG_LOG_LEVEL=4)
# add_definitions(-DRTC_CONFIG_LOG_ENABLED=1)
# add_definitions(-DRTC_CONFIG_LOG_LEVEL=4)

# Nimble Logging
add_definitions(-DMYNEWT_VAL_NEWT_FEATURE_LOGCFG=1)
# add_definitions(-DMYNEWT_VAL_LOG_LEVEL=0)
Expand Down
114 changes: 114 additions & 0 deletions src/components/ble/ImmediateAlertClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include "components/ble/ImmediateAlertClient.h"
#include <cstring>
#include <nrf_log.h>
#include "systemtask/SystemTask.h"

using namespace Pinetime::Controllers;

constexpr ble_uuid16_t ImmediateAlertClient::immediateAlertClientUuid;
constexpr ble_uuid16_t ImmediateAlertClient::alertLevelCharacteristicUuid;

ImmediateAlertClient::ImmediateAlertClient(Pinetime::System::SystemTask& systemTask) : systemTask {systemTask} {
}

void ImmediateAlertClient::Init() {
}

bool ImmediateAlertClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
if (service == nullptr && error->status == BLE_HS_EDONE) {
if (iasHandles.has_value()) {
NRF_LOG_INFO("[IAS] service found, starting characteristics discovery");

ble_gattc_disc_all_chrs(connectionHandle,
iasHandles->startHandle,
iasHandles->endHandle,
OnImmediateAlertCharacteristicDiscoveredCallback,
this);
} else {
NRF_LOG_INFO("[IAS] service not found");
onServiceDiscovered(connectionHandle);
}
return true;
}

if (service != nullptr && ble_uuid_cmp(&immediateAlertClientUuid.u, &service->uuid.u) == 0) {
NRF_LOG_INFO("[IAS] discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
iasHandles.emplace(HandleRange {
.startHandle = service->start_handle,
.endHandle = service->end_handle,
});
}
return false;
}

int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle,
const ble_gatt_error* error,
const ble_gatt_chr* characteristic) {

if (error->status != 0 && error->status != BLE_HS_EDONE) {
NRF_LOG_INFO("[IAS] Characteristic discovery ERROR");
onServiceDiscovered(conn_handle);
return 0;
}

if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
if (!alertLevelHandle.has_value()) {
NRF_LOG_INFO("[IAS] Alert level characteristic not found.");
onServiceDiscovered(conn_handle);
}

return 0;
}

if (characteristic != nullptr && ble_uuid_cmp(&alertLevelCharacteristicUuid.u, &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("[IAS] Characteristic discovered : 0x%x", characteristic->val_handle);
alertLevelHandle = characteristic->val_handle;
state = State::Connected;
}
return 0;
}

void ImmediateAlertClient::Discover(uint16_t connectionHandle, std::function<void(uint16_t)> onServiceDiscovered) {
NRF_LOG_INFO("[IAS] Starting discovery");
vchigrin marked this conversation as resolved.
Show resolved Hide resolved
this->onServiceDiscovered = onServiceDiscovered;
state = State::NoIAS;
ble_gattc_disc_svc_by_uuid(connectionHandle, &immediateAlertClientUuid.u, OnDiscoveryEventCallback, this);
}

void ImmediateAlertClient::Reset() {
state = State::NoConnection;
iasHandles = std::nullopt;
alertLevelHandle = std::nullopt;
}

bool ImmediateAlertClient::SendImmediateAlert(ImmediateAlertClient::Levels level) {

auto* om = ble_hs_mbuf_from_flat(&level, 1);

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

if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return false;
}
if (!alertLevelHandle.has_value()) {
return false;
}

return ble_gattc_write_no_rsp(connectionHandle, *alertLevelHandle, om) == 0;
}

int ImmediateAlertClient::OnDiscoveryEventCallback(uint16_t conn_handle,
const struct ble_gatt_error* error,
const struct ble_gatt_svc* service,
void* arg) {
auto client = static_cast<ImmediateAlertClient*>(arg);
return client->OnDiscoveryEvent(conn_handle, error, service);
}

int ImmediateAlertClient::OnImmediateAlertCharacteristicDiscoveredCallback(uint16_t conn_handle,
const struct ble_gatt_error* error,
const struct ble_gatt_chr* chr,
void* arg) {
auto client = static_cast<ImmediateAlertClient*>(arg);
return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr);
}
77 changes: 77 additions & 0 deletions src/components/ble/ImmediateAlertClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once
#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
#undef max
#undef min
#include <cstdint>
#include "components/ble/BleClient.h"

namespace Pinetime {
namespace System {
class SystemTask;
}

namespace Controllers {
class NotificationManager;

class ImmediateAlertClient : public BleClient {
public:
enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 };
enum class State {
NoConnection,
NoIAS,
Connected,
};

explicit ImmediateAlertClient(Pinetime::System::SystemTask& systemTask);
void Init();

bool SendImmediateAlert(Levels level);

State GetState() const {
return state;
}

void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
void Reset();

private:
bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service);
int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);

static constexpr const ble_uuid16_t* Uuid() {
return &ImmediateAlertClient::immediateAlertClientUuid;
}

static constexpr const ble_uuid16_t* AlertLevelCharacteristicUuid() {
return &ImmediateAlertClient::alertLevelCharacteristicUuid;
}

static int
OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg);
static int OnImmediateAlertCharacteristicDiscoveredCallback(uint16_t conn_handle,
const struct ble_gatt_error* error,
const struct ble_gatt_chr* chr,
void* arg);

Pinetime::System::SystemTask& systemTask;

static constexpr uint16_t immediateAlertClientId {0x1802};
static constexpr uint16_t alertLevelId {0x2A06};

static constexpr ble_uuid16_t immediateAlertClientUuid {.u {.type = BLE_UUID_TYPE_16}, .value = immediateAlertClientId};
static constexpr ble_uuid16_t alertLevelCharacteristicUuid {.u {.type = BLE_UUID_TYPE_16}, .value = alertLevelId};

struct HandleRange {
uint16_t startHandle;
uint16_t endHandle;
};

std::optional<HandleRange> iasHandles;
std::optional<uint16_t> alertLevelHandle;
std::function<void(uint16_t)> onServiceDiscovered;
State state {State::NoConnection};
};
}
}
6 changes: 5 additions & 1 deletion src/components/ble/NimbleController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
weatherService {dateTimeController},
batteryInformationService {batteryController},
immediateAlertService {systemTask, notificationManager},
iaClient {systemTask},
heartRateService {*this, heartRateController},
motionService {*this, motionController},
fsService {systemTask, fs},
serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
serviceDiscovery({&currentTimeClient, &alertNotificationClient, &iaClient}) {
}

void nimble_on_reset(int reason) {
Expand Down Expand Up @@ -95,6 +96,7 @@ void NimbleController::Init() {
dfuService.Init();
batteryInformationService.Init();
immediateAlertService.Init();
iaClient.Init();
heartRateService.Init();
motionService.Init();
fsService.Init();
Expand Down Expand Up @@ -200,6 +202,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
/* Connection failed; resume advertising. */
currentTimeClient.Reset();
alertNotificationClient.Reset();
iaClient.Reset();
connectionHandle = BLE_HS_CONN_HANDLE_NONE;
bleController.Disconnect();
fastAdvCount = 0;
Expand All @@ -223,6 +226,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {

currentTimeClient.Reset();
alertNotificationClient.Reset();
iaClient.Reset();
connectionHandle = BLE_HS_CONN_HANDLE_NONE;
if (bleController.IsConnected()) {
bleController.Disconnect();
Expand Down
6 changes: 6 additions & 0 deletions src/components/ble/NimbleController.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "components/ble/FSService.h"
#include "components/ble/HeartRateService.h"
#include "components/ble/ImmediateAlertService.h"
#include "components/ble/ImmediateAlertClient.h"
#include "components/ble/MusicService.h"
#include "components/ble/NavigationService.h"
#include "components/ble/ServiceDiscovery.h"
Expand Down Expand Up @@ -71,6 +72,10 @@ namespace Pinetime {
return weatherService;
};

Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient() {
return iaClient;
}

uint16_t connHandle();
void NotifyBatteryLevel(uint8_t level);

Expand Down Expand Up @@ -103,6 +108,7 @@ namespace Pinetime {
NavigationService navService;
BatteryInformationService batteryInformationService;
ImmediateAlertService immediateAlertService;
ImmediateAlertClient iaClient;
HeartRateService heartRateService;
MotionService motionService;
FSService fsService;
Expand Down
4 changes: 2 additions & 2 deletions src/components/ble/ServiceDiscovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using namespace Pinetime::Controllers;

ServiceDiscovery::ServiceDiscovery(std::array<BleClient*, 2>&& clients) : clients {clients} {
ServiceDiscovery::ServiceDiscovery(std::array<BleClient*, 3>&& clients) : clients {clients} {
}

void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) {
Expand All @@ -29,4 +29,4 @@ void ServiceDiscovery::DiscoverNextService(uint16_t connectionHandle) {
this->OnServiceDiscovered(connectionHandle);
};
(*clientIterator)->Discover(connectionHandle, discoverNextService);
}
}
4 changes: 2 additions & 2 deletions src/components/ble/ServiceDiscovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ namespace Pinetime {

class ServiceDiscovery {
public:
ServiceDiscovery(std::array<BleClient*, 2>&& bleClients);
ServiceDiscovery(std::array<BleClient*, 3>&& bleClients);

void StartDiscovery(uint16_t connectionHandle);

private:
BleClient** clientIterator;
std::array<BleClient*, 2> clients;
std::array<BleClient*, 3> clients;
void OnServiceDiscovered(uint16_t connectionHandle);
void DiscoverNextService(uint16_t connectionHandle);
};
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "displayapp/DisplayApp.h"
#include <libraries/log/nrf_log.h>
#include "displayapp/screens/HeartRate.h"
#include "displayapp/screens/FindMyPhone.h"
#include "displayapp/screens/Motion.h"
#include "displayapp/screens/Timer.h"
#include "displayapp/screens/Alarm.h"
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/Apps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace Pinetime {
SettingChimes,
SettingShakeThreshold,
SettingBluetooth,
FindMyPhone,
Error
};

Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ else ()
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Dice")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::FindMyPhone")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion")
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/fonts/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743"
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf002"
}
],
"bpp": 1,
Expand Down
Loading
Loading