From d1f50157c7e0c471ae0e260fdca82fc472635079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Milants?= Date: Sun, 17 Oct 2021 08:23:01 +0200 Subject: MotionService : fix typo and characteristic array size + send notification only if the host subscribed to them. diff --git a/src/components/ble/HeartRateService.cpp b/src/components/ble/HeartRateService.cpp index 5b00f49..75a038a 100644 --- a/src/components/ble/HeartRateService.cpp +++ b/src/components/ble/HeartRateService.cpp @@ -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(arg); return heartRateService->OnHeartRateRequested(conn_handle, attr_handle, ctxt); } @@ -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}, @@ -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); @@ -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; +} \ No newline at end of file diff --git a/src/components/ble/HeartRateService.h b/src/components/ble/HeartRateService.h index 0b16703..4e4a5a4 100644 --- a/src/components/ble/HeartRateService.h +++ b/src/components/ble/HeartRateService.h @@ -2,6 +2,7 @@ #define min // workaround: nimble's min/max macros conflict with libstdc++ #define max #include +#include #undef max #undef min @@ -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; @@ -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}; }; } } diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 879421e..931c3ae 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -215,6 +215,16 @@ 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); + } + else if(event->subscribe.prev_notify == 0 && event->subscribe.cur_notify == 1) { + heartRateService.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); + } break; case BLE_GAP_EVENT_MTU: -- cgit v0.10.2 From 60a49af886f16b4bbd8012cd711374f3fdb94efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Milants?= Date: Sun, 17 Oct 2021 08:23:44 +0200 Subject: Add MotionService : expose step count and RAW X/Y/Z values to the host. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07eabe1..a839e08 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 @@ -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 @@ -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 diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp new file mode 100644 index 0000000..2bb5289 --- /dev/null +++ b/src/components/ble/MotionService.cpp @@ -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, 0x02, 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(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; +} diff --git a/src/components/ble/MotionService.h b/src/components/ble/MotionService.h new file mode 100644 index 0000000..75ad518 --- /dev/null +++ b/src/components/ble/MotionService.h @@ -0,0 +1,39 @@ +#pragma once +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include +#include +#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}; + }; + } +} diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 931c3ae..09a9b99 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -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}, @@ -39,6 +40,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, batteryInformationService {batteryController}, immediateAlertService {systemTask, notificationManager}, heartRateService {systemTask, heartRateController}, + motionService{systemTask, motionController}, serviceDiscovery({¤tTimeClient, &alertNotificationClient}) { } @@ -81,6 +83,7 @@ void NimbleController::Init() { batteryInformationService.Init(); immediateAlertService.Init(); heartRateService.Init(); + motionService.Init(); int rc; rc = ble_hs_util_ensure_addr(0); diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 473bb1a..76f89ba 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -19,6 +19,7 @@ #include "NavigationService.h" #include "ServiceDiscovery.h" #include "HeartRateService.h" +#include "MotionService.h" namespace Pinetime { namespace Drivers { @@ -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); @@ -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; diff --git a/src/components/motion/MotionController.cpp b/src/components/motion/MotionController.cpp index b0dbada..a2384d7 100644 --- a/src/components/motion/MotionController.cpp +++ b/src/components/motion/MotionController.cpp @@ -3,6 +3,14 @@ using namespace Pinetime::Controllers; void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) { + if (this->nbSteps != nbSteps && service != nullptr) { + service->OnNewStepCountValue(nbSteps); + } + + if(service != nullptr && (this->x != x || this->y != y || this->z != z)) { + service->OnNewMotionValues(x, y, z); + } + this->x = x; this->y = y; this->z = z; @@ -41,3 +49,6 @@ void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) { default: this->deviceType = DeviceTypes::Unknown; break; } } +void MotionController::SetService(Pinetime::Controllers::MotionService* service) { + this->service = service; +} diff --git a/src/components/motion/MotionController.h b/src/components/motion/MotionController.h index ff71509..c72d8a4 100644 --- a/src/components/motion/MotionController.h +++ b/src/components/motion/MotionController.h @@ -2,6 +2,7 @@ #include #include +#include namespace Pinetime { namespace Controllers { @@ -39,6 +40,7 @@ namespace Pinetime { } void Init(Pinetime::Drivers::Bma421::DeviceTypes types); + void SetService(Pinetime::Controllers::MotionService* service); private: uint32_t nbSteps; @@ -48,6 +50,7 @@ namespace Pinetime { int16_t lastYForWakeUp = 0; bool isSensorOk = false; DeviceTypes deviceType = DeviceTypes::Unknown; + Pinetime::Controllers::MotionService* service = nullptr; }; } } \ No newline at end of file diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index f1c5165..e0a5907 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -101,7 +101,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, heartRateApp(heartRateApp), fs {fs}, touchHandler {touchHandler}, - nimbleController(*this, bleController, dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController) { + nimbleController(*this, bleController, dateTimeController, notificationManager, + batteryController, spiNorFlash, heartRateController, motionController) { } void SystemTask::Start() { -- cgit v0.10.2 From 31badd2eb3a46ed75a752f7b30015a1eaa205cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Milants?= Date: Sun, 17 Oct 2021 08:34:24 +0200 Subject: Add doc about the new motion service. diff --git a/doc/MotionService.md b/doc/MotionService.md new file mode 100644 index 0000000..0d0a551 --- /dev/null +++ b/doc/MotionService.md @@ -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 diff --git a/doc/ble.md b/doc/ble.md index 518b99c..89ad877 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -29,14 +29,15 @@ 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 1.7: + * [Motion Service](MotionService.md) : 00020000-78fc-48fe-8e23-433b3a1942d0 ## BLE services [List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/) -- cgit v0.10.2 From 2c5015add7a67f950e7f5f33e5bc84b7275b4ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Milants?= Date: Sun, 17 Oct 2021 08:42:49 +0200 Subject: Enable/disable notifications for motion service. diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 09a9b99..1bcae1b 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -221,12 +221,15 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { 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; -- cgit v0.10.2 From b3a82288997556b04a64d452ec2067747f1fb706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Milants?= Date: Wed, 20 Oct 2021 20:52:04 +0200 Subject: Add mention to Call characteristic (which was missing in the doc) and change the UUID of the new Motion service from 00020000-* to 00030000-*. diff --git a/doc/ble.md b/doc/ble.md index 89ad877..8125e98 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -34,10 +34,14 @@ The following custom services are implemented in InfiniTime: - Since InfiniTime 0.11: * [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) : 00020000-78fc-48fe-8e23-433b3a1942d0 + * [Motion Service](MotionService.md) : 00030000-78fc-48fe-8e23-433b3a1942d0 ## BLE services [List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/) diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp index 2bb5289..e305021 100644 --- a/src/components/ble/MotionService.cpp +++ b/src/components/ble/MotionService.cpp @@ -9,7 +9,7 @@ namespace { 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, 0x02, 0x00 } + .value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x03, 0x00 } }; } -- cgit v0.10.2 From d5dfa8087679b644c13e1d420b8ef2fc894b3b51 Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Mon, 25 Oct 2021 12:53:14 +0300 Subject: Newer buttonhandler diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a839e08..e727b2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -507,6 +507,7 @@ list(APPEND SOURCE_FILES components/heartrate/Ptagc.cpp components/heartrate/HeartRateController.cpp + buttonhandler/ButtonHandler.cpp touchhandler/TouchHandler.cpp ) @@ -567,6 +568,7 @@ list(APPEND RECOVERY_SOURCE_FILES components/heartrate/Ptagc.cpp components/motor/MotorController.cpp components/fs/FS.cpp + buttonhandler/ButtonHandler.cpp touchhandler/TouchHandler.cpp ) @@ -681,6 +683,7 @@ set(INCLUDE_FILES components/heartrate/Ptagc.h components/heartrate/HeartRateController.h components/motor/MotorController.h + buttonhandler/ButtonHandler.h touchhandler/TouchHandler.h ) diff --git a/src/buttonhandler/ButtonHandler.cpp b/src/buttonhandler/ButtonHandler.cpp new file mode 100644 index 0000000..997409e --- /dev/null +++ b/src/buttonhandler/ButtonHandler.cpp @@ -0,0 +1,85 @@ +#include "ButtonHandler.h" + +using namespace Pinetime::Controllers; + +void ButtonTimerCallback(TimerHandle_t xTimer) { + auto* buttonHandler = static_cast(pvTimerGetTimerID(xTimer)); + buttonHandler->HandleEvent(ButtonHandler::Timer); +} + +void ButtonHandler::Init(Pinetime::System::SystemTask* systemTask) { + this->systemTask = systemTask; + buttonTimer = xTimerCreate("buttonTimer", 0, pdFALSE, this, ButtonTimerCallback); +} + +void ButtonHandler::HandleEvent(events event) { + static constexpr TickType_t doubleClickTime = pdMS_TO_TICKS(200); + static constexpr TickType_t longPressTime = pdMS_TO_TICKS(400); + static constexpr TickType_t longerPressTime = pdMS_TO_TICKS(2000); + + if (systemTask->IsSleeping()) { + // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping + systemTask->PushMessage(System::Messages::GoToRunning); + } else { + systemTask->PushMessage(System::Messages::ReloadIdleTimer); + } + + if (event == Press) { + buttonPressed = true; + } else if (event == Release) { + releaseTime = xTaskGetTickCount(); + buttonPressed = false; + } + + switch (state) { + case Idle: + if (event == Press) { + xTimerChangePeriod(buttonTimer, doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + state = Pressed; + } + break; + case Pressed: + if (event == Press) { + if (xTaskGetTickCount() - releaseTime < doubleClickTime) { + systemTask->PushMessage(System::Messages::OnButtonDoubleClicked); + xTimerStop(buttonTimer, 0); + state = Idle; + } + } else if (event == Release) { + xTimerChangePeriod(buttonTimer, doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + } else if (event == Timer) { + if (buttonPressed) { + xTimerChangePeriod(buttonTimer, longPressTime - doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + state = Holding; + } else { + systemTask->PushMessage(System::Messages::OnButtonPushed); + state = Idle; + } + } + break; + case Holding: + if (event == Release) { + systemTask->PushMessage(System::Messages::OnButtonPushed); + xTimerStop(buttonTimer, 0); + state = Idle; + } else if (event == Timer) { + xTimerChangePeriod(buttonTimer, longerPressTime - longPressTime - doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + systemTask->PushMessage(System::Messages::OnButtonLongPressed); + state = LongHeld; + } + break; + case LongHeld: + if (event == Release) { + xTimerStop(buttonTimer, 0); + state = Idle; + } else if (event == Timer) { + systemTask->PushMessage(System::Messages::OnButtonLongerPressed); + state = Idle; + } + break; + } +} diff --git a/src/buttonhandler/ButtonHandler.h b/src/buttonhandler/ButtonHandler.h new file mode 100644 index 0000000..5d5b57e --- /dev/null +++ b/src/buttonhandler/ButtonHandler.h @@ -0,0 +1,24 @@ +#pragma once + +#include "systemtask/SystemTask.h" +#include +#include + +namespace Pinetime { + namespace Controllers { + class ButtonHandler { + public: + enum events { Press, Release, Timer }; + void Init(Pinetime::System::SystemTask* systemTask); + void HandleEvent(events event); + + private: + Pinetime::System::SystemTask* systemTask = nullptr; + TickType_t releaseTime = 0; + TimerHandle_t buttonTimer; + bool buttonPressed = false; + enum states { Idle, Pressed, Holding, LongHeld }; + states state = Idle; + }; + } +} diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index abe5851..13ee004 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -260,6 +260,20 @@ void DisplayApp::Refresh() { } } break; + case Messages::ButtonLongPressed: + if (currentApp != Apps::Clock) { + LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::Down); + } + break; + case Messages::ButtonLongerPressed: + // Create reboot app and open it instead + LoadApp(Apps::SysInfo, DisplayApp::FullRefreshDirections::Up); + break; + case Messages::ButtonDoubleClicked: + if (currentApp != Apps::Notifications && currentApp != Apps::NotificationsPreview) { + LoadApp(Apps::Notifications, DisplayApp::FullRefreshDirections::Down); + } + break; case Messages::BleFirmwareUpdateStarted: LoadApp(Apps::FirmwareUpdate, DisplayApp::FullRefreshDirections::Down); diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index d48b646..ab0a060 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -9,6 +9,9 @@ namespace Pinetime { UpdateBleConnection, TouchEvent, ButtonPushed, + ButtonLongPressed, + ButtonLongerPressed, + ButtonDoubleClicked, NewNotification, TimerDone, BleFirmwareUpdateStarted, diff --git a/src/main.cpp b/src/main.cpp index fc77235..b942fd4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,7 @@ #include "systemtask/SystemTask.h" #include "drivers/PinMap.h" #include "touchhandler/TouchHandler.h" +#include "buttonhandler/ButtonHandler.h" #if NRF_LOG_ENABLED #include "logging/NrfLogger.h" @@ -96,8 +97,6 @@ TimerHandle_t debounceTimer; TimerHandle_t debounceChargeTimer; Pinetime::Controllers::Battery batteryController; Pinetime::Controllers::Ble bleController; -static constexpr uint8_t pinTouchIrq = Pinetime::PinMap::Cst816sIrq; -static constexpr uint8_t pinPowerPresentIrq = Pinetime::PinMap::PowerPresent; Pinetime::Controllers::HeartRateController heartRateController; Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController); @@ -110,6 +109,7 @@ Pinetime::Controllers::MotionController motionController; Pinetime::Controllers::TimerController timerController; Pinetime::Controllers::AlarmController alarmController {dateTimeController}; Pinetime::Controllers::TouchHandler touchHandler(touchPanel, lvgl); +Pinetime::Controllers::ButtonHandler buttonHandler; Pinetime::Controllers::FS fs {spiNorFlash}; Pinetime::Controllers::Settings settingsController {fs}; @@ -153,7 +153,8 @@ Pinetime::System::SystemTask systemTask(spi, displayApp, heartRateApp, fs, - touchHandler); + touchHandler, + buttonHandler); /* Variable Declarations for variables in noinit SRAM Increment NoInit_MagicValue upon adding variables to this area @@ -176,11 +177,11 @@ void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action if (pin == Pinetime::PinMap::PowerPresent and action == NRF_GPIOTE_POLARITY_TOGGLE) { xTimerStartFromISR(debounceChargeTimer, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - return; + } else if (pin == Pinetime::PinMap::Button) { + // This activates on button release as well due to bouncing + xTimerStartFromISR(debounceTimer, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } - - xTimerStartFromISR(debounceTimer, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void DebounceTimerChargeCallback(TimerHandle_t xTimer) { @@ -188,9 +189,8 @@ void DebounceTimerChargeCallback(TimerHandle_t xTimer) { systemTask.PushMessage(Pinetime::System::Messages::OnChargingEvent); } -void DebounceTimerCallback(TimerHandle_t xTimer) { - xTimerStop(xTimer, 0); - systemTask.OnButtonPushed(); +void DebounceTimerCallback(TimerHandle_t /*unused*/) { + systemTask.PushMessage(Pinetime::System::Messages::HandleButtonEvent); } void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) { @@ -319,8 +319,8 @@ int main(void) { } nrf_gpio_cfg_default(Pinetime::PinMap::TwiScl); - debounceTimer = xTimerCreate("debounceTimer", 200, pdFALSE, (void*) 0, DebounceTimerCallback); - debounceChargeTimer = xTimerCreate("debounceTimerCharge", 200, pdFALSE, (void*) 0, DebounceTimerChargeCallback); + debounceTimer = xTimerCreate("debounceTimer", 10, pdFALSE, nullptr, DebounceTimerCallback); + debounceChargeTimer = xTimerCreate("debounceTimerCharge", 200, pdFALSE, nullptr, DebounceTimerChargeCallback); // retrieve version stored by bootloader Pinetime::BootloaderVersion::SetVersion(NRF_TIMER2->CC[0]); diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index 5aa218d..b0bdbf3 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -15,12 +15,17 @@ namespace Pinetime { BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, - OnButtonEvent, + OnButtonPushed, + OnButtonLongPressed, + OnButtonLongerPressed, + OnButtonDoubleClicked, + HandleButtonEvent, OnDisplayTaskSleeping, EnableSleeping, DisableSleeping, OnNewDay, OnChargingEvent, + ReloadIdleTimer, SetOffAlarm, StopRinging, MeasureBatteryTimerExpired, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index e0a5907..b7db0b9 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -25,7 +25,6 @@ #include "main.h" #include "BootErrors.h" - #include using namespace Pinetime::System; @@ -77,7 +76,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, Pinetime::Applications::DisplayApp& displayApp, Pinetime::Applications::HeartRateTask& heartRateApp, Pinetime::Controllers::FS& fs, - Pinetime::Controllers::TouchHandler& touchHandler) + Pinetime::Controllers::TouchHandler& touchHandler, + Pinetime::Controllers::ButtonHandler& buttonHandler) : spi {spi}, lcd {lcd}, spiNorFlash {spiNorFlash}, @@ -101,8 +101,15 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, heartRateApp(heartRateApp), fs {fs}, touchHandler {touchHandler}, - nimbleController(*this, bleController, dateTimeController, notificationManager, - batteryController, spiNorFlash, heartRateController, motionController) { + buttonHandler {buttonHandler}, + nimbleController(*this, + bleController, + dateTimeController, + notificationManager, + batteryController, + spiNorFlash, + heartRateController, + motionController) { } void SystemTask::Start() { @@ -163,6 +170,8 @@ void SystemTask::Work() { heartRateSensor.Disable(); heartRateApp.Start(); + buttonHandler.Init(this); + // Button nrf_gpio_cfg_output(15); nrf_gpio_pin_set(15); @@ -326,9 +335,32 @@ void SystemTask::Work() { ReloadIdleTimer(); displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); break; - case Messages::OnButtonEvent: - ReloadIdleTimer(); - displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonPushed); + case Messages::OnButtonPushed: + if (!isSleeping && !isGoingToSleep) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonPushed); + } + break; + case Messages::OnButtonLongPressed: + if (!isSleeping) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonLongPressed); + } + break; + case Messages::OnButtonLongerPressed: + if (!isSleeping) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonLongerPressed); + } + break; + case Messages::OnButtonDoubleClicked: + if (!isSleeping) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonDoubleClicked); + } + break; + case Messages::HandleButtonEvent: + if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) { + buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Release); + } else { + buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Press); + } break; case Messages::OnDisplayTaskSleeping: if (BootloaderVersion::IsValid()) { @@ -366,6 +398,9 @@ void SystemTask::Work() { case Messages::BatteryPercentageUpdated: nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); break; + case Messages::ReloadIdleTimer: + ReloadIdleTimer(); + break; default: break; @@ -414,20 +449,6 @@ void SystemTask::UpdateMotion() { } } -void SystemTask::OnButtonPushed() { - if (isGoingToSleep) - return; - if (!isSleeping) { - NRF_LOG_INFO("[systemtask] Button pushed"); - PushMessage(Messages::OnButtonEvent); - } else { - if (!isWakingUp) { - NRF_LOG_INFO("[systemtask] Button pushed, waking up"); - GoToRunning(); - } - } -} - void SystemTask::GoToRunning() { if (isGoingToSleep or (not isSleeping) or isWakingUp) return; diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 879c1be..9967f75 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -20,6 +20,7 @@ #include "components/alarm/AlarmController.h" #include "components/fs/FS.h" #include "touchhandler/TouchHandler.h" +#include "buttonhandler/ButtonHandler.h" #ifdef PINETIME_IS_RECOVERY #include "displayapp/DisplayAppRecovery.h" @@ -45,6 +46,7 @@ namespace Pinetime { } namespace Controllers { class TouchHandler; + class ButtonHandler; } namespace System { class SystemTask { @@ -71,12 +73,12 @@ namespace Pinetime { Pinetime::Applications::DisplayApp& displayApp, Pinetime::Applications::HeartRateTask& heartRateApp, Pinetime::Controllers::FS& fs, - Pinetime::Controllers::TouchHandler& touchHandler); + Pinetime::Controllers::TouchHandler& touchHandler, + Pinetime::Controllers::ButtonHandler& buttonHandler); void Start(); void PushMessage(Messages msg); - void OnButtonPushed(); void OnTouchEvent(); void OnIdle(); @@ -123,6 +125,7 @@ namespace Pinetime { Pinetime::Applications::HeartRateTask& heartRateApp; Pinetime::Controllers::FS& fs; Pinetime::Controllers::TouchHandler& touchHandler; + Pinetime::Controllers::ButtonHandler& buttonHandler; Pinetime::Controllers::NimbleController nimbleController; static void Process(void* instance); -- cgit v0.10.2 From b19a2a760b74f27c8d3db262bf43437f722f74bd Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Mon, 25 Oct 2021 13:40:43 +0300 Subject: Use enum classes, remove old comment diff --git a/src/buttonhandler/ButtonHandler.cpp b/src/buttonhandler/ButtonHandler.cpp index 997409e..b6067d2 100644 --- a/src/buttonhandler/ButtonHandler.cpp +++ b/src/buttonhandler/ButtonHandler.cpp @@ -4,7 +4,7 @@ using namespace Pinetime::Controllers; void ButtonTimerCallback(TimerHandle_t xTimer) { auto* buttonHandler = static_cast(pvTimerGetTimerID(xTimer)); - buttonHandler->HandleEvent(ButtonHandler::Timer); + buttonHandler->HandleEvent(ButtonHandler::Events::Timer); } void ButtonHandler::Init(Pinetime::System::SystemTask* systemTask) { @@ -12,7 +12,7 @@ void ButtonHandler::Init(Pinetime::System::SystemTask* systemTask) { buttonTimer = xTimerCreate("buttonTimer", 0, pdFALSE, this, ButtonTimerCallback); } -void ButtonHandler::HandleEvent(events event) { +void ButtonHandler::HandleEvent(Events event) { static constexpr TickType_t doubleClickTime = pdMS_TO_TICKS(200); static constexpr TickType_t longPressTime = pdMS_TO_TICKS(400); static constexpr TickType_t longerPressTime = pdMS_TO_TICKS(2000); @@ -24,61 +24,61 @@ void ButtonHandler::HandleEvent(events event) { systemTask->PushMessage(System::Messages::ReloadIdleTimer); } - if (event == Press) { + if (event == Events::Press) { buttonPressed = true; - } else if (event == Release) { + } else if (event == Events::Release) { releaseTime = xTaskGetTickCount(); buttonPressed = false; } switch (state) { - case Idle: - if (event == Press) { + case States::Idle: + if (event == Events::Press) { xTimerChangePeriod(buttonTimer, doubleClickTime, 0); xTimerStart(buttonTimer, 0); - state = Pressed; + state = States::Pressed; } break; - case Pressed: - if (event == Press) { + case States::Pressed: + if (event == Events::Press) { if (xTaskGetTickCount() - releaseTime < doubleClickTime) { systemTask->PushMessage(System::Messages::OnButtonDoubleClicked); xTimerStop(buttonTimer, 0); - state = Idle; + state = States::Idle; } - } else if (event == Release) { + } else if (event == Events::Release) { xTimerChangePeriod(buttonTimer, doubleClickTime, 0); xTimerStart(buttonTimer, 0); - } else if (event == Timer) { + } else if (event == Events::Timer) { if (buttonPressed) { xTimerChangePeriod(buttonTimer, longPressTime - doubleClickTime, 0); xTimerStart(buttonTimer, 0); - state = Holding; + state = States::Holding; } else { systemTask->PushMessage(System::Messages::OnButtonPushed); - state = Idle; + state = States::Idle; } } break; - case Holding: - if (event == Release) { + case States::Holding: + if (event == Events::Release) { systemTask->PushMessage(System::Messages::OnButtonPushed); xTimerStop(buttonTimer, 0); - state = Idle; - } else if (event == Timer) { + state = States::Idle; + } else if (event == Events::Timer) { xTimerChangePeriod(buttonTimer, longerPressTime - longPressTime - doubleClickTime, 0); xTimerStart(buttonTimer, 0); systemTask->PushMessage(System::Messages::OnButtonLongPressed); - state = LongHeld; + state = States::LongHeld; } break; - case LongHeld: - if (event == Release) { + case States::LongHeld: + if (event == Events::Release) { xTimerStop(buttonTimer, 0); - state = Idle; - } else if (event == Timer) { + state = States::Idle; + } else if (event == Events::Timer) { systemTask->PushMessage(System::Messages::OnButtonLongerPressed); - state = Idle; + state = States::Idle; } break; } diff --git a/src/buttonhandler/ButtonHandler.h b/src/buttonhandler/ButtonHandler.h index 5d5b57e..b4c36bd 100644 --- a/src/buttonhandler/ButtonHandler.h +++ b/src/buttonhandler/ButtonHandler.h @@ -8,17 +8,17 @@ namespace Pinetime { namespace Controllers { class ButtonHandler { public: - enum events { Press, Release, Timer }; + enum class Events : uint8_t { Press, Release, Timer }; void Init(Pinetime::System::SystemTask* systemTask); - void HandleEvent(events event); + void HandleEvent(Events event); private: + enum class States : uint8_t { Idle, Pressed, Holding, LongHeld }; Pinetime::System::SystemTask* systemTask = nullptr; TickType_t releaseTime = 0; TimerHandle_t buttonTimer; bool buttonPressed = false; - enum states { Idle, Pressed, Holding, LongHeld }; - states state = Idle; + States state = States::Idle; }; } } diff --git a/src/main.cpp b/src/main.cpp index b942fd4..53f78ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -178,7 +178,6 @@ void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action xTimerStartFromISR(debounceChargeTimer, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } else if (pin == Pinetime::PinMap::Button) { - // This activates on button release as well due to bouncing xTimerStartFromISR(debounceTimer, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index b7db0b9..8a4f894 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -357,9 +357,9 @@ void SystemTask::Work() { break; case Messages::HandleButtonEvent: if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) { - buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Release); + buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Events::Release); } else { - buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Press); + buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Events::Press); } break; case Messages::OnDisplayTaskSleeping: -- cgit v0.10.2 From 351c60a13167c05dfdbb0516f84077a4cd6adeec Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Mon, 25 Oct 2021 16:57:29 +0300 Subject: Return button action instead of pushing messages diff --git a/src/buttonhandler/ButtonActions.h b/src/buttonhandler/ButtonActions.h new file mode 100644 index 0000000..21be441 --- /dev/null +++ b/src/buttonhandler/ButtonActions.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Pinetime { + namespace Controllers { + enum class ButtonActions { None, Click, DoubleClick, LongPress, LongerPress }; + } +} diff --git a/src/buttonhandler/ButtonHandler.cpp b/src/buttonhandler/ButtonHandler.cpp index b6067d2..91e8bbd 100644 --- a/src/buttonhandler/ButtonHandler.cpp +++ b/src/buttonhandler/ButtonHandler.cpp @@ -3,27 +3,19 @@ using namespace Pinetime::Controllers; void ButtonTimerCallback(TimerHandle_t xTimer) { - auto* buttonHandler = static_cast(pvTimerGetTimerID(xTimer)); - buttonHandler->HandleEvent(ButtonHandler::Events::Timer); + auto* sysTask = static_cast(pvTimerGetTimerID(xTimer)); + sysTask->PushMessage(Pinetime::System::Messages::HandleButtonTimerEvent); } void ButtonHandler::Init(Pinetime::System::SystemTask* systemTask) { - this->systemTask = systemTask; - buttonTimer = xTimerCreate("buttonTimer", 0, pdFALSE, this, ButtonTimerCallback); + buttonTimer = xTimerCreate("buttonTimer", 0, pdFALSE, systemTask, ButtonTimerCallback); } -void ButtonHandler::HandleEvent(Events event) { +ButtonActions ButtonHandler::HandleEvent(Events event) { static constexpr TickType_t doubleClickTime = pdMS_TO_TICKS(200); static constexpr TickType_t longPressTime = pdMS_TO_TICKS(400); static constexpr TickType_t longerPressTime = pdMS_TO_TICKS(2000); - if (systemTask->IsSleeping()) { - // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping - systemTask->PushMessage(System::Messages::GoToRunning); - } else { - systemTask->PushMessage(System::Messages::ReloadIdleTimer); - } - if (event == Events::Press) { buttonPressed = true; } else if (event == Events::Release) { @@ -42,9 +34,9 @@ void ButtonHandler::HandleEvent(Events event) { case States::Pressed: if (event == Events::Press) { if (xTaskGetTickCount() - releaseTime < doubleClickTime) { - systemTask->PushMessage(System::Messages::OnButtonDoubleClicked); xTimerStop(buttonTimer, 0); state = States::Idle; + return ButtonActions::DoubleClick; } } else if (event == Events::Release) { xTimerChangePeriod(buttonTimer, doubleClickTime, 0); @@ -55,21 +47,21 @@ void ButtonHandler::HandleEvent(Events event) { xTimerStart(buttonTimer, 0); state = States::Holding; } else { - systemTask->PushMessage(System::Messages::OnButtonPushed); state = States::Idle; + return ButtonActions::Click; } } break; case States::Holding: if (event == Events::Release) { - systemTask->PushMessage(System::Messages::OnButtonPushed); xTimerStop(buttonTimer, 0); state = States::Idle; + return ButtonActions::Click; } else if (event == Events::Timer) { xTimerChangePeriod(buttonTimer, longerPressTime - longPressTime - doubleClickTime, 0); xTimerStart(buttonTimer, 0); - systemTask->PushMessage(System::Messages::OnButtonLongPressed); state = States::LongHeld; + return ButtonActions::LongPress; } break; case States::LongHeld: @@ -77,9 +69,10 @@ void ButtonHandler::HandleEvent(Events event) { xTimerStop(buttonTimer, 0); state = States::Idle; } else if (event == Events::Timer) { - systemTask->PushMessage(System::Messages::OnButtonLongerPressed); state = States::Idle; + return ButtonActions::LongerPress; } break; } + return ButtonActions::None; } diff --git a/src/buttonhandler/ButtonHandler.h b/src/buttonhandler/ButtonHandler.h index b4c36bd..44b20f1 100644 --- a/src/buttonhandler/ButtonHandler.h +++ b/src/buttonhandler/ButtonHandler.h @@ -1,5 +1,6 @@ #pragma once +#include "ButtonActions.h" #include "systemtask/SystemTask.h" #include #include @@ -10,11 +11,10 @@ namespace Pinetime { public: enum class Events : uint8_t { Press, Release, Timer }; void Init(Pinetime::System::SystemTask* systemTask); - void HandleEvent(Events event); + ButtonActions HandleEvent(Events event); private: enum class States : uint8_t { Idle, Pressed, Holding, LongHeld }; - Pinetime::System::SystemTask* systemTask = nullptr; TickType_t releaseTime = 0; TimerHandle_t buttonTimer; bool buttonPressed = false; diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index b0bdbf3..b714270 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -15,17 +15,13 @@ namespace Pinetime { BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, - OnButtonPushed, - OnButtonLongPressed, - OnButtonLongerPressed, - OnButtonDoubleClicked, HandleButtonEvent, + HandleButtonTimerEvent, OnDisplayTaskSleeping, EnableSleeping, DisableSleeping, OnNewDay, OnChargingEvent, - ReloadIdleTimer, SetOffAlarm, StopRinging, MeasureBatteryTimerExpired, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 8a4f894..85cefb6 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -335,33 +335,25 @@ void SystemTask::Work() { ReloadIdleTimer(); displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); break; - case Messages::OnButtonPushed: - if (!isSleeping && !isGoingToSleep) { - displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonPushed); - } - break; - case Messages::OnButtonLongPressed: - if (!isSleeping) { - displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonLongPressed); - } - break; - case Messages::OnButtonLongerPressed: - if (!isSleeping) { - displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonLongerPressed); - } - break; - case Messages::OnButtonDoubleClicked: - if (!isSleeping) { - displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonDoubleClicked); + case Messages::HandleButtonEvent: { + // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping + if (IsSleeping()) { + GoToRunning(); + break; } - break; - case Messages::HandleButtonEvent: + + Controllers::ButtonActions action; if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) { - buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Events::Release); + action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Release); } else { - buttonHandler.HandleEvent(Pinetime::Controllers::ButtonHandler::Events::Press); + action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); } - break; + HandleButtonAction(action); + } break; + case Messages::HandleButtonTimerEvent: { + auto action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Timer); + HandleButtonAction(action); + } break; case Messages::OnDisplayTaskSleeping: if (BootloaderVersion::IsValid()) { // First versions of the bootloader do not expose their version and cannot initialize the SPI NOR FLASH @@ -398,9 +390,6 @@ void SystemTask::Work() { case Messages::BatteryPercentageUpdated: nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); break; - case Messages::ReloadIdleTimer: - ReloadIdleTimer(); - break; default: break; @@ -449,6 +438,35 @@ void SystemTask::UpdateMotion() { } } +void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { + if (IsSleeping()) { + return; + } + + ReloadIdleTimer(); + + using Actions = Controllers::ButtonActions; + + switch (action) { + case Actions::Click: + if (!isGoingToSleep) { + displayApp.PushMessage(Applications::Display::Messages::ButtonPushed); + } + break; + case Actions::DoubleClick: + displayApp.PushMessage(Applications::Display::Messages::ButtonDoubleClicked); + break; + case Actions::LongPress: + displayApp.PushMessage(Applications::Display::Messages::ButtonLongPressed); + break; + case Actions::LongerPress: + displayApp.PushMessage(Applications::Display::Messages::ButtonLongerPressed); + break; + default: + break; + } +} + void SystemTask::GoToRunning() { if (isGoingToSleep or (not isSleeping) or isWakingUp) return; diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 9967f75..d6045e9 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -21,6 +21,7 @@ #include "components/fs/FS.h" #include "touchhandler/TouchHandler.h" #include "buttonhandler/ButtonHandler.h" +#include "buttonhandler/ButtonActions.h" #ifdef PINETIME_IS_RECOVERY #include "displayapp/DisplayAppRecovery.h" @@ -138,6 +139,7 @@ namespace Pinetime { TimerHandle_t measureBatteryTimer; bool doNotGoToSleep = false; + void HandleButtonAction(Controllers::ButtonActions action); void GoToRunning(); void UpdateMotion(); bool stepCounterMustBeReset = false; -- cgit v0.10.2 From 887c409b135bb2f21f2fb5ae70a4d8831049d14d Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Mon, 25 Oct 2021 17:13:02 +0300 Subject: Only wake up on press. Fixes issue with longer press and sleep diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 85cefb6..51dbc3e 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -337,15 +337,14 @@ void SystemTask::Work() { break; case Messages::HandleButtonEvent: { // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping - if (IsSleeping()) { - GoToRunning(); - break; - } - Controllers::ButtonActions action; if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) { action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Release); } else { + if (IsSleeping()) { + GoToRunning(); + break; + } action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); } HandleButtonAction(action); -- cgit v0.10.2 From 60a717b1a2272e61dfc4d297998da1c7672a8316 Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Mon, 25 Oct 2021 17:45:48 +0300 Subject: Make it so special actions can be input while sleeping, like in #480 diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 51dbc3e..0a3f995 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -336,16 +336,17 @@ void SystemTask::Work() { displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); break; case Messages::HandleButtonEvent: { - // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping Controllers::ButtonActions action; if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) { action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Release); } else { + action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); + // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping if (IsSleeping()) { + fastWakeUpDone = true; GoToRunning(); break; } - action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); } HandleButtonAction(action); } break; @@ -448,7 +449,8 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { switch (action) { case Actions::Click: - if (!isGoingToSleep) { + // If the first action after fast wakeup is a click, it should be ignored. + if (!fastWakeUpDone && !isGoingToSleep) { displayApp.PushMessage(Applications::Display::Messages::ButtonPushed); } break; @@ -462,8 +464,10 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { displayApp.PushMessage(Applications::Display::Messages::ButtonLongerPressed); break; default: - break; + return; } + + fastWakeUpDone = false; } void SystemTask::GoToRunning() { diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index d6045e9..412878b 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -140,6 +140,8 @@ namespace Pinetime { bool doNotGoToSleep = false; void HandleButtonAction(Controllers::ButtonActions action); + bool fastWakeUpDone = false; + void GoToRunning(); void UpdateMotion(); bool stepCounterMustBeReset = false; -- cgit v0.10.2 From 0fc081fe03eddae41c8824cb3a55c054b653bb15 Mon Sep 17 00:00:00 2001 From: Steveis Date: Tue, 26 Oct 2021 16:57:20 +0100 Subject: Correctly define DFU diff --git a/doc/gettingStarted/gettingStarted-1.0.md b/doc/gettingStarted/gettingStarted-1.0.md index 3f8f38f..409b7c7 100644 --- a/doc/gettingStarted/gettingStarted-1.0.md +++ b/doc/gettingStarted/gettingStarted-1.0.md @@ -12,7 +12,7 @@ Basically, a **firmware** is just a software running on the embedded hardware of - **[The bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader)** is responsible for safely applying **updates** of the *application firmware*, reverting them in case of issues and load the recovery firmware when requested. - **The recovery firmware** is a specific *application firmware* than can be loaded by the bootloader on user request. This firmware can be useful in case of serious issue, when the main application firmware cannot perform an OTA update correctly. Currently, this recovery firmware is based on [InfiniTime 0.14.1](https://github.com/InfiniTimeOrg/InfiniTime/releases/tag/0.14.1). -**OTA** and **DFU** refer to the update of the firmware over BLE (**B**luetooth **L**ow **E**nergy). **OTA** means **O**ver **T**he **A**ir, this is a functionality that allows the user to update the firmware how their device using a wireless communication like BLE. When we talk about **DFU** (**D**igital **F**irmware **U**pdate), we refer to the file format and protocol used to send the update of the firmware to the watch over-the-air. InfiniTime implement the (legacy) DFU protocol from Nordic Semiconductor (NRF). +**OTA** and **DFU** refer to the update of the firmware over BLE (**B**luetooth **L**ow **E**nergy). **OTA** means **O**ver **T**he **A**ir, this is a functionality that allows the user to update the firmware how their device using a wireless communication like BLE. When we talk about **DFU** (**D**evice **F**irmware **U**pdate), we refer to the file format and protocol used to send the update of the firmware to the watch over-the-air. InfiniTime implement the (legacy) DFU protocol from Nordic Semiconductor (NRF). ## How to check the version of InfiniTime and the bootloader? -- cgit v0.10.2 From 71ce13d3095febf9dcdb0ddc6ca03b515fd7d57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Milants?= Date: Tue, 26 Oct 2021 20:31:18 +0200 Subject: Motion service : fix step notifications that were sent as a single byte instead of 4 (uint32_t). diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp index e305021..b4786ab 100644 --- a/src/components/ble/MotionService.cpp +++ b/src/components/ble/MotionService.cpp @@ -80,7 +80,7 @@ int MotionService::OnStepCountRequested(uint16_t connectionHandle, uint16_t attr return 0; } -void MotionService::OnNewStepCountValue(uint8_t stepCount) { +void MotionService::OnNewStepCountValue(uint32_t stepCount) { if(!stepCountNoficationEnabled) return; uint32_t buffer = stepCount; diff --git a/src/components/ble/MotionService.h b/src/components/ble/MotionService.h index 75ad518..1b4ac0a 100644 --- a/src/components/ble/MotionService.h +++ b/src/components/ble/MotionService.h @@ -17,7 +17,7 @@ namespace Pinetime { 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 OnNewStepCountValue(uint32_t stepCount); void OnNewMotionValues(int16_t x, int16_t y, int16_t z); void SubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle); -- cgit v0.10.2 From e051504deaf4db166c08ec9693c575006ee12c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Milants?= Date: Thu, 28 Oct 2021 20:56:17 +0200 Subject: Add doc : how to generate files needed by the factory. diff --git a/doc/buildAndProgram.md b/doc/buildAndProgram.md index 3e000a3..3686871 100644 --- a/doc/buildAndProgram.md +++ b/doc/buildAndProgram.md @@ -231,3 +231,33 @@ Loading section .sec7, size 0xdf08 lma 0x40000 Start address 0x0, load size 314200 Transfer rate: 45 KB/sec, 969 bytes/write. ``` + +### How to generate files needed by the factory +These files are needed by the Pine64 factory to flash InfiniTime as the default firmware on the PineTimes. + +Two files are needed: an **HEX (.hex)** file that contains the content of the internal flash memory (bootloader + InfiniTime) and a **binary (.bin)** file that contains the content of the external flash memory (recovery firmware). + +#### merged-internal.hex +First, convert the bootloader to hex: + ``` + /bin/arm-none-eabi-objcopy -I binary -O ihex ./bootloader.bin ./bootloader.hex + ``` +where `bootloader.bin` is the [last stable version](https://github.com/JF002/pinetime-mcuboot-bootloader/releases) of the [bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader). + +Then, convert the MCUBoot image of InfiniTime: +``` +/bin/arm-none-eabi-objcopy -I binary -O ihex --change-addresses 0x8000 ./pinetime-mcuboot-app-image-1.6.0.bin ./pinetime-mcuboot-app-image-1.6.0.hex +``` +where `pinetime-mcuboot-app-image-1.6.0.bin` is [the bin of the last MCUBoot image](https://github.com/InfiniTimeOrg/InfiniTime/releases) of [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime). + +Pay attention to the parameter `--change-addresses 0x8000`. It's needed to ensure the image will be flashed at the offset expected by the bootloader (0x8000). + +Finally, merge them together with **mergehex**: +``` +/opt/mergehex/mergehex -m ./bootloader.hex ./pinetime-mcuboot-app-image-1.6.0.hex -o merged-internal.hex +``` + +This file must be flashed at offset **0x00** of the internal memory of the NRF52832. + +#### spinor.bin +This file is the MCUBoot image of the last stable version of the recovery firmware. It must be flashed at offset **0x00** of the external SPINOR flash memory. \ No newline at end of file -- cgit v0.10.2 From 456084499464e242faf36606edada10ab6056939 Mon Sep 17 00:00:00 2001 From: "James A. Jerkins" Date: Thu, 28 Oct 2021 20:38:59 -0500 Subject: Fix recovery firmware build diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h index 9f5fb13..7286815 100644 --- a/src/displayapp/DisplayAppRecovery.h +++ b/src/displayapp/DisplayAppRecovery.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include "BootErrors.h" #include "TouchEvents.h" #include "Apps.h" #include "Messages.h" -- cgit v0.10.2 From 30520d262b8bae9868645462e196027389dce246 Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Fri, 29 Oct 2021 13:10:34 +0300 Subject: Memory savings by replacing strings diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index 343b72b..dd223b2 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -209,7 +209,7 @@ std::unique_ptr SystemInfo::CreateScreen4() { static constexpr uint8_t maxTaskCount = 9; TaskStatus_t tasksStatus[maxTaskCount]; - lv_obj_t* infoTask = lv_table_create(lv_scr_act(), NULL); + lv_obj_t* infoTask = lv_table_create(lv_scr_act(), nullptr); lv_table_set_col_cnt(infoTask, 4); lv_table_set_row_cnt(infoTask, maxTaskCount + 1); lv_obj_set_style_local_pad_all(infoTask, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, 0); @@ -227,35 +227,37 @@ std::unique_ptr SystemInfo::CreateScreen4() { auto nb = uxTaskGetSystemState(tasksStatus, maxTaskCount, nullptr); std::sort(tasksStatus, tasksStatus + nb, sortById); for (uint8_t i = 0; i < nb && i < maxTaskCount; i++) { + char buffer[7] = {0}; - lv_table_set_cell_value(infoTask, i + 1, 0, std::to_string(tasksStatus[i].xTaskNumber).c_str()); - char state[2] = {0}; + sprintf(buffer, "%lu", tasksStatus[i].xTaskNumber); + lv_table_set_cell_value(infoTask, i + 1, 0, buffer); switch (tasksStatus[i].eCurrentState) { case eReady: case eRunning: - state[0] = 'R'; + buffer[0] = 'R'; break; case eBlocked: - state[0] = 'B'; + buffer[0] = 'B'; break; case eSuspended: - state[0] = 'S'; + buffer[0] = 'S'; break; case eDeleted: - state[0] = 'D'; + buffer[0] = 'D'; break; default: - state[0] = 'I'; // Invalid + buffer[0] = 'I'; // Invalid break; } - lv_table_set_cell_value(infoTask, i + 1, 1, state); + buffer[1] = '\0'; + lv_table_set_cell_value(infoTask, i + 1, 1, buffer); lv_table_set_cell_value(infoTask, i + 1, 2, tasksStatus[i].pcTaskName); if (tasksStatus[i].usStackHighWaterMark < 20) { - std::string str1 = std::to_string(tasksStatus[i].usStackHighWaterMark) + " low"; - lv_table_set_cell_value(infoTask, i + 1, 3, str1.c_str()); + sprintf(buffer, "%d low", tasksStatus[i].usStackHighWaterMark); } else { - lv_table_set_cell_value(infoTask, i + 1, 3, std::to_string(tasksStatus[i].usStackHighWaterMark).c_str()); + sprintf(buffer, "%d", tasksStatus[i].usStackHighWaterMark); } + lv_table_set_cell_value(infoTask, i + 1, 3, buffer); } return std::make_unique(3, 5, app, infoTask); } diff --git a/src/displayapp/screens/Twos.cpp b/src/displayapp/screens/Twos.cpp index 4201d50..d12ef90 100644 --- a/src/displayapp/screens/Twos.cpp +++ b/src/displayapp/screens/Twos.cpp @@ -1,10 +1,10 @@ #include "Twos.h" -#include -#include -#include #include -#include +#include +#include +#include #include +#include using namespace Pinetime::Applications::Screens; @@ -265,7 +265,9 @@ void Twos::updateGridDisplay(Tile grid[][4]) { for (int row = 0; row < 4; row++) { for (int col = 0; col < 4; col++) { if (grid[row][col].value) { - lv_table_set_cell_value(gridDisplay, row, col, (std::to_string(grid[row][col].value)).c_str()); + char buffer[7]; + sprintf(buffer, "%d", grid[row][col].value); + lv_table_set_cell_value(gridDisplay, row, col, buffer); } else { lv_table_set_cell_value(gridDisplay, row, col, ""); } -- cgit v0.10.2 From 22f167dd0ae37a35fdfd9a8222c85384a4bc3b4c Mon Sep 17 00:00:00 2001 From: Riku Isokoski Date: Sat, 30 Oct 2021 11:51:11 +0300 Subject: Ignore some warnings diff --git a/.clang-tidy b/.clang-tidy index 8b9e7c0..df52357 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,7 @@ Checks: '*, -altera-unroll-loops, -llvmlibc-callee-namespace, + -llvmlibc-restrict-system-libc-headers, -llvm-header-guard, -llvm-namespace-comment, -google-build-using-namespace, @@ -9,6 +10,7 @@ Checks: '*, -fuchsia-statically-constructed-objects, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-type-static-cast-downcast, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-pro-type-vararg, -- cgit v0.10.2