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

Extend CTS Server #1161

Merged
merged 1 commit into from
Nov 11, 2022
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
20 changes: 5 additions & 15 deletions src/components/ble/CurrentTimeClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,11 @@ int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_g
// TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
CtsData result;
os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d",
result.year,
result.month,
result.dayofmonth,
result.hour,
result.minute,
result.second);
dateTimeController.SetTime(result.year,
result.month,
result.dayofmonth,
0,
result.hour,
result.minute,
result.second,
nrf_rtc_counter_get(portNRF_RTC_REG));
uint16_t year = ((uint16_t) result.year_MSO << 8) + result.year_LSO;

NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
dateTimeController
.SetTime(year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
} else {
NRF_LOG_INFO("Error retrieving current time: %d", error->status);
}
Expand Down
10 changes: 6 additions & 4 deletions src/components/ble/CurrentTimeClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ namespace Pinetime {

private:
typedef struct __attribute__((packed)) {
uint16_t year;
uint8_t year_LSO; // explicit byte ordering to be independent of machine order
uint8_t year_MSO; // BLE GATT is little endian
uint8_t month;
uint8_t dayofmonth;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t millis;
uint8_t reason;
uint8_t dayofweek;
uint8_t fractions256; // currently ignored
uint8_t reason; // currently ignored, not that any host would set it anyway
} CtsData;

static constexpr uint16_t ctsServiceId {0x1805};
Expand All @@ -55,4 +57,4 @@ namespace Pinetime {
std::function<void(uint16_t)> onServiceDiscovered;
};
}
}
}
99 changes: 71 additions & 28 deletions src/components/ble/CurrentTimeService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@
using namespace Pinetime::Controllers;

constexpr ble_uuid16_t CurrentTimeService::ctsUuid;
constexpr ble_uuid16_t CurrentTimeService::ctChrUuid;
constexpr ble_uuid16_t CurrentTimeService::ctsCtChrUuid;
constexpr ble_uuid16_t CurrentTimeService::ctsLtChrUuid;

int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto cts = static_cast<CurrentTimeService*>(arg);
return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt);

return cts->OnCurrentTimeServiceAccessed(conn_handle, attr_handle, ctxt);
}

int CurrentTimeService::OnCurrentTimeServiceAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
switch (ble_uuid_u16(ctxt->chr->uuid)) {
case ctsCurrentTimeCharId:
return OnCurrentTimeAccessed(conn_handle, attr_handle, ctxt);
case ctsLocalTimeCharId:
return OnLocalTimeAccessed(conn_handle, attr_handle, ctxt);
}
return -1; // Unknown characteristic
}

void CurrentTimeService::Init() {
Expand All @@ -18,58 +30,89 @@ void CurrentTimeService::Init() {
ASSERT(res == 0);

res = ble_gatts_add_svcs(serviceDefinition);

ASSERT(res == 0);
}

int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
int CurrentTimeService::OnCurrentTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {

NRF_LOG_INFO("Setting time...");

if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
CtsData result;
os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result);

NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d",
result.year,
result.month,
result.dayofmonth,
result.hour,
result.minute,
result.second);

m_dateTimeController.SetTime(result.year,
result.month,
result.dayofmonth,
0,
result.hour,
result.minute,
result.second,
nrf_rtc_counter_get(portNRF_RTC_REG));
CtsCurrentTimeData result;
int res = os_mbuf_copydata(ctxt->om, 0, sizeof(CtsCurrentTimeData), &result);
if (res < 0) {
NRF_LOG_ERROR("Error reading BLE Data writing to CTS Current Time (too little data)")
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}

uint16_t year = ((uint16_t) result.year_MSO << 8) + result.year_LSO;

NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", year, result.month, result.dayofmonth, result.hour, result.minute, result.second);

m_dateTimeController
.SetTime(year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));

} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
CtsData currentDateTime;
currentDateTime.year = m_dateTimeController.Year();
CtsCurrentTimeData currentDateTime;
currentDateTime.year_LSO = m_dateTimeController.Year() & 0xff;
currentDateTime.year_MSO = (m_dateTimeController.Year() >> 8) & 0xff;
currentDateTime.month = static_cast<u_int8_t>(m_dateTimeController.Month());
currentDateTime.dayofmonth = m_dateTimeController.Day();
currentDateTime.hour = m_dateTimeController.Hours();
currentDateTime.minute = m_dateTimeController.Minutes();
currentDateTime.second = m_dateTimeController.Seconds();
currentDateTime.millis = 0;
currentDateTime.fractions256 = 0;

int res = os_mbuf_append(ctxt->om, &currentDateTime, sizeof(CtsData));
int res = os_mbuf_append(ctxt->om, &currentDateTime, sizeof(CtsCurrentTimeData));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}

return 0;
}

int CurrentTimeService::OnLocalTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
NRF_LOG_INFO("Setting timezone...");

if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
CtsLocalTimeData result;
int res = os_mbuf_copydata(ctxt->om, 0, sizeof(CtsLocalTimeData), &result);

if (res < 0) {
NRF_LOG_ERROR("Error reading BLE Data writing to CTS Local Time (too little data)")
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}

NRF_LOG_INFO("Received data: %d %d", result.timezone, result.dst);

