add CTS local time characteristic and use it to provide UTC in DateTimeController
This commit is contained in:
parent
840aab7f90
commit
38092fcb40
|
@ -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
|
// TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
|
||||||
CtsData result;
|
CtsData result;
|
||||||
os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
|
os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
|
||||||
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d",
|
uint16_t year = ((uint16_t) result.year_MSO << 8) + result.year_LSO;
|
||||||
result.year,
|
|
||||||
result.month,
|
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
|
||||||
result.dayofmonth,
|
dateTimeController
|
||||||
result.hour,
|
.SetTime(year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||||
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));
|
|
||||||
} else {
|
} else {
|
||||||
NRF_LOG_INFO("Error retrieving current time: %d", error->status);
|
NRF_LOG_INFO("Error retrieving current time: %d", error->status);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,14 +29,16 @@ namespace Pinetime {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef struct __attribute__((packed)) {
|
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 month;
|
||||||
uint8_t dayofmonth;
|
uint8_t dayofmonth;
|
||||||
uint8_t hour;
|
uint8_t hour;
|
||||||
uint8_t minute;
|
uint8_t minute;
|
||||||
uint8_t second;
|
uint8_t second;
|
||||||
uint8_t millis;
|
uint8_t dayofweek;
|
||||||
uint8_t reason;
|
uint8_t fractions256; // currently ignored
|
||||||
|
uint8_t reason; // currently ignored, not that any host would set it anyway
|
||||||
} CtsData;
|
} CtsData;
|
||||||
|
|
||||||
static constexpr uint16_t ctsServiceId {0x1805};
|
static constexpr uint16_t ctsServiceId {0x1805};
|
||||||
|
|
|
@ -5,11 +5,23 @@
|
||||||
using namespace Pinetime::Controllers;
|
using namespace Pinetime::Controllers;
|
||||||
|
|
||||||
constexpr ble_uuid16_t CurrentTimeService::ctsUuid;
|
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) {
|
int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
|
||||||
auto cts = static_cast<CurrentTimeService*>(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() {
|
void CurrentTimeService::Init() {
|
||||||
|
@ -18,45 +30,69 @@ void CurrentTimeService::Init() {
|
||||||
ASSERT(res == 0);
|
ASSERT(res == 0);
|
||||||
|
|
||||||
res = ble_gatts_add_svcs(serviceDefinition);
|
res = ble_gatts_add_svcs(serviceDefinition);
|
||||||
|
|
||||||
ASSERT(res == 0);
|
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...");
|
NRF_LOG_INFO("Setting time...");
|
||||||
|
|
||||||
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
|
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
|
||||||
CtsData result;
|
CtsCurrentTimeData result;
|
||||||
os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &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;
|
||||||
|
}
|
||||||
|
|
||||||
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d",
|
uint16_t year = ((uint16_t) result.year_MSO << 8) + result.year_LSO;
|
||||||
result.year,
|
|
||||||
result.month,
|
|
||||||
result.dayofmonth,
|
|
||||||
result.hour,
|
|
||||||
result.minute,
|
|
||||||
result.second);
|
|
||||||
|
|
||||||
m_dateTimeController.SetTime(result.year,
|
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
|
||||||
result.month,
|
|
||||||
result.dayofmonth,
|
m_dateTimeController
|
||||||
0,
|
.SetTime(year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||||
result.hour,
|
|
||||||
result.minute,
|
|
||||||
result.second,
|
|
||||||
nrf_rtc_counter_get(portNRF_RTC_REG));
|
|
||||||
|
|
||||||
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
|
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
|
||||||
CtsData currentDateTime;
|
CtsCurrentTimeData currentDateTime;
|
||||||
currentDateTime.year = m_dateTimeController.Year();
|
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.month = static_cast<u_int8_t>(m_dateTimeController.Month());
|
||||||
currentDateTime.dayofmonth = m_dateTimeController.Day();
|
currentDateTime.dayofmonth = m_dateTimeController.Day();
|
||||||
currentDateTime.hour = m_dateTimeController.Hours();
|
currentDateTime.hour = m_dateTimeController.Hours();
|
||||||
currentDateTime.minute = m_dateTimeController.Minutes();
|
currentDateTime.minute = m_dateTimeController.Minutes();
|
||||||
currentDateTime.second = m_dateTimeController.Seconds();
|
currentDateTime.second = m_dateTimeController.Seconds();
|
||||||
currentDateTime.millis = 0;
|
currentDateTime.fractions256 = 0;
|
||||||
|
|
||||||
int res = os_mbuf_append(ctxt->om, ¤tDateTime, sizeof(CtsData));
|
int res = os_mbuf_append(ctxt->om, ¤tDateTime, 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, ¤tTimezone, sizeof(currentTimezone));
|
||||||
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,12 +100,19 @@ int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handl
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentTimeService::CurrentTimeService(DateTime& dateTimeController)
|
CurrentTimeService::CurrentTimeService(DateTime& dateTimeController)
|
||||||
: characteristicDefinition {{.uuid = &ctChrUuid.u,
|
: characteristicDefinition {
|
||||||
.access_cb = CTSCallback,
|
|
||||||
|
|
||||||
|
{.uuid = &ctsLtChrUuid.u,
|
||||||
|
.access_cb = CTSCallback,
|
||||||
.arg = this,
|
.arg = this,
|
||||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
|
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
|
||||||
{0}},
|
|
||||||
|
{.uuid = &ctsCtChrUuid.u,
|
||||||
|
.access_cb = CTSCallback,
|
||||||
|
.arg = this,
|
||||||
|
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
|
||||||
|
|
||||||
|
{0}},
|
||||||
serviceDefinition {
|
serviceDefinition {
|
||||||
{/* Device Information Service */
|
{/* Device Information Service */
|
||||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||||
|
|
|
@ -16,29 +16,40 @@ namespace Pinetime {
|
||||||
CurrentTimeService(DateTime& dateTimeController);
|
CurrentTimeService(DateTime& dateTimeController);
|
||||||
void Init();
|
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:
|
private:
|
||||||
static constexpr uint16_t ctsId {0x1805};
|
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 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];
|
struct ble_gatt_svc_def serviceDefinition[2];
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
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 month;
|
||||||
uint8_t dayofmonth;
|
uint8_t dayofmonth;
|
||||||
uint8_t hour;
|
uint8_t hour;
|
||||||
uint8_t minute;
|
uint8_t minute;
|
||||||
uint8_t second;
|
uint8_t second;
|
||||||
uint8_t millis;
|
uint8_t dayofweek;
|
||||||
uint8_t reason;
|
uint8_t fractions256; // currently ignored
|
||||||
} CtsData;
|
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;
|
DateTime& m_dateTimeController;
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,7 @@ void DateTime::SetTime(uint16_t year,
|
||||||
/* .tm_mon = */ month - 1,
|
/* .tm_mon = */ month - 1,
|
||||||
/* .tm_year = */ year - 1900,
|
/* .tm_year = */ year - 1900,
|
||||||
};
|
};
|
||||||
|
|
||||||
tm.tm_isdst = -1; // Use DST value from local time zone
|
tm.tm_isdst = -1; // Use DST value from local time zone
|
||||||
currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
||||||
|
|
||||||
|
@ -50,6 +51,11 @@ void DateTime::SetTime(uint16_t year,
|
||||||
systemTask->PushMessage(System::Messages::OnNewTime);
|
systemTask->PushMessage(System::Messages::OnNewTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DateTime::SetTimeZone(uint8_t timezone, uint8_t dst) {
|
||||||
|
tzOffset = timezone;
|
||||||
|
dstOffset = dst;
|
||||||
|
}
|
||||||
|
|
||||||
void DateTime::UpdateTime(uint32_t systickCounter) {
|
void DateTime::UpdateTime(uint32_t systickCounter) {
|
||||||
// Handle systick counter overflow
|
// Handle systick counter overflow
|
||||||
uint32_t systickDelta = 0;
|
uint32_t systickDelta = 0;
|
||||||
|
|
|
@ -38,6 +38,18 @@ namespace Pinetime {
|
||||||
uint8_t minute,
|
uint8_t minute,
|
||||||
uint8_t second,
|
uint8_t second,
|
||||||
uint32_t systickCounter);
|
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);
|
void UpdateTime(uint32_t systickCounter);
|
||||||
uint16_t Year() const {
|
uint16_t Year() const {
|
||||||
return year;
|
return year;
|
||||||
|
@ -61,6 +73,42 @@ namespace Pinetime {
|
||||||
return second;
|
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* MonthShortToString() const;
|
||||||
const char* DayOfWeekShortToString() const;
|
const char* DayOfWeekShortToString() const;
|
||||||
static const char* MonthShortToStringLow(Months month);
|
static const char* MonthShortToStringLow(Months month);
|
||||||
|
@ -69,6 +117,9 @@ namespace Pinetime {
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
|
||||||
return currentDateTime;
|
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 {
|
std::chrono::seconds Uptime() const {
|
||||||
return uptime;
|
return uptime;
|
||||||
}
|
}
|
||||||
|
@ -85,6 +136,8 @@ namespace Pinetime {
|
||||||
uint8_t hour = 0;
|
uint8_t hour = 0;
|
||||||
uint8_t minute = 0;
|
uint8_t minute = 0;
|
||||||
uint8_t second = 0;
|
uint8_t second = 0;
|
||||||
|
uint8_t tzOffset = 0;
|
||||||
|
uint8_t dstOffset = 0;
|
||||||
|
|
||||||
uint32_t previousSystickCounter = 0;
|
uint32_t previousSystickCounter = 0;
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user