diff options
| author | Jean-François Milants <jf@codingfield.com> | 2021-11-11 09:08:20 (GMT) |
|---|---|---|
| committer | Jean-François Milants <jf@codingfield.com> | 2021-11-11 09:08:20 (GMT) |
| commit | f6d0ec49e6f2669c64729b081eb3342f02151f4a (patch) | |
| tree | 7e21c83726bd3e0d18ec4ebe871274b1dbe2eb38 /src | |
| parent | f41aaad6836ae348d1b5b084b4533b636f516b93 (diff) | |
| parent | a57fda6ba4a29866083a1254ffdf92939d00e182 (diff) | |
Merge branch 'develop'
# Conflicts:
# doc/buildAndProgram.md
Diffstat (limited to 'src')
63 files changed, 1446 insertions, 375 deletions
diff --git a/src/BootErrors.h b/src/BootErrors.h new file mode 100644 index 0000000..d00418c --- /dev/null +++ b/src/BootErrors.h @@ -0,0 +1,10 @@ +#pragma once + +namespace Pinetime { + namespace System { + enum class BootErrors { + None, + TouchController, + }; + } +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37ee084..e727b2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -421,6 +421,7 @@ list(APPEND SOURCE_FILES displayapp/screens/BatteryInfo.cpp displayapp/screens/Steps.cpp displayapp/screens/Timer.cpp + displayapp/screens/Error.cpp displayapp/screens/Alarm.cpp displayapp/Colors.cpp @@ -433,6 +434,8 @@ list(APPEND SOURCE_FILES displayapp/screens/settings/SettingDisplay.cpp displayapp/screens/settings/SettingSteps.cpp displayapp/screens/settings/SettingPineTimeStyle.cpp + displayapp/screens/settings/SettingSetDate.cpp + displayapp/screens/settings/SettingSetTime.cpp ## Watch faces displayapp/icons/bg_clock.c @@ -474,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 @@ -503,6 +507,7 @@ list(APPEND SOURCE_FILES components/heartrate/Ptagc.cpp components/heartrate/HeartRateController.cpp + buttonhandler/ButtonHandler.cpp touchhandler/TouchHandler.cpp ) @@ -542,6 +547,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 @@ -562,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 ) @@ -649,6 +656,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 @@ -675,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/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 new file mode 100644 index 0000000..91e8bbd --- /dev/null +++ b/src/buttonhandler/ButtonHandler.cpp @@ -0,0 +1,78 @@ +#include "ButtonHandler.h" + +using namespace Pinetime::Controllers; + +void ButtonTimerCallback(TimerHandle_t xTimer) { + auto* sysTask = static_cast<Pinetime::System::SystemTask*>(pvTimerGetTimerID(xTimer)); + sysTask->PushMessage(Pinetime::System::Messages::HandleButtonTimerEvent); +} + +void ButtonHandler::Init(Pinetime::System::SystemTask* systemTask) { + buttonTimer = xTimerCreate("buttonTimer", 0, pdFALSE, systemTask, ButtonTimerCallback); +} + +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 (event == Events::Press) { + buttonPressed = true; + } else if (event == Events::Release) { + releaseTime = xTaskGetTickCount(); + buttonPressed = false; + } + + switch (state) { + case States::Idle: + if (event == Events::Press) { + xTimerChangePeriod(buttonTimer, doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + state = States::Pressed; + } + break; + case States::Pressed: + if (event == Events::Press) { + if (xTaskGetTickCount() - releaseTime < doubleClickTime) { + xTimerStop(buttonTimer, 0); + state = States::Idle; + return ButtonActions::DoubleClick; + } + } else if (event == Events::Release) { + xTimerChangePeriod(buttonTimer, doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + } else if (event == Events::Timer) { + if (buttonPressed) { + xTimerChangePeriod(buttonTimer, longPressTime - doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + state = States::Holding; + } else { + state = States::Idle; + return ButtonActions::Click; + } + } + break; + case States::Holding: + if (event == Events::Release) { + xTimerStop(buttonTimer, 0); + state = States::Idle; + return ButtonActions::Click; + } else if (event == Events::Timer) { + xTimerChangePeriod(buttonTimer, longerPressTime - longPressTime - doubleClickTime, 0); + xTimerStart(buttonTimer, 0); + state = States::LongHeld; + return ButtonActions::LongPress; + } + break; + case States::LongHeld: + if (event == Events::Release) { + xTimerStop(buttonTimer, 0); + state = States::Idle; + } else if (event == Events::Timer) { + state = States::Idle; + return ButtonActions::LongerPress; + } + break; + } + return ButtonActions::None; +} diff --git a/src/buttonhandler/ButtonHandler.h b/src/buttonhandler/ButtonHandler.h new file mode 100644 index 0000000..44b20f1 --- /dev/null +++ b/src/buttonhandler/ButtonHandler.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ButtonActions.h" +#include "systemtask/SystemTask.h" +#include <FreeRTOS.h> +#include <timers.h> + +namespace Pinetime { + namespace Controllers { + class ButtonHandler { + public: + enum class Events : uint8_t { Press, Release, Timer }; + void Init(Pinetime::System::SystemTask* systemTask); + ButtonActions HandleEvent(Events event); + + private: + enum class States : uint8_t { Idle, Pressed, Holding, LongHeld }; + TickType_t releaseTime = 0; + TimerHandle_t buttonTimer; + bool buttonPressed = false; + States state = States::Idle; + }; + } +} diff --git a/src/components/battery/BatteryController.cpp b/src/components/battery/BatteryController.cpp index 4ef20a2..e807f03 100644 --- a/src/components/battery/BatteryController.cpp +++ b/src/components/battery/BatteryController.cpp @@ -13,10 +13,20 @@ Battery::Battery() { nrf_gpio_cfg_input(PinMap::Charging, static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Disabled); } -void Battery::Update() { +void Battery::ReadPowerState() { isCharging = !nrf_gpio_pin_read(PinMap::Charging); isPowerPresent = !nrf_gpio_pin_read(PinMap::PowerPresent); + if (isPowerPresent && !isCharging) { + isFull = true; + } else if (!isPowerPresent) { + isFull = false; + } +} + +void Battery::MeasureVoltage() { + ReadPowerState(); + if (isReading) { return; } @@ -63,18 +73,23 @@ void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) { // p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024 voltage = p_event->data.done.p_buffer[0] * (8 * 600) / 1024; - if (voltage > battery_max) { - percentRemaining = 100; + uint8_t newPercent; + if (isFull) { + newPercent = 100; } else if (voltage < battery_min) { - percentRemaining = 0; + newPercent = 0; } else { - percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min); + newPercent = std::min((voltage - battery_min) * 100 / (battery_max - battery_min), isCharging ? 99 : 100); + } + + if ((isPowerPresent && newPercent > percentRemaining) || (!isPowerPresent && newPercent < percentRemaining) || firstMeasurement) { + firstMeasurement = false; + percentRemaining = newPercent; + systemTask->PushMessage(System::Messages::BatteryPercentageUpdated); } nrfx_saadc_uninit(); isReading = false; - - systemTask->PushMessage(System::Messages::BatteryMeasurementDone); } } diff --git a/src/components/battery/BatteryController.h b/src/components/battery/BatteryController.h index 8af27ea..5a7394c 100644 --- a/src/components/battery/BatteryController.h +++ b/src/components/battery/BatteryController.h @@ -10,7 +10,8 @@ namespace Pinetime { public: Battery(); - void Update(); + void ReadPowerState(); + void MeasureVoltage(); void Register(System::SystemTask* systemTask); uint8_t PercentRemaining() const { @@ -22,7 +23,9 @@ namespace Pinetime { } bool IsCharging() const { - return isCharging; + // isCharging will go up and down when fully charged + // isFull makes sure this returns false while fully charged. + return isCharging && !isFull; } bool IsPowerPresent() const { @@ -37,8 +40,10 @@ namespace Pinetime { uint16_t voltage = 0; uint8_t percentRemaining = 0; + bool isFull = false; bool isCharging = false; bool isPowerPresent = false; + bool firstMeasurement = true; void SaadcInit(); diff --git a/src/components/ble/AlertNotificationClient.cpp b/src/components/ble/AlertNotificationClient.cpp index c3d1d69..5e5c25c 100644 --- a/src/components/ble/AlertNotificationClient.cpp +++ b/src/components/ble/AlertNotificationClient.cpp @@ -55,7 +55,7 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const return true; } - if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ansServiceUuid), &service->uuid.u) == 0) { + if (service != nullptr && ble_uuid_cmp(&ansServiceUuid.u, &service->uuid.u) == 0) { NRF_LOG_INFO("ANS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); ansStartHandle = service->start_handle; ansEndHandle = service->end_handle; @@ -80,21 +80,21 @@ int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connection } else onServiceDiscovered(connectionHandle); } else { - if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) { + if (characteristic != nullptr && ble_uuid_cmp(&supportedNewAlertCategoryUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid"); supportedNewAlertCategoryHandle = characteristic->val_handle; - } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) { + } else if (characteristic != nullptr && ble_uuid_cmp(&supportedUnreadAlertCategoryUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid"); supportedUnreadAlertCategoryHandle = characteristic->val_handle; - } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &characteristic->uuid.u) == 0) { + } else if (characteristic != nullptr && ble_uuid_cmp(&newAlertUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid"); newAlertHandle = characteristic->val_handle; newAlertDefHandle = characteristic->def_handle; isCharacteristicDiscovered = true; - } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) { + } else if (characteristic != nullptr && ble_uuid_cmp(&unreadAlertStatusUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid"); unreadAlertStatusHandle = characteristic->val_handle; - } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &controlPointUuid), &characteristic->uuid.u) == 0) { + } else if (characteristic != nullptr && ble_uuid_cmp(&controlPointUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid"); controlPointHandle = characteristic->val_handle; } else @@ -119,7 +119,7 @@ int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connect uint16_t characteristicValueHandle, const ble_gatt_dsc* descriptor) { if (error->status == 0) { - if (characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &descriptor->uuid.u)) { + if (characteristicValueHandle == newAlertHandle && ble_uuid_cmp(&newAlertUuid.u, &descriptor->uuid.u)) { if (newAlertDescriptorHandle == 0) { NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle); newAlertDescriptorHandle = descriptor->handle; diff --git a/src/components/ble/AlertNotificationService.cpp b/src/components/ble/AlertNotificationService.cpp index d5fc7f6..56fc595 100644 --- a/src/components/ble/AlertNotificationService.cpp +++ b/src/components/ble/AlertNotificationService.cpp @@ -26,11 +26,8 @@ void AlertNotificationService::Init() { } AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager) - : characteristicDefinition {{.uuid = (ble_uuid_t*) &ansCharUuid, - .access_cb = AlertNotificationCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_WRITE}, - {.uuid = (ble_uuid_t*) ¬ificationEventUuid, + : characteristicDefinition {{.uuid = &ansCharUuid.u, .access_cb = AlertNotificationCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE}, + {.uuid = ¬ificationEventUuid.u, .access_cb = AlertNotificationCallback, .arg = this, .flags = BLE_GATT_CHR_F_NOTIFY, @@ -39,7 +36,7 @@ AlertNotificationService::AlertNotificationService(System::SystemTask& systemTas serviceDefinition { {/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t*) &ansUuid, + .uuid = &ansUuid.u, .characteristics = characteristicDefinition}, {0}, }, @@ -123,4 +120,4 @@ void AlertNotificationService::MuteIncomingCall() { } ble_gattc_notify_custom(connectionHandle, eventHandle, om); -}
\ No newline at end of file +} diff --git a/src/components/ble/BatteryInformationService.cpp b/src/components/ble/BatteryInformationService.cpp index 7f17690..2917866 100644 --- a/src/components/ble/BatteryInformationService.cpp +++ b/src/components/ble/BatteryInformationService.cpp @@ -14,7 +14,7 @@ int BatteryInformationServiceCallback(uint16_t conn_handle, uint16_t attr_handle BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController) : batteryController {batteryController}, - characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid, + characteristicDefinition {{.uuid = &batteryLevelUuid.u, .access_cb = BatteryInformationServiceCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, @@ -23,7 +23,7 @@ BatteryInformationService::BatteryInformationService(Controllers::Battery& batte serviceDefinition { {/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t*) &batteryInformationServiceUuid, + .uuid = &batteryInformationServiceUuid.u, .characteristics = characteristicDefinition}, {0}, } { @@ -43,7 +43,7 @@ int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHand ble_gatt_access_ctxt* context) { if (attributeHandle == batteryLevelHandle) { NRF_LOG_INFO("BATTERY : handle = %d", batteryLevelHandle); - static uint8_t batteryValue = batteryController.PercentRemaining(); + uint8_t batteryValue = batteryController.PercentRemaining(); int res = os_mbuf_append(context->om, &batteryValue, 1); return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; } diff --git a/src/components/ble/CurrentTimeClient.cpp b/src/components/ble/CurrentTimeClient.cpp index c6e6831..90d1f0c 100644 --- a/src/components/ble/CurrentTimeClient.cpp +++ b/src/components/ble/CurrentTimeClient.cpp @@ -47,7 +47,7 @@ bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_ga return true; } - if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ctsServiceUuid), &service->uuid.u) == 0) { + if (service != nullptr && ble_uuid_cmp(&ctsServiceUuid.u, &service->uuid.u) == 0) { NRF_LOG_INFO("CTS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); isDiscovered = true; ctsStartHandle = service->start_handle; @@ -72,7 +72,7 @@ int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, return 0; } - if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) ¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) { + if (characteristic != nullptr && ble_uuid_cmp(¤tTimeCharacteristicUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle); isCharacteristicDiscovered = true; currentTimeHandle = characteristic->val_handle; diff --git a/src/components/ble/CurrentTimeService.cpp b/src/components/ble/CurrentTimeService.cpp index b49be39..eefb7ec 100644 --- a/src/components/ble/CurrentTimeService.cpp +++ b/src/components/ble/CurrentTimeService.cpp @@ -53,7 +53,7 @@ int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handl } CurrentTimeService::CurrentTimeService(DateTime& dateTimeController) - : characteristicDefinition {{.uuid = (ble_uuid_t*) &ctChrUuid, + : characteristicDefinition {{.uuid = &ctChrUuid.u, .access_cb = CTSCallback, .arg = this, @@ -62,7 +62,7 @@ CurrentTimeService::CurrentTimeService(DateTime& dateTimeController) serviceDefinition { {/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t*) &ctsUuid, + .uuid = &ctsUuid.u, .characteristics = characteristicDefinition}, {0}, }, diff --git a/src/components/ble/DeviceInformationService.cpp b/src/components/ble/DeviceInformationService.cpp index cf48207..778d6e3 100644 --- a/src/components/ble/DeviceInformationService.cpp +++ b/src/components/ble/DeviceInformationService.cpp @@ -56,37 +56,37 @@ int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16 DeviceInformationService::DeviceInformationService() : characteristicDefinition {{ - .uuid = (ble_uuid_t*) &manufacturerNameUuid, + .uuid = &manufacturerNameUuid.u, .access_cb = DeviceInformationCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, }, { - .uuid = (ble_uuid_t*) &modelNumberUuid, + .uuid = &modelNumberUuid.u, .access_cb = DeviceInformationCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, }, { - .uuid = (ble_uuid_t*) &serialNumberUuid, + .uuid = &serialNumberUuid.u, .access_cb = DeviceInformationCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, }, { - .uuid = (ble_uuid_t*) &fwRevisionUuid, + .uuid = &fwRevisionUuid.u, .access_cb = DeviceInformationCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, }, { - .uuid = (ble_uuid_t*) &hwRevisionUuid, + .uuid = &hwRevisionUuid.u, .access_cb = DeviceInformationCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, }, { - .uuid = (ble_uuid_t*) &swRevisionUuid, + .uuid = &swRevisionUuid.u, .access_cb = DeviceInformationCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, @@ -95,7 +95,7 @@ DeviceInformationService::DeviceInformationService() serviceDefinition { {/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t*) &deviceInfoUuid, + .uuid = &deviceInfoUuid.u, .characteristics = characteristicDefinition}, {0}, } { diff --git a/src/components/ble/DfuService.cpp b/src/components/ble/DfuService.cpp index 4179994..3d6416f 100644 --- a/src/components/ble/DfuService.cpp +++ b/src/components/ble/DfuService.cpp @@ -33,21 +33,21 @@ DfuService::DfuService(Pinetime::System::SystemTask& systemTask, bleController {bleController}, dfuImage {spiNorFlash}, characteristicDefinition {{ - .uuid = (ble_uuid_t*) &packetCharacteristicUuid, + .uuid = &packetCharacteristicUuid.u, .access_cb = DfuServiceCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, .val_handle = nullptr, }, { - .uuid = (ble_uuid_t*) &controlPointCharacteristicUuid, + .uuid = &controlPointCharacteristicUuid.u, .access_cb = DfuServiceCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY, .val_handle = nullptr, }, { - .uuid = (ble_uuid_t*) &revisionCharacteristicUuid, + .uuid = &revisionCharacteristicUuid.u, .access_cb = DfuServiceCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, @@ -60,7 +60,7 @@ DfuService::DfuService(Pinetime::System::SystemTask& systemTask, serviceDefinition { {/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t*) &serviceUuid, + .uuid = &serviceUuid.u, .characteristics = characteristicDefinition}, {0}, } { @@ -81,9 +81,9 @@ int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandl xTimerStart(timeoutTimer, 0); } - ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &packetCharacteristicUuid, nullptr, &packetCharacteristicHandle); - ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &controlPointCharacteristicUuid, nullptr, &controlPointCharacteristicHandle); - ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &revisionCharacteristicUuid, nullptr, &revisionCharacteristicHandle); + ble_gatts_find_chr(&serviceUuid.u, &packetCharacteristicUuid.u, nullptr, &packetCharacteristicHandle); + ble_gatts_find_chr(&serviceUuid.u, &controlPointCharacteristicUuid.u, nullptr, &controlPointCharacteristicHandle); + ble_gatts_find_chr(&serviceUuid.u, &revisionCharacteristicUuid.u, nullptr, &revisionCharacteristicHandle); if (attributeHandle == packetCharacteristicHandle) { if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) @@ -164,10 +164,10 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) { if ((nbPacketReceived % nbPacketsToNotify) == 0 && bytesReceived != applicationSize) { uint8_t data[5] {static_cast<uint8_t>(Opcodes::PacketReceiptNotification), - (uint8_t) (bytesReceived & 0x000000FFu), - (uint8_t) (bytesReceived >> 8u), - (uint8_t) (bytesReceived >> 16u), - (uint8_t) (bytesReceived >> 24u)}; + (uint8_t)(bytesReceived & 0x000000FFu), + (uint8_t)(bytesReceived >> 8u), + (uint8_t)(bytesReceived >> 16u), + (uint8_t)(bytesReceived >> 24u)}; NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received", bytesReceived); notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 5); } @@ -422,9 +422,9 @@ uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc; for (uint32_t i = 0; i < size; i++) { - crc = (uint8_t) (crc >> 8) | (crc << 8); + crc = (uint8_t)(crc >> 8) | (crc << 8); crc ^= p_data[i]; - crc ^= (uint8_t) (crc & 0xFF) >> 4; + crc ^= (uint8_t)(crc & 0xFF) >> 4; crc ^= (crc << 8) << 4; crc ^= ((crc & 0xFF) << 4) << 1; } diff --git a/src/components/ble/HeartRateService.cpp b/src/components/ble/HeartRateService.cpp index c556566..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<HeartRateService*>(arg); return heartRateService->OnHeartRateRequested(conn_handle, attr_handle, ctxt); } @@ -18,8 +18,8 @@ namespace { HeartRateService::HeartRateService(Pinetime::System::SystemTask& system, Controllers::HeartRateController& heartRateController) : system {system}, heartRateController {heartRateController}, - characteristicDefinition {{.uuid = (ble_uuid_t*) &heartRateMeasurementUuid, - .access_cb = HeartRateServiceServiceCallback, + characteristicDefinition {{.uuid = &heartRateMeasurementUuid.u, + .access_cb = HeartRateServiceCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, .val_handle = &heartRateMeasurementHandle}, @@ -27,7 +27,7 @@ HeartRateService::HeartRateService(Pinetime::System::SystemTask& system, Control serviceDefinition { {/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t*) &heartRateServiceUuid, + .uuid = &heartRateServiceUuid.u, .characteristics = characteristicDefinition}, {0}, } { @@ -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 <host/ble_gap.h> +#include <atomic> #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/ImmediateAlertService.cpp b/src/components/ble/ImmediateAlertService.cpp index 820d3b6..17ed1a9 100644 --- a/src/components/ble/ImmediateAlertService.cpp +++ b/src/components/ble/ImmediateAlertService.cpp @@ -32,7 +32,7 @@ ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask& syste Pinetime::Controllers::NotificationManager& notificationManager) : systemTask {systemTask}, notificationManager {notificationManager}, - characteristicDefinition {{.uuid = (ble_uuid_t*) &alertLevelUuid, + characteristicDefinition {{.uuid = &alertLevelUuid.u, .access_cb = AlertLevelCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, @@ -41,7 +41,7 @@ ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask& syste serviceDefinition { {/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t*) &immediateAlertServiceUuid, + .uuid = &immediateAlertServiceUuid.u, .characteristics = characteristicDefinition}, {0}, } { @@ -72,4 +72,4 @@ int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16 } return 0; -}
\ No newline at end of file +} diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp new file mode 100644 index 0000000..b4786ab --- /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, 0x03, 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<MotionService*>(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(uint32_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..1b4ac0a --- /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 <host/ble_gap.h> +#include <atomic> +#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(uint32_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/NavigationService.cpp b/src/components/ble/NavigationService.cpp index e1c20bf..b49148d 100644 --- a/src/components/ble/NavigationService.cpp +++ b/src/components/ble/NavigationService.cpp @@ -20,54 +20,45 @@ #include "systemtask/SystemTask.h" -int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { - auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg); - return navService->OnCommand(conn_handle, attr_handle, ctxt); -} - -Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) { - navUuid.value[14] = navId[0]; - navUuid.value[15] = navId[1]; +namespace { + // 0001yyxx-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, 0x01, 0x00}}; + } - navFlagCharUuid.value[12] = navFlagCharId[0]; - navFlagCharUuid.value[13] = navFlagCharId[1]; - navFlagCharUuid.value[14] = navId[0]; - navFlagCharUuid.value[15] = navId[1]; + // 00010000-78fc-48fe-8e23-433b3a1942d0 + constexpr ble_uuid128_t BaseUuid() { + return CharUuid(0x00, 0x00); + } - navNarrativeCharUuid.value[12] = navNarrativeCharId[0]; - navNarrativeCharUuid.value[13] = navNarrativeCharId[1]; - navNarrativeCharUuid.value[14] = navId[0]; - navNarrativeCharUuid.value[15] = navId[1]; + constexpr ble_uuid128_t navUuid {BaseUuid()}; - navManDistCharUuid.value[12] = navManDistCharId[0]; - navManDistCharUuid.value[13] = navManDistCharId[1]; - navManDistCharUuid.value[14] = navId[0]; - navManDistCharUuid.value[15] = navId[1]; + constexpr ble_uuid128_t navFlagCharUuid {CharUuid(0x01, 0x00)}; + constexpr ble_uuid128_t navNarrativeCharUuid {CharUuid(0x02, 0x00)}; + constexpr ble_uuid128_t navManDistCharUuid {CharUuid(0x03, 0x00)}; + constexpr ble_uuid128_t navProgressCharUuid {CharUuid(0x04, 0x00)}; - navProgressCharUuid.value[12] = navProgressCharId[0]; - navProgressCharUuid.value[13] = navProgressCharId[1]; - navProgressCharUuid.value[14] = navId[0]; - navProgressCharUuid.value[15] = navId[1]; + int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg); + return navService->OnCommand(conn_handle, attr_handle, ctxt); + } +} // namespace +Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) { characteristicDefinition[0] = { - .uuid = (ble_uuid_t*) (&navFlagCharUuid), .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; - - characteristicDefinition[1] = {.uuid = (ble_uuid_t*) (&navNarrativeCharUuid), - .access_cb = NAVCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; - characteristicDefinition[2] = {.uuid = (ble_uuid_t*) (&navManDistCharUuid), - .access_cb = NAVCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; - characteristicDefinition[3] = {.uuid = (ble_uuid_t*) (&navProgressCharUuid), - .access_cb = NAVCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; + .uuid = &navFlagCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; + + characteristicDefinition[1] = { + .uuid = &navNarrativeCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; + characteristicDefinition[2] = { + .uuid = &navManDistCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; + characteristicDefinition[3] = { + .uuid = &navProgressCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; characteristicDefinition[4] = {0}; - serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &navUuid, .characteristics = characteristicDefinition}; + serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &navUuid.u, .characteristics = characteristicDefinition}; serviceDefinition[1] = {0}; m_progress = 0; @@ -90,13 +81,13 @@ int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, ui data[notifSize] = '\0'; os_mbuf_copydata(ctxt->om, 0, notifSize, data); char* s = (char*) &data[0]; - if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navFlagCharUuid) == 0) { + if (ble_uuid_cmp(ctxt->chr->uuid, &navFlagCharUuid.u) == 0) { m_flag = s; - } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navNarrativeCharUuid) == 0) { + } else if (ble_uuid_cmp(ctxt->chr->uuid, &navNarrativeCharUuid.u) == 0) { m_narrative = s; - } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navManDistCharUuid) == 0) { + } else if (ble_uuid_cmp(ctxt->chr->uuid, &navManDistCharUuid.u) == 0) { m_manDist = s; - } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navProgressCharUuid) == 0) { + } else if (ble_uuid_cmp(ctxt->chr->uuid, &navProgressCharUuid.u) == 0) { m_progress = data[0]; } } diff --git a/src/components/ble/NavigationService.h b/src/components/ble/NavigationService.h index 5aab263..c0c77f3 100644 --- a/src/components/ble/NavigationService.h +++ b/src/components/ble/NavigationService.h @@ -26,10 +26,6 @@ #undef max #undef min -// c7e60000-78fc-48fe-8e23-433b3a1942d0 -#define NAVIGATION_SERVICE_UUID_BASE \ - { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 } - namespace Pinetime { namespace System { class SystemTask; @@ -53,19 +49,6 @@ namespace Pinetime { int getProgress(); private: - static constexpr uint8_t navId[2] = {0x01, 0x00}; - static constexpr uint8_t navFlagCharId[2] = {0x01, 0x00}; - static constexpr uint8_t navNarrativeCharId[2] = {0x02, 0x00}; - static constexpr uint8_t navManDistCharId[2] = {0x03, 0x00}; - static constexpr uint8_t navProgressCharId[2] = {0x04, 0x00}; - - ble_uuid128_t navUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE}; - - ble_uuid128_t navFlagCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE}; - ble_uuid128_t navNarrativeCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE}; - ble_uuid128_t navManDistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE}; - ble_uuid128_t navProgressCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE}; - struct ble_gatt_chr_def characteristicDefinition[5]; struct ble_gatt_svc_def serviceDefinition[2]; diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 879421e..1bcae1b 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); @@ -215,6 +218,19 @@ 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); + 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; case BLE_GAP_EVENT_MTU: 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/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp index 0756d38..e9c5d87 100644 --- a/src/components/datetime/DateTimeController.cpp +++ b/src/components/datetime/DateTimeController.cpp @@ -5,6 +5,12 @@ using namespace Pinetime::Controllers; +namespace { + char const* DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"}; + char const* MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; + char const* MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +} + void DateTime::SetCurrentTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> t) { this->currentDateTime = t; UpdateTime(previousSystickCounter); // Update internal state without updating the time @@ -80,48 +86,18 @@ void DateTime::UpdateTime(uint32_t systickCounter) { } const char* DateTime::MonthShortToString() { - return DateTime::MonthsString[static_cast<uint8_t>(month)]; -} - -const char* DateTime::MonthShortToStringLow() { - return DateTime::MonthsStringLow[static_cast<uint8_t>(month)]; -} - -const char* DateTime::MonthsToStringLow() { - return DateTime::MonthsLow[static_cast<uint8_t>(month)]; -} - -const char* DateTime::DayOfWeekToString() { - return DateTime::DaysString[static_cast<uint8_t>(dayOfWeek)]; + return MonthsString[static_cast<uint8_t>(month)]; } const char* DateTime::DayOfWeekShortToString() { - return DateTime::DaysStringShort[static_cast<uint8_t>(dayOfWeek)]; + return DaysStringShort[static_cast<uint8_t>(dayOfWeek)]; } -const char* DateTime::DayOfWeekToStringLow() { - return DateTime::DaysStringLow[static_cast<uint8_t>(dayOfWeek)]; -} - -const char* DateTime::DayOfWeekShortToStringLow() { - return DateTime::DaysStringShortLow[static_cast<uint8_t>(dayOfWeek)]; +const char* DateTime::MonthShortToStringLow(Months month) { + return MonthsStringLow[static_cast<uint8_t>(month)]; } void DateTime::Register(Pinetime::System::SystemTask* systemTask) { this->systemTask = systemTask; } -char const* DateTime::DaysStringLow[] = {"--", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; - -char const* DateTime::DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; - -char const* DateTime::DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"}; - -char const* DateTime::DaysString[] = {"--", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"}; - -char const* DateTime::MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; - -char const* DateTime::MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - -char const* DateTime::MonthsLow[] = { - "--", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
\ No newline at end of file diff --git a/src/components/datetime/DateTimeController.h b/src/components/datetime/DateTimeController.h index 061c303..77ed68e 100644 --- a/src/components/datetime/DateTimeController.h +++ b/src/components/datetime/DateTimeController.h @@ -59,12 +59,8 @@ namespace Pinetime { } const char* MonthShortToString(); - const char* MonthShortToStringLow(); - const char* MonthsToStringLow(); - const char* DayOfWeekToString(); const char* DayOfWeekShortToString(); - const char* DayOfWeekToStringLow(); - const char* DayOfWeekShortToStringLow(); + static const char* MonthShortToStringLow(Months month); std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const { return currentDateTime; @@ -91,14 +87,6 @@ namespace Pinetime { bool isMidnightAlreadyNotified = false; System::SystemTask* systemTask = nullptr; - - static char const* DaysString[]; - static char const* DaysStringShort[]; - static char const* DaysStringLow[]; - static char const* DaysStringShortLow[]; - static char const* MonthsString[]; - static char const* MonthsStringLow[]; - static char const* MonthsLow[]; }; } -}
\ No newline at end of file +} diff --git a/src/components/fs/FS.h b/src/components/fs/FS.h index 1f2eb7e..75ba16c 100644 --- a/src/components/fs/FS.h +++ b/src/components/fs/FS.h @@ -53,7 +53,7 @@ namespace Pinetime { * */ static constexpr size_t startAddress = 0x0B4000; - static constexpr size_t size = 0x3C0000; + static constexpr size_t size = 0x34C000; static constexpr size_t blockSize = 4096; bool resourcesValid = false; 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 <cstdint> #include <drivers/Bma421.h> +#include <components/ble/MotionService.h> 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/displayapp/Apps.h b/src/displayapp/Apps.h index e3aca8c..d340efe 100644 --- a/src/displayapp/Apps.h +++ b/src/displayapp/Apps.h @@ -32,7 +32,10 @@ namespace Pinetime { SettingDisplay, SettingWakeUp, SettingSteps, - SettingPineTimeStyle + SettingPineTimeStyle, + SettingSetDate, + SettingSetTime, + Error, }; } } diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 9d47310..13ee004 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -29,6 +29,7 @@ #include "displayapp/screens/FlashLight.h" #include "displayapp/screens/BatteryInfo.h" #include "displayapp/screens/Steps.h" +#include "displayapp/screens/Error.h" #include "drivers/Cst816s.h" #include "drivers/St7789.h" @@ -44,6 +45,8 @@ #include "displayapp/screens/settings/SettingDisplay.h" #include "displayapp/screens/settings/SettingSteps.h" #include "displayapp/screens/settings/SettingPineTimeStyle.h" +#include "displayapp/screens/settings/SettingSetDate.h" +#include "displayapp/screens/settings/SettingSetTime.h" #include "libs/lv_conf.h" @@ -110,11 +113,16 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, touchHandler {touchHandler} { } -void DisplayApp::Start() { +void DisplayApp::Start(System::BootErrors error) { msgQueue = xQueueCreate(queueSize, itemSize); - // Start clock when smartwatch boots - LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None); + bootError = error; + + if (error == System::BootErrors::TouchController) { + LoadApp(Apps::Error, DisplayApp::FullRefreshDirections::None); + } else { + LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None); + } if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 800, this, 0, &taskHandle)) { APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); @@ -141,19 +149,15 @@ void DisplayApp::InitHw() { void DisplayApp::Refresh() { TickType_t queueTimeout; - TickType_t delta; switch (state) { case States::Idle: - IdleState(); queueTimeout = portMAX_DELAY; break; case States::Running: - RunningState(); - delta = xTaskGetTickCount() - lastWakeTime; - if (delta > LV_DISP_DEF_REFR_PERIOD) { - delta = LV_DISP_DEF_REFR_PERIOD; + if (!currentScreen->IsRunning()) { + LoadApp(returnToApp, returnDirection); } - queueTimeout = LV_DISP_DEF_REFR_PERIOD - delta; + queueTimeout = lv_task_handler(); break; default: queueTimeout = portMAX_DELAY; @@ -161,9 +165,7 @@ void DisplayApp::Refresh() { } Messages msg; - bool messageReceived = xQueueReceive(msgQueue, &msg, queueTimeout); - lastWakeTime = xTaskGetTickCount(); - if (messageReceived) { + if (xQueueReceive(msgQueue, &msg, queueTimeout)) { switch (msg) { case Messages::DimScreen: // Backup brightness is the brightness to return to after dimming or sleeping @@ -258,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); @@ -279,13 +295,6 @@ void DisplayApp::Refresh() { } } -void DisplayApp::RunningState() { - if (!currentScreen->IsRunning()) { - LoadApp(returnToApp, returnDirection); - } - lv_task_handler(); -} - void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) { nextApp = app; nextDirection = direction; @@ -322,6 +331,11 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) motionController); break; + case Apps::Error: + currentScreen = std::make_unique<Screens::Error>(this, bootError); + ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); + break; + case Apps::FirmwareValidation: currentScreen = std::make_unique<Screens::FirmwareValidation>(this, validator); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); @@ -378,6 +392,14 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentScreen = std::make_unique<Screens::SettingSteps>(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; + case Apps::SettingSetDate: + currentScreen = std::make_unique<Screens::SettingSetDate>(this, dateTimeController); + ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); + break; + case Apps::SettingSetTime: + currentScreen = std::make_unique<Screens::SettingSetTime>(this, dateTimeController); + ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); + break; case Apps::SettingPineTimeStyle: currentScreen = std::make_unique<Screens::SettingPineTimeStyle>(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); @@ -388,12 +410,12 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) break; case Apps::SysInfo: currentScreen = std::make_unique<Screens::SystemInfo>( - this, dateTimeController, batteryController, brightnessController, bleController, watchdog, motionController); + this, dateTimeController, batteryController, brightnessController, bleController, watchdog, motionController, touchPanel); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::FlashLight: currentScreen = std::make_unique<Screens::FlashLight>(this, *systemTask, brightnessController); - ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); + ReturnApp(Apps::QuickSettings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::StopWatch: currentScreen = std::make_unique<Screens::StopWatch>(this, *systemTask); @@ -430,9 +452,6 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentApp = app; } -void DisplayApp::IdleState() { -} - void DisplayApp::PushMessage(Messages msg) { if (in_isr()) { BaseType_t xHigherPriorityTaskWoken; diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index 4254523..a87cab0 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -18,6 +18,7 @@ #include "touchhandler/TouchHandler.h" #include "Messages.h" +#include "BootErrors.h" namespace Pinetime { @@ -61,7 +62,7 @@ namespace Pinetime { Pinetime::Controllers::TimerController& timerController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::TouchHandler& touchHandler); - void Start(); + void Start(System::BootErrors error); void PushMessage(Display::Messages msg); void StartApp(Apps app, DisplayApp::FullRefreshDirections direction); @@ -107,8 +108,6 @@ namespace Pinetime { TouchEvents returnTouchEvent = TouchEvents::None; TouchEvents GetGesture(); - void RunningState(); - void IdleState(); static void Process(void* instance); void InitHw(); void Refresh(); @@ -118,7 +117,7 @@ namespace Pinetime { Apps nextApp = Apps::None; DisplayApp::FullRefreshDirections nextDirection; - TickType_t lastWakeTime; + System::BootErrors bootError; }; } } diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h index 425e0ac..7286815 100644 --- a/src/displayapp/DisplayAppRecovery.h +++ b/src/displayapp/DisplayAppRecovery.h @@ -10,6 +10,7 @@ #include <date/date.h> #include <drivers/Watchdog.h> #include <components/motor/MotorController.h> +#include "BootErrors.h" #include "TouchEvents.h" #include "Apps.h" #include "Messages.h" @@ -58,6 +59,7 @@ namespace Pinetime { Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::TouchHandler& touchHandler); void Start(); + void Start(Pinetime::System::BootErrors){ Start(); }; void PushMessage(Pinetime::Applications::Display::Messages msg); void Register(Pinetime::System::SystemTask* systemTask); 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/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp index 959cb0b..6b45a36 100644 --- a/src/displayapp/screens/Alarm.cpp +++ b/src/displayapp/screens/Alarm.cpp @@ -120,10 +120,7 @@ void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) { return; } if (obj == btnMessage) { - lv_obj_del(txtMessage); - lv_obj_del(btnMessage); - txtMessage = nullptr; - btnMessage = nullptr; + HideInfo(); return; } // If any other button was pressed, disable the alarm @@ -174,6 +171,14 @@ void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) { } } +bool Alarm::OnButtonPushed() { + if (txtMessage != nullptr && btnMessage != nullptr) { + HideInfo(); + return true; + } + return false; +} + void Alarm::UpdateAlarmTime() { lv_label_set_text_fmt(time, "%02d:%02d", alarmHours, alarmMinutes); alarmController.SetAlarmTime(alarmHours, alarmMinutes); @@ -224,6 +229,12 @@ void Alarm::ShowInfo() { } } +void Alarm::HideInfo() { + lv_obj_del(btnMessage); + txtMessage = nullptr; + btnMessage = nullptr; +} + void Alarm::SetRecurButtonState() { using Pinetime::Controllers::AlarmController; switch (alarmController.Recurrence()) { diff --git a/src/displayapp/screens/Alarm.h b/src/displayapp/screens/Alarm.h index abf97eb..487ba1d 100644 --- a/src/displayapp/screens/Alarm.h +++ b/src/displayapp/screens/Alarm.h @@ -31,6 +31,7 @@ namespace Pinetime { ~Alarm() override; void SetAlerting(); void OnButtonEvent(lv_obj_t* obj, lv_event_t event); + bool OnButtonPushed() override; private: bool running; @@ -39,13 +40,16 @@ namespace Pinetime { Controllers::AlarmController& alarmController; lv_obj_t *time, *btnEnable, *txtEnable, *btnMinutesUp, *btnMinutesDown, *btnHoursUp, *btnHoursDown, *txtMinUp, *txtMinDown, - *txtHrUp, *txtHrDown, *btnRecur, *txtRecur, *btnMessage, *txtMessage, *btnInfo, *txtInfo; + *txtHrUp, *txtHrDown, *btnRecur, *txtRecur, *btnInfo, *txtInfo; + lv_obj_t* txtMessage = nullptr; + lv_obj_t* btnMessage = nullptr; enum class EnableButtonState { On, Off, Alerting }; void SetEnableButtonState(); void SetRecurButtonState(); void SetAlarm(); void ShowInfo(); + void HideInfo(); void ToggleRecurrence(); void UpdateAlarmTime(); }; diff --git a/src/displayapp/screens/BatteryInfo.cpp b/src/displayapp/screens/BatteryInfo.cpp index ad9af15..44ea7f5 100644 --- a/src/displayapp/screens/BatteryInfo.cpp +++ b/src/displayapp/screens/BatteryInfo.cpp @@ -58,7 +58,7 @@ void BatteryInfo::Refresh() { batteryPercent = batteryController.PercentRemaining(); batteryVoltage = batteryController.Voltage(); - if (batteryController.IsCharging() and batteryPercent < 100) { + if (batteryController.IsCharging()) { lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED); lv_label_set_text_static(status, "Charging"); } else if (batteryPercent == 100) { diff --git a/src/displayapp/screens/Error.cpp b/src/displayapp/screens/Error.cpp new file mode 100644 index 0000000..75946ab --- /dev/null +++ b/src/displayapp/screens/Error.cpp @@ -0,0 +1,50 @@ +#include "Error.h" + +using namespace Pinetime::Applications::Screens; + +namespace { + void ButtonEventCallback(lv_obj_t* obj, lv_event_t /*event*/) { + auto* errorScreen = static_cast<Error*>(obj->user_data); + errorScreen->ButtonEventHandler(); + } +} + +Error::Error(Pinetime::Applications::DisplayApp* app, System::BootErrors error) + : Screen(app) { + + lv_obj_t* warningLabel = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(warningLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); + lv_label_set_text_static(warningLabel, "Warning"); + lv_obj_align(warningLabel, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0); + + lv_obj_t* causeLabel = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_long_mode(causeLabel, LV_LABEL_LONG_BREAK); + lv_obj_set_width(causeLabel, LV_HOR_RES); + lv_obj_align(causeLabel, warningLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); + + if (error == System::BootErrors::TouchController) { + lv_label_set_text_static(causeLabel, "Touch controller error detected."); + } + + lv_obj_t* tipLabel = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_long_mode(tipLabel, LV_LABEL_LONG_BREAK); + lv_obj_set_width(tipLabel, LV_HOR_RES); + lv_label_set_text_static(tipLabel, "If you encounter problems and your device is under warranty, contact the devices seller."); + lv_obj_align(tipLabel, causeLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); + + btnOk = lv_btn_create(lv_scr_act(), nullptr); + btnOk->user_data = this; + lv_obj_set_event_cb(btnOk, ButtonEventCallback); + lv_obj_set_size(btnOk, LV_HOR_RES, 50); + lv_obj_align(btnOk, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); + lv_obj_set_style_local_value_str(btnOk, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Proceed"); + lv_obj_set_style_local_bg_color(btnOk, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); +} + +void Error::ButtonEventHandler() { + running = false; +} + +Error::~Error() { + lv_obj_clean(lv_scr_act()); +} diff --git a/src/displayapp/screens/Error.h b/src/displayapp/screens/Error.h new file mode 100644 index 0000000..20dde7e --- /dev/null +++ b/src/displayapp/screens/Error.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Screen.h" +#include "BootErrors.h" +#include <lvgl/lvgl.h> + +namespace Pinetime { + namespace Applications { + namespace Screens { + class Error : public Screen { + public: + Error(DisplayApp* app, System::BootErrors error); + ~Error() override; + + void ButtonEventHandler(); + private: + lv_obj_t* btnOk; + }; + } + } +} diff --git a/src/displayapp/screens/FlashLight.cpp b/src/displayapp/screens/FlashLight.cpp index 4bc5b55..dcb31a7 100644 --- a/src/displayapp/screens/FlashLight.cpp +++ b/src/displayapp/screens/FlashLight.cpp @@ -5,30 +5,41 @@ using namespace Pinetime::Applications::Screens; namespace { - static void event_handler(lv_obj_t* obj, lv_event_t event) { - FlashLight* screen = static_cast<FlashLight*>(obj->user_data); + void event_handler(lv_obj_t* obj, lv_event_t event) { + auto* screen = static_cast<FlashLight*>(obj->user_data); screen->OnClickEvent(obj, event); } } FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app, System::SystemTask& systemTask, - Controllers::BrightnessController& brightness) + Controllers::BrightnessController& brightnessController) : Screen(app), systemTask {systemTask}, - brightness {brightness} + brightnessController {brightnessController} { - brightness.Backup(); - brightness.Set(Controllers::BrightnessController::Levels::High); - // Set the background - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); + brightnessController.Backup(); - flashLight = lv_label_create(lv_scr_act(), NULL); - lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + brightnessLevel = brightnessController.Level(); + + flashLight = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48); lv_label_set_text_static(flashLight, Symbols::highlight); - lv_obj_align(flashLight, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(flashLight, nullptr, LV_ALIGN_CENTER, 0, 0); + + for (auto & i : indicators) { + i = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_size(i, 15, 10); + lv_obj_set_style_local_border_width(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 2); + } + + lv_obj_align(indicators[1], flashLight, LV_ALIGN_OUT_BOTTOM_MID, 0, 5); + lv_obj_align(indicators[0], indicators[1], LV_ALIGN_OUT_LEFT_MID, -8, 0); + lv_obj_align(indicators[2], indicators[1], LV_ALIGN_OUT_RIGHT_MID, 8, 0); + + SetIndicators(); + SetColors(); backgroundAction = lv_label_create(lv_scr_act(), nullptr); lv_label_set_long_mode(backgroundAction, LV_LABEL_LONG_CROP); @@ -44,27 +55,80 @@ FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app, FlashLight::~FlashLight() { lv_obj_clean(lv_scr_act()); - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - brightness.Restore(); + lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); + brightnessController.Restore(); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); } -void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) { - if (obj == backgroundAction) { - if (event == LV_EVENT_CLICKED) { - isOn = !isOn; - - if (isOn) { - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); - lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - } else { - lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); - } +void FlashLight::SetColors() { + if (isOn) { + lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + for (auto & i : indicators) { + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_WHITE); + lv_obj_set_style_local_border_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); } + } else { + lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + for (auto & i : indicators) { + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_BLACK); + lv_obj_set_style_local_border_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + } + } +} + +void FlashLight::SetIndicators() { + using namespace Pinetime::Controllers; + + if (brightnessLevel == BrightnessController::Levels::High) { + lv_obj_set_state(indicators[1], LV_STATE_DEFAULT); + lv_obj_set_state(indicators[2], LV_STATE_DEFAULT); + } else if (brightnessLevel == BrightnessController::Levels::Medium) { + lv_obj_set_state(indicators[1], LV_STATE_DEFAULT); + lv_obj_set_state(indicators[2], LV_STATE_DISABLED); + } else { + lv_obj_set_state(indicators[1], LV_STATE_DISABLED); + lv_obj_set_state(indicators[2], LV_STATE_DISABLED); + } +} + +void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) { + if (obj == backgroundAction && event == LV_EVENT_CLICKED) { + isOn = !isOn; + SetColors(); } } bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + using namespace Pinetime::Controllers; + + if (event == TouchEvents::SwipeLeft) { + if (brightnessLevel == BrightnessController::Levels::High) { + brightnessLevel = BrightnessController::Levels::Medium; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } else if (brightnessLevel == BrightnessController::Levels::Medium) { + brightnessLevel = BrightnessController::Levels::Low; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } + return true; + } + if (event == TouchEvents::SwipeRight) { + if (brightnessLevel == BrightnessController::Levels::Low) { + brightnessLevel = BrightnessController::Levels::Medium; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } else if (brightnessLevel == BrightnessController::Levels::Medium) { + brightnessLevel = BrightnessController::Levels::High; + brightnessController.Set(brightnessLevel); + SetIndicators(); + } + return true; + } + return false; } diff --git a/src/displayapp/screens/FlashLight.h b/src/displayapp/screens/FlashLight.h index 7f5ca6c..f2c65bb 100644 --- a/src/displayapp/screens/FlashLight.h +++ b/src/displayapp/screens/FlashLight.h @@ -1,10 +1,10 @@ #pragma once -#include <cstdint> #include "Screen.h" -#include <lvgl/lvgl.h> -#include "systemtask/SystemTask.h" #include "components/brightness/BrightnessController.h" +#include "systemtask/SystemTask.h" +#include <cstdint> +#include <lvgl/lvgl.h> namespace Pinetime { @@ -20,12 +20,18 @@ namespace Pinetime { void OnClickEvent(lv_obj_t* obj, lv_event_t event); private: + void SetIndicators(); + void SetColors(); + Pinetime::System::SystemTask& systemTask; - Controllers::BrightnessController& brightness; + Controllers::BrightnessController& brightnessController; + + Controllers::BrightnessController::Levels brightnessLevel; lv_obj_t* flashLight; lv_obj_t* backgroundAction; - bool isOn = true; + lv_obj_t* indicators[3]; + bool isOn = false; }; } } diff --git a/src/displayapp/screens/Paddle.cpp b/src/displayapp/screens/Paddle.cpp index 3b6d60e..26c2368 100644 --- a/src/displayapp/screens/Paddle.cpp +++ b/src/displayapp/screens/Paddle.cpp @@ -47,8 +47,8 @@ void Paddle::Refresh() { dy *= -1; } - // checks if it has touched the side (left side) - if (ballX >= LV_VER_RES - ballSize - 1) { + // checks if it has touched the side (right side) + if (ballX >= LV_HOR_RES - ballSize - 1) { dx *= -1; } diff --git a/src/displayapp/screens/PineTimeStyle.cpp b/src/displayapp/screens/PineTimeStyle.cpp index 7a712f4..fa88d45 100644 --- a/src/displayapp/screens/PineTimeStyle.cpp +++ b/src/displayapp/screens/PineTimeStyle.cpp @@ -1,5 +1,5 @@ /* - * This file is part of the Infinitime distribution (https://github.com/JF002/Infinitime). + * This file is part of the Infinitime distribution (https://github.com/InfiniTimeOrg/Infinitime). * Copyright (c) 2021 Kieran Cawthray. * * This program is free software: you can redistribute it and/or modify @@ -100,10 +100,7 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app, lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); lv_label_set_text(batteryIcon, Symbols::batteryFull); lv_obj_align(batteryIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 2); - - batteryPlug = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_align(batteryPlug, sidebar, LV_ALIGN_IN_TOP_MID, 0, 2); + lv_obj_set_auto_realign(batteryIcon, true); bleIcon = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); @@ -205,18 +202,24 @@ PineTimeStyle::~PineTimeStyle() { lv_obj_clean(lv_scr_act()); } +void PineTimeStyle::SetBatteryIcon() { + auto batteryPercent = batteryPercentRemaining.Get(); + lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); +} + void PineTimeStyle::Refresh() { - batteryPercentRemaining = batteryController.PercentRemaining(); - if (batteryPercentRemaining.IsUpdated()) { - auto batteryPercent = batteryPercentRemaining.Get(); - if (batteryController.IsCharging()) { - auto isCharging = batteryController.IsCharging() || batteryController.IsPowerPresent(); - lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging)); - lv_obj_realign(batteryPlug); - lv_label_set_text(batteryIcon, ""); + isCharging = batteryController.IsCharging(); + if (isCharging.IsUpdated()) { + if (isCharging.Get()) { + lv_label_set_text(batteryIcon, Symbols::plug); } else { - lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); - lv_label_set_text(batteryPlug, ""); + SetBatteryIcon(); + } + } + if (!isCharging.Get()) { + batteryPercentRemaining = batteryController.PercentRemaining(); + if (batteryPercentRemaining.IsUpdated()) { + SetBatteryIcon(); } } diff --git a/src/displayapp/screens/PineTimeStyle.h b/src/displayapp/screens/PineTimeStyle.h index cb74ead..ba47380 100644 --- a/src/displayapp/screens/PineTimeStyle.h +++ b/src/displayapp/screens/PineTimeStyle.h @@ -41,6 +41,7 @@ namespace Pinetime { uint8_t currentDay = 0; DirtyValue<uint8_t> batteryPercentRemaining {}; + DirtyValue<bool> isCharging {}; DirtyValue<bool> bleState {}; DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {}; DirtyValue<bool> motionSensorOk {}; @@ -58,7 +59,6 @@ namespace Pinetime { lv_obj_t* backgroundLabel; lv_obj_t* batteryIcon; lv_obj_t* bleIcon; - lv_obj_t* batteryPlug; lv_obj_t* calendarOuter; lv_obj_t* calendarInner; lv_obj_t* calendarBar1; @@ -76,6 +76,8 @@ namespace Pinetime { Controllers::Settings& settingsController; Controllers::MotionController& motionController; + void SetBatteryIcon(); + lv_task_t* taskRefresh; }; } diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index b7a4fc6..dd223b2 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -33,7 +33,8 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::Ble& bleController, Pinetime::Drivers::WatchdogView& watchdog, - Pinetime::Controllers::MotionController& motionController) + Pinetime::Controllers::MotionController& motionController, + Pinetime::Drivers::Cst816S& touchPanel) : Screen(app), dateTimeController {dateTimeController}, batteryController {batteryController}, @@ -41,6 +42,7 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app, bleController {bleController}, watchdog {watchdog}, motionController{motionController}, + touchPanel{touchPanel}, screens {app, 0, {[this]() -> std::unique_ptr<Screen> { @@ -141,7 +143,8 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() { "#444444 Battery# %d%%/%03imV\n" "#444444 Backlight# %s\n" "#444444 Last reset# %s\n" - "#444444 Accel.# %s\n", + "#444444 Accel.# %s\n" + "#444444 Touch.# %x.%x.%x\n", dateTimeController.Day(), static_cast<uint8_t>(dateTimeController.Month()), dateTimeController.Year(), @@ -156,7 +159,10 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() { batteryController.Voltage(), brightnessController.ToString(), resetReason, - ToString(motionController.DeviceType())); + ToString(motionController.DeviceType()), + touchPanel.GetChipId(), + touchPanel.GetVendorId(), + touchPanel.GetFwVersion()); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::make_unique<Screens::Label>(1, 5, app, label); } @@ -200,11 +206,14 @@ bool SystemInfo::sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) { } std::unique_ptr<Screen> SystemInfo::CreateScreen4() { - TaskStatus_t tasksStatus[10]; - lv_obj_t* infoTask = lv_table_create(lv_scr_act(), NULL); + static constexpr uint8_t maxTaskCount = 9; + TaskStatus_t tasksStatus[maxTaskCount]; + + lv_obj_t* infoTask = lv_table_create(lv_scr_act(), nullptr); lv_table_set_col_cnt(infoTask, 4); - lv_table_set_row_cnt(infoTask, 8); - lv_obj_set_pos(infoTask, 0, 10); + lv_table_set_row_cnt(infoTask, maxTaskCount + 1); + lv_obj_set_style_local_pad_all(infoTask, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, 0); + lv_obj_set_style_local_border_color(infoTask, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, LV_COLOR_GRAY); lv_table_set_cell_value(infoTask, 0, 0, "#"); lv_table_set_col_width(infoTask, 0, 30); @@ -215,38 +224,40 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen4() { lv_table_set_cell_value(infoTask, 0, 3, "Free"); lv_table_set_col_width(infoTask, 3, 90); - auto nb = uxTaskGetSystemState(tasksStatus, sizeof(tasksStatus) / sizeof(tasksStatus[0]), nullptr); + auto nb = uxTaskGetSystemState(tasksStatus, maxTaskCount, nullptr); std::sort(tasksStatus, tasksStatus + nb, sortById); - for (uint8_t i = 0; i < nb && i < 7; i++) { + 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<Screens::Label>(3, 5, app, infoTask); } @@ -261,7 +272,8 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen5() { "Public License v3\n" "#444444 Source code#\n" "#FFFF00 https://github.com/#\n" - "#FFFF00 JF002/InfiniTime#"); + "#FFFF00 InfiniTimeOrg/#\n" + "#FFFF00 InfiniTime#"); lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::make_unique<Screens::Label>(4, 5, app, label); diff --git a/src/displayapp/screens/SystemInfo.h b/src/displayapp/screens/SystemInfo.h index 5eb7054..bfcc3aa 100644 --- a/src/displayapp/screens/SystemInfo.h +++ b/src/displayapp/screens/SystemInfo.h @@ -28,7 +28,8 @@ namespace Pinetime { Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::Ble& bleController, Pinetime::Drivers::WatchdogView& watchdog, - Pinetime::Controllers::MotionController& motionController); + Pinetime::Controllers::MotionController& motionController, + Pinetime::Drivers::Cst816S& touchPanel); ~SystemInfo() override; bool OnTouchEvent(TouchEvents event) override; @@ -39,6 +40,7 @@ namespace Pinetime { Pinetime::Controllers::Ble& bleController; Pinetime::Drivers::WatchdogView& watchdog; Pinetime::Controllers::MotionController& motionController; + Pinetime::Drivers::Cst816S& touchPanel; ScreenList<5> screens; 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 <lvgl/lvgl.h> -#include <string> -#include <charconv> #include <array> -#include <vector> +#include <cstdio> +#include <cstdlib> +#include <lvgl/lvgl.h> #include <utility> +#include <vector> 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, ""); } diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp index 75e35c1..53e7faf 100644 --- a/src/displayapp/screens/WatchFaceAnalog.cpp +++ b/src/displayapp/screens/WatchFaceAnalog.cpp @@ -68,6 +68,7 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app, batteryIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(batteryIcon, Symbols::batteryHalf); lv_obj_align(batteryIcon, NULL, LV_ALIGN_IN_TOP_RIGHT, 0, 0); + lv_obj_set_auto_realign(batteryIcon, true); notificationIcon = lv_label_create(lv_scr_act(), NULL); lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00)); @@ -176,11 +177,31 @@ void WatchFaceAnalog::UpdateClock() { } } +void WatchFaceAnalog::SetBatteryIcon() { + auto batteryPercent = batteryPercentRemaining.Get(); + if (batteryPercent == 100) { + lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); + } else { + lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + } + lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); +} + void WatchFaceAnalog::Refresh() { - batteryPercentRemaining = batteryController.PercentRemaining(); - if (batteryPercentRemaining.IsUpdated()) { - auto batteryPercent = batteryPercentRemaining.Get(); - lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); + isCharging = batteryController.IsCharging(); + if (isCharging.IsUpdated()) { + if (isCharging.Get()) { + lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + lv_label_set_text(batteryIcon, Symbols::plug); + } else { + SetBatteryIcon(); + } + } + if (!isCharging.Get()) { + batteryPercentRemaining = batteryController.PercentRemaining(); + if (batteryPercentRemaining.IsUpdated()) { + SetBatteryIcon(); + } } notificationState = notificationManager.AreNewNotificationsAvailable(); diff --git a/src/displayapp/screens/WatchFaceAnalog.h b/src/displayapp/screens/WatchFaceAnalog.h index 406f4d5..001414a 100644 --- a/src/displayapp/screens/WatchFaceAnalog.h +++ b/src/displayapp/screens/WatchFaceAnalog.h @@ -49,6 +49,7 @@ namespace Pinetime { uint8_t currentDay = 0; DirtyValue<uint8_t> batteryPercentRemaining {0}; + DirtyValue<bool> isCharging {}; DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime; DirtyValue<bool> notificationState {false}; @@ -81,6 +82,7 @@ namespace Pinetime { Controllers::Settings& settingsController; void UpdateClock(); + void SetBatteryIcon(); lv_task_t* taskRefresh; }; diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index 58ab619..2ecab60 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -102,12 +102,20 @@ WatchFaceDigital::~WatchFaceDigital() { } void WatchFaceDigital::Refresh() { + powerPresent = batteryController.IsPowerPresent(); + if (powerPresent.IsUpdated()) { + lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(powerPresent.Get())); + } + batteryPercentRemaining = batteryController.PercentRemaining(); if (batteryPercentRemaining.IsUpdated()) { auto batteryPercent = batteryPercentRemaining.Get(); + if (batteryPercent == 100) { + lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); + } else { + lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + } lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); - auto isCharging = batteryController.IsCharging() or batteryController.IsPowerPresent(); - lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging)); } bleState = bleController.IsConnected(); diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h index 48dc137..e27545f 100644 --- a/src/displayapp/screens/WatchFaceDigital.h +++ b/src/displayapp/screens/WatchFaceDigital.h @@ -44,6 +44,7 @@ namespace Pinetime { uint8_t currentDay = 0; DirtyValue<uint8_t> batteryPercentRemaining {}; + DirtyValue<bool> powerPresent {}; DirtyValue<bool> bleState {}; DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {}; DirtyValue<bool> motionSensorOk {}; diff --git a/src/displayapp/screens/settings/QuickSettings.cpp b/src/displayapp/screens/settings/QuickSettings.cpp index 691c40c..dd62607 100644 --- a/src/displayapp/screens/settings/QuickSettings.cpp +++ b/src/displayapp/screens/settings/QuickSettings.cpp @@ -131,7 +131,7 @@ void QuickSettings::OnButtonEvent(lv_obj_t* object, lv_event_t event) { if (object == btn2 && event == LV_EVENT_CLICKED) { running = false; - app->StartApp(Apps::FlashLight, DisplayApp::FullRefreshDirections::None); + app->StartApp(Apps::FlashLight, DisplayApp::FullRefreshDirections::Up); } else if (object == btn1 && event == LV_EVENT_CLICKED) { diff --git a/src/displayapp/screens/settings/SettingSetDate.cpp b/src/displayapp/screens/settings/SettingSetDate.cpp new file mode 100644 index 0000000..ba3413e --- /dev/null +++ b/src/displayapp/screens/settings/SettingSetDate.cpp @@ -0,0 +1,198 @@ +#include "SettingSetDate.h" +#include <lvgl/lvgl.h> +#include <hal/nrf_rtc.h> +#include <nrf_log.h> +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Symbols.h" + +using namespace Pinetime::Applications::Screens; + +namespace { + constexpr int16_t POS_X_DAY = -72; + constexpr int16_t POS_X_MONTH = 0; + constexpr int16_t POS_X_YEAR = 72; + constexpr int16_t POS_Y_PLUS = -50; + constexpr int16_t POS_Y_TEXT = -6; + constexpr int16_t POS_Y_MINUS = 40; + + void event_handler(lv_obj_t * obj, lv_event_t event) { + auto* screen = static_cast<SettingSetDate *>(obj->user_data); + screen->HandleButtonPress(obj, event); + } +} + +SettingSetDate::SettingSetDate(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::DateTime &dateTimeController) : + Screen(app), + dateTimeController {dateTimeController} { + lv_obj_t * title = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(title, "Set current date"); + lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); + lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); + + lv_obj_t * icon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); + + lv_label_set_text_static(icon, Symbols::clock); + lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); + lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); + + dayValue = static_cast<int>(dateTimeController.Day()); + lblDay = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_fmt(lblDay, "%d", dayValue); + lv_label_set_align(lblDay, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblDay, lv_scr_act(), LV_ALIGN_CENTER, POS_X_DAY, POS_Y_TEXT); + lv_obj_set_auto_realign(lblDay, true); + + monthValue = static_cast<int>(dateTimeController.Month()); + lblMonth = lv_label_create(lv_scr_act(), nullptr); + UpdateMonthLabel(); + lv_label_set_align(lblMonth, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblMonth, lv_scr_act(), LV_ALIGN_CENTER, POS_X_MONTH, POS_Y_TEXT); + lv_obj_set_auto_realign(lblMonth, true); + + yearValue = static_cast<int>(dateTimeController.Year()); + if (yearValue < 2021) + yearValue = 2021; + lblYear = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_fmt(lblYear, "%d", yearValue); + lv_label_set_align(lblYear, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblYear, lv_scr_act(), LV_ALIGN_CENTER, POS_X_YEAR, POS_Y_TEXT); + lv_obj_set_auto_realign(lblYear, true); + + btnDayPlus = lv_btn_create(lv_scr_act(), nullptr); + btnDayPlus->user_data = this; + lv_obj_set_size(btnDayPlus, 50, 40); + lv_obj_align(btnDayPlus, lv_scr_act(), LV_ALIGN_CENTER, POS_X_DAY, POS_Y_PLUS); + lv_obj_set_style_local_value_str(btnDayPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+"); + lv_obj_set_event_cb(btnDayPlus, event_handler); + + btnDayMinus = lv_btn_create(lv_scr_act(), nullptr); + btnDayMinus->user_data = this; + lv_obj_set_size(btnDayMinus, 50, 40); + lv_obj_align(btnDayMinus, lv_scr_act(), LV_ALIGN_CENTER, POS_X_DAY, POS_Y_MINUS); + lv_obj_set_style_local_value_str(btnDayMinus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "-"); + lv_obj_set_event_cb(btnDayMinus, event_handler); + + btnMonthPlus = lv_btn_create(lv_scr_act(), nullptr); + btnMonthPlus->user_data = this; + lv_obj_set_size(btnMonthPlus, 50, 40); + lv_obj_align(btnMonthPlus, lv_scr_act(), LV_ALIGN_CENTER, POS_X_MONTH, POS_Y_PLUS); + lv_obj_set_style_local_value_str(btnMonthPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+"); + lv_obj_set_event_cb(btnMonthPlus, event_handler); + + btnMonthMinus = lv_btn_create(lv_scr_act(), nullptr); + btnMonthMinus->user_data = this; + lv_obj_set_size(btnMonthMinus, 50, 40); + lv_obj_align(btnMonthMinus, lv_scr_act(), LV_ALIGN_CENTER, POS_X_MONTH, POS_Y_MINUS); + lv_obj_set_style_local_value_str(btnMonthMinus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "-"); + lv_obj_set_event_cb(btnMonthMinus, event_handler); + + btnYearPlus = lv_btn_create(lv_scr_act(), nullptr); + btnYearPlus->user_data = this; + lv_obj_set_size(btnYearPlus, 50, 40); + lv_obj_align(btnYearPlus, lv_scr_act(), LV_ALIGN_CENTER, POS_X_YEAR, POS_Y_PLUS); + lv_obj_set_style_local_value_str(btnYearPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+"); + lv_obj_set_event_cb(btnYearPlus, event_handler); + + btnYearMinus = lv_btn_create(lv_scr_act(), nullptr); + btnYearMinus->user_data = this; + lv_obj_set_size(btnYearMinus, 50, 40); + lv_obj_align(btnYearMinus, lv_scr_act(), LV_ALIGN_CENTER, POS_X_YEAR, POS_Y_MINUS); + lv_obj_set_style_local_value_str(btnYearMinus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "-"); + lv_obj_set_event_cb(btnYearMinus, event_handler); + + btnSetTime = lv_btn_create(lv_scr_act(), nullptr); + btnSetTime->user_data = this; + lv_obj_set_size(btnSetTime, 120, 48); + lv_obj_align(btnSetTime, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); + lv_obj_set_style_local_value_str(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Set"); + lv_obj_set_event_cb(btnSetTime, event_handler); +} + +SettingSetDate::~SettingSetDate() { + lv_obj_clean(lv_scr_act()); +} + +void SettingSetDate::HandleButtonPress(lv_obj_t *object, lv_event_t event) { + if (event != LV_EVENT_CLICKED) + return; + + if (object == btnDayPlus) { + dayValue++; + if (dayValue > MaximumDayOfMonth()) + dayValue = 1; + lv_label_set_text_fmt(lblDay, "%d", dayValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + } else if (object == btnDayMinus) { + dayValue--; + if (dayValue < 1) + dayValue = MaximumDayOfMonth(); + lv_label_set_text_fmt(lblDay, "%d", dayValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + } else if (object == btnMonthPlus) { + monthValue++; + if (monthValue > 12) + monthValue = 1; + UpdateMonthLabel(); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + CheckDay(); + } else if (object == btnMonthMinus) { + monthValue--; + if (monthValue < 1) + monthValue = 12; + UpdateMonthLabel(); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + CheckDay(); + } else if (object == btnYearPlus) { + yearValue++; + lv_label_set_text_fmt(lblYear, "%d", yearValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + CheckDay(); + } else if (object == btnYearMinus) { + yearValue--; + lv_label_set_text_fmt(lblYear, "%d", yearValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + CheckDay(); + } else if (object == btnSetTime) { + NRF_LOG_INFO("Setting date (manually) to %04d-%02d-%02d", yearValue, monthValue, dayValue); + dateTimeController.SetTime(static_cast<uint16_t>(yearValue), + static_cast<uint8_t>(monthValue), + static_cast<uint8_t>(dayValue), + 0, + dateTimeController.Hours(), + dateTimeController.Minutes(), + dateTimeController.Seconds(), + nrf_rtc_counter_get(portNRF_RTC_REG)); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_DISABLED); + } +} + +int SettingSetDate::MaximumDayOfMonth() const { + switch (monthValue) { + case 2: + if ((((yearValue % 4) == 0) && ((yearValue % 100) != 0)) || ((yearValue % 400) == 0)) + return 29; + return 28; + case 4: + case 6: + case 9: + case 11: + return 30; + default: + return 31; + } +} + +void SettingSetDate::CheckDay() { + int maxDay = MaximumDayOfMonth(); + if (dayValue > maxDay) { + dayValue = maxDay; + lv_label_set_text_fmt(lblDay, "%d", dayValue); + lv_obj_align(lblDay, lv_scr_act(), LV_ALIGN_CENTER, POS_X_DAY, POS_Y_TEXT); + } +} + +void SettingSetDate::UpdateMonthLabel() { + lv_label_set_text_static( + lblMonth, Pinetime::Controllers::DateTime::MonthShortToStringLow(static_cast<Pinetime::Controllers::DateTime::Months>(monthValue))); +} diff --git a/src/displayapp/screens/settings/SettingSetDate.h b/src/displayapp/screens/settings/SettingSetDate.h new file mode 100644 index 0000000..477337f --- /dev/null +++ b/src/displayapp/screens/settings/SettingSetDate.h @@ -0,0 +1,41 @@ +#pragma once + +#include <cstdint> +#include <lvgl/lvgl.h> +#include "components/datetime/DateTimeController.h" +#include "displayapp/screens/Screen.h" + +namespace Pinetime { + namespace Applications { + namespace Screens { + class SettingSetDate : public Screen{ + public: + SettingSetDate(DisplayApp* app, Pinetime::Controllers::DateTime &dateTimeController); + ~SettingSetDate() override; + + void HandleButtonPress(lv_obj_t *object, lv_event_t event); + + private: + Controllers::DateTime& dateTimeController; + + int dayValue; + int monthValue; + int yearValue; + lv_obj_t * lblDay; + lv_obj_t * lblMonth; + lv_obj_t * lblYear; + lv_obj_t * btnDayPlus; + lv_obj_t * btnDayMinus; + lv_obj_t * btnMonthPlus; + lv_obj_t * btnMonthMinus; + lv_obj_t * btnYearPlus; + lv_obj_t * btnYearMinus; + lv_obj_t * btnSetTime; + + int MaximumDayOfMonth() const; + void CheckDay(); + void UpdateMonthLabel(); + }; + } + } +} diff --git a/src/displayapp/screens/settings/SettingSetTime.cpp b/src/displayapp/screens/settings/SettingSetTime.cpp new file mode 100644 index 0000000..194bf5e --- /dev/null +++ b/src/displayapp/screens/settings/SettingSetTime.cpp @@ -0,0 +1,154 @@ +#include "SettingSetTime.h" +#include <lvgl/lvgl.h> +#include <hal/nrf_rtc.h> +#include <nrf_log.h> +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Symbols.h" + +using namespace Pinetime::Applications::Screens; + +namespace { + constexpr int16_t POS_X_HOURS = -72; + constexpr int16_t POS_X_MINUTES = 0; + constexpr int16_t POS_X_SECONDS = 72; + constexpr int16_t POS_Y_PLUS = -50; + constexpr int16_t POS_Y_TEXT = -6; + constexpr int16_t POS_Y_MINUS = 40; + constexpr int16_t OFS_Y_COLON = -2; + + void event_handler(lv_obj_t * obj, lv_event_t event) { + auto* screen = static_cast<SettingSetTime *>(obj->user_data); + screen->HandleButtonPress(obj, event); + } +} + +SettingSetTime::SettingSetTime(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::DateTime &dateTimeController) : + Screen(app), + dateTimeController {dateTimeController} { + lv_obj_t * title = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(title, "Set current time"); + lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); + lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); + + lv_obj_t * icon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); + + lv_label_set_text_static(icon, Symbols::clock); + lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); + lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); + + hoursValue = static_cast<int>(dateTimeController.Hours()); + lblHours = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(lblHours, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + lv_label_set_text_fmt(lblHours, "%02d", hoursValue); + lv_label_set_align(lblHours, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblHours, lv_scr_act(), LV_ALIGN_CENTER, POS_X_HOURS, POS_Y_TEXT); + lv_obj_set_auto_realign(lblHours, true); + + lv_obj_t * lblColon1 = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(lblColon1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + lv_label_set_text_static(lblColon1, ":"); + lv_label_set_align(lblColon1, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblColon1, lv_scr_act(), LV_ALIGN_CENTER, (POS_X_HOURS + POS_X_MINUTES) / 2, POS_Y_TEXT + OFS_Y_COLON); + + minutesValue = static_cast<int>(dateTimeController.Minutes()); + lblMinutes = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(lblMinutes, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + lv_label_set_text_fmt(lblMinutes, "%02d", minutesValue); + lv_label_set_align(lblMinutes, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblMinutes, lv_scr_act(), LV_ALIGN_CENTER, POS_X_MINUTES, POS_Y_TEXT); + lv_obj_set_auto_realign(lblMinutes, true); + + lv_obj_t * lblColon2 = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(lblColon2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + lv_label_set_text_static(lblColon2, ":"); + lv_label_set_align(lblColon2, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblColon2, lv_scr_act(), LV_ALIGN_CENTER, (POS_X_MINUTES + POS_X_SECONDS) / 2, POS_Y_TEXT + OFS_Y_COLON); + + lv_obj_t * lblSeconds = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(lblSeconds, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + lv_label_set_text_static(lblSeconds, "00"); + lv_label_set_align(lblSeconds, LV_LABEL_ALIGN_CENTER); + lv_obj_align(lblSeconds, lv_scr_act(), LV_ALIGN_CENTER, POS_X_SECONDS, POS_Y_TEXT); + + btnHoursPlus = lv_btn_create(lv_scr_act(), nullptr); + btnHoursPlus->user_data = this; + lv_obj_set_size(btnHoursPlus, 50, 40); + lv_obj_align(btnHoursPlus, lv_scr_act(), LV_ALIGN_CENTER, -72, -50); + lv_obj_set_style_local_value_str(btnHoursPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+"); + lv_obj_set_event_cb(btnHoursPlus, event_handler); + + btnHoursMinus = lv_btn_create(lv_scr_act(), nullptr); + btnHoursMinus->user_data = this; + lv_obj_set_size(btnHoursMinus, 50, 40); + lv_obj_align(btnHoursMinus, lv_scr_act(), LV_ALIGN_CENTER, -72, 40); + lv_obj_set_style_local_value_str(btnHoursMinus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "-"); + lv_obj_set_event_cb(btnHoursMinus, event_handler); + + btnMinutesPlus = lv_btn_create(lv_scr_act(), nullptr); + btnMinutesPlus->user_data = this; + lv_obj_set_size(btnMinutesPlus, 50, 40); + lv_obj_align(btnMinutesPlus, lv_scr_act(), LV_ALIGN_CENTER, 0, -50); + lv_obj_set_style_local_value_str(btnMinutesPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+"); + lv_obj_set_event_cb(btnMinutesPlus, event_handler); + + btnMinutesMinus = lv_btn_create(lv_scr_act(), nullptr); + btnMinutesMinus->user_data = this; + lv_obj_set_size(btnMinutesMinus, 50, 40); + lv_obj_align(btnMinutesMinus, lv_scr_act(), LV_ALIGN_CENTER, 0, 40); + lv_obj_set_style_local_value_str(btnMinutesMinus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "-"); + lv_obj_set_event_cb(btnMinutesMinus, event_handler); + + btnSetTime = lv_btn_create(lv_scr_act(), nullptr); + btnSetTime->user_data = this; + lv_obj_set_size(btnSetTime, 120, 48); + lv_obj_align(btnSetTime, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); + lv_obj_set_style_local_value_str(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Set"); + lv_obj_set_event_cb(btnSetTime, event_handler); +} + +SettingSetTime::~SettingSetTime() { + lv_obj_clean(lv_scr_act()); +} + +void SettingSetTime::HandleButtonPress(lv_obj_t *object, lv_event_t event) { + if (event != LV_EVENT_CLICKED) + return; + + if (object == btnHoursPlus) { + hoursValue++; + if (hoursValue > 23) + hoursValue = 0; + lv_label_set_text_fmt(lblHours, "%02d", hoursValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + } else if (object == btnHoursMinus) { + hoursValue--; + if (hoursValue < 0) + hoursValue = 23; + lv_label_set_text_fmt(lblHours, "%02d", hoursValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + } else if (object == btnMinutesPlus) { + minutesValue++; + if (minutesValue > 59) + minutesValue = 0; + lv_label_set_text_fmt(lblMinutes, "%02d", minutesValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + } else if (object == btnMinutesMinus) { + minutesValue--; + if (minutesValue < 0) + minutesValue = 59; + lv_label_set_text_fmt(lblMinutes, "%02d", minutesValue); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED); + } else if (object == btnSetTime) { + NRF_LOG_INFO("Setting time (manually) to %02d:%02d:00", hoursValue, minutesValue); + dateTimeController.SetTime(dateTimeController.Year(), + static_cast<uint8_t>(dateTimeController.Month()), + dateTimeController.Day(), + static_cast<uint8_t>(dateTimeController.DayOfWeek()), + static_cast<uint8_t>(hoursValue), + static_cast<uint8_t>(minutesValue), + 0, + nrf_rtc_counter_get(portNRF_RTC_REG)); + lv_btn_set_state(btnSetTime, LV_BTN_STATE_DISABLED); + } +} diff --git a/src/displayapp/screens/settings/SettingSetTime.h b/src/displayapp/screens/settings/SettingSetTime.h new file mode 100644 index 0000000..8ba41ea --- /dev/null +++ b/src/displayapp/screens/settings/SettingSetTime.h @@ -0,0 +1,33 @@ +#pragma once + +#include <cstdint> +#include <lvgl/lvgl.h> +#include "components/datetime/DateTimeController.h" +#include "displayapp/screens/Screen.h" + +namespace Pinetime { + namespace Applications { + namespace Screens { + class SettingSetTime : public Screen{ + public: + SettingSetTime(DisplayApp* app, Pinetime::Controllers::DateTime &dateTimeController); + ~SettingSetTime() override; + + void HandleButtonPress(lv_obj_t *object, lv_event_t event); + + private: + Controllers::DateTime& dateTimeController; + + int hoursValue; + int minutesValue; + lv_obj_t * lblHours; + lv_obj_t * lblMinutes; + lv_obj_t * btnHoursPlus; + lv_obj_t * btnHoursMinus; + lv_obj_t * btnMinutesPlus; + lv_obj_t * btnMinutesMinus; + lv_obj_t * btnSetTime; + }; + } + } +} diff --git a/src/displayapp/screens/settings/Settings.cpp b/src/displayapp/screens/settings/Settings.cpp index e3319f0..1daf311 100644 --- a/src/displayapp/screens/settings/Settings.cpp +++ b/src/displayapp/screens/settings/Settings.cpp @@ -49,9 +49,9 @@ std::unique_ptr<Screen> Settings::CreateScreen2() { std::array<Screens::List::Applications, 4> applications {{ {Symbols::shoe, "Steps", Apps::SettingSteps}, - {Symbols::batteryHalf, "Battery", Apps::BatteryInfo}, - {Symbols::paintbrush, "PTS Colors", Apps::SettingPineTimeStyle}, - {Symbols::check, "Firmware", Apps::FirmwareValidation}, + {Symbols::clock, "Set date", Apps::SettingSetDate}, + {Symbols::clock, "Set time", Apps::SettingSetTime}, + {Symbols::batteryHalf, "Battery", Apps::BatteryInfo} }}; return std::make_unique<Screens::List>(1, 3, app, settingsController, applications); @@ -60,10 +60,10 @@ std::unique_ptr<Screen> Settings::CreateScreen2() { std::unique_ptr<Screen> Settings::CreateScreen3() { std::array<Screens::List::Applications, 4> applications {{ + {Symbols::paintbrush, "PTS Colors", Apps::SettingPineTimeStyle}, + {Symbols::check, "Firmware", Apps::FirmwareValidation}, {Symbols::list, "About", Apps::SysInfo}, {Symbols::none, "None", Apps::None}, - {Symbols::none, "None", Apps::None}, - {Symbols::none, "None", Apps::None}, }}; return std::make_unique<Screens::List>(2, 3, app, settingsController, applications); diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp index 1ff163b..4aac19f 100644 --- a/src/drivers/Cst816s.cpp +++ b/src/drivers/Cst816s.cpp @@ -18,10 +18,8 @@ using namespace Pinetime::Drivers; Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} { } -void Cst816S::Init() { +bool Cst816S::Init() { nrf_gpio_cfg_output(PinMap::Cst816sReset); - nrf_gpio_pin_set(PinMap::Cst816sReset); - vTaskDelay(50); nrf_gpio_pin_clear(PinMap::Cst816sReset); vTaskDelay(5); nrf_gpio_pin_set(PinMap::Cst816sReset); @@ -34,6 +32,18 @@ void Cst816S::Init() { twiMaster.Read(twiAddress, 0xa7, &dummy, 1); vTaskDelay(5); + static constexpr uint8_t maxRetries = 3; + bool isDeviceOk; + uint8_t retries = 0; + do { + isDeviceOk = CheckDeviceIds(); + retries++; + } while (!isDeviceOk && retries < maxRetries); + + if (!isDeviceOk) { + return false; + } + /* [2] EnConLR - Continuous operation can slide around [1] EnConUD - Slide up and down to enable continuous operation @@ -51,10 +61,13 @@ void Cst816S::Init() { */ static constexpr uint8_t irqCtl = 0b01110000; twiMaster.Write(twiAddress, 0xFA, &irqCtl, 1); + + return true; } Cst816S::TouchInfos Cst816S::GetTouchInfo() { Cst816S::TouchInfos info; + uint8_t touchData[7]; auto ret = twiMaster.Read(twiAddress, 0, touchData, sizeof(touchData)); if (ret != TwiMaster::ErrorCodes::NoError) { @@ -62,21 +75,35 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() { return info; } - auto nbTouchPoints = touchData[2] & 0x0f; - - auto xHigh = touchData[touchXHighIndex] & 0x0f; - auto xLow = touchData[touchXLowIndex]; + // This can only be 0 or 1 + uint8_t nbTouchPoints = touchData[touchPointNumIndex] & 0x0f; + uint8_t xHigh = touchData[touchXHighIndex] & 0x0f; + uint8_t xLow = touchData[touchXLowIndex]; uint16_t x = (xHigh << 8) | xLow; - - auto yHigh = touchData[touchYHighIndex] & 0x0f; - auto yLow = touchData[touchYLowIndex]; + uint8_t yHigh = touchData[touchYHighIndex] & 0x0f; + uint8_t yLow = touchData[touchYLowIndex]; uint16_t y = (yHigh << 8) | yLow; + Gestures gesture = static_cast<Gestures>(touchData[gestureIndex]); + + // Validity check + if(x >= maxX || y >= maxY || + (gesture != Gestures::None && + gesture != Gestures::SlideDown && + gesture != Gestures::SlideUp && + gesture != Gestures::SlideLeft && + gesture != Gestures::SlideRight && + gesture != Gestures::SingleTap && + gesture != Gestures::DoubleTap && + gesture != Gestures::LongPress)) { + info.isValid = false; + return info; + } info.x = x; info.y = y; info.touching = (nbTouchPoints > 0); - info.gesture = static_cast<Gestures>(touchData[gestureIndex]); - + info.gesture = gesture; + info.isValid = true; return info; } @@ -94,3 +121,21 @@ void Cst816S::Wakeup() { Init(); NRF_LOG_INFO("[TOUCHPANEL] Wakeup"); } + +bool Cst816S::CheckDeviceIds() { + // There's mixed information about which register contains which information + if (twiMaster.Read(twiAddress, 0xA7, &chipId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + chipId = 0xFF; + return false; + } + if (twiMaster.Read(twiAddress, 0xA8, &vendorId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + vendorId = 0xFF; + return false; + } + if (twiMaster.Read(twiAddress, 0xA9, &fwVersion, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + fwVersion = 0xFF; + return false; + } + + return chipId == 0xb4 && vendorId == 0 && fwVersion == 1; +} diff --git a/src/drivers/Cst816s.h b/src/drivers/Cst816s.h index 7b46c5d..507dd4f 100644 --- a/src/drivers/Cst816s.h +++ b/src/drivers/Cst816s.h @@ -21,7 +21,7 @@ namespace Pinetime { uint16_t y = 0; Gestures gesture = Gestures::None; bool touching = false; - bool isValid = true; + bool isValid = false; }; Cst816S(TwiMaster& twiMaster, uint8_t twiAddress); @@ -30,12 +30,23 @@ namespace Pinetime { Cst816S(Cst816S&&) = delete; Cst816S& operator=(Cst816S&&) = delete; - void Init(); + bool Init(); TouchInfos GetTouchInfo(); void Sleep(); void Wakeup(); + uint8_t GetChipId() const { + return chipId; + } + uint8_t GetVendorId() const { + return vendorId; + } + uint8_t GetFwVersion() const { + return fwVersion; + } private: + bool CheckDeviceIds(); + // Unused/Unavailable commented out static constexpr uint8_t gestureIndex = 1; static constexpr uint8_t touchPointNumIndex = 2; @@ -49,9 +60,15 @@ namespace Pinetime { //static constexpr uint8_t touchXYIndex = 7; //static constexpr uint8_t touchMiscIndex = 8; - uint8_t touchData[7]; + static constexpr uint8_t maxX = 240; + static constexpr uint8_t maxY = 240; + TwiMaster& twiMaster; uint8_t twiAddress; + + uint8_t chipId; + uint8_t vendorId; + uint8_t fwVersion; }; } diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index 0f1dc02..4d81cf2 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -170,16 +170,7 @@ void St7789::Sleep() { void St7789::Wakeup() { nrf_gpio_cfg_output(pinDataCommand); - // TODO why do we need to reset the controller? - HardwareReset(); - SoftwareReset(); SleepOut(); - ColMod(); - MemoryDataAccessControl(); - ColumnAddressSet(); - RowAddressSet(); - DisplayInversionOn(); - NormalModeOn(); VerticalScrollStartAddress(verticalScrollingStartAddress); DisplayOn(); NRF_LOG_INFO("[LCD] Wakeup") diff --git a/src/main.cpp b/src/main.cpp index fc77235..53f78ce 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,10 @@ 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) { + xTimerStartFromISR(debounceTimer, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } - - xTimerStartFromISR(debounceTimer, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void DebounceTimerChargeCallback(TimerHandle_t xTimer) { @@ -188,9 +188,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 +318,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 bd1de23..b714270 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -15,7 +15,8 @@ namespace Pinetime { BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, - OnButtonEvent, + HandleButtonEvent, + HandleButtonTimerEvent, OnDisplayTaskSleeping, EnableSleeping, DisableSleeping, @@ -24,7 +25,7 @@ namespace Pinetime { SetOffAlarm, StopRinging, MeasureBatteryTimerExpired, - BatteryMeasurementDone, + BatteryPercentageUpdated, }; } } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 5441c16..4b03f9a 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -23,7 +23,7 @@ #include "drivers/Hrs3300.h" #include "drivers/PinMap.h" #include "main.h" - +#include "BootErrors.h" #include <memory> @@ -76,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}, @@ -100,7 +101,15 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, heartRateApp(heartRateApp), fs {fs}, touchHandler {touchHandler}, - nimbleController(*this, bleController, dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController) { + buttonHandler {buttonHandler}, + nimbleController(*this, + bleController, + dateTimeController, + notificationManager, + batteryController, + spiNorFlash, + heartRateController, + motionController) { } void SystemTask::Start() { @@ -116,6 +125,8 @@ void SystemTask::Process(void* instance) { } void SystemTask::Work() { + BootErrors bootError = BootErrors::None; + watchdog.Setup(7); watchdog.Start(); NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason())); @@ -133,10 +144,17 @@ void SystemTask::Work() { lcd.Init(); twiMaster.Init(); + /* + * TODO We disable this warning message until we ensure it won't be displayed + * on legitimate PineTime equipped with a compatible touch controller. + * (some users reported false positive). See https://github.com/InfiniTimeOrg/InfiniTime/issues/763 + if (!touchPanel.Init()) { + bootError = BootErrors::TouchController; + } + */ touchPanel.Init(); dateTimeController.Register(this); batteryController.Register(this); - batteryController.Update(); motorController.Init(); motionSensor.SoftReset(); timerController.Register(this); @@ -152,25 +170,29 @@ void SystemTask::Work() { settingsController.Init(); displayApp.Register(this); - displayApp.Start(); + displayApp.Start(bootError); heartRateSensor.Init(); heartRateSensor.Disable(); heartRateApp.Start(); - nrf_gpio_cfg_sense_input(PinMap::Button, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pulldown, (nrf_gpio_pin_sense_t) GPIO_PIN_CNF_SENSE_High); + buttonHandler.Init(this); + + // Button nrf_gpio_cfg_output(15); nrf_gpio_pin_set(15); nrfx_gpiote_in_config_t pinConfig; - pinConfig.skip_gpio_setup = true; + pinConfig.skip_gpio_setup = false; pinConfig.hi_accuracy = false; pinConfig.is_watcher = false; - pinConfig.sense = (nrf_gpiote_polarity_t) NRF_GPIOTE_POLARITY_HITOLO; + pinConfig.sense = (nrf_gpiote_polarity_t) NRF_GPIOTE_POLARITY_TOGGLE; pinConfig.pull = (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pulldown; nrfx_gpiote_in_init(PinMap::Button, &pinConfig, nrfx_gpiote_evt_handler); + nrfx_gpiote_in_event_enable(PinMap::Button, true); + // Touchscreen nrf_gpio_cfg_sense_input(PinMap::Cst816sIrq, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup, (nrf_gpio_pin_sense_t) GPIO_PIN_CNF_SENSE_Low); pinConfig.skip_gpio_setup = true; @@ -181,18 +203,16 @@ void SystemTask::Work() { nrfx_gpiote_in_init(PinMap::Cst816sIrq, &pinConfig, nrfx_gpiote_evt_handler); + // Power present pinConfig.sense = NRF_GPIOTE_POLARITY_TOGGLE; pinConfig.pull = NRF_GPIO_PIN_NOPULL; pinConfig.is_watcher = false; pinConfig.hi_accuracy = false; - pinConfig.skip_gpio_setup = true; + pinConfig.skip_gpio_setup = false; nrfx_gpiote_in_init(PinMap::PowerPresent, &pinConfig, nrfx_gpiote_evt_handler); + nrfx_gpiote_in_event_enable(PinMap::PowerPresent, true); - if (nrf_gpio_pin_read(PinMap::PowerPresent)) { - nrf_gpio_cfg_sense_input(PinMap::PowerPresent, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW); - } else { - nrf_gpio_cfg_sense_input(PinMap::PowerPresent, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH); - } + batteryController.MeasureVoltage(); idleTimer = xTimerCreate("idleTimer", pdMS_TO_TICKS(2000), pdFALSE, this, IdleTimerCallback); dimTimer = xTimerCreate("dimTimer", pdMS_TO_TICKS(settingsController.GetScreenTimeOut() - 2000), pdFALSE, this, DimTimerCallback); @@ -246,12 +266,13 @@ void SystemTask::Work() { isDimmed = false; break; case Messages::TouchWakeUp: { - if(touchHandler.GetNewTouchInfo()) { + if (touchHandler.GetNewTouchInfo()) { auto gesture = touchHandler.GestureGet(); - if (gesture != Pinetime::Drivers::Cst816S::Gestures::None and ((gesture == Pinetime::Drivers::Cst816S::Gestures::DoubleTap and - settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) or - (gesture == Pinetime::Drivers::Cst816S::Gestures::SingleTap and - settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap)))) { + if (gesture != Pinetime::Drivers::Cst816S::Gestures::None and + ((gesture == Pinetime::Drivers::Cst816S::Gestures::DoubleTap and + settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) or + (gesture == Pinetime::Drivers::Cst816S::Gestures::SingleTap and + settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap)))) { GoToRunning(); } } @@ -272,6 +293,8 @@ void SystemTask::Work() { if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::ON) { if (isSleeping && !isWakingUp) { GoToRunning(); + } else { + ReloadIdleTimer(); } displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification); } @@ -318,10 +341,25 @@ void SystemTask::Work() { ReloadIdleTimer(); displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); break; - case Messages::OnButtonEvent: - ReloadIdleTimer(); - displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonPushed); - break; + case Messages::HandleButtonEvent: { + 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; + } + } + 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 @@ -345,18 +383,18 @@ void SystemTask::Work() { stepCounterMustBeReset = true; break; case Messages::OnChargingEvent: - batteryController.Update(); + batteryController.ReadPowerState(); motorController.RunForDuration(15); + ReloadIdleTimer(); + if (isSleeping && !isWakingUp) { + GoToRunning(); + } break; case Messages::MeasureBatteryTimerExpired: - sendBatteryNotification = true; - batteryController.Update(); + batteryController.MeasureVoltage(); break; - case Messages::BatteryMeasurementDone: - if (sendBatteryNotification) { - sendBatteryNotification = false; - nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); - } + case Messages::BatteryPercentageUpdated: + nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); break; default: @@ -406,18 +444,36 @@ void SystemTask::UpdateMotion() { } } -void SystemTask::OnButtonPushed() { - if (isGoingToSleep) +void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { + if (IsSleeping()) { return; - if (!isSleeping) { - NRF_LOG_INFO("[systemtask] Button pushed"); - PushMessage(Messages::OnButtonEvent); - } else { - if (!isWakingUp) { - NRF_LOG_INFO("[systemtask] Button pushed, waking up"); - GoToRunning(); - } } + + ReloadIdleTimer(); + + using Actions = Controllers::ButtonActions; + + switch (action) { + case Actions::Click: + // If the first action after fast wakeup is a click, it should be ignored. + if (!fastWakeUpDone && !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: + return; + } + + fastWakeUpDone = false; } void SystemTask::GoToRunning() { diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 1fcfeb8..412878b 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -20,6 +20,8 @@ #include "components/alarm/AlarmController.h" #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" @@ -45,6 +47,7 @@ namespace Pinetime { } namespace Controllers { class TouchHandler; + class ButtonHandler; } namespace System { class SystemTask { @@ -71,12 +74,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 +126,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); @@ -133,14 +137,15 @@ namespace Pinetime { TimerHandle_t dimTimer; TimerHandle_t idleTimer; TimerHandle_t measureBatteryTimer; - bool sendBatteryNotification = false; bool doNotGoToSleep = false; + void HandleButtonAction(Controllers::ButtonActions action); + bool fastWakeUpDone = false; + void GoToRunning(); void UpdateMotion(); bool stepCounterMustBeReset = false; static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000); - TickType_t lastBatteryNotificationTime = 0; #if configUSE_TRACE_FACILITY == 1 SystemMonitor<FreeRtosMonitor> monitor; |