m_dateTimeController.SetTimeZone(result.timezone, result.dst);

} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
CtsLocalTimeData currentTimezone;
currentTimezone.timezone = m_dateTimeController.TzOffset();
currentTimezone.dst = m_dateTimeController.DstOffset();

int res = os_mbuf_append(ctxt->om, &currentTimezone, sizeof(currentTimezone));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}

return 0;
}

CurrentTimeService::CurrentTimeService(DateTime& dateTimeController)
: characteristicDefinition {{.uuid = &ctChrUuid.u,
: characteristicDefinition {

{.uuid = &ctsLtChrUuid.u,
.access_cb = CTSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},

{.uuid = &ctsCtChrUuid.u,
.access_cb = CTSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
{0}},

{0}},
serviceDefinition {
{/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
Expand Down
27 changes: 19 additions & 8 deletions src/components/ble/CurrentTimeService.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,40 @@ namespace Pinetime {
CurrentTimeService(DateTime& dateTimeController);
void Init();

int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
int OnCurrentTimeServiceAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
int OnCurrentTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
int OnLocalTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);

private:
static constexpr uint16_t ctsId {0x1805};
static constexpr uint16_t ctsCharId {0x2a2b};
static constexpr uint16_t ctsCurrentTimeCharId {0x2a2b};
static constexpr uint16_t ctsLocalTimeCharId {0x2a0f};

static constexpr ble_uuid16_t ctsUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsId};

static constexpr ble_uuid16_t ctChrUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsCharId};
static constexpr ble_uuid16_t ctsCtChrUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsCurrentTimeCharId};
static constexpr ble_uuid16_t ctsLtChrUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsLocalTimeCharId};

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

typedef struct __attribute__((packed)) {
uint16_t year;
uint8_t year_LSO; // BLE GATT is little endian
uint8_t year_MSO;
uint8_t month;
uint8_t dayofmonth;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t millis;
uint8_t reason;
} CtsData;
uint8_t dayofweek;
uint8_t fractions256; // currently ignored
uint8_t reason; // currently ignored, not that any host would set it anyway
} CtsCurrentTimeData;

typedef struct __attribute__((packed)) {
uint8_t timezone;
uint8_t dst;
} CtsLocalTimeData;

DateTime& m_dateTimeController;
};
Expand Down
6 changes: 6 additions & 0 deletions src/components/datetime/DateTimeController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ void DateTime::SetTime(uint16_t year,
/* .tm_mon = */ month - 1,
/* .tm_year = */ year - 1900,
};

tm.tm_isdst = -1; // Use DST value from local time zone
currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm));

Expand All @@ -49,6 +50,11 @@ void DateTime::SetTime(uint16_t year,
systemTask->PushMessage(System::Messages::OnNewTime);
}

void DateTime::SetTimeZone(uint8_t timezone, uint8_t dst) {
tzOffset = timezone;
dstOffset = dst;
}

void DateTime::UpdateTime(uint32_t systickCounter) {
// Handle systick counter overflow
uint32_t systickDelta = 0;
Expand Down
53 changes: 53 additions & 0 deletions src/components/datetime/DateTimeController.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ namespace Pinetime {
uint8_t minute,
uint8_t second,
uint32_t systickCounter);

/*
* setter corresponding to the BLE Set Local Time characteristic.
*
* used to update difference between utc and local time (see UtcOffset())
*
* parameters are in quarters of an our. Following the BLE CTS specification,
* timezone is expected to be constant over DST which will be reported in
* dst field.
*/
void SetTimeZone(uint8_t timezone, uint8_t dst);

void UpdateTime(uint32_t systickCounter);
uint16_t Year() const {
return year;
Expand All @@ -61,13 +73,52 @@ namespace Pinetime {
return second;
}

/*
* returns the offset between local time and UTC in quarters of an hour
*
* Availability of this field depends on wether the companion app
* supports the BLE CTS Local Time Characteristic. Expect it to be 0
* if not.
*/
uint8_t UtcOffset() const {
return tzOffset + dstOffset;
}

/*
* returns the offset between the (dst independent) local time zone and UTC
* in quarters of an hour
*
* Availability of this field depends on wether the companion app
* supports the BLE CTS Local Time Characteristic. Expect it to be 0
* if not.
*/
uint8_t TzOffset() const {
return tzOffset;
}

/*
* returns the offset between the local time zone and local time
* in quarters of an hour
* if != 0, DST is in effect, if == 0 not.
*
* Availability of this field depends on wether the companion app
* supports the BLE CTS Local Time Characteristic. Expect it to be 0
* if not.
*/
uint8_t DstOffset() const {
return dstOffset;
}

const char* MonthShortToString() const;
const char* DayOfWeekShortToString() const;
static const char* MonthShortToStringLow(Months month);

std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
return currentDateTime;
}
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> UTCDateTime() const {
return currentDateTime - std::chrono::seconds((tzOffset + dstOffset) * 15 * 60);
}
std::chrono::seconds Uptime() const {
return uptime;
}
Expand All @@ -84,6 +135,8 @@ namespace Pinetime {
uint8_t hour = 0;
uint8_t minute = 0;
uint8_t second = 0;
uint8_t tzOffset = 0;
uint8_t dstOffset = 0;

uint32_t previousSystickCounter = 0;
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
Expand Down