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
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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() {
|
||||
|
@ -18,45 +30,69 @@ 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);
|
||||
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;
|
||||
}
|
||||
|
||||
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d",
|
||||
result.year,
|
||||
result.month,
|
||||
result.dayofmonth,
|
||||
result.hour,
|
||||
result.minute,
|
||||
result.second);
|
||||
uint16_t year = ((uint16_t) result.year_MSO << 8) + result.year_LSO;
|
||||
|
||||
m_dateTimeController.SetTime(result.year,
|
||||
result.month,
|
||||
result.dayofmonth,
|
||||
0,
|
||||
result.hour,
|
||||
result.minute,
|
||||
result.second,
|
||||
nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||
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, ¤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;
|
||||
}
|
||||
|
||||
|
@ -64,12 +100,19 @@ int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handl
|
|||
}
|
||||
|
||||
CurrentTimeService::CurrentTimeService(DateTime& dateTimeController)
|
||||
: characteristicDefinition {{.uuid = &ctChrUuid.u,
|
||||
.access_cb = CTSCallback,
|
||||
: characteristicDefinition {
|
||||
|
||||
{.uuid = &ctsLtChrUuid.u,
|
||||
.access_cb = CTSCallback,
|
||||
.arg = this,
|
||||
.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 {
|
||||
{/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -36,6 +36,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));
|
||||
|
||||
|
@ -50,6 +51,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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -61,6 +73,42 @@ 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);
|
||||
|
@ -69,6 +117,9 @@ namespace Pinetime {
|
|||
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;
|
||||
}
|
||||
|
@ -85,6 +136,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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user