diff options
Diffstat (limited to 'src')
181 files changed, 3593 insertions, 1060 deletions
diff --git a/src/BootloaderVersion.h b/src/BootloaderVersion.h index f8127414..309c23c3 100644 --- a/src/BootloaderVersion.h +++ b/src/BootloaderVersion.h @@ -1,5 +1,8 @@ #pragma once +#include <cstdint> +#include <cstddef> + namespace Pinetime { class BootloaderVersion { public: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46deb201..80f900d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -154,6 +154,7 @@ set(NIMBLE_SRC libs/mynewt-nimble/nimble/controller/src/ble_ll_supp_cmd.c libs/mynewt-nimble/nimble/controller/src/ble_ll_hci_ev.c libs/mynewt-nimble/nimble/controller/src/ble_ll_rfmgmt.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_resolv.c libs/mynewt-nimble/porting/nimble/src/os_cputime.c libs/mynewt-nimble/porting/nimble/src/os_cputime_pwr2.c libs/mynewt-nimble/porting/nimble/src/os_mbuf.c @@ -357,6 +358,14 @@ set(LVGL_SRC libs/lvgl/src/lv_widgets/lv_win.c ) +set(QCBOR_SRC + libs/QCBOR/src/ieee754.c + libs/QCBOR/src/qcbor_decode.c + libs/QCBOR/src/qcbor_encode.c + libs/QCBOR/src/qcbor_err_to_str.c + libs/QCBOR/src/UsefulBuf.c + ) + list(APPEND IMAGE_FILES displayapp/icons/battery/os_battery_error.c displayapp/icons/battery/os_battery_100.c @@ -407,6 +416,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Label.cpp displayapp/screens/FirmwareUpdate.cpp displayapp/screens/Music.cpp + displayapp/screens/Weather.cpp displayapp/screens/Navigation.cpp displayapp/screens/Metronome.cpp displayapp/screens/Motion.cpp @@ -421,8 +431,10 @@ list(APPEND SOURCE_FILES displayapp/screens/BatteryInfo.cpp displayapp/screens/Steps.cpp displayapp/screens/Timer.cpp + displayapp/screens/PassKey.cpp displayapp/screens/Error.cpp displayapp/screens/Alarm.cpp + displayapp/screens/Styles.cpp displayapp/Colors.cpp ## Settings @@ -433,7 +445,6 @@ list(APPEND SOURCE_FILES displayapp/screens/settings/SettingWakeUp.cpp 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 displayapp/screens/settings/SettingChimes.cpp @@ -472,9 +483,11 @@ list(APPEND SOURCE_FILES components/ble/CurrentTimeService.cpp components/ble/AlertNotificationService.cpp components/ble/MusicService.cpp + components/ble/weather/WeatherService.cpp components/ble/NavigationService.cpp displayapp/fonts/lv_font_navi_80.c components/ble/BatteryInformationService.cpp + components/ble/FSService.cpp components/ble/ImmediateAlertService.cpp components/ble/ServiceDiscovery.cpp components/ble/HeartRateService.cpp @@ -508,6 +521,7 @@ list(APPEND SOURCE_FILES components/heartrate/Ptagc.cpp components/heartrate/HeartRateController.cpp + buttonhandler/ButtonHandler.cpp touchhandler/TouchHandler.cpp ) @@ -542,7 +556,9 @@ list(APPEND RECOVERY_SOURCE_FILES components/ble/CurrentTimeService.cpp components/ble/AlertNotificationService.cpp components/ble/MusicService.cpp + components/ble/weather/WeatherService.cpp components/ble/BatteryInformationService.cpp + components/ble/FSService.cpp components/ble/ImmediateAlertService.cpp components/ble/ServiceDiscovery.cpp components/ble/NavigationService.cpp @@ -568,6 +584,7 @@ list(APPEND RECOVERY_SOURCE_FILES components/heartrate/Ptagc.cpp components/motor/MotorController.cpp components/fs/FS.cpp + buttonhandler/ButtonHandler.cpp touchhandler/TouchHandler.cpp ) @@ -644,6 +661,9 @@ set(INCLUDE_FILES components/datetime/DateTimeController.h components/brightness/BrightnessController.h components/motion/MotionController.h + components/firmwarevalidator/FirmwareValidator.h + components/ble/BleController.h + components/ble/NotificationManager.h components/ble/NimbleController.h components/ble/DeviceInformationService.h components/ble/CurrentTimeClient.h @@ -651,11 +671,13 @@ set(INCLUDE_FILES components/ble/DfuService.h components/firmwarevalidator/FirmwareValidator.h components/ble/BatteryInformationService.h + components/ble/FSService.h components/ble/ImmediateAlertService.h components/ble/ServiceDiscovery.h components/ble/BleClient.h components/ble/HeartRateService.h components/ble/MotionService.h + components/ble/weather/WeatherService.h components/settings/Settings.h components/timer/TimerController.h components/alarm/AlarmController.h @@ -682,6 +704,7 @@ set(INCLUDE_FILES components/heartrate/Ptagc.h components/heartrate/HeartRateController.h components/motor/MotorController.h + buttonhandler/ButtonHandler.h touchhandler/TouchHandler.h ) @@ -780,7 +803,7 @@ link_directories( ) -set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -Wno-unknown-pragmas -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wreturn-type -Werror=return-type -fstack-usage -fno-exceptions -fno-non-call-exceptions) +set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -Wextra -Warray-bounds=2 -Wformat=2 -Wformat-overflow=2 -Wformat-truncation=2 -Wformat-nonliteral -ftree-vrp -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unknown-pragmas -Wno-expansion-to-defined -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wreturn-type -Werror=return-type -fstack-usage -fno-exceptions -fno-non-call-exceptions) add_definitions(-DCONFIG_GPIO_AS_PINRESET) add_definitions(-DNIMBLE_CFG_CONTROLLER) add_definitions(-DOS_CPUTIME_FREQ) @@ -802,10 +825,10 @@ add_library(nrf-sdk STATIC ${SDK_SOURCE_FILES}) target_include_directories(nrf-sdk SYSTEM PUBLIC . ../) target_include_directories(nrf-sdk SYSTEM PUBLIC ${INCLUDES_FROM_LIBS}) target_compile_options(nrf-sdk PRIVATE - $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3> - $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os> - $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -fno-rtti> - $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti> + $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wno-expansion-to-defined -Og -g3> + $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wno-expansion-to-defined -O3> + $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wno-expansion-to-defined -Og -fno-rtti> + $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wno-expansion-to-defined -O3 -fno-rtti> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> ) @@ -833,6 +856,25 @@ target_compile_options(lvgl PRIVATE $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> ) +# QCBOR +add_library(QCBOR STATIC ${QCBOR_SRC}) +target_include_directories(QCBOR SYSTEM PUBLIC libs/QCBOR/inc) +# This is required with the current configuration +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_FLOAT_HW_USE) +# These are for space-saving +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_PREFERRED_FLOAT) +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_EXP_AND_MANTISSA) +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS) +#target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS) +target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_UNCOMMON_TAGS) +target_compile_definitions(QCBOR PUBLIC USEFULBUF_CONFIG_LITTLE_ENDIAN) +set_target_properties(QCBOR PROPERTIES LINKER_LANGUAGE C) +target_compile_options(QCBOR PRIVATE + $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> + $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> + $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> + ) + # LITTLEFS_SRC add_library(littlefs STATIC ${LITTLEFS_SRC}) target_include_directories(littlefs SYSTEM PUBLIC . ../) @@ -851,12 +893,12 @@ set(EXECUTABLE_FILE_NAME ${EXECUTABLE_NAME}-${pinetime_VERSION_MAJOR}.${pinetime set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld") add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES}) set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME}) -target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs) +target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs QCBOR) target_compile_options(${EXECUTABLE_NAME} PUBLIC - $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3> - $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os> - $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti> - $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti> + $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3> + $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os> + $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Og -g3 -fno-rtti> + $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wextra -Wformat -Wno-missing-field-initializers -Wno-unused-parameter -Os -fno-rtti> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> ) @@ -880,7 +922,7 @@ set(IMAGE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERSION_ set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld") add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES}) -target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs) +target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs QCBOR) set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME}) target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3> @@ -916,7 +958,7 @@ endif() set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery") set(EXECUTABLE_RECOVERY_FILE_NAME ${EXECUTABLE_RECOVERY_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}) add_executable(${EXECUTABLE_RECOVERY_NAME} ${RECOVERY_SOURCE_FILES}) -target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs) +target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs QCBOR) set_target_properties(${EXECUTABLE_RECOVERY_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_FILE_NAME}) target_compile_definitions(${EXECUTABLE_RECOVERY_NAME} PUBLIC "PINETIME_IS_RECOVERY") target_compile_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC @@ -946,7 +988,7 @@ set(EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-${ set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.bin) set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES}) -target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs) +target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs QCBOR) set_target_properties(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}) target_compile_definitions(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC "PINETIME_IS_RECOVERY") target_compile_options(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC @@ -984,7 +1026,7 @@ endif() set(EXECUTABLE_RECOVERYLOADER_NAME "pinetime-recovery-loader") set(EXECUTABLE_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_RECOVERYLOADER_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}) add_executable(${EXECUTABLE_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE_FILES}) -target_link_libraries(${EXECUTABLE_RECOVERYLOADER_NAME} nrf-sdk) +target_link_libraries(${EXECUTABLE_RECOVERYLOADER_NAME} nrf-sdk QCBOR) set_target_properties(${EXECUTABLE_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERYLOADER_FILE_NAME}) target_compile_options(${EXECUTABLE_RECOVERYLOADER_NAME} PUBLIC $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3> @@ -1017,7 +1059,7 @@ set(EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOA set(IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.bin) set(DFU_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) add_executable(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE_FILES}) -target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk) +target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk QCBOR) set_target_properties(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}) target_compile_options(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PUBLIC $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3> diff --git a/src/Version.h.in b/src/Version.h.in index 8cd39c96..0d6219c0 100644 --- a/src/Version.h.in +++ b/src/Version.h.in @@ -2,6 +2,8 @@ @VERSION_EDIT_WARNING@ +#include <cstdint> + namespace Pinetime { class Version { public: diff --git a/src/buttonhandler/ButtonActions.h b/src/buttonhandler/ButtonActions.h new file mode 100644 index 00000000..21be441b --- /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 00000000..02ee22cf --- /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", pdMS_TO_TICKS(200), 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 00000000..44b20f19 --- /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/alarm/AlarmController.cpp b/src/components/alarm/AlarmController.cpp index 67ca05a9..28b328d5 100644 --- a/src/components/alarm/AlarmController.cpp +++ b/src/components/alarm/AlarmController.cpp @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "AlarmController.h" +#include "components/alarm/AlarmController.h" #include "systemtask/SystemTask.h" #include "app_timer.h" #include "task.h" diff --git a/src/components/alarm/AlarmController.h b/src/components/alarm/AlarmController.h index bf85d431..f39fbded 100644 --- a/src/components/alarm/AlarmController.h +++ b/src/components/alarm/AlarmController.h @@ -18,7 +18,6 @@ #pragma once #include <cstdint> -#include "app_timer.h" #include "components/datetime/DateTimeController.h" namespace Pinetime { diff --git a/src/components/battery/BatteryController.cpp b/src/components/battery/BatteryController.cpp index e807f033..300d0978 100644 --- a/src/components/battery/BatteryController.cpp +++ b/src/components/battery/BatteryController.cpp @@ -1,8 +1,9 @@ -#include "BatteryController.h" +#include "components/battery/BatteryController.h" #include "drivers/PinMap.h" #include <hal/nrf_gpio.h> #include <nrfx_saadc.h> #include <algorithm> +#include <cmath> using namespace Pinetime::Controllers; diff --git a/src/components/ble/AlertNotificationClient.cpp b/src/components/ble/AlertNotificationClient.cpp index 5e5c25cf..0f850874 100644 --- a/src/components/ble/AlertNotificationClient.cpp +++ b/src/components/ble/AlertNotificationClient.cpp @@ -1,6 +1,6 @@ -#include "AlertNotificationClient.h" +#include "components/ble/AlertNotificationClient.h" #include <algorithm> -#include "NotificationManager.h" +#include "components/ble/NotificationManager.h" #include "systemtask/SystemTask.h" using namespace Pinetime::Controllers; diff --git a/src/components/ble/AlertNotificationClient.h b/src/components/ble/AlertNotificationClient.h index dfba8143..2d6a3873 100644 --- a/src/components/ble/AlertNotificationClient.h +++ b/src/components/ble/AlertNotificationClient.h @@ -7,7 +7,7 @@ #include <host/ble_gap.h> #undef max #undef min -#include "BleClient.h" +#include "components/ble/BleClient.h" namespace Pinetime { diff --git a/src/components/ble/AlertNotificationService.cpp b/src/components/ble/AlertNotificationService.cpp index 56fc595f..04819122 100644 --- a/src/components/ble/AlertNotificationService.cpp +++ b/src/components/ble/AlertNotificationService.cpp @@ -1,8 +1,8 @@ -#include "AlertNotificationService.h" +#include "components/ble/AlertNotificationService.h" #include <hal/nrf_rtc.h> #include <cstring> #include <algorithm> -#include "NotificationManager.h" +#include "components/ble/NotificationManager.h" #include "systemtask/SystemTask.h" using namespace Pinetime::Controllers; @@ -53,8 +53,9 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle // Ignore notifications with empty message const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); - if (packetLen <= headerSize) + if (packetLen <= headerSize) { return 0; + } size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize); auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize)); diff --git a/src/components/ble/BatteryInformationService.cpp b/src/components/ble/BatteryInformationService.cpp index 29178667..9a3f86f5 100644 --- a/src/components/ble/BatteryInformationService.cpp +++ b/src/components/ble/BatteryInformationService.cpp @@ -1,5 +1,5 @@ +#include "components/ble/BatteryInformationService.h" #include <nrf_log.h> -#include "BatteryInformationService.h" #include "components/battery/BatteryController.h" using namespace Pinetime::Controllers; diff --git a/src/components/ble/BleController.cpp b/src/components/ble/BleController.cpp index 7c14aeb7..a80c9719 100644 --- a/src/components/ble/BleController.cpp +++ b/src/components/ble/BleController.cpp @@ -1,4 +1,4 @@ -#include "BleController.h" +#include "components/ble/BleController.h" using namespace Pinetime::Controllers; diff --git a/src/components/ble/BleController.h b/src/components/ble/BleController.h index 2cba26a9..72b87663 100644 --- a/src/components/ble/BleController.h +++ b/src/components/ble/BleController.h @@ -9,7 +9,7 @@ namespace Pinetime { public: using BleAddress = std::array<uint8_t, 6>; enum class FirmwareUpdateStates { Idle, Running, Validated, Error }; - enum class AddressTypes { Public, Random }; + enum class AddressTypes { Public, Random, RPA_Public, RPA_Random }; Ble() = default; bool IsConnected() const { @@ -48,6 +48,12 @@ namespace Pinetime { void AddressType(AddressTypes t) { addressType = t; } + void SetPairingKey(uint32_t k) { + pairingKey = k; + } + uint32_t GetPairingKey() const { + return pairingKey; + } private: bool isConnected = false; @@ -57,6 +63,7 @@ namespace Pinetime { FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle; BleAddress address; AddressTypes addressType; + uint32_t pairingKey = 0; }; } -}
\ No newline at end of file +} diff --git a/src/components/ble/CurrentTimeClient.cpp b/src/components/ble/CurrentTimeClient.cpp index 90d1f0c7..dd8171b9 100644 --- a/src/components/ble/CurrentTimeClient.cpp +++ b/src/components/ble/CurrentTimeClient.cpp @@ -1,4 +1,4 @@ -#include "CurrentTimeClient.h" +#include "components/ble/CurrentTimeClient.h" #include <hal/nrf_rtc.h> #include <nrf_log.h> #include "components/datetime/DateTimeController.h" diff --git a/src/components/ble/CurrentTimeClient.h b/src/components/ble/CurrentTimeClient.h index 6424c035..9e48be79 100644 --- a/src/components/ble/CurrentTimeClient.h +++ b/src/components/ble/CurrentTimeClient.h @@ -5,7 +5,7 @@ #undef max #undef min #include <cstdint> -#include "BleClient.h" +#include "components/ble/BleClient.h" namespace Pinetime { namespace Controllers { diff --git a/src/components/ble/CurrentTimeService.cpp b/src/components/ble/CurrentTimeService.cpp index eefb7ec1..e509aeaf 100644 --- a/src/components/ble/CurrentTimeService.cpp +++ b/src/components/ble/CurrentTimeService.cpp @@ -1,4 +1,4 @@ -#include "CurrentTimeService.h" +#include "components/ble/CurrentTimeService.h" #include <hal/nrf_rtc.h> #include <nrf_log.h> diff --git a/src/components/ble/DeviceInformationService.cpp b/src/components/ble/DeviceInformationService.cpp index 778d6e35..0f47c90f 100644 --- a/src/components/ble/DeviceInformationService.cpp +++ b/src/components/ble/DeviceInformationService.cpp @@ -1,4 +1,4 @@ -#include "DeviceInformationService.h" +#include "components/ble/DeviceInformationService.h" using namespace Pinetime::Controllers; diff --git a/src/components/ble/DeviceInformationService.h b/src/components/ble/DeviceInformationService.h index 54b3e961..a269afb4 100644 --- a/src/components/ble/DeviceInformationService.h +++ b/src/components/ble/DeviceInformationService.h @@ -1,4 +1,5 @@ #pragma once +#include <cstdint> #define min // workaround: nimble's min/max macros conflict with libstdc++ #define max #include <host/ble_gap.h> diff --git a/src/components/ble/DfuService.cpp b/src/components/ble/DfuService.cpp index 3d6416fa..71dcc7e6 100644 --- a/src/components/ble/DfuService.cpp +++ b/src/components/ble/DfuService.cpp @@ -1,4 +1,4 @@ -#include "DfuService.h" +#include "components/ble/DfuService.h" #include <cstring> #include "components/ble/BleController.h" #include "drivers/SpiNorFlash.h" diff --git a/src/components/ble/FSService.cpp b/src/components/ble/FSService.cpp new file mode 100644 index 00000000..8dc9ed67 --- /dev/null +++ b/src/components/ble/FSService.cpp @@ -0,0 +1,330 @@ +#include <nrf_log.h> +#include "FSService.h" +#include "components/ble/BleController.h" +#include "systemtask/SystemTask.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t FSService::fsServiceUuid; +constexpr ble_uuid128_t FSService::fsVersionUuid; +constexpr ble_uuid128_t FSService::fsTransferUuid; + +int FSServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + auto* fsService = static_cast<FSService*>(arg); + return fsService->OnFSServiceRequested(conn_handle, attr_handle, ctxt); +} + +FSService::FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs) + : systemTask {systemTask}, + fs {fs}, + characteristicDefinition {{.uuid = &fsVersionUuid.u, + .access_cb = FSServiceCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + .val_handle = &versionCharacteristicHandle}, + { + .uuid = &fsTransferUuid.u, + .access_cb = FSServiceCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, + .val_handle = &transferCharacteristicHandle, + }, + {0}}, + serviceDefinition { + {/* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &fsServiceUuid.u, + .characteristics = characteristicDefinition}, + {0}, + } { +} + +void FSService::Init() { + int res = 0; + res = ble_gatts_count_cfg(serviceDefinition); + ASSERT(res == 0); + + res = ble_gatts_add_svcs(serviceDefinition); + ASSERT(res == 0); +} + +int FSService::OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) { + if (attributeHandle == versionCharacteristicHandle) { + NRF_LOG_INFO("FS_S : handle = %d", versionCharacteristicHandle); + int res = os_mbuf_append(context->om, &fsVersion, sizeof(fsVersion)); + return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + if (attributeHandle == transferCharacteristicHandle) { + return FSCommandHandler(connectionHandle, context->om); + } + return 0; +} + +int FSService::FSCommandHandler(uint16_t connectionHandle, os_mbuf* om) { + auto command = static_cast<commands>(om->om_data[0]); + NRF_LOG_INFO("[FS_S] -> FSCommandHandler Command %d", command); + // Just always make sure we are awake... + systemTask.PushMessage(Pinetime::System::Messages::StartFileTransfer); + vTaskDelay(10); + while (systemTask.IsSleeping()) { + vTaskDelay(100); // 50ms + } + lfs_dir_t dir = {0}; + lfs_info info = {0}; + lfs_file f = {0}; + switch (command) { + case commands::READ: { + NRF_LOG_INFO("[FS_S] -> Read"); + auto* header = (ReadHeader*) om->om_data; + uint16_t plen = header->pathlen; + if (plen > maxpathlen) { //> counts for null term + return -1; + } + memcpy(filepath, header->pathstr, plen); + filepath[plen] = 0; // Copy and null teminate string + ReadResponse resp; + os_mbuf* om; + resp.command = commands::READ_DATA; + resp.status = 0x01; + resp.chunkoff = header->chunkoff; + int res = fs.Stat(filepath, &info); + if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) { + resp.status = (int8_t) res; + resp.chunklen = 0; + resp.totallen = 0; + om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse)); + } else { + resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow + resp.totallen = info.size; + fs.FileOpen(&f, filepath, LFS_O_RDONLY); + fs.FileSeek(&f, header->chunkoff); + uint8_t fileData[resp.chunklen] = {0}; + resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen); + om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse)); + os_mbuf_append(om, fileData, resp.chunklen); + fs.FileClose(&f); + } + + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + } + case commands::READ_PACING: { + NRF_LOG_INFO("[FS_S] -> Readpacing"); + auto* header = (ReadHeader*) om->om_data; + ReadResponse resp; + resp.command = commands::READ_DATA; + resp.status = 0x01; + resp.chunkoff = header->chunkoff; + int res = fs.Stat(filepath, &info); + if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) { + resp.status = (int8_t) res; + resp.chunklen = 0; + resp.totallen = 0; + } else { + resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow + resp.totallen = info.size; + fs.FileOpen(&f, filepath, LFS_O_RDONLY); + fs.FileSeek(&f, header->chunkoff); + } + os_mbuf* om; + if (resp.chunklen > 0) { + uint8_t fileData[resp.chunklen] = {0}; + resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen); + om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse)); + os_mbuf_append(om, fileData, resp.chunklen); + } else { + resp.chunklen = 0; + om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse)); + } + fs.FileClose(&f); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + } + case commands::WRITE: { + NRF_LOG_INFO("[FS_S] -> Write"); + auto* header = (WriteHeader*) om->om_data; + uint16_t plen = header->pathlen; + if (plen > maxpathlen) { //> counts for null term + return -1; // TODO make this actually return a BLE notif + } + memcpy(filepath, header->pathstr, plen); + filepath[plen] = 0; // Copy and null teminate string + fileSize = header->totalSize; + WriteResponse resp; + resp.command = commands::WRITE_PACING; + resp.offset = header->offset; + resp.modTime = 0; + + int res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT); + if (res == 0) { + fs.FileClose(&f); + resp.status = (res == 0) ? 0x01 : (int8_t) res; + } + resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset); + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse)); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + } + case commands::WRITE_DATA: { + NRF_LOG_INFO("[FS_S] -> WriteData"); + auto* header = (WritePacing*) om->om_data; + WriteResponse resp; + resp.command = commands::WRITE_PACING; + resp.offset = header->offset; + int res = 0; + + if (!(res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT))) { + if ((res = fs.FileSeek(&f, header->offset)) >= 0) { + res = fs.FileWrite(&f, header->data, header->dataSize); + } + fs.FileClose(&f); + } + if (res < 0) { + resp.status = (int8_t) res; + } + resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset); + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse)); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + } + case commands::DELETE: { + NRF_LOG_INFO("[FS_S] -> Delete"); + auto* header = (DelHeader*) om->om_data; + uint16_t plen = header->pathlen; + char path[plen + 1] = {0}; + memcpy(path, header->pathstr, plen); + path[plen] = 0; // Copy and null teminate string + DelResponse resp {}; + resp.command = commands::DELETE_STATUS; + int res = fs.FileDelete(path); + resp.status = (res == 0) ? 0x01 : (int8_t) res; + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(DelResponse)); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + } + case commands::MKDIR: { + NRF_LOG_INFO("[FS_S] -> MKDir"); + auto* header = (MKDirHeader*) om->om_data; + uint16_t plen = header->pathlen; + char path[plen + 1] = {0}; + memcpy(path, header->pathstr, plen); + path[plen] = 0; // Copy and null teminate string + MKDirResponse resp {}; + resp.command = commands::MKDIR_STATUS; + resp.modification_time = 0; + int res = fs.DirCreate(path); + resp.status = (res == 0) ? 0x01 : (int8_t) res; + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MKDirResponse)); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + } + case commands::LISTDIR: { + NRF_LOG_INFO("[FS_S] -> ListDir"); + ListDirHeader* header = (ListDirHeader*) om->om_data; + uint16_t plen = header->pathlen; + char path[plen + 1] = {0}; + path[plen] = 0; // Copy and null teminate string + memcpy(path, header->pathstr, plen); + + ListDirResponse resp {}; + + resp.command = commands::LISTDIR_ENTRY; + resp.status = 0x01; + resp.totalentries = 0; + resp.entry = 0; + resp.modification_time = 0; + int res = fs.DirOpen(path, &dir); + if (res != 0) { + resp.status = (int8_t) res; + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse)); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + }; + while (fs.DirRead(&dir, &info)) { + resp.totalentries++; + } + fs.DirRewind(&dir); + while (true) { + res = fs.DirRead(&dir, &info); + if (res <= 0) { + break; + } + switch (info.type) { + case LFS_TYPE_REG: { + resp.flags = 0; + resp.file_size = info.size; + break; + } + case LFS_TYPE_DIR: { + resp.flags = 1; + resp.file_size = 0; + break; + } + } + + // strcpy(resp.path, info.name); + resp.path_length = strlen(info.name); + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse)); + os_mbuf_append(om, info.name, resp.path_length); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + /* + * Todo Figure out how to know when the previous Notify was TX'd + * For now just delay 100ms to make sure that the data went out... + */ + vTaskDelay(100); // Allow stuff to actually go out over the BLE conn + resp.entry++; + } + assert(fs.DirClose(&dir) == 0); + resp.file_size = 0; + resp.path_length = 0; + resp.flags = 0; + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse)); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + break; + } + case commands::MOVE: { + NRF_LOG_INFO("[FS_S] -> Move"); + MoveHeader* header = (MoveHeader*) om->om_data; + uint16_t plen = header->OldPathLength; + // Null Terminate string + header->pathstr[plen] = 0; + char path[header->NewPathLength + 1] = {0}; + memcpy(path, &header->pathstr[plen + 1], header->NewPathLength); + path[header->NewPathLength] = 0; // Copy and null teminate string + MoveResponse resp {}; + resp.command = commands::MOVE_STATUS; + int8_t res = (int8_t) fs.Rename(header->pathstr, path); + resp.status = (res == 0) ? 1 : res; + auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MoveResponse)); + ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om); + } + default: + break; + } + NRF_LOG_INFO("[FS_S] -> done "); + systemTask.PushMessage(Pinetime::System::Messages::StopFileTransfer); + return 0; +} + +// Loads resp with file data given a valid filepath header and resp +void FSService::prepareReadDataResp(ReadHeader* header, ReadResponse* resp) { + // uint16_t plen = header->pathlen; + resp->command = commands::READ_DATA; + resp->chunkoff = header->chunkoff; + resp->status = 0x01; + struct lfs_info info = {}; + int res = fs.Stat(filepath, &info); + if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) { + resp->status = 0x03; + resp->chunklen = 0; + resp->totallen = 0; + } else { + lfs_file f; + resp->chunklen = std::min(header->chunksize, info.size); + resp->totallen = info.size; + fs.FileOpen(&f, filepath, LFS_O_RDONLY); + fs.FileSeek(&f, header->chunkoff); + resp->chunklen = fs.FileRead(&f, resp->chunk, resp->chunklen); + fs.FileClose(&f); + } +} diff --git a/src/components/ble/FSService.h b/src/components/ble/FSService.h new file mode 100644 index 00000000..828925a8 --- /dev/null +++ b/src/components/ble/FSService.h @@ -0,0 +1,191 @@ +#pragma once +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include <host/ble_gap.h> +#undef max +#undef min + +#include "components/fs/FS.h" + +namespace Pinetime { + namespace System { + class SystemTask; + } + namespace Controllers { + class Ble; + class FSService { + public: + FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs); + void Init(); + + int OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context); + void NotifyFSRaw(uint16_t connectionHandle); + + private: + Pinetime::System::SystemTask& systemTask; + Pinetime::Controllers::FS& fs; + static constexpr uint16_t FSServiceId {0xFEBB}; + static constexpr uint16_t fsVersionId {0x0100}; + static constexpr uint16_t fsTransferId {0x0200}; + uint16_t fsVersion = {0x0004}; + static constexpr uint16_t maxpathlen = 256; + static constexpr ble_uuid16_t fsServiceUuid { + .u {.type = BLE_UUID_TYPE_16}, + .value = {0xFEBB}}; // {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0xBB, 0xFE, 0xAF, 0xAD}}; + + static constexpr ble_uuid128_t fsVersionUuid { + .u {.type = BLE_UUID_TYPE_128}, + .value = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x01, 0xAF, 0xAD}}; + + static constexpr ble_uuid128_t fsTransferUuid { + .u {.type = BLE_UUID_TYPE_128}, + .value = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x02, 0xAF, 0xAD}}; + + struct ble_gatt_chr_def characteristicDefinition[3]; + struct ble_gatt_svc_def serviceDefinition[2]; + uint16_t versionCharacteristicHandle; + uint16_t transferCharacteristicHandle; + + enum class commands : uint8_t { + INVALID = 0x00, + READ = 0x10, + READ_DATA = 0x11, + READ_PACING = 0x12, + WRITE = 0x20, + WRITE_PACING = 0x21, + WRITE_DATA = 0x22, + DELETE = 0x30, + DELETE_STATUS = 0x31, + MKDIR = 0x40, + MKDIR_STATUS = 0x41, + LISTDIR = 0x50, + LISTDIR_ENTRY = 0x51, + MOVE = 0x60, + MOVE_STATUS = 0x61 + }; + enum class FSState : uint8_t { + IDLE = 0x00, + READ = 0x01, + WRITE = 0x02, + }; + FSState state; + char filepath[maxpathlen]; // TODO ..ugh fixed filepath len + int fileSize; + using ReadHeader = struct __attribute__((packed)) { + commands command; + uint8_t padding; + uint16_t pathlen; + uint32_t chunkoff; + uint32_t chunksize; + char pathstr[]; + }; + + using ReadResponse = struct __attribute__((packed)) { + commands command; + uint8_t status; + uint16_t padding; + uint32_t chunkoff; + uint32_t totallen; + uint32_t chunklen; + uint8_t chunk[]; + }; + using ReadPacing = struct __attribute__((packed)) { + commands command; + uint8_t status; + uint16_t padding; + uint32_t chunkoff; + uint32_t chunksize; + }; + + using WriteHeader = struct __attribute__((packed)) { + commands command; + uint8_t padding; + uint16_t pathlen; + uint32_t offset; + uint64_t modTime; + uint32_t totalSize; + char pathstr[]; + }; + + using WriteResponse = struct __attribute__((packed)) { + commands command; + uint8_t status; + uint16_t padding; + uint32_t offset; + uint64_t modTime; + uint32_t freespace; + }; + + using WritePacing = struct __attribute__((packed)) { + commands command; + uint8_t status; + uint16_t padding; + uint32_t offset; + uint32_t dataSize; + uint8_t data[]; + }; + using ListDirHeader = struct __attribute__((packed)) { + commands command; + uint8_t padding; + uint16_t pathlen; + char pathstr[]; + }; + + using ListDirResponse = struct __attribute__((packed)) { + commands command; + uint8_t status; + uint16_t path_length; + uint32_t entry; + uint32_t totalentries; + uint32_t flags; + uint64_t modification_time; + uint32_t file_size; + char path[]; + }; + + using MKDirHeader = struct __attribute__((packed)) { + commands command; + uint8_t padding; + uint16_t pathlen; + uint32_t padding2; + uint64_t time; + char pathstr[]; + }; + + using MKDirResponse = struct __attribute__((packed)) { + commands command; + uint8_t status; + uint32_t padding1; + uint16_t padding2; + uint64_t modification_time; + }; + + using DelHeader = struct __attribute__((packed)) { + commands command; + uint8_t padding; + uint16_t pathlen; + char pathstr[]; + }; + + using DelResponse = struct __attribute__((packed)) { + commands command; + uint8_t status; + }; + using MoveHeader = struct __attribute__((packed)) { + commands command; + uint8_t padding; + uint16_t OldPathLength; + uint16_t NewPathLength; + char pathstr[]; + }; + + using MoveResponse = struct __attribute__((packed)) { + commands command; + uint8_t status; + }; + + int FSCommandHandler(uint16_t connectionHandle, os_mbuf* om); + void prepareReadDataResp(ReadHeader* header, ReadResponse* resp); + }; + } +} diff --git a/src/components/ble/HeartRateService.cpp b/src/components/ble/HeartRateService.cpp index 75a038a2..f178af79 100644 --- a/src/components/ble/HeartRateService.cpp +++ b/src/components/ble/HeartRateService.cpp @@ -1,4 +1,4 @@ -#include "HeartRateService.h" +#include "components/ble/HeartRateService.h" #include "components/heartrate/HeartRateController.h" #include "systemtask/SystemTask.h" diff --git a/src/components/ble/ImmediateAlertService.cpp b/src/components/ble/ImmediateAlertService.cpp index 17ed1a96..c80b3783 100644 --- a/src/components/ble/ImmediateAlertService.cpp +++ b/src/components/ble/ImmediateAlertService.cpp @@ -1,6 +1,6 @@ -#include "ImmediateAlertService.h" +#include "components/ble/ImmediateAlertService.h" #include <cstring> -#include "NotificationManager.h" +#include "components/ble/NotificationManager.h" #include "systemtask/SystemTask.h" using namespace Pinetime::Controllers; diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp index b4786ab5..6381915d 100644 --- a/src/components/ble/MotionService.cpp +++ b/src/components/ble/MotionService.cpp @@ -1,11 +1,11 @@ -#include "MotionService.h" -#include "components/motion//MotionController.h" +#include "components/ble/MotionService.h" +#include "components/motion/MotionController.h" #include "systemtask/SystemTask.h" using namespace Pinetime::Controllers; namespace { - // 0002yyxx-78fc-48fe-8e23-433b3a1942d0 + // 0003yyxx-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}, @@ -13,7 +13,7 @@ namespace { }; } - // 00020000-78fc-48fe-8e23-433b3a1942d0 + // 00030000-78fc-48fe-8e23-433b3a1942d0 constexpr ble_uuid128_t BaseUuid() { return CharUuid(0x00, 0x00); } diff --git a/src/components/ble/MusicService.cpp b/src/components/ble/MusicService.cpp index 74fe9522..3457ce4c 100644 --- a/src/components/ble/MusicService.cpp +++ b/src/components/ble/MusicService.cpp @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "MusicService.h" +#include "components/ble/MusicService.h" #include "systemtask/SystemTask.h" namespace { diff --git a/src/components/ble/NavigationService.cpp b/src/components/ble/NavigationService.cpp index b49148d2..5418b9e5 100644 --- a/src/components/ble/NavigationService.cpp +++ b/src/components/ble/NavigationService.cpp @@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "NavigationService.h" +#include "components/ble/NavigationService.h" #include "systemtask/SystemTask.h" diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 1bcae1bc..d8510bd3 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -1,4 +1,6 @@ -#include "NimbleController.h" +#include "components/ble/NimbleController.h" +#include <cstring> + #include <hal/nrf_rtc.h> #define min // workaround: nimble's min/max macros conflict with libstdc++ #define max @@ -6,13 +8,16 @@ #include <host/ble_hs.h> #include <host/ble_hs_id.h> #include <host/util/util.h> -#undef max -#undef min +#include <controller/ble_ll.h> +#include <controller/ble_hw.h> #include <services/gap/ble_svc_gap.h> #include <services/gatt/ble_svc_gatt.h> +#undef max +#undef min #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/datetime/DateTimeController.h" +#include "components/fs/FS.h" #include "systemtask/SystemTask.h" using namespace Pinetime::Controllers; @@ -24,37 +29,44 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController) + Controllers::MotionController& motionController, + Controllers::FS& fs) : systemTask {systemTask}, bleController {bleController}, dateTimeController {dateTimeController}, notificationManager {notificationManager}, spiNorFlash {spiNorFlash}, + fs {fs}, dfuService {systemTask, bleController, spiNorFlash}, + currentTimeClient {dateTimeController}, anService {systemTask, notificationManager}, alertNotificationClient {systemTask, notificationManager}, currentTimeService {dateTimeController}, musicService {systemTask}, + weatherService {systemTask, dateTimeController}, navService {systemTask}, batteryInformationService {batteryController}, immediateAlertService {systemTask, notificationManager}, heartRateService {systemTask, heartRateController}, - motionService{systemTask, motionController}, + motionService {systemTask, motionController}, + fsService {systemTask, fs}, serviceDiscovery({¤tTimeClient, &alertNotificationClient}) { } void nimble_on_reset(int reason) { - NRF_LOG_INFO("Resetting state; reason=%d\n", reason); + NRF_LOG_INFO("Nimble lost sync, resetting state; reason=%d", reason); } void nimble_on_sync(void) { - int rc; + int rc; - rc = ble_hs_util_ensure_addr(0); - ASSERT(rc == 0); + NRF_LOG_INFO("Nimble is synced"); - nptr->StartAdvertising(); + rc = ble_hs_util_ensure_addr(0); + ASSERT(rc == 0); + + nptr->StartAdvertising(); } int GAPEventCallback(struct ble_gap_event* event, void* arg) { @@ -69,6 +81,7 @@ void NimbleController::Init() { nptr = this; ble_hs_cfg.reset_cb = nimble_on_reset; ble_hs_cfg.sync_cb = nimble_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; ble_svc_gap_init(); ble_svc_gatt_init(); @@ -77,6 +90,7 @@ void NimbleController::Init() { currentTimeClient.Init(); currentTimeService.Init(); musicService.Init(); + weatherService.Init(); navService.Init(); anService.Init(); dfuService.Init(); @@ -84,6 +98,7 @@ void NimbleController::Init() { immediateAlertService.Init(); heartRateService.Init(); motionService.Init(); + fsService.Init(); int rc; rc = ble_hs_util_ensure_addr(0); @@ -97,28 +112,36 @@ void NimbleController::Init() { Pinetime::Controllers::Ble::BleAddress address; rc = ble_hs_id_copy_addr(addrType, address.data(), nullptr); ASSERT(rc == 0); - bleController.AddressType((addrType == 0) ? Ble::AddressTypes::Public : Ble::AddressTypes::Random); + bleController.Address(std::move(address)); + switch (addrType) { + case BLE_OWN_ADDR_PUBLIC: + bleController.AddressType(Ble::AddressTypes::Public); + break; + case BLE_OWN_ADDR_RANDOM: + bleController.AddressType(Ble::AddressTypes::Random); + break; + case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT: + bleController.AddressType(Ble::AddressTypes::RPA_Public); + break; + case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT: + bleController.AddressType(Ble::AddressTypes::RPA_Random); + break; + } rc = ble_gatts_start(); ASSERT(rc == 0); - if (!ble_gap_adv_active() && !bleController.IsConnected()) - StartAdvertising(); + RestoreBond(); + + StartAdvertising(); } void NimbleController::StartAdvertising() { - int rc; - - /* set adv parameters */ struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; - /* advertising payload is split into advertising data and advertising - response, because all data cannot fit into single packet; name of device - is sent as response to scan request */ struct ble_hs_adv_fields rsp_fields; - /* fill all fields and parameters with zeros */ memset(&adv_params, 0, sizeof(adv_params)); memset(&fields, 0, sizeof(fields)); memset(&rsp_fields, 0, sizeof(rsp_fields)); @@ -141,10 +164,11 @@ void NimbleController::StartAdvertising() { fields.uuids128_is_complete = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; - rsp_fields.name = (uint8_t*) deviceName; + rsp_fields.name = reinterpret_cast<const uint8_t*>(deviceName); rsp_fields.name_len = strlen(deviceName); rsp_fields.name_is_complete = 1; + int rc; rc = ble_gap_adv_set_fields(&fields); ASSERT(rc == 0); @@ -159,15 +183,14 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { switch (event->type) { case BLE_GAP_EVENT_ADV_COMPLETE: NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE"); - NRF_LOG_INFO("reason=%d; status=%d", event->adv_complete.reason, event->connect.status); + NRF_LOG_INFO("reason=%d; status=%0X", event->adv_complete.reason, event->connect.status); StartAdvertising(); break; case BLE_GAP_EVENT_CONNECT: - NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONNECT"); - /* A new connection was established or a connection attempt failed. */ - NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed", event->connect.status); + NRF_LOG_INFO("Connect event : BLE_GAP_EVENT_CONNECT"); + NRF_LOG_INFO("connection %s; status=%0X ", event->connect.status == 0 ? "established" : "failed", event->connect.status); if (event->connect.status != 0) { /* Connection failed; resume advertising. */ @@ -186,10 +209,14 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { break; case BLE_GAP_EVENT_DISCONNECT: - NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_DISCONNECT"); + /* Connection terminated; resume advertising. */ + NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT"); NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason); - /* Connection terminated; resume advertising. */ + if (event->disconnect.conn.sec_state.bonded) { + PersistBond(event->disconnect.conn); + } + currentTimeClient.Reset(); alertNotificationClient.Reset(); connectionHandle = BLE_HS_CONN_HANDLE_NONE; @@ -199,18 +226,67 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { break; case BLE_GAP_EVENT_CONN_UPDATE: - NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONN_UPDATE"); /* The central has updated the connection parameters. */ - NRF_LOG_INFO("update status=%d ", event->conn_update.status); + NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE"); + NRF_LOG_INFO("update status=%0X ", event->conn_update.status); + break; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + /* The central has requested updated connection parameters */ + NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE_REQ"); + NRF_LOG_INFO("update request : itvl_min=%d itvl_max=%d latency=%d supervision=%d", + event->conn_update_req.peer_params->itvl_min, + event->conn_update_req.peer_params->itvl_max, + event->conn_update_req.peer_params->latency, + event->conn_update_req.peer_params->supervision_timeout); break; case BLE_GAP_EVENT_ENC_CHANGE: /* Encryption has been enabled or disabled for this connection. */ - NRF_LOG_INFO("encryption change event; status=%d ", event->enc_change.status); + NRF_LOG_INFO("Security event : BLE_GAP_EVENT_ENC_CHANGE"); + NRF_LOG_INFO("encryption change event; status=%0X ", event->enc_change.status); + + if (event->enc_change.status == 0) { + struct ble_gap_conn_desc desc; + ble_gap_conn_find(event->enc_change.conn_handle, &desc); + if (desc.sec_state.bonded) { + PersistBond(desc); + } + + NRF_LOG_INFO("new state: encrypted=%d authenticated=%d bonded=%d key_size=%d", + desc.sec_state.encrypted, + desc.sec_state.authenticated, + desc.sec_state.bonded, + desc.sec_state.key_size); + } + break; + + case BLE_GAP_EVENT_PASSKEY_ACTION: + /* Authentication has been requested for this connection. + * + * BLE authentication is determined by the combination of I/O capabilities + * on the central and peripheral. When the peripheral is display only and + * the central has a keyboard and display then passkey auth is selected. + * When both the central and peripheral have displays and support yes/no + * buttons then numeric comparison is selected. We currently advertise + * display capability only so we only handle the "display" action here. + * + * Standards insist that the rand() PRNG be deterministic. + * Use the tinycrypt prng here since rand() is predictable. + */ + NRF_LOG_INFO("Security event : BLE_GAP_EVENT_PASSKEY_ACTION"); + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + struct ble_sm_io pkey = {0}; + pkey.action = event->passkey.params.action; + pkey.passkey = ble_ll_rand() % 1000000; + bleController.SetPairingKey(pkey.passkey); + systemTask.PushMessage(Pinetime::System::Messages::OnPairing); + ble_sm_inject_io(event->passkey.conn_handle, &pkey); + } break; case BLE_GAP_EVENT_SUBSCRIBE: - NRF_LOG_INFO("subscribe event; conn_handle=%d attr_handle=%d " + NRF_LOG_INFO("Subscribe event; conn_handle=%d attr_handle=%d " "reason=%d prevn=%d curn=%d previ=%d curi=???\n", event->subscribe.conn_handle, event->subscribe.attr_handle, @@ -219,26 +295,24 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { event->subscribe.cur_notify, event->subscribe.prev_indicate); - if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { + 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) { + } 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) { + } 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: - NRF_LOG_INFO("mtu update event; conn_handle=%d cid=%d mtu=%d\n", - event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value); + NRF_LOG_INFO("MTU Update event; conn_handle=%d cid=%d mtu=%d", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value); break; case BLE_GAP_EVENT_REPEAT_PAIRING: { + NRF_LOG_INFO("Pairing event : BLE_GAP_EVENT_REPEAT_PAIRING"); /* We already have a bond with the peer, but it is attempting to * establish a new secure link. This app sacrifices security for * convenience: just throw away the old bond and accept the new link. @@ -257,6 +331,8 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { case BLE_GAP_EVENT_NOTIFY_RX: { /* Peer sent us a notification or indication. */ + /* Attribute data is contained in event->notify_rx.attr_data. */ + NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_RX"); size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om); NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d " @@ -268,10 +344,17 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { alertNotificationClient.OnNotification(event); } break; - /* Attribute data is contained in event->notify_rx.attr_data. */ + + case BLE_GAP_EVENT_NOTIFY_TX: + NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_TX"); + break; + + case BLE_GAP_EVENT_IDENTITY_RESOLVED: + NRF_LOG_INFO("Identity event : BLE_GAP_EVENT_IDENTITY_RESOLVED"); + break; default: - // NRF_LOG_INFO("Advertising event : %d", event->type); + NRF_LOG_INFO("UNHANDLED GAP event : %d", event->type); break; } return 0; @@ -292,3 +375,82 @@ void NimbleController::NotifyBatteryLevel(uint8_t level) { batteryInformationService.NotifyBatteryLevel(connectionHandle, level); } } + +void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) { + union ble_store_key key; + union ble_store_value our_sec, peer_sec, peer_cccd_set[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)] = {0}; + int rc; + + memset(&key, 0, sizeof key); + memset(&our_sec, 0, sizeof our_sec); + key.sec.peer_addr = desc.peer_id_addr; + rc = ble_store_read_our_sec(&key.sec, &our_sec.sec); + + if (memcmp(&our_sec.sec, &bondId, sizeof bondId) == 0) { + return; + } + + memcpy(&bondId, &our_sec.sec, sizeof bondId); + + memset(&key, 0, sizeof key); + memset(&peer_sec, 0, sizeof peer_sec); + key.sec.peer_addr = desc.peer_id_addr; + rc += ble_store_read_peer_sec(&key.sec, &peer_sec.sec); + + if (rc == 0) { + memset(&key, 0, sizeof key); + key.cccd.peer_addr = desc.peer_id_addr; + int peer_count = 0; + ble_store_util_count(BLE_STORE_OBJ_TYPE_CCCD, &peer_count); + for (int i = 0; i < peer_count; i++) { + key.cccd.idx = peer_count; + ble_store_read_cccd(&key.cccd, &peer_cccd_set[i].cccd); + } + + /* Wakeup Spi and SpiNorFlash before accessing the file system + * This should be fixed in the FS driver + */ + systemTask.PushMessage(Pinetime::System::Messages::GoToRunning); + systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping); + vTaskDelay(10); + + lfs_file_t file_p; + + rc = fs.FileOpen(&file_p, "/bond.dat", LFS_O_WRONLY | LFS_O_CREAT); + if (rc == 0) { + fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&our_sec.sec), sizeof our_sec); + fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_sec.sec), sizeof peer_sec); + fs.FileWrite(&file_p, reinterpret_cast<const uint8_t*>(&peer_count), 1); + for (int i = 0; i < peer_count; i++) { + fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_cccd_set[i].cccd), sizeof(struct ble_store_value_cccd)); + } + fs.FileClose(&file_p); + } + systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); + } +} + +void NimbleController::RestoreBond() { + lfs_file_t file_p; + union ble_store_value sec, cccd; + uint8_t peer_count = 0; + + if (fs.FileOpen(&file_p, "/bond.dat", LFS_O_RDONLY) == 0) { + memset(&sec, 0, sizeof sec); + fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec); + ble_store_write_our_sec(&sec.sec); + + memset(&sec, 0, sizeof sec); + fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec); + ble_store_write_peer_sec(&sec.sec); + + fs.FileRead(&file_p, &peer_count, 1); + for (int i = 0; i < peer_count; i++) { + fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&cccd.cccd), sizeof(struct ble_store_value_cccd)); + ble_store_write_cccd(&cccd.cccd); + } + + fs.FileClose(&file_p); + fs.FileDelete("/bond.dat"); + } +} diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 76f89ba8..2b300e63 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -7,19 +7,22 @@ #include <host/ble_gap.h> #undef max #undef min -#include "AlertNotificationClient.h" -#include "AlertNotificationService.h" -#include "BatteryInformationService.h" -#include "CurrentTimeClient.h" -#include "CurrentTimeService.h" -#include "DeviceInformationService.h" -#include "DfuService.h" -#include "ImmediateAlertService.h" -#include "MusicService.h" -#include "NavigationService.h" -#include "ServiceDiscovery.h" -#include "HeartRateService.h" -#include "MotionService.h" +#include "components/ble/AlertNotificationClient.h" +#include "components/ble/AlertNotificationService.h" +#include "components/ble/BatteryInformationService.h" +#include "components/ble/CurrentTimeClient.h" +#include "components/ble/CurrentTimeService.h" +#include "components/ble/DeviceInformationService.h" +#include "components/ble/DfuService.h" +#include "components/ble/HeartRateService.h" +#include "components/ble/ImmediateAlertService.h" +#include "components/ble/MusicService.h" +#include "components/ble/NavigationService.h" +#include "components/ble/ServiceDiscovery.h" +#include "components/ble/MotionService.h" +#include "components/ble/weather/WeatherService.h" +#include "components/fs/FS.h" +#include "components/ble/FSService.h" namespace Pinetime { namespace Drivers { @@ -45,7 +48,8 @@ namespace Pinetime { Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController); + Controllers::MotionController& motionController, + Pinetime::Controllers::FS& fs); void Init(); void StartAdvertising(); int OnGAPEvent(ble_gap_event* event); @@ -70,6 +74,9 @@ namespace Pinetime { Pinetime::Controllers::AlertNotificationService& alertService() { return anService; }; + Pinetime::Controllers::WeatherService& weather() { + return weatherService; + }; uint16_t connHandle(); void NotifyBatteryLevel(uint8_t level); @@ -79,12 +86,16 @@ namespace Pinetime { } private: + void PersistBond(struct ble_gap_conn_desc& desc); + void RestoreBond(); + static constexpr const char* deviceName = "InfiniTime"; Pinetime::System::SystemTask& systemTask; Pinetime::Controllers::Ble& bleController; DateTime& dateTimeController; Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Drivers::SpiNorFlash& spiNorFlash; + Pinetime::Controllers::FS& fs; Pinetime::Controllers::DfuService dfuService; DeviceInformationService deviceInformationService; @@ -93,23 +104,25 @@ namespace Pinetime { AlertNotificationClient alertNotificationClient; CurrentTimeService currentTimeService; MusicService musicService; + WeatherService weatherService; NavigationService navService; BatteryInformationService batteryInformationService; ImmediateAlertService immediateAlertService; HeartRateService heartRateService; MotionService motionService; + FSService fsService; + ServiceDiscovery serviceDiscovery; - uint8_t addrType; // 1 = Random, 0 = PUBLIC + uint8_t addrType; uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE; uint8_t fastAdvCount = 0; + uint8_t bondId[16] = {0}; ble_uuid128_t dfuServiceUuid { .u {.type = BLE_UUID_TYPE_128}, .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}}; - - ServiceDiscovery serviceDiscovery; }; - static NimbleController* nptr; + static NimbleController* nptr; } } diff --git a/src/components/ble/NotificationManager.cpp b/src/components/ble/NotificationManager.cpp index 7ffed300..ec99c4ed 100644 --- a/src/components/ble/NotificationManager.cpp +++ b/src/components/ble/NotificationManager.cpp @@ -1,4 +1,4 @@ -#include "NotificationManager.h" +#include "components/ble/NotificationManager.h" #include <cstring> #include <algorithm> diff --git a/src/components/ble/ServiceDiscovery.cpp b/src/components/ble/ServiceDiscovery.cpp index b36b241c..03bcfeb4 100644 --- a/src/components/ble/ServiceDiscovery.cpp +++ b/src/components/ble/ServiceDiscovery.cpp @@ -1,6 +1,6 @@ -#include "ServiceDiscovery.h" +#include "components/ble/ServiceDiscovery.h" #include <libraries/log/nrf_log.h> -#include "BleClient.h" +#include "components/ble/BleClient.h" using namespace Pinetime::Controllers; diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h new file mode 100644 index 00000000..613d5acb --- /dev/null +++ b/src/components/ble/weather/WeatherData.h @@ -0,0 +1,385 @@ +/* Copyright (C) 2021 Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +#pragma once + +/** + * Different weather events, weather data structures used by {@link WeatherService.h} + * + * How to upload events to the timeline? + * + * All timeline write payloads are simply CBOR-encoded payloads of the structs described below. + * + * All payloads have a mandatory header part and the dynamic part that + * depends on the event type specified in the header. If you don't, + * you'll get an error returned. Data is relatively well-validated, + * so keep in the bounds of the data types given. + * + * Write all struct members (CamelCase keys) into a single finite-sized map, and write it to the characteristic. + * Mind the MTU. + * + * How to debug? + * + * There's a Screen that you can compile into your firmware that shows currently valid events. + * You can adapt that to display something else. That part right now is very much work in progress + * because the exact requirements are not yet known. + * + * + * Implemented based on and other material: + * https://en.wikipedia.org/wiki/METAR + * https://www.weather.gov/jetstream/obscurationtypes + * http://www.faraim.org/aim/aim-4-03-14-493.html + */ + +namespace Pinetime { + namespace Controllers { + class WeatherData { + public: + /** + * Visibility obscuration types + */ + enum class obscurationtype { + /** No obscuration */ + None = 0, + /** Water particles suspended in the air; low visibility; does not fall */ + Fog = 1, + /** Tiny, dry particles in the air; invisible to the eye; opalescent */ + Haze = 2, + /** Small fire-created particles suspended in the air */ + Smoke = 3, + /** Fine rock powder, from for example volcanoes */ + Ash = 4, + /** Fine particles of earth suspended in the air by the wind */ + Dust = 5, + /** Fine particles of sand suspended in the air by the wind */ + Sand = 6, + /** Water particles suspended in the air; low-ish visibility; temperature is near dewpoint */ + Mist = 7, + /** This is SPECIAL in the sense that the thing raining down is doing the obscuration */ + Precipitation = 8, + Length + }; + + /** + * Types of precipitation + */ + enum class precipitationtype { + /** + * No precipitation + * + * Theoretically we could just _not_ send the event, but then + * how do we differentiate between no precipitation and + * no information about precipitation + */ + None = 0, + /** Drops larger than a drizzle; also widely separated drizzle */ + Rain = 1, + /** Fairly uniform rain consisting of fine drops */ + Drizzle = 2, + /** Rain that freezes upon contact with objects and ground */ + FreezingRain = 3, + /** Rain + hail; ice pellets; small translucent frozen raindrops */ + Sleet = 4, + /** Larger ice pellets; falling separately or in irregular clumps */ + Hail = 5, + /** Hail with smaller grains of ice; mini-snowballs */ + SmallHail = 6, + /** Snow... */ + Snow = 7, + /** Frozen drizzle; very small snow crystals */ + SnowGrains = 8, + /** Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions */ + IceCrystals = 9, + /** It's raining down ash, e.g. from a volcano */ + Ash = 10, + Length + }; + + /** + * These are special events that can "enhance" the "experience" of existing weather events + */ + enum class specialtype { + /** Strong wind with a sudden onset that lasts at least a minute */ + Squall = 0, + /** Series of waves in a water body caused by the displacement of a large volume of water */ + Tsunami = 1, + /** Violent; rotating column of air */ + Tornado = 2, + /** Unplanned; unwanted; uncontrolled fire in an area */ + Fire = 3, + /** Thunder and/or lightning */ + Thunder = 4, + Length + }; + + /** + * These are used for weather timeline manipulation + * that isn't just adding to the stack of weather events + */ + enum class controlcodes { + /** How much is stored already */ + GetLength = 0, + /** This wipes the entire timeline */ + DelTimeline = 1, + /** There's a currently valid timeline event with the given type */ + HasValidEvent = 3, + Length + }; + + /** + * Events have types + * then they're easier to parse after sending them over the air + */ + enum class eventtype : uint8_t { + /** @see obscuration */ + Obscuration = 0, + /** @see precipitation */ + Precipitation = 1, + /** @see wind */ + Wind = 2, + /** @see temperature */ + Temperature = 3, + /** @see airquality */ + AirQuality = 4, + /** @see special */ + Special = 5, + /** @see pressure */ + Pressure = 6, + /** @see location */ + Location = 7, + /** @see cloud */ + Clouds = 8, + /** @see humidity */ + Humidity = 9, + Length + }; + + /** + * Valid event query + * + * NOTE: Not currently available, until needs are better known + */ + class ValidEventQuery { + public: + static constexpr controlcodes code = controlcodes::HasValidEvent; + eventtype eventType; + }; + + /** The header used for further parsing */ + class TimelineHeader { + public: + /** + * UNIX timestamp + * TODO: This is currently WITH A TIMEZONE OFFSET! + * Please send events with the timestamp offset by the timezone. + **/ + uint64_t timestamp; + /** + * Time in seconds until the event expires + * + * 32 bits ought to be enough for everyone + * + * If there's a newer event of the same type then it overrides this one, even if it hasn't expired + */ + uint32_t expires; + /** + * What type of weather-related event + */ + eventtype eventType; + }; + + /** Specifies how cloudiness is stored */ + class Clouds : public TimelineHeader { + public: + /** Cloud coverage in percentage, 0-100% */ + uint8_t amount; + }; + + /** Specifies how obscuration is stored */ + class Obscuration : public TimelineHeader { + public: + /** Type of precipitation */ + obscurationtype type; + /** + * Visibility distance in meters + * 65535 is reserved for unspecified + */ + uint16_t amount; + }; + + /** Specifies how precipitation is stored */ + class Precipitation : public TimelineHeader { + public: + /** Type of precipitation */ + precipitationtype type; + /** + * How much is it going to rain? In millimeters + * 255 is reserved for unspecified + **/ + uint8_t amount; + }; + + /** + * How wind speed is stored + * + * In order to represent bursts of wind instead of constant wind, + * you have minimum and maximum speeds. + * + * As direction can fluctuate wildly and some watchfaces might wish to display it nicely, + * we're following the aerospace industry weather report option of specifying a range. + */ + class Wind : public TimelineHeader { + public: + /** Meters per second */ + uint8_t speedMin; + /** Meters per second */ + uint8_t speedMax; + /** Unitless direction between 0-255; approximately 1 unit per 0.71 degrees */ + uint8_t directionMin; + /** Unitless direction between 0-255; approximately 1 unit per 0.71 degrees */ + uint8_t directionMax; + }; + + /** + * How temperature is stored + * + * As it's annoying to figure out the dewpoint on the watch, + * please send it from the companion + * + * We don't do floats, picodegrees are not useful. Make sure to multiply. + */ + class Temperature : public TimelineHeader { + public: + /** + * Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) + * -32768 is reserved for "no data" + */ + int16_t temperature; + /** + * Dewpoint °C but multiplied by 100 (e.g. -12.50°C becomes -1250) + * -32768 is reserved for "no data" + */ + int16_t dewPoint; + }; + + /** + * How location info is stored + * + * This can be mostly static with long expiration, + * as it usually is, but it could change during a trip for ex. + * so we allow changing it dynamically. + * + * Location info can be for some kind of map watchface + * or daylight calculations, should those be required. + * + */ + class Location : public TimelineHeader { + public: + /** Location name */ + std::string location; + /** Altitude relative to sea level in meters */ + int16_t altitude; + /** Latitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ + int32_t latitude; + /** Longitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ + int32_t longitude; + }; + + /** + * How humidity is stored + */ + class Humidity : public TimelineHeader { + public: + /** Relative humidity, 0-100% */ + uint8_t humidity; + }; + + /** + * How air pressure is stored + */ + class Pressure : public TimelineHeader { + public: + /** Air pressure in hectopascals (hPa) */ + int16_t pressure; + }; + + /** + * How special events are stored + */ + class Special : public TimelineHeader { + public: + /** Special event's type */ + specialtype type; + }; + + /** + * How air quality is stored + * + * These events are a bit more complex because the topic is not simple, + * the intention is to heavy-lift the annoying preprocessing from the watch + * this allows watchface or watchapp makers to generate accurate alerts and graphics + * + * If this needs further enforced standardization, pull requests are welcome + */ + class AirQuality : public TimelineHeader { + public: + /** + * The name of the pollution + * + * for the sake of better compatibility with watchapps + * that might want to use this data for say visuals + * don't localize the name. + * + * Ideally watchapp itself localizes the name, if it's at all needed. + * + * E.g. + * For generic ones use "PM0.1", "PM5", "PM10" + * For chemical compounds use the molecular formula e.g. "NO2", "CO2", "O3" + * For pollen use the genus, e.g. "Betula" for birch or "Alternaria" for that mold's spores + */ + std::string polluter; + /** + * Amount of the pollution in SI units, + * otherwise it's going to be difficult to create UI, alerts + * and so on and for. + * + * See more: + * https://ec.europa.eu/environment/air/quality/standards.htm + * http://www.ourair.org/wp-content/uploads/2012-aaqs2.pdf + * + * Example units: + * count/m³ for pollen + * µgC/m³ for micrograms of organic carbon + * µg/m³ sulfates, PM0.1, PM1, PM2, PM10 and so on, dust + * mg/m³ CO2, CO + * ng/m³ for heavy metals + * + * List is not comprehensive, should be improved. + * The current ones are what watchapps assume! + * + * Note: ppb and ppm to concentration should be calculated on the companion, using + * the correct formula (taking into account temperature and air pressure) + * + * Note2: The amount is off by times 100, for two decimal places of precision. + * E.g. 54.32µg/m³ is 5432 + * + */ + uint32_t amount; + }; + }; + } +} diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp new file mode 100644 index 00000000..23f53b74 --- /dev/null +++ b/src/components/ble/weather/WeatherService.cpp @@ -0,0 +1,604 @@ +/* Copyright (C) 2021 Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +#include <qcbor/qcbor_spiffy_decode.h> +#include "WeatherService.h" +#include "libs/QCBOR/inc/qcbor/qcbor.h" +#include "systemtask/SystemTask.h" + +int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + return static_cast<Pinetime::Controllers::WeatherService*>(arg)->OnCommand(connHandle, attrHandle, ctxt); +} + +namespace Pinetime { + namespace Controllers { + WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController) + : system(system), dateTimeController(dateTimeController) { + nullHeader = &nullTimelineheader; + nullTimelineheader->timestamp = 0; + } + + void WeatherService::Init() { + uint8_t res = 0; + res = ble_gatts_count_cfg(serviceDefinition); + ASSERT(res == 0); + + res = ble_gatts_add_svcs(serviceDefinition); + ASSERT(res == 0); + } + + int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) { + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (packetLen <= 0) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + // Decode + QCBORDecodeContext decodeContext; + UsefulBufC encodedCbor = {ctxt->om->om_data, OS_MBUF_PKTLEN(ctxt->om)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL); + // KINDLY provide us a fixed-length map + QCBORDecode_EnterMap(&decodeContext, nullptr); + // Always encodes to the smallest number of bytes based on the value + int64_t tmpTimestamp = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp); + if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + int64_t tmpExpires = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); + if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpExpires < 0 || tmpExpires > 4294967295) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + int64_t tmpEventType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType); + if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpEventType < 0 || + tmpEventType >= static_cast<int64_t>(WeatherData::eventtype::Length)) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + switch (static_cast<WeatherData::eventtype>(tmpEventType)) { + case WeatherData::eventtype::AirQuality: { + std::unique_ptr<WeatherData::AirQuality> airquality = std::make_unique<WeatherData::AirQuality>(); + airquality->timestamp = tmpTimestamp; + airquality->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + airquality->expires = tmpExpires; + + UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? + QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &stringBuf); + if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + airquality->polluter = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len); + + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 4294967295) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + airquality->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (!AddEventToTimeline(std::move(airquality))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Obscuration: { + std::unique_ptr<WeatherData::Obscuration> obscuration = std::make_unique<WeatherData::Obscuration>(); + obscuration->timestamp = tmpTimestamp; + obscuration->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + obscuration->expires = tmpExpires; + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); + if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::obscurationtype::Length)) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + obscuration->type = static_cast<WeatherData::obscurationtype>(tmpType); + + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 65535) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + obscuration->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (!AddEventToTimeline(std::move(obscuration))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Precipitation: { + std::unique_ptr<WeatherData::Precipitation> precipitation = std::make_unique<WeatherData::Precipitation>(); + precipitation->timestamp = tmpTimestamp; + precipitation->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + precipitation->expires = tmpExpires; + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); + if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::precipitationtype::Length)) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + precipitation->type = static_cast<WeatherData::precipitationtype>(tmpType); + + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 255) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + precipitation->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (!AddEventToTimeline(std::move(precipitation))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Wind: { + std::unique_ptr<WeatherData::Wind> wind = std::make_unique<WeatherData::Wind>(); + wind->timestamp = tmpTimestamp; + wind->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + wind->expires = tmpExpires; + + int64_t tmpMin = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMin); + if (tmpMin < 0 || tmpMin > 255) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + wind->speedMin = tmpMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + int64_t tmpMax = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMax); + if (tmpMax < 0 || tmpMax > 255) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + wind->speedMax = tmpMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + int64_t tmpDMin = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMin", &tmpDMin); + if (tmpDMin < 0 || tmpDMin > 255) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + wind->directionMin = tmpDMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + int64_t tmpDMax = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMax", &tmpDMax); + if (tmpDMax < 0 || tmpDMax > 255) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + wind->directionMax = tmpDMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (!AddEventToTimeline(std::move(wind))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Temperature: { + std::unique_ptr<WeatherData::Temperature> temperature = std::make_unique<WeatherData::Temperature>(); + temperature->timestamp = tmpTimestamp; + temperature->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + temperature->expires = tmpExpires; + + int64_t tmpTemperature = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature); + if (tmpTemperature < -32768 || tmpTemperature > 32767) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + temperature->temperature = + static_cast<int16_t>(tmpTemperature); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + int64_t tmpDewPoint = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint); + if (tmpDewPoint < -32768 || tmpDewPoint > 32767) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + temperature->dewPoint = + static_cast<int16_t>(tmpDewPoint); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (!AddEventToTimeline(std::move(temperature))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Special: { + std::unique_ptr<WeatherData::Special> special = std::make_unique<WeatherData::Special>(); + special->timestamp = tmpTimestamp; + special->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + special->expires = tmpExpires; + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); + if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::specialtype::Length)) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + special->type = static_cast<WeatherData::specialtype>(tmpType); + + if (!AddEventToTimeline(std::move(special))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Pressure: { + std::unique_ptr<WeatherData::Pressure> pressure = std::make_unique<WeatherData::Pressure>(); + pressure->timestamp = tmpTimestamp; + pressure->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + pressure->expires = tmpExpires; + + int64_t tmpPressure = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Pressure", &tmpPressure); + if (tmpPressure < 0 || tmpPressure >= 65535) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + pressure->pressure = tmpPressure; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + + if (!AddEventToTimeline(std::move(pressure))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Location: { + std::unique_ptr<WeatherData::Location> location = std::make_unique<WeatherData::Location>(); + location->timestamp = tmpTimestamp; + location->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + location->expires = tmpExpires; + + UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? + QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Location", &stringBuf); + if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->location = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len); + + int64_t tmpAltitude = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Altitude", &tmpAltitude); + if (tmpAltitude < -32768 || tmpAltitude >= 32767) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->altitude = static_cast<int16_t>(tmpAltitude); + + int64_t tmpLatitude = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Latitude", &tmpLatitude); + if (tmpLatitude < -2147483648 || tmpLatitude >= 2147483647) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->latitude = static_cast<int32_t>(tmpLatitude); + + int64_t tmpLongitude = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Longitude", &tmpLongitude); + if (tmpLongitude < -2147483648 || tmpLongitude >= 2147483647) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + location->latitude = static_cast<int32_t>(tmpLongitude); + + if (!AddEventToTimeline(std::move(location))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Clouds: { + std::unique_ptr<WeatherData::Clouds> clouds = std::make_unique<WeatherData::Clouds>(); + clouds->timestamp = tmpTimestamp; + clouds->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + clouds->expires = tmpExpires; + + int64_t tmpAmount = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); + if (tmpAmount < 0 || tmpAmount > 255) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + clouds->amount = static_cast<uint8_t>(tmpAmount); + + if (!AddEventToTimeline(std::move(clouds))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + case WeatherData::eventtype::Humidity: { + std::unique_ptr<WeatherData::Humidity> humidity = std::make_unique<WeatherData::Humidity>(); + humidity->timestamp = tmpTimestamp; + humidity->eventType = static_cast<WeatherData::eventtype>(tmpEventType); + humidity->expires = tmpExpires; + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Humidity", &tmpType); + if (tmpType < 0 || tmpType >= 255) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + humidity->humidity = static_cast<uint8_t>(tmpType); + + if (!AddEventToTimeline(std::move(humidity))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } + default: { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + } + + QCBORDecode_ExitMap(&decodeContext); + GetTimelineLength(); + TidyTimeline(); + + if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + // Encode + uint8_t buffer[64]; + QCBOREncodeContext encodeContext; + /* TODO: This is very much still a test endpoint + * it needs a characteristic UUID check + * and actual implementations that show + * what actually has to be read. + * WARN: Consider commands not part of the API for now! + */ + QCBOREncode_Init(&encodeContext, UsefulBuf_FROM_BYTE_ARRAY(buffer)); + QCBOREncode_OpenMap(&encodeContext); + QCBOREncode_AddTextToMap(&encodeContext, "test", UsefulBuf_FROM_SZ_LITERAL("test")); + QCBOREncode_AddInt64ToMap(&encodeContext, "test", 1ul); + QCBOREncode_CloseMap(&encodeContext); + + UsefulBufC encodedEvent; + auto uErr = QCBOREncode_Finish(&encodeContext, &encodedEvent); + if (uErr != 0) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + auto res = os_mbuf_append(ctxt->om, &buffer, sizeof(buffer)); + if (res == 0) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + + return 0; + } + return 0; + } + + std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(*this->nullHeader); + } + + std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header); + } + } + + return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(*this->nullHeader); + } + + size_t WeatherService::GetTimelineLength() const { + return timeline.size(); + } + + bool WeatherService::AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event) { + if (timeline.size() == timeline.max_size()) { + return false; + } + + timeline.push_back(std::move(event)); + return true; + } + + bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : timeline) { + if (header->eventType == type && IsEventStillValid(header, currentTimestamp)) { + return true; + } + } + return false; + } + + void WeatherService::TidyTimeline() { + uint64_t timeCurrent = GetCurrentUnixTimestamp(); + timeline.erase(std::remove_if(std::begin(timeline), + std::end(timeline), + [&](std::unique_ptr<WeatherData::TimelineHeader> const& header) { + return !IsEventStillValid(header, timeCurrent); + }), + std::end(timeline)); + + std::sort(std::begin(timeline), std::end(timeline), CompareTimelineEvents); + } + + bool WeatherService::CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first, + const std::unique_ptr<WeatherData::TimelineHeader>& second) { + return first->timestamp > second->timestamp; + } + + bool WeatherService::IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp) { + // Not getting timestamp in isEventStillValid for more speed + return uniquePtr->timestamp + uniquePtr->expires >= timestamp; + } + + uint64_t WeatherService::GetCurrentUnixTimestamp() const { + return std::chrono::duration_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime().time_since_epoch()).count(); + } + + int16_t WeatherService::GetTodayMinTemp() const { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) - + ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); + int16_t result = -32768; + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && + header->timestamp < currentDayEnd && + reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) { + int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature; + if (result == -32768) { + result = temperature; + } else if (result > temperature) { + result = temperature; + } else { + // The temperature in this item is higher than the lowest we've found + } + } + } + + return result; + } + + int16_t WeatherService::GetTodayMaxTemp() const { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) - + ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); + int16_t result = -32768; + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && + header->timestamp < currentDayEnd && + reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) { + int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature; + if (result == -32768) { + result = temperature; + } else if (result < temperature) { + result = temperature; + } else { + // The temperature in this item is lower than the highest we've found + } + } + } + + return result; + } + + void WeatherService::CleanUpQcbor(QCBORDecodeContext* decodeContext) { + QCBORDecode_ExitMap(decodeContext); + QCBORDecode_Finish(decodeContext); + } + } +} diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h new file mode 100644 index 00000000..eca70cbd --- /dev/null +++ b/src/components/ble/weather/WeatherService.h @@ -0,0 +1,172 @@ +/* Copyright (C) 2021 Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <cstdint> +#include <string> +#include <vector> +#include <memory> + +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include <host/ble_gap.h> +#include <host/ble_uuid.h> +#undef max +#undef min + +#include "WeatherData.h" +#include "libs/QCBOR/inc/qcbor/qcbor.h" +#include "components/datetime/DateTimeController.h" + +int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg); + +namespace Pinetime { + namespace System { + class SystemTask; + } + namespace Controllers { + + class WeatherService { + public: + explicit WeatherService(System::SystemTask& system, DateTime& dateTimeController); + + void Init(); + + int OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt); + + /* + * Helper functions for quick access to currently valid data + */ + std::unique_ptr<WeatherData::Location>& GetCurrentLocation(); + std::unique_ptr<WeatherData::Clouds>& GetCurrentClouds(); + std::unique_ptr<WeatherData::Obscuration>& GetCurrentObscuration(); + std::unique_ptr<WeatherData::Precipitation>& GetCurrentPrecipitation(); + std::unique_ptr<WeatherData::Wind>& GetCurrentWind(); + std::unique_ptr<WeatherData::Temperature>& GetCurrentTemperature(); + std::unique_ptr<WeatherData::Humidity>& GetCurrentHumidity(); + std::unique_ptr<WeatherData::Pressure>& GetCurrentPressure(); + std::unique_ptr<WeatherData::AirQuality>& GetCurrentQuality(); + + /** + * Searches for the current day's maximum temperature + * @return -32768 if there's no data, degrees Celsius times 100 otherwise + */ + int16_t GetTodayMaxTemp() const; + /** + * Searches for the current day's minimum temperature + * @return -32768 if there's no data, degrees Celsius times 100 otherwise + */ + int16_t GetTodayMinTemp() const; + + /* + * Management functions + */ + /** + * Adds an event to the timeline + * @return + */ + bool AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event); + /** + * Gets the current timeline length + */ + size_t GetTimelineLength() const; + /** + * Checks if an event of a certain type exists in the timeline + */ + bool HasTimelineEventOfType(WeatherData::eventtype type) const; + + private: + // 00040000-78fc-48fe-8e23-433b3a1942d0 + static constexpr ble_uuid128_t BaseUuid() { + return CharUuid(0x00, 0x00); + } + + // 0004yyxx-78fc-48fe-8e23-433b3a1942d0 + static 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, y, x, 0x04, 0x00}}; + } + + ble_uuid128_t weatherUuid {BaseUuid()}; + + /** + * Just write timeline data here. + * + * See {@link WeatherData.h} for more information. + */ + ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)}; + /** + * This doesn't take timeline data, provides some control over it. + * + * NOTE: Currently not supported. Companion app implementer feedback required. + * There's very little point in solidifying an API before we know the needs. + */ + ble_uuid128_t weatherControlCharUuid {CharUuid(0x00, 0x02)}; + + const struct ble_gatt_chr_def characteristicDefinition[3] = { + {.uuid = &weatherDataCharUuid.u, + .access_cb = WeatherCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE, + .val_handle = &eventHandle}, + {.uuid = &weatherControlCharUuid.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}, + {nullptr}}; + const struct ble_gatt_svc_def serviceDefinition[2] = { + {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, {0}}; + + uint16_t eventHandle {}; + + Pinetime::System::SystemTask& system; + Pinetime::Controllers::DateTime& dateTimeController; + + std::vector<std::unique_ptr<WeatherData::TimelineHeader>> timeline; + std::unique_ptr<WeatherData::TimelineHeader> nullTimelineheader = std::make_unique<WeatherData::TimelineHeader>(); + std::unique_ptr<WeatherData::TimelineHeader>* nullHeader; + + /** + * Cleans up the timeline of expired events + */ + void TidyTimeline(); + + /** + * Compares two timeline events + */ + static bool CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first, + const std::unique_ptr<WeatherData::TimelineHeader>& second); + + /** + * Returns current UNIX timestamp + */ + uint64_t GetCurrentUnixTimestamp() const; + + /** + * Checks if the event hasn't gone past and expired + * + * @param header timeline event to check + * @param currentTimestamp what's the time right now + * @return if the event is valid + */ + static bool IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp); + + /** + * This is a helper function that closes a QCBOR map and decoding context cleanly + */ + void CleanUpQcbor(QCBORDecodeContext* decodeContext); + }; + } +} diff --git a/src/components/brightness/BrightnessController.cpp b/src/components/brightness/BrightnessController.cpp index 6c524679..2d9f980a 100644 --- a/src/components/brightness/BrightnessController.cpp +++ b/src/components/brightness/BrightnessController.cpp @@ -1,4 +1,4 @@ -#include "BrightnessController.h" +#include "components/brightness/BrightnessController.h" #include <hal/nrf_gpio.h> #include "displayapp/screens/Symbols.h" #include "drivers/PinMap.h" diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp index 762cded4..673903cb 100644 --- a/src/components/datetime/DateTimeController.cpp +++ b/src/components/datetime/DateTimeController.cpp @@ -1,4 +1,4 @@ -#include "DateTimeController.h" +#include "components/datetime/DateTimeController.h" #include <date/date.h> #include <libraries/log/nrf_log.h> #include <systemtask/SystemTask.h> diff --git a/src/components/firmwarevalidator/FirmwareValidator.cpp b/src/components/firmwarevalidator/FirmwareValidator.cpp index 68e66d37..5a63b6b4 100644 --- a/src/components/firmwarevalidator/FirmwareValidator.cpp +++ b/src/components/firmwarevalidator/FirmwareValidator.cpp @@ -1,4 +1,4 @@ -#include "FirmwareValidator.h" +#include "components/firmwarevalidator/FirmwareValidator.h" #include <hal/nrf_rtc.h> #include "drivers/InternalFlash.h" diff --git a/src/components/fs/FS.cpp b/src/components/fs/FS.cpp index 857e6bf9..8c98ae34 100644 --- a/src/components/fs/FS.cpp +++ b/src/components/fs/FS.cpp @@ -1,33 +1,32 @@ -#include "FS.h" +#include "components/fs/FS.h" #include <cstring> #include <littlefs/lfs.h> #include <lvgl/lvgl.h> using namespace Pinetime::Controllers; -FS::FS(Pinetime::Drivers::SpiNorFlash& driver) : - flashDriver{ driver }, - lfsConfig{ - .context = this, - .read = SectorRead, - .prog = SectorProg, - .erase = SectorErase, - .sync = SectorSync, - - .read_size = 16, - .prog_size = 8, - .block_size = blockSize, - .block_count = size / blockSize, - .block_cycles = 1000u, - - .cache_size = 16, - .lookahead_size = 16, - - .name_max = 50, - .attr_max = 50, - } -{ } - +FS::FS(Pinetime::Drivers::SpiNorFlash& driver) + : flashDriver {driver}, + lfsConfig { + .context = this, + .read = SectorRead, + .prog = SectorProg, + .erase = SectorErase, + .sync = SectorSync, + + .read_size = 16, + .prog_size = 8, + .block_size = blockSize, + .block_count = size / blockSize, + .block_cycles = 1000u, + + .cache_size = 16, + .lookahead_size = 16, + + .name_max = 50, + .attr_max = 50, + } { +} void FS::Init() { @@ -48,7 +47,6 @@ void FS::Init() { VerifyResource(); LVGLFileSystemInit(); #endif - } void FS::VerifyResource() { @@ -56,7 +54,7 @@ void FS::VerifyResource() { resourcesValid = true; } -int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) { +int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) { return lfs_file_open(&lfs, file_p, fileName, flags); } @@ -80,27 +78,31 @@ int FS::FileDelete(const char* fileName) { return lfs_remove(&lfs, fileName); } +int FS::DirOpen(const char* path, lfs_dir_t* lfs_dir) { + return lfs_dir_open(&lfs, lfs_dir, path); +} + +int FS::DirClose(lfs_dir_t* lfs_dir) { + return lfs_dir_close(&lfs, lfs_dir); +} +int FS::DirRead(lfs_dir_t* dir, lfs_info* info) { + return lfs_dir_read(&lfs, dir, info); +} +int FS::DirRewind(lfs_dir_t* dir) { + return lfs_dir_rewind(&lfs, dir); +} int FS::DirCreate(const char* path) { return lfs_mkdir(&lfs, path); } - -// Delete directory and all files inside -int FS::DirDelete(const char* path) { - - lfs_dir_t lfs_dir; - lfs_info entryInfo; - - int err; - err = lfs_dir_open(&lfs, &lfs_dir, path); - if (err) { - return err; - } - while (lfs_dir_read(&lfs, &lfs_dir, &entryInfo)) { - lfs_remove(&lfs, entryInfo.name); - } - lfs_dir_close(&lfs, &lfs_dir); - return LFS_ERR_OK; +int FS::Rename(const char* oldPath, const char* newPath){ + return lfs_rename(&lfs,oldPath,newPath); +} +int FS::Stat(const char* path, lfs_info* info) { + return lfs_stat(&lfs, path, info); +} +lfs_ssize_t FS::GetFSSize() { + return lfs_fs_size(&lfs); } /* @@ -141,17 +143,17 @@ int FS::SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, namespace { lv_fs_res_t lvglOpen(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode) { - lfs_file_t* file = static_cast<lfs_file_t*>(file_p); FS* filesys = static_cast<FS*>(drv->user_data); - filesys->FileOpen(file, path, LFS_O_RDONLY); - - if (file->type == 0) { - return LV_FS_RES_FS_ERR; - } - else { - return LV_FS_RES_OK; + int res = filesys->FileOpen(file, path, LFS_O_RDONLY); + if (res == 0) { + if (file->type == 0) { + return LV_FS_RES_FS_ERR; + } else { + return LV_FS_RES_OK; + } } + return LV_FS_RES_NOT_EX; } lv_fs_res_t lvglClose(lv_fs_drv_t* drv, void* file_p) { @@ -193,5 +195,4 @@ void FS::LVGLFileSystemInit() { fs_drv.user_data = this; lv_fs_drv_register(&fs_drv); - }
\ No newline at end of file diff --git a/src/components/fs/FS.h b/src/components/fs/FS.h index 75ba16c8..2b27ae5d 100644 --- a/src/components/fs/FS.h +++ b/src/components/fs/FS.h @@ -21,37 +21,49 @@ namespace Pinetime { int FileDelete(const char* fileName); + int DirOpen(const char* path, lfs_dir_t* lfs_dir); + int DirClose(lfs_dir_t* lfs_dir); + int DirRead(lfs_dir_t* dir, lfs_info* info); + int DirRewind(lfs_dir_t* dir); int DirCreate(const char* path); - int DirDelete(const char* path); - + + lfs_ssize_t GetFSSize(); + int Rename(const char* oldPath, const char* newPath); + int Stat(const char* path, lfs_info* info); void VerifyResource(); - private: + static size_t getSize() { + return size; + } + static size_t getBlockSize() { + return blockSize; + } + private: Pinetime::Drivers::SpiNorFlash& flashDriver; /* - * External Flash MAP (4 MBytes) - * - * 0x000000 +---------------------------------------+ - * | Bootloader Assets | - * | 256 KBytes | - * | | - * 0x040000 +---------------------------------------+ - * | OTA | - * | 464 KBytes | - * | | - * | | - * | | - * 0x0B4000 +---------------------------------------+ - * | File System | - * | | - * | | - * | | - * | | - * 0x400000 +---------------------------------------+ - * - */ + * External Flash MAP (4 MBytes) + * + * 0x000000 +---------------------------------------+ + * | Bootloader Assets | + * | 256 KBytes | + * | | + * 0x040000 +---------------------------------------+ + * | OTA | + * | 464 KBytes | + * | | + * | | + * | | + * 0x0B4000 +---------------------------------------+ + * | File System | + * | | + * | | + * | | + * | | + * 0x400000 +---------------------------------------+ + * + */ static constexpr size_t startAddress = 0x0B4000; static constexpr size_t size = 0x34C000; static constexpr size_t blockSize = 4096; @@ -65,7 +77,6 @@ namespace Pinetime { static int SectorErase(const struct lfs_config* c, lfs_block_t block); static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size); static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size); - }; } } diff --git a/src/components/gfx/Gfx.cpp b/src/components/gfx/Gfx.cpp index cf271032..3eaaa3fe 100644 --- a/src/components/gfx/Gfx.cpp +++ b/src/components/gfx/Gfx.cpp @@ -1,4 +1,4 @@ -#include "Gfx.h" +#include "components/gfx/Gfx.h" #include "drivers/St7789.h" using namespace Pinetime::Components; diff --git a/src/components/heartrate/Biquad.cpp b/src/components/heartrate/Biquad.cpp index 0341eda6..b7edd403 100644 --- a/src/components/heartrate/Biquad.cpp +++ b/src/components/heartrate/Biquad.cpp @@ -4,7 +4,7 @@ C++ port Copyright (C) 2021 Jean-François Milants */ -#include "Biquad.h" +#include "components/heartrate/Biquad.h" using namespace Pinetime::Controllers; diff --git a/src/components/heartrate/HeartRateController.cpp b/src/components/heartrate/HeartRateController.cpp index 716813b3..e0d69272 100644 --- a/src/components/heartrate/HeartRateController.cpp +++ b/src/components/heartrate/HeartRateController.cpp @@ -1,4 +1,4 @@ -#include "HeartRateController.h" +#include "components/heartrate/HeartRateController.h" #include <heartratetask/HeartRateTask.h> #include <systemtask/SystemTask.h> diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index fcba3815..a5d83696 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -4,9 +4,9 @@ C++ port Copyright (C) 2021 Jean-François Milants */ +#include "components/heartrate/Ppg.h" #include <vector> #include <nrf_log.h> -#include "Ppg.h" using namespace Pinetime::Controllers; /** Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/ppg.py */ diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index 00014162..ed79b082 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -1,8 +1,8 @@ #pragma once #include <array> -#include "Biquad.h" -#include "Ptagc.h" +#include "components/heartrate/Biquad.h" +#include "components/heartrate/Ptagc.h" namespace Pinetime { namespace Controllers { diff --git a/src/components/heartrate/Ptagc.cpp b/src/components/heartrate/Ptagc.cpp index e358371e..1c60bc23 100644 --- a/src/components/heartrate/Ptagc.cpp +++ b/src/components/heartrate/Ptagc.cpp @@ -4,8 +4,8 @@ C++ port Copyright (C) 2021 Jean-François Milants */ +#include "components/heartrate/Ptagc.h" #include <cmath> -#include "Ptagc.h" using namespace Pinetime::Controllers; diff --git a/src/components/motion/MotionController.cpp b/src/components/motion/MotionController.cpp index a2384d79..97a8feb2 100644 --- a/src/components/motion/MotionController.cpp +++ b/src/components/motion/MotionController.cpp @@ -1,4 +1,4 @@ -#include "MotionController.h" +#include "components/motion/MotionController.h" using namespace Pinetime::Controllers; @@ -14,7 +14,11 @@ void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) this->x = x; this->y = y; this->z = z; + int32_t deltaSteps = nbSteps - this->nbSteps; this->nbSteps = nbSteps; + if (deltaSteps > 0) { + currentTripSteps += deltaSteps; + } } bool MotionController::ShouldWakeUp(bool isSleeping) { @@ -43,10 +47,16 @@ void MotionController::IsSensorOk(bool isOk) { isSensorOk = isOk; } void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) { - switch(types){ - case Drivers::Bma421::DeviceTypes::BMA421: this->deviceType = DeviceTypes::BMA421; break; - case Drivers::Bma421::DeviceTypes::BMA425: this->deviceType = DeviceTypes::BMA425; break; - default: this->deviceType = DeviceTypes::Unknown; break; + switch (types) { + case Drivers::Bma421::DeviceTypes::BMA421: + this->deviceType = DeviceTypes::BMA421; + break; + case Drivers::Bma421::DeviceTypes::BMA425: + this->deviceType = DeviceTypes::BMA425; + break; + default: + this->deviceType = DeviceTypes::Unknown; + break; } } void MotionController::SetService(Pinetime::Controllers::MotionService* service) { diff --git a/src/components/motion/MotionController.h b/src/components/motion/MotionController.h index c72d8a4a..3eac7176 100644 --- a/src/components/motion/MotionController.h +++ b/src/components/motion/MotionController.h @@ -8,7 +8,7 @@ namespace Pinetime { namespace Controllers { class MotionController { public: - enum class DeviceTypes{ + enum class DeviceTypes { Unknown, BMA421, BMA425, @@ -28,6 +28,13 @@ namespace Pinetime { uint32_t NbSteps() const { return nbSteps; } + + void ResetTrip() { + currentTripSteps = 0; + } + uint32_t GetTripSteps() const { + return currentTripSteps; + } bool ShouldWakeUp(bool isSleeping); void IsSensorOk(bool isOk); @@ -44,6 +51,7 @@ namespace Pinetime { private: uint32_t nbSteps; + uint32_t currentTripSteps = 0; int16_t x; int16_t y; int16_t z; diff --git a/src/components/motor/MotorController.cpp b/src/components/motor/MotorController.cpp index f596c718..c794a02c 100644 --- a/src/components/motor/MotorController.cpp +++ b/src/components/motor/MotorController.cpp @@ -1,4 +1,4 @@ -#include "MotorController.h" +#include "components/motor/MotorController.h" #include <hal/nrf_gpio.h> #include "systemtask/SystemTask.h" #include "app_timer.h" diff --git a/src/components/rle/RleDecoder.cpp b/src/components/rle/RleDecoder.cpp index df2bcb6b..19ebfec0 100644 --- a/src/components/rle/RleDecoder.cpp +++ b/src/components/rle/RleDecoder.cpp @@ -1,4 +1,4 @@ -#include "RleDecoder.h" +#include "components/rle/RleDecoder.h" using namespace Pinetime::Tools; diff --git a/src/components/settings/Settings.cpp b/src/components/settings/Settings.cpp index 37c09f91..ef73ad1c 100644 --- a/src/components/settings/Settings.cpp +++ b/src/components/settings/Settings.cpp @@ -1,4 +1,4 @@ -#include "Settings.h" +#include "components/settings/Settings.h" #include <cstdlib> #include <cstring> diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index 09886011..f87b6938 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -4,7 +4,6 @@ #include "components/datetime/DateTimeController.h" #include "components/brightness/BrightnessController.h" #include "components/fs/FS.h" -#include "drivers/Cst816s.h" namespace Pinetime { namespace Controllers { diff --git a/src/components/timer/TimerController.cpp b/src/components/timer/TimerController.cpp index 8d5f5c33..79e44d6f 100644 --- a/src/components/timer/TimerController.cpp +++ b/src/components/timer/TimerController.cpp @@ -2,7 +2,7 @@ // Created by florian on 16.05.21. // -#include "TimerController.h" +#include "components/timer/TimerController.h" #include "systemtask/SystemTask.h" #include "app_timer.h" #include "task.h" diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h index 71166e42..79485d62 100644 --- a/src/displayapp/Apps.h +++ b/src/displayapp/Apps.h @@ -25,6 +25,8 @@ namespace Pinetime { Metronome, Motion, Steps, + Weather, + PassKey, QuickSettings, Settings, SettingWatchFace, @@ -32,7 +34,6 @@ namespace Pinetime { SettingDisplay, SettingWakeUp, SettingSteps, - SettingPineTimeStyle, SettingSetDate, SettingSetTime, SettingChimes, diff --git a/src/displayapp/Colors.cpp b/src/displayapp/Colors.cpp index f45f0722..93b1aa06 100644 --- a/src/displayapp/Colors.cpp +++ b/src/displayapp/Colors.cpp @@ -1,4 +1,4 @@ -#include "Colors.h" +#include "displayapp/Colors.h" using namespace Pinetime::Applications; using namespace Pinetime::Controllers; diff --git a/src/displayapp/Colors.h b/src/displayapp/Colors.h index 9db7dd20..43e2b801 100644 --- a/src/displayapp/Colors.h +++ b/src/displayapp/Colors.h @@ -1,7 +1,7 @@ #pragma once #include <lvgl/src/lv_misc/lv_color.h> -#include <components/settings/Settings.h> +#include "components/settings/Settings.h" namespace Pinetime { namespace Applications { diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index be1dad12..6eb45645 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -1,9 +1,9 @@ -#include "DisplayApp.h" +#include "displayapp/DisplayApp.h" #include <libraries/log/nrf_log.h> -#include <displayapp/screens/HeartRate.h> -#include <displayapp/screens/Motion.h> -#include <displayapp/screens/Timer.h> -#include <displayapp/screens/Alarm.h> +#include "displayapp/screens/HeartRate.h" +#include "displayapp/screens/Motion.h" +#include "displayapp/screens/Timer.h" +#include "displayapp/screens/Alarm.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/datetime/DateTimeController.h" @@ -29,6 +29,7 @@ #include "displayapp/screens/FlashLight.h" #include "displayapp/screens/BatteryInfo.h" #include "displayapp/screens/Steps.h" +#include "displayapp/screens/PassKey.h" #include "displayapp/screens/Error.h" #include "drivers/Cst816s.h" @@ -214,6 +215,10 @@ void DisplayApp::Refresh() { } else { LoadApp(Apps::Alarm, DisplayApp::FullRefreshDirections::None); } + break; + case Messages::ShowPairingKey: + LoadApp(Apps::PassKey, DisplayApp::FullRefreshDirections::Up); + break; case Messages::TouchEvent: { if (state != States::Running) { break; @@ -250,16 +255,36 @@ void DisplayApp::Refresh() { } } break; case Messages::ButtonPushed: - if (currentApp == Apps::Clock) { - PushMessageToSystemTask(System::Messages::GoToSleep); - } else { - if (!currentScreen->OnButtonPushed()) { + if (!currentScreen->OnButtonPushed()) { + if (currentApp == Apps::Clock) { + PushMessageToSystemTask(System::Messages::GoToSleep); + } else { LoadApp(returnToApp, returnDirection); brightnessController.Set(settingsController.GetBrightness()); brightnessController.Backup(); } } break; + case Messages::ButtonLongPressed: + if (currentApp != Apps::Clock) { + if (currentApp == Apps::Notifications) { + LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::Up); + } else if (currentApp == Apps::QuickSettings) { + LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::LeftAnim); + } else { + 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); @@ -334,6 +359,11 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); break; + case Apps::PassKey: + currentScreen = std::make_unique<Screens::PassKey>(this, bleController.GetPairingKey()); + ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::SwipeDown); + break; + case Apps::Notifications: currentScreen = std::make_unique<Screens::Notifications>( this, notificationManager, systemTask->nimble().alertService(), motorController, Screens::Notifications::Modes::Normal); @@ -389,10 +419,13 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentScreen = std::make_unique<Screens::SettingSetTime>(this, dateTimeController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; +<<<<<<< HEAD case Apps::SettingChimes: currentScreen = std::make_unique<Screens::SettingChimes>(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; +======= +>>>>>>> develop case Apps::BatteryInfo: currentScreen = std::make_unique<Screens::BatteryInfo>(this, batteryController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); @@ -413,7 +446,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentScreen = std::make_unique<Screens::Twos>(this); break; case Apps::Paint: - currentScreen = std::make_unique<Screens::InfiniPaint>(this, lvgl); + currentScreen = std::make_unique<Screens::InfiniPaint>(this, lvgl, motorController); break; case Apps::Paddle: currentScreen = std::make_unique<Screens::Paddle>(this, lvgl); @@ -480,8 +513,9 @@ void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) { } void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) { - if (systemTask != nullptr) + if (systemTask != nullptr) { systemTask->PushMessage(message); + } } void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) { diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index a87cab0b..39fe6314 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -5,9 +5,9 @@ #include <task.h> #include <memory> #include <systemtask/Messages.h> -#include "Apps.h" -#include "LittleVgl.h" -#include "TouchEvents.h" +#include "displayapp/Apps.h" +#include "displayapp/LittleVgl.h" +#include "displayapp/TouchEvents.h" #include "components/brightness/BrightnessController.h" #include "components/motor/MotorController.h" #include "components/firmwarevalidator/FirmwareValidator.h" @@ -17,7 +17,7 @@ #include "components/alarm/AlarmController.h" #include "touchhandler/TouchHandler.h" -#include "Messages.h" +#include "displayapp/Messages.h" #include "BootErrors.h" namespace Pinetime { diff --git a/src/displayapp/DisplayAppRecovery.cpp b/src/displayapp/DisplayAppRecovery.cpp index a42d81a2..fd7017a4 100644 --- a/src/displayapp/DisplayAppRecovery.cpp +++ b/src/displayapp/DisplayAppRecovery.cpp @@ -1,9 +1,9 @@ -#include "DisplayAppRecovery.h" +#include "displayapp/DisplayAppRecovery.h" #include <FreeRTOS.h> #include <task.h> #include <libraries/log/nrf_log.h> -#include <components/rle/RleDecoder.h> -#include <touchhandler/TouchHandler.h> +#include "components/rle/RleDecoder.h" +#include "touchhandler/TouchHandler.h" #include "displayapp/icons/infinitime/infinitime-nb.c" #include "components/ble/BleController.h" diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h index 72868159..86e956d1 100644 --- a/src/displayapp/DisplayAppRecovery.h +++ b/src/displayapp/DisplayAppRecovery.h @@ -11,10 +11,10 @@ #include <drivers/Watchdog.h> #include <components/motor/MotorController.h> #include "BootErrors.h" -#include "TouchEvents.h" -#include "Apps.h" -#include "Messages.h" -#include "DummyLittleVgl.h" +#include "displayapp/TouchEvents.h" +#include "displayapp/Apps.h" +#include "displayapp/Messages.h" +#include "displayapp/DummyLittleVgl.h" namespace Pinetime { namespace Drivers { diff --git a/src/displayapp/DummyLittleVgl.h b/src/displayapp/DummyLittleVgl.h index 1db51343..47c9e021 100644 --- a/src/displayapp/DummyLittleVgl.h +++ b/src/displayapp/DummyLittleVgl.h @@ -1,8 +1,8 @@ #pragma once -#include <libs/lvgl/src/lv_core/lv_style.h> -#include <libs/lvgl/src/lv_themes/lv_theme.h> -#include <libs/lvgl/src/lv_hal/lv_hal.h> +#include <lvgl/src/lv_core/lv_style.h> +#include <lvgl/src/lv_themes/lv_theme.h> +#include <lvgl/src/lv_hal/lv_hal.h> #include <drivers/St7789.h> #include <drivers/Cst816s.h> diff --git a/src/displayapp/LittleVgl.cpp b/src/displayapp/LittleVgl.cpp index 2bd5e57b..e7b58c16 100644 --- a/src/displayapp/LittleVgl.cpp +++ b/src/displayapp/LittleVgl.cpp @@ -1,5 +1,5 @@ -#include "LittleVgl.h" -#include "lv_pinetime_theme.h" +#include "displayapp/LittleVgl.h" +#include "displayapp/lv_pinetime_theme.h" #include <FreeRTOS.h> #include <task.h> diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index 19d2c4d3..a3a78cc9 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -1,4 +1,5 @@ #pragma once +#include <cstdint> namespace Pinetime { namespace Applications { namespace Display { @@ -9,6 +10,9 @@ namespace Pinetime { UpdateBleConnection, TouchEvent, ButtonPushed, + ButtonLongPressed, + ButtonLongerPressed, + ButtonDoubleClicked, NewNotification, TimerDone, BleFirmwareUpdateStarted, diff --git a/src/displayapp/lv_pinetime_theme.c b/src/displayapp/lv_pinetime_theme.c index 1b8b1980..b74b2fd7 100644 --- a/src/displayapp/lv_pinetime_theme.c +++ b/src/displayapp/lv_pinetime_theme.c @@ -6,7 +6,7 @@ /********************* * INCLUDES *********************/ -#include "lv_pinetime_theme.h" +#include "displayapp/lv_pinetime_theme.h" /********************* * DEFINES @@ -119,7 +119,6 @@ static void basic_init(void) { lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED | LV_STATE_CHECKED, lv_color_hex3(0x888)); lv_style_set_border_color(&style_btn, LV_STATE_DEFAULT, theme.color_primary); lv_style_set_border_width(&style_btn, LV_STATE_DEFAULT, 0); - lv_style_set_border_opa(&style_btn, LV_STATE_CHECKED, LV_OPA_TRANSP); lv_style_set_text_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff)); lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff)); diff --git a/src/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp index 6b45a36e..537ac0e0 100644 --- a/src/displayapp/screens/Alarm.cpp +++ b/src/displayapp/screens/Alarm.cpp @@ -15,9 +15,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "Alarm.h" -#include "Screen.h" -#include "Symbols.h" +#include "displayapp/screens/Alarm.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; using Pinetime::Controllers::AlarmController; @@ -36,7 +36,7 @@ Alarm::Alarm(DisplayApp* app, Controllers::AlarmController& alarmController) alarmHours = alarmController.Hours(); alarmMinutes = alarmController.Minutes(); - lv_label_set_text_fmt(time, "%02lu:%02lu", alarmHours, alarmMinutes); + lv_label_set_text_fmt(time, "%02hhu:%02hhu", alarmHours, alarmMinutes); lv_obj_align(time, lv_scr_act(), LV_ALIGN_CENTER, 0, -25); @@ -223,7 +223,7 @@ void Alarm::ShowInfo() { auto secToAlarm = timeToAlarm % 60; lv_label_set_text_fmt( - txtMessage, "Time to\nalarm:\n%2d Days\n%2d Hours\n%2d Minutes\n%2d Seconds", daysToAlarm, hrsToAlarm, minToAlarm, secToAlarm); + txtMessage, "Time to\nalarm:\n%2lu Days\n%2lu Hours\n%2lu Minutes\n%2lu Seconds", daysToAlarm, hrsToAlarm, minToAlarm, secToAlarm); } else { lv_label_set_text(txtMessage, "Alarm\nis not\nset."); } diff --git a/src/displayapp/screens/Alarm.h b/src/displayapp/screens/Alarm.h index 32a14d2f..4b301ce1 100644 --- a/src/displayapp/screens/Alarm.h +++ b/src/displayapp/screens/Alarm.h @@ -17,9 +17,9 @@ */ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "systemtask/SystemTask.h" -#include "../LittleVgl.h" +#include "displayapp/LittleVgl.h" #include "components/alarm/AlarmController.h" namespace Pinetime { @@ -40,7 +40,9 @@ 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(); diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp index 5c582f60..29c8affb 100644 --- a/src/displayapp/screens/ApplicationList.cpp +++ b/src/displayapp/screens/ApplicationList.cpp @@ -1,10 +1,10 @@ -#include "ApplicationList.h" +#include "displayapp/screens/ApplicationList.h" #include <lvgl/lvgl.h> #include <array> -#include "Symbols.h" -#include "Tile.h" +#include "displayapp/screens/Symbols.h" +#include "displayapp/screens/Tile.h" #include "displayapp/Apps.h" -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/ApplicationList.h b/src/displayapp/screens/ApplicationList.h index 103c38ae..f430a89e 100644 --- a/src/displayapp/screens/ApplicationList.h +++ b/src/displayapp/screens/ApplicationList.h @@ -2,8 +2,8 @@ #include <memory> -#include "Screen.h" -#include "ScreenList.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/ScreenList.h" #include "components/datetime/DateTimeController.h" #include "components/settings/Settings.h" #include "components/battery/BatteryController.h" diff --git a/src/displayapp/screens/BatteryIcon.cpp b/src/displayapp/screens/BatteryIcon.cpp index c67bcb23..08aaab70 100644 --- a/src/displayapp/screens/BatteryIcon.cpp +++ b/src/displayapp/screens/BatteryIcon.cpp @@ -1,6 +1,6 @@ +#include "displayapp/screens/BatteryIcon.h" #include <cstdint> -#include "BatteryIcon.h" -#include "Symbols.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/BatteryIcon.h b/src/displayapp/screens/BatteryIcon.h index b370b331..bec2e4e0 100644 --- a/src/displayapp/screens/BatteryIcon.h +++ b/src/displayapp/screens/BatteryIcon.h @@ -1,4 +1,5 @@ #pragma once +#include <cstdint> namespace Pinetime { namespace Applications { diff --git a/src/displayapp/screens/BatteryInfo.cpp b/src/displayapp/screens/BatteryInfo.cpp index 44ea7f51..e17de9ab 100644 --- a/src/displayapp/screens/BatteryInfo.cpp +++ b/src/displayapp/screens/BatteryInfo.cpp @@ -1,5 +1,5 @@ -#include "BatteryInfo.h" -#include "../DisplayApp.h" +#include "displayapp/screens/BatteryInfo.h" +#include "displayapp/DisplayApp.h" #include "components/battery/BatteryController.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/BatteryInfo.h b/src/displayapp/screens/BatteryInfo.h index 63454a26..de34cdff 100644 --- a/src/displayapp/screens/BatteryInfo.h +++ b/src/displayapp/screens/BatteryInfo.h @@ -1,9 +1,7 @@ #pragma once #include <cstdint> -#include <FreeRTOS.h> -#include <timers.h> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <lvgl/lvgl.h> namespace Pinetime { diff --git a/src/displayapp/screens/BleIcon.cpp b/src/displayapp/screens/BleIcon.cpp index da3d15e7..5058f3eb 100644 --- a/src/displayapp/screens/BleIcon.cpp +++ b/src/displayapp/screens/BleIcon.cpp @@ -1,5 +1,5 @@ -#include "BleIcon.h" -#include "Symbols.h" +#include "displayapp/screens/BleIcon.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; const char* BleIcon::GetIcon(bool isConnected) { diff --git a/src/displayapp/screens/Brightness.cpp b/src/displayapp/screens/Brightness.cpp index 1278cd62..d9901ae8 100644 --- a/src/displayapp/screens/Brightness.cpp +++ b/src/displayapp/screens/Brightness.cpp @@ -1,4 +1,4 @@ -#include "Brightness.h" +#include "displayapp/screens/Brightness.h" #include <lvgl/lvgl.h> using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Brightness.h b/src/displayapp/screens/Brightness.h index 14e48592..693570c7 100644 --- a/src/displayapp/screens/Brightness.h +++ b/src/displayapp/screens/Brightness.h @@ -2,7 +2,7 @@ #include <lvgl/src/lv_core/lv_obj.h> #include <cstdint> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "components/brightness/BrightnessController.h" namespace Pinetime { diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp index 5a5cd18b..1415e8ec 100644 --- a/src/displayapp/screens/Clock.cpp +++ b/src/displayapp/screens/Clock.cpp @@ -1,4 +1,4 @@ -#include "Clock.h" +#include "displayapp/screens/Clock.h" #include <date/date.h> #include <lvgl/lvgl.h> @@ -6,10 +6,11 @@ #include "components/motion/MotionController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" -#include "../DisplayApp.h" -#include "WatchFaceDigital.h" -#include "WatchFaceAnalog.h" -#include "PineTimeStyle.h" +#include "components/settings/Settings.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/WatchFaceDigital.h" +#include "displayapp/screens/WatchFaceAnalog.h" +#include "displayapp/screens/PineTimeStyle.h" using namespace Pinetime::Applications::Screens; @@ -54,6 +55,10 @@ bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) { return screen->OnTouchEvent(event); } +bool Clock::OnButtonPushed() { + return screen->OnButtonPushed(); +} + std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() { return std::make_unique<Screens::WatchFaceDigital>(app, dateTimeController, diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h index 648f72da..fcecc6b3 100644 --- a/src/displayapp/screens/Clock.h +++ b/src/displayapp/screens/Clock.h @@ -5,7 +5,7 @@ #include <cstdint> #include <memory> #include <components/heartrate/HeartRateController.h> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "components/datetime/DateTimeController.h" namespace Pinetime { @@ -32,6 +32,7 @@ namespace Pinetime { ~Clock() override; bool OnTouchEvent(TouchEvents event) override; + bool OnButtonPushed() override; private: Controllers::DateTime& dateTimeController; diff --git a/src/displayapp/screens/DropDownDemo.cpp b/src/displayapp/screens/DropDownDemo.cpp index 9043c20d..cf239a2f 100644 --- a/src/displayapp/screens/DropDownDemo.cpp +++ b/src/displayapp/screens/DropDownDemo.cpp @@ -1,7 +1,7 @@ -#include "DropDownDemo.h" +#include "displayapp/screens/DropDownDemo.h" #include <lvgl/lvgl.h> #include <libraries/log/nrf_log.h> -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/DropDownDemo.h b/src/displayapp/screens/DropDownDemo.h index ff388c57..bcf0f45c 100644 --- a/src/displayapp/screens/DropDownDemo.h +++ b/src/displayapp/screens/DropDownDemo.h @@ -1,7 +1,7 @@ #pragma once #include <cstdint> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <lvgl/src/lv_core/lv_obj.h> namespace Pinetime { diff --git a/src/displayapp/screens/Error.cpp b/src/displayapp/screens/Error.cpp index 75946aba..1dbc3447 100644 --- a/src/displayapp/screens/Error.cpp +++ b/src/displayapp/screens/Error.cpp @@ -1,4 +1,4 @@ -#include "Error.h" +#include "displayapp/screens/Error.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Error.h b/src/displayapp/screens/Error.h index 20dde7ee..23167545 100644 --- a/src/displayapp/screens/Error.h +++ b/src/displayapp/screens/Error.h @@ -1,6 +1,6 @@ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "BootErrors.h" #include <lvgl/lvgl.h> diff --git a/src/displayapp/screens/FirmwareUpdate.cpp b/src/displayapp/screens/FirmwareUpdate.cpp index 79bda0ba..373fcae4 100644 --- a/src/displayapp/screens/FirmwareUpdate.cpp +++ b/src/displayapp/screens/FirmwareUpdate.cpp @@ -1,7 +1,7 @@ -#include "FirmwareUpdate.h" +#include "displayapp/screens/FirmwareUpdate.h" #include <lvgl/lvgl.h> #include "components/ble/BleController.h" -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/FirmwareUpdate.h b/src/displayapp/screens/FirmwareUpdate.h index 8fc86d8c..a61178ce 100644 --- a/src/displayapp/screens/FirmwareUpdate.h +++ b/src/displayapp/screens/FirmwareUpdate.h @@ -1,6 +1,6 @@ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <lvgl/src/lv_core/lv_obj.h> #include "FreeRTOS.h" diff --git a/src/displayapp/screens/FirmwareValidation.cpp b/src/displayapp/screens/FirmwareValidation.cpp index eef8f919..c7a5b27e 100644 --- a/src/displayapp/screens/FirmwareValidation.cpp +++ b/src/displayapp/screens/FirmwareValidation.cpp @@ -1,8 +1,8 @@ -#include "FirmwareValidation.h" +#include "displayapp/screens/FirmwareValidation.h" #include <lvgl/lvgl.h> #include "Version.h" #include "components/firmwarevalidator/FirmwareValidator.h" -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; @@ -18,7 +18,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, : Screen {app}, validator {validator} { labelVersion = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_fmt(labelVersion, - "Version : %d.%d.%d\n" + "Version : %lu.%lu.%lu\n" "ShortRef : %s", Version::Major(), Version::Minor(), diff --git a/src/displayapp/screens/FirmwareValidation.h b/src/displayapp/screens/FirmwareValidation.h index bfdb096d..278c4adf 100644 --- a/src/displayapp/screens/FirmwareValidation.h +++ b/src/displayapp/screens/FirmwareValidation.h @@ -1,6 +1,6 @@ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <lvgl/src/lv_core/lv_obj.h> namespace Pinetime { diff --git a/src/displayapp/screens/FlashLight.cpp b/src/displayapp/screens/FlashLight.cpp index dcb31a7f..c4d02643 100644 --- a/src/displayapp/screens/FlashLight.cpp +++ b/src/displayapp/screens/FlashLight.cpp @@ -1,6 +1,6 @@ -#include "FlashLight.h" -#include "../DisplayApp.h" -#include "Symbols.h" +#include "displayapp/screens/FlashLight.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/FlashLight.h b/src/displayapp/screens/FlashLight.h index f2c65bbe..e91a1032 100644 --- a/src/displayapp/screens/FlashLight.h +++ b/src/displayapp/screens/FlashLight.h @@ -1,6 +1,6 @@ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "components/brightness/BrightnessController.h" #include "systemtask/SystemTask.h" #include <cstdint> diff --git a/src/displayapp/screens/HeartRate.cpp b/src/displayapp/screens/HeartRate.cpp index b6ece27f..513c40bf 100644 --- a/src/displayapp/screens/HeartRate.cpp +++ b/src/displayapp/screens/HeartRate.cpp @@ -1,8 +1,8 @@ -#include <libs/lvgl/lvgl.h> -#include "HeartRate.h" +#include "displayapp/screens/HeartRate.h" +#include <lvgl/lvgl.h> #include <components/heartrate/HeartRateController.h> -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/HeartRate.h b/src/displayapp/screens/HeartRate.h index 7f7d3ad3..baa0ccdd 100644 --- a/src/displayapp/screens/HeartRate.h +++ b/src/displayapp/screens/HeartRate.h @@ -2,11 +2,11 @@ #include <cstdint> #include <chrono> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <bits/unique_ptr.h> #include "systemtask/SystemTask.h" -#include <libs/lvgl/src/lv_core/lv_style.h> -#include <libs/lvgl/src/lv_core/lv_obj.h> +#include <lvgl/src/lv_core/lv_style.h> +#include <lvgl/src/lv_core/lv_obj.h> namespace Pinetime { namespace Controllers { diff --git a/src/displayapp/screens/InfiniPaint.cpp b/src/displayapp/screens/InfiniPaint.cpp index 85a5e826..d279fafc 100644 --- a/src/displayapp/screens/InfiniPaint.cpp +++ b/src/displayapp/screens/InfiniPaint.cpp @@ -1,10 +1,15 @@ -#include "InfiniPaint.h" -#include "../DisplayApp.h" -#include "../LittleVgl.h" +#include "displayapp/screens/InfiniPaint.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/LittleVgl.h" + +#include <algorithm> // std::fill using namespace Pinetime::Applications::Screens; -InfiniPaint::InfiniPaint(Pinetime::Applications::DisplayApp* app, Pinetime::Components::LittleVgl& lvgl) : Screen(app), lvgl {lvgl} { +InfiniPaint::InfiniPaint(Pinetime::Applications::DisplayApp* app, + Pinetime::Components::LittleVgl& lvgl, + Pinetime::Controllers::MotorController& motor) + : Screen(app), lvgl {lvgl}, motor {motor} { std::fill(b, b + bufferSize, selectColor); } @@ -15,6 +20,7 @@ InfiniPaint::~InfiniPaint() { bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) { switch (event) { case Pinetime::Applications::TouchEvents::LongTap: + color = (color + 1) % 8; switch (color) { case 0: selectColor = LV_COLOR_MAGENTA; @@ -47,7 +53,7 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) { } std::fill(b, b + bufferSize, selectColor); - color++; + motor.RunForDuration(35); return true; default: return true; diff --git a/src/displayapp/screens/InfiniPaint.h b/src/displayapp/screens/InfiniPaint.h index 0a70e033..8c427402 100644 --- a/src/displayapp/screens/InfiniPaint.h +++ b/src/displayapp/screens/InfiniPaint.h @@ -2,7 +2,9 @@ #include <lvgl/lvgl.h> #include <cstdint> -#include "Screen.h" +#include <algorithm> // std::fill +#include "displayapp/screens/Screen.h" +#include "components/motor/MotorController.h" namespace Pinetime { namespace Components { @@ -13,7 +15,7 @@ namespace Pinetime { class InfiniPaint : public Screen { public: - InfiniPaint(DisplayApp* app, Pinetime::Components::LittleVgl& lvgl); + InfiniPaint(DisplayApp* app, Pinetime::Components::LittleVgl& lvgl, Controllers::MotorController& motor); ~InfiniPaint() override; @@ -23,6 +25,7 @@ namespace Pinetime { private: Pinetime::Components::LittleVgl& lvgl; + Controllers::MotorController& motor; static constexpr uint16_t width = 10; static constexpr uint16_t height = 10; static constexpr uint16_t bufferSize = width * height; diff --git a/src/displayapp/screens/Label.cpp b/src/displayapp/screens/Label.cpp index 1761a7b5..62ec1f0a 100644 --- a/src/displayapp/screens/Label.cpp +++ b/src/displayapp/screens/Label.cpp @@ -1,4 +1,4 @@ -#include "Label.h" +#include "displayapp/screens/Label.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Label.h b/src/displayapp/screens/Label.h index f1e49079..3fe5111f 100644 --- a/src/displayapp/screens/Label.h +++ b/src/displayapp/screens/Label.h @@ -1,6 +1,6 @@ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <lvgl/lvgl.h> namespace Pinetime { diff --git a/src/displayapp/screens/List.cpp b/src/displayapp/screens/List.cpp index 064b47a6..af3f30f6 100644 --- a/src/displayapp/screens/List.cpp +++ b/src/displayapp/screens/List.cpp @@ -1,6 +1,6 @@ -#include "List.h" -#include "../DisplayApp.h" -#include "Symbols.h" +#include "displayapp/screens/List.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/List.h b/src/displayapp/screens/List.h index d9f61f29..023de3aa 100644 --- a/src/displayapp/screens/List.h +++ b/src/displayapp/screens/List.h @@ -3,8 +3,8 @@ #include <lvgl/lvgl.h> #include <cstdint> #include <memory> -#include "Screen.h" -#include "../Apps.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/Apps.h" #include "components/settings/Settings.h" #define MAXLISTITEMS 4 diff --git a/src/displayapp/screens/Meter.cpp b/src/displayapp/screens/Meter.cpp index 57cde9cf..9c853109 100644 --- a/src/displayapp/screens/Meter.cpp +++ b/src/displayapp/screens/Meter.cpp @@ -1,6 +1,6 @@ -#include "Meter.h" +#include "displayapp/screens/Meter.h" #include <lvgl/lvgl.h> -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Meter.h b/src/displayapp/screens/Meter.h index 9b3d1d48..50d9f83c 100644 --- a/src/displayapp/screens/Meter.h +++ b/src/displayapp/screens/Meter.h @@ -1,7 +1,7 @@ #pragma once #include <cstdint> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <lvgl/src/lv_core/lv_style.h> #include <lvgl/src/lv_core/lv_obj.h> diff --git a/src/displayapp/screens/Metronome.cpp b/src/displayapp/screens/Metronome.cpp index 52cb8519..f6f269dc 100644 --- a/src/displayapp/screens/Metronome.cpp +++ b/src/displayapp/screens/Metronome.cpp @@ -1,5 +1,5 @@ -#include "Metronome.h" -#include "Symbols.h" +#include "displayapp/screens/Metronome.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; @@ -113,9 +113,16 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) { lv_label_set_text_fmt(bpmValue, "%03d", bpm); } tappedTime = xTaskGetTickCount(); + allowExit = true; } break; } + case LV_EVENT_RELEASED: + case LV_EVENT_PRESS_LOST: + if (obj == bpmTap) { + allowExit = false; + } + break; case LV_EVENT_CLICKED: { if (obj == playPause) { metronomeStarted = !metronomeStarted; @@ -135,3 +142,11 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) { break; } } + +bool Metronome::OnTouchEvent(TouchEvents event) { + if (event == TouchEvents::SwipeDown && allowExit) { + running = false; + return true; + } + return false; +} diff --git a/src/displayapp/screens/Metronome.h b/src/displayapp/screens/Metronome.h index f3a84dc8..6e6589fe 100644 --- a/src/displayapp/screens/Metronome.h +++ b/src/displayapp/screens/Metronome.h @@ -2,6 +2,7 @@ #include "systemtask/SystemTask.h" #include "components/motor/MotorController.h" +#include "displayapp/screens/Screen.h" namespace Pinetime { namespace Applications { @@ -13,6 +14,7 @@ namespace Pinetime { ~Metronome() override; void Refresh() override; void OnEvent(lv_obj_t* obj, lv_event_t event); + bool OnTouchEvent(TouchEvents event) override; private: TickType_t startTime = 0; @@ -24,6 +26,7 @@ namespace Pinetime { uint8_t counter = 1; bool metronomeStarted = false; + bool allowExit = false; lv_obj_t *bpmArc, *bpmTap, *bpmValue; lv_obj_t *bpbDropdown, *currentBpbText; diff --git a/src/displayapp/screens/Motion.cpp b/src/displayapp/screens/Motion.cpp index 2f1f7c21..23eb2765 100644 --- a/src/displayapp/screens/Motion.cpp +++ b/src/displayapp/screens/Motion.cpp @@ -1,6 +1,6 @@ -#include <libs/lvgl/lvgl.h> -#include "Motion.h" -#include "../DisplayApp.h" +#include "displayapp/screens/Motion.h" +#include <lvgl/lvgl.h> +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Motion.h b/src/displayapp/screens/Motion.h index 20a18d02..d6997409 100644 --- a/src/displayapp/screens/Motion.h +++ b/src/displayapp/screens/Motion.h @@ -2,10 +2,10 @@ #include <cstdint> #include <chrono> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <bits/unique_ptr.h> -#include <libs/lvgl/src/lv_core/lv_style.h> -#include <libs/lvgl/src/lv_core/lv_obj.h> +#include <lvgl/src/lv_core/lv_style.h> +#include <lvgl/src/lv_core/lv_obj.h> #include <components/motion/MotionController.h> namespace Pinetime { diff --git a/src/displayapp/screens/Music.cpp b/src/displayapp/screens/Music.cpp index 47ddb655..9f17b956 100644 --- a/src/displayapp/screens/Music.cpp +++ b/src/displayapp/screens/Music.cpp @@ -15,10 +15,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "Music.h" -#include "Symbols.h" +#include "displayapp/screens/Music.h" +#include "displayapp/screens/Symbols.h" #include <cstdint> -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" #include "components/ble/MusicService.h" #include "displayapp/icons/music/disc.cpp" #include "displayapp/icons/music/disc_f_1.cpp" @@ -277,12 +277,14 @@ bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) { return true; } case TouchEvents::SwipeDown: { - lv_obj_set_hidden(btnNext, false); - lv_obj_set_hidden(btnPrev, false); - - lv_obj_set_hidden(btnVolDown, true); - lv_obj_set_hidden(btnVolUp, true); - return true; + if (lv_obj_get_hidden(btnNext)) { + lv_obj_set_hidden(btnNext, false); + lv_obj_set_hidden(btnPrev, false); + lv_obj_set_hidden(btnVolDown, true); + lv_obj_set_hidden(btnVolUp, true); + return true; + } + return false; } case TouchEvents::SwipeLeft: { musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT); diff --git a/src/displayapp/screens/Music.h b/src/displayapp/screens/Music.h index 6f2d80a0..f9b4eaab 100644 --- a/src/displayapp/screens/Music.h +++ b/src/displayapp/screens/Music.h @@ -20,7 +20,7 @@ #include <FreeRTOS.h> #include <lvgl/src/lv_core/lv_obj.h> #include <string> -#include "Screen.h" +#include "displayapp/screens/Screen.h" namespace Pinetime { namespace Controllers { diff --git a/src/displayapp/screens/Navigation.cpp b/src/displayapp/screens/Navigation.cpp index d437cc6d..674362a6 100644 --- a/src/displayapp/screens/Navigation.cpp +++ b/src/displayapp/screens/Navigation.cpp @@ -15,9 +15,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "Navigation.h" +#include "displayapp/screens/Navigation.h" #include <cstdint> -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" #include "components/ble/NavigationService.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Navigation.h b/src/displayapp/screens/Navigation.h index 48f00a76..07674ef1 100644 --- a/src/displayapp/screens/Navigation.h +++ b/src/displayapp/screens/Navigation.h @@ -20,7 +20,7 @@ #include <FreeRTOS.h> #include <lvgl/src/lv_core/lv_obj.h> #include <string> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <array> namespace Pinetime { diff --git a/src/displayapp/screens/NotificationIcon.cpp b/src/displayapp/screens/NotificationIcon.cpp index d8792f9d..0e913ae7 100644 --- a/src/displayapp/screens/NotificationIcon.cpp +++ b/src/displayapp/screens/NotificationIcon.cpp @@ -1,5 +1,5 @@ -#include "NotificationIcon.h" -#include "Symbols.h" +#include "displayapp/screens/NotificationIcon.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; const char* NotificationIcon::GetIcon(bool newNotificationAvailable) { diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index 4f475813..569c422b 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -1,8 +1,8 @@ -#include "Notifications.h" -#include <displayapp/DisplayApp.h> +#include "displayapp/screens/Notifications.h" +#include "displayapp/DisplayApp.h" #include "components/ble/MusicService.h" #include "components/ble/AlertNotificationService.h" -#include "Symbols.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; extern lv_font_t jetbrains_mono_extrabold_compressed; diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h index 0b5271e7..cbb7af6c 100644 --- a/src/displayapp/screens/Notifications.h +++ b/src/displayapp/screens/Notifications.h @@ -3,7 +3,7 @@ #include <lvgl/lvgl.h> #include <cstdint> #include <memory> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "components/ble/NotificationManager.h" #include "components/motor/MotorController.h" diff --git a/src/displayapp/screens/Paddle.cpp b/src/displayapp/screens/Paddle.cpp index 26c2368b..608eb644 100644 --- a/src/displayapp/screens/Paddle.cpp +++ b/src/displayapp/screens/Paddle.cpp @@ -1,6 +1,8 @@ -#include "Paddle.h" -#include "../DisplayApp.h" -#include "../LittleVgl.h" +#include "displayapp/screens/Paddle.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/LittleVgl.h" + +#include <cstdlib> // for rand() using namespace Pinetime::Applications::Screens; @@ -50,6 +52,13 @@ void Paddle::Refresh() { // checks if it has touched the side (right side) if (ballX >= LV_HOR_RES - ballSize - 1) { dx *= -1; + dy += rand() % 3 - 1; // add a little randomization in wall bounce direction, one of [-1, 0, 1] + if (dy > 5) { // limit dy to be in range [-5 to 5] + dy = 5; + } + if (dy < -5) { + dy = -5; + } } // checks if it is in the position of the paddle diff --git a/src/displayapp/screens/Paddle.h b/src/displayapp/screens/Paddle.h index fc2131a1..3a30eee6 100644 --- a/src/displayapp/screens/Paddle.h +++ b/src/displayapp/screens/Paddle.h @@ -2,7 +2,7 @@ #include <lvgl/lvgl.h> #include <cstdint> -#include "Screen.h" +#include "displayapp/screens/Screen.h" namespace Pinetime { namespace Components { diff --git a/src/displayapp/screens/PassKey.cpp b/src/displayapp/screens/PassKey.cpp new file mode 100644 index 00000000..9e43a541 --- /dev/null +++ b/src/displayapp/screens/PassKey.cpp @@ -0,0 +1,24 @@ +#include "PassKey.h" +#include "displayapp/DisplayApp.h" + +using namespace Pinetime::Applications::Screens; + +PassKey::PassKey(Pinetime::Applications::DisplayApp* app, uint32_t key) : Screen(app) { + passkeyLabel = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(passkeyLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFF00)); + lv_obj_set_style_local_text_font(passkeyLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + lv_label_set_text_fmt(passkeyLabel, "%06u", key); + lv_obj_align(passkeyLabel, nullptr, LV_ALIGN_CENTER, 0, -20); + + backgroundLabel = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_click(backgroundLabel, true); + lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); + lv_obj_set_size(backgroundLabel, 240, 240); + lv_obj_set_pos(backgroundLabel, 0, 0); + lv_label_set_text(backgroundLabel, ""); +} + +PassKey::~PassKey() { + lv_obj_clean(lv_scr_act()); +} + diff --git a/src/displayapp/screens/PassKey.h b/src/displayapp/screens/PassKey.h new file mode 100644 index 00000000..16e72a3c --- /dev/null +++ b/src/displayapp/screens/PassKey.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Screen.h" +#include <lvgl/lvgl.h> + +namespace Pinetime { + namespace Applications { + namespace Screens { + + class PassKey : public Screen { + public: + PassKey(DisplayApp* app, uint32_t key); + ~PassKey() override; + + private: + lv_obj_t* passkeyLabel; + lv_obj_t* backgroundLabel; + }; + } + } +} diff --git a/src/displayapp/screens/PineTimeStyle.cpp b/src/displayapp/screens/PineTimeStyle.cpp index fa88d459..b2b972dc 100644 --- a/src/displayapp/screens/PineTimeStyle.cpp +++ b/src/displayapp/screens/PineTimeStyle.cpp @@ -19,24 +19,31 @@ * Style/layout copied from TimeStyle for Pebble by Dan Tilden (github.com/tilden) */ -#include "PineTimeStyle.h" +#include "displayapp/screens/PineTimeStyle.h" #include <date/date.h> #include <lvgl/lvgl.h> #include <cstdio> #include <displayapp/Colors.h> -#include "BatteryIcon.h" -#include "BleIcon.h" -#include "NotificationIcon.h" -#include "Symbols.h" +#include "displayapp/screens/BatteryIcon.h" +#include "displayapp/screens/BleIcon.h" +#include "displayapp/screens/NotificationIcon.h" +#include "displayapp/screens/Symbols.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/motion/MotionController.h" #include "components/settings/Settings.h" -#include "../DisplayApp.h" +#include "displayapp/DisplayApp.h" using namespace Pinetime::Applications::Screens; +namespace { + void event_handler(lv_obj_t* obj, lv_event_t event) { + auto* screen = static_cast<PineTimeStyle*>(obj->user_data); + screen->UpdateSelected(obj, event); + } +} + PineTimeStyle::PineTimeStyle(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::Battery& batteryController, @@ -53,33 +60,30 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app, settingsController {settingsController}, motionController {motionController} { - // This sets the watchface number to return to after leaving the menu - settingsController.SetClockFace(2); - displayedChar[0] = 0; displayedChar[1] = 0; displayedChar[2] = 0; displayedChar[3] = 0; displayedChar[4] = 0; - //Create a 200px wide background rectangle + // Create a 200px wide background rectangle timebar = lv_obj_create(lv_scr_act(), nullptr); lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorBG())); lv_obj_set_style_local_radius(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_size(timebar, 200, 240); - lv_obj_align(timebar, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 0); + lv_obj_align(timebar, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 0, 0); // Display the time timeDD1 = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light); lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorTime())); - lv_label_set_text(timeDD1, "12"); + lv_label_set_text(timeDD1, "00"); lv_obj_align(timeDD1, timebar, LV_ALIGN_IN_TOP_MID, 5, 5); timeDD2 = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light); lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorTime())); - lv_label_set_text(timeDD2, "34"); + lv_label_set_text(timeDD2, "00"); lv_obj_align(timeDD2, timebar, LV_ALIGN_IN_BOTTOM_MID, 5, -5); timeAMPM = lv_label_create(lv_scr_act(), nullptr); @@ -97,74 +101,78 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app, // Display icons batteryIcon = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_label_set_text(batteryIcon, Symbols::batteryFull); lv_obj_align(batteryIcon, 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)); - lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25); + lv_label_set_text(bleIcon, ""); notificationIcon = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 40); + lv_label_set_text(notificationIcon, ""); // Calendar icon calendarOuter = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_obj_set_style_local_radius(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_size(calendarOuter, 34, 34); lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 0); calendarInner = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xffffff)); + lv_obj_set_style_local_bg_color(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); lv_obj_set_style_local_radius(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_size(calendarInner, 27, 27); lv_obj_align(calendarInner, calendarOuter, LV_ALIGN_CENTER, 0, 0); calendarBar1 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_bg_color(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_obj_set_style_local_radius(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_size(calendarBar1, 3, 12); lv_obj_align(calendarBar1, calendarOuter, LV_ALIGN_IN_TOP_MID, -6, -3); calendarBar2 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_bg_color(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_obj_set_style_local_radius(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_size(calendarBar2, 3, 12); lv_obj_align(calendarBar2, calendarOuter, LV_ALIGN_IN_TOP_MID, 6, -3); calendarCrossBar1 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_bg_color(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_obj_set_style_local_radius(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_size(calendarCrossBar1, 8, 3); lv_obj_align(calendarCrossBar1, calendarBar1, LV_ALIGN_IN_BOTTOM_MID, 0, 0); calendarCrossBar2 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_bg_color(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_obj_set_style_local_radius(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_size(calendarCrossBar2, 8, 3); lv_obj_align(calendarCrossBar2, calendarBar2, LV_ALIGN_IN_BOTTOM_MID, 0, 0); // Display date dateDayOfWeek = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_label_set_text(dateDayOfWeek, "THU"); lv_obj_align(dateDayOfWeek, sidebar, LV_ALIGN_CENTER, 0, -34); dateDay = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(dateDay, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_text_color(dateDay, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_label_set_text(dateDay, "25"); lv_obj_align(dateDay, sidebar, LV_ALIGN_CENTER, 0, 3); dateMonth = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(dateMonth, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_text_color(dateMonth, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_label_set_text(dateMonth, "MAR"); lv_obj_align(dateMonth, sidebar, LV_ALIGN_CENTER, 0, 32); // Step count gauge - needle_colors[0] = LV_COLOR_WHITE; + if (settingsController.GetPTSColorBar() == Pinetime::Controllers::Settings::Colors::White) { + needle_colors[0] = LV_COLOR_BLACK; + } else { + needle_colors[0] = LV_COLOR_WHITE; + } stepGauge = lv_gauge_create(lv_scr_act(), nullptr); lv_gauge_set_needle_count(stepGauge, 1, needle_colors); lv_obj_set_size(stepGauge, 40, 40); @@ -193,6 +201,100 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app, lv_obj_set_pos(backgroundLabel, 0, 0); lv_label_set_text(backgroundLabel, ""); + btnNextTime = lv_btn_create(lv_scr_act(), nullptr); + btnNextTime->user_data = this; + lv_obj_set_size(btnNextTime, 60, 60); + lv_obj_align(btnNextTime, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, -80); + lv_obj_set_style_local_bg_opa(btnNextTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnNextTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">"); + lv_obj_set_event_cb(btnNextTime, event_handler); + lv_obj_set_hidden(btnNextTime, true); + + btnPrevTime = lv_btn_create(lv_scr_act(), nullptr); + btnPrevTime->user_data = this; + lv_obj_set_size(btnPrevTime, 60, 60); + lv_obj_align(btnPrevTime, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, -80); + lv_obj_set_style_local_bg_opa(btnPrevTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnPrevTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<"); + lv_obj_set_event_cb(btnPrevTime, event_handler); + lv_obj_set_hidden(btnPrevTime, true); + + btnNextBar = lv_btn_create(lv_scr_act(), nullptr); + btnNextBar->user_data = this; + lv_obj_set_size(btnNextBar, 60, 60); + lv_obj_align(btnNextBar, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, 0); + lv_obj_set_style_local_bg_opa(btnNextBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnNextBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">"); + lv_obj_set_event_cb(btnNextBar, event_handler); + lv_obj_set_hidden(btnNextBar, true); + + btnPrevBar = lv_btn_create(lv_scr_act(), nullptr); + btnPrevBar->user_data = this; + lv_obj_set_size(btnPrevBar, 60, 60); + lv_obj_align(btnPrevBar, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, 0); + lv_obj_set_style_local_bg_opa(btnPrevBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnPrevBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<"); + lv_obj_set_event_cb(btnPrevBar, event_handler); + lv_obj_set_hidden(btnPrevBar, true); + + btnNextBG = lv_btn_create(lv_scr_act(), nullptr); + btnNextBG->user_data = this; + lv_obj_set_size(btnNextBG, 60, 60); + lv_obj_align(btnNextBG, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, 80); + lv_obj_set_style_local_bg_opa(btnNextBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnNextBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">"); + lv_obj_set_event_cb(btnNextBG, event_handler); + lv_obj_set_hidden(btnNextBG, true); + + btnPrevBG = lv_btn_create(lv_scr_act(), nullptr); + btnPrevBG->user_data = this; + lv_obj_set_size(btnPrevBG, 60, 60); + lv_obj_align(btnPrevBG, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, 80); + lv_obj_set_style_local_bg_opa(btnPrevBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnPrevBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<"); + lv_obj_set_event_cb(btnPrevBG, event_handler); + lv_obj_set_hidden(btnPrevBG, true); + + btnReset = lv_btn_create(lv_scr_act(), nullptr); + btnReset->user_data = this; + lv_obj_set_size(btnReset, 60, 60); + lv_obj_align(btnReset, lv_scr_act(), LV_ALIGN_CENTER, 0, 80); + lv_obj_set_style_local_bg_opa(btnReset, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnReset, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Rst"); + lv_obj_set_event_cb(btnReset, event_handler); + lv_obj_set_hidden(btnReset, true); + + btnRandom = lv_btn_create(lv_scr_act(), nullptr); + btnRandom->user_data = this; + lv_obj_set_size(btnRandom, 60, 60); + lv_obj_align(btnRandom, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_local_bg_opa(btnRandom, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnRandom, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Rnd"); + lv_obj_set_event_cb(btnRandom, event_handler); + lv_obj_set_hidden(btnRandom, true); + + btnClose = lv_btn_create(lv_scr_act(), nullptr); + btnClose->user_data = this; + lv_obj_set_size(btnClose, 60, 60); + lv_obj_align(btnClose, lv_scr_act(), LV_ALIGN_CENTER, 0, -80); + lv_obj_set_style_local_bg_opa(btnClose, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_style_local_value_str(btnClose, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "X"); + lv_obj_set_event_cb(btnClose, event_handler); + lv_obj_set_hidden(btnClose, true); + + btnSet = lv_btn_create(lv_scr_act(), nullptr); + btnSet->user_data = this; + lv_obj_set_height(btnSet, 150); + lv_obj_set_width(btnSet, 150); + lv_obj_align(btnSet, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_local_radius(btnSet, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 30); + lv_obj_set_style_local_bg_opa(btnSet, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50); + lv_obj_set_event_cb(btnSet, event_handler); + lbl_btnSet = lv_label_create(btnSet, nullptr); + lv_obj_set_style_local_text_font(lbl_btnSet, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48); + lv_label_set_text_static(lbl_btnSet, Symbols::settings); + lv_obj_set_hidden(btnSet, true); + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); Refresh(); } @@ -202,11 +304,55 @@ PineTimeStyle::~PineTimeStyle() { lv_obj_clean(lv_scr_act()); } +bool PineTimeStyle::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + if ((event == Pinetime::Applications::TouchEvents::LongTap) && lv_obj_get_hidden(btnRandom)) { + lv_obj_set_hidden(btnSet, false); + savedTick = lv_tick_get(); + return true; + } + if ((event == Pinetime::Applications::TouchEvents::DoubleTap) && (lv_obj_get_hidden(btnRandom) == false)) { + return true; + } + return false; +} + +void PineTimeStyle::CloseMenu() { + settingsController.SaveSettings(); + lv_obj_set_hidden(btnNextTime, true); + lv_obj_set_hidden(btnPrevTime, true); + lv_obj_set_hidden(btnNextBar, true); + lv_obj_set_hidden(btnPrevBar, true); + lv_obj_set_hidden(btnNextBG, true); + lv_obj_set_hidden(btnPrevBG, true); + lv_obj_set_hidden(btnReset, true); + lv_obj_set_hidden(btnRandom, true); + lv_obj_set_hidden(btnClose, true); +} + +bool PineTimeStyle::OnButtonPushed() { + if (!lv_obj_get_hidden(btnClose)) { + CloseMenu(); + return true; + } + return false; +} + void PineTimeStyle::SetBatteryIcon() { auto batteryPercent = batteryPercentRemaining.Get(); lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); } +void PineTimeStyle::AlignIcons() { + if (notificationState.Get() && bleState.Get()) { + lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 8, 25); + lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, -8, 25); + } else if (notificationState.Get() && !bleState.Get()) { + lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25); + } else { + lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25); + } +} + void PineTimeStyle::Refresh() { isCharging = batteryController.IsCharging(); if (isCharging.IsUpdated()) { @@ -226,13 +372,13 @@ void PineTimeStyle::Refresh() { bleState = bleController.IsConnected(); if (bleState.IsUpdated()) { lv_label_set_text(bleIcon, BleIcon::GetIcon(bleState.Get())); - lv_obj_realign(bleIcon); + AlignIcons(); } notificationState = notificatioManager.AreNewNotificationsAvailable(); if (notificationState.IsUpdated()) { lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(notificationState.Get())); - lv_obj_realign(notificationIcon); + AlignIcons(); } currentDateTime = dateTimeController.CurrentDateTime(); @@ -257,7 +403,7 @@ void PineTimeStyle::Refresh() { char hoursChar[3]; char ampmChar[5]; if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) { - sprintf(hoursChar, "%02d", hour); + sprintf(hoursChar, "%02d", hour); } else { if (hour == 0 && hour != 12) { hour = 12; @@ -312,4 +458,154 @@ void PineTimeStyle::Refresh() { lv_obj_set_style_local_scale_grad_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); } } + if (!lv_obj_get_hidden(btnSet)) { + if ((savedTick > 0) && (lv_tick_get() - savedTick > 3000)) { + lv_obj_set_hidden(btnSet, true); + savedTick = 0; + } + } +} + +void PineTimeStyle::UpdateSelected(lv_obj_t* object, lv_event_t event) { + auto valueTime = settingsController.GetPTSColorTime(); + auto valueBar = settingsController.GetPTSColorBar(); + auto valueBG = settingsController.GetPTSColorBG(); + + if (event == LV_EVENT_CLICKED) { + if (object == btnNextTime) { + valueTime = GetNext(valueTime); + if (valueTime == valueBG) { + valueTime = GetNext(valueTime); + } + settingsController.SetPTSColorTime(valueTime); + lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + } + if (object == btnPrevTime) { + valueTime = GetPrevious(valueTime); + if (valueTime == valueBG) { + valueTime = GetPrevious(valueTime); + } + settingsController.SetPTSColorTime(valueTime); + lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + } + if (object == btnNextBar) { + valueBar = GetNext(valueBar); + if (valueBar == Controllers::Settings::Colors::Black) { + valueBar = GetNext(valueBar); + } + if (valueBar == Controllers::Settings::Colors::White) { + needle_colors[0] = LV_COLOR_BLACK; + } else { + needle_colors[0] = LV_COLOR_WHITE; + } + settingsController.SetPTSColorBar(valueBar); + lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBar)); + } + if (object == btnPrevBar) { + valueBar = GetPrevious(valueBar); + if (valueBar == Controllers::Settings::Colors::Black) { + valueBar = GetPrevious(valueBar); + } + if (valueBar == Controllers::Settings::Colors::White) { + needle_colors[0] = LV_COLOR_BLACK; + } else { + needle_colors[0] = LV_COLOR_WHITE; + } + settingsController.SetPTSColorBar(valueBar); + lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBar)); + } + if (object == btnNextBG) { + valueBG = GetNext(valueBG); + if (valueBG == valueTime) { + valueBG = GetNext(valueBG); + } + settingsController.SetPTSColorBG(valueBG); + lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBG)); + } + if (object == btnPrevBG) { + valueBG = GetPrevious(valueBG); + if (valueBG == valueTime) { + valueBG = GetPrevious(valueBG); + } + settingsController.SetPTSColorBG(valueBG); + lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBG)); + } + if (object == btnReset) { + needle_colors[0] = LV_COLOR_WHITE; + settingsController.SetPTSColorTime(Controllers::Settings::Colors::Teal); + lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); + lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); + lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); + settingsController.SetPTSColorBar(Controllers::Settings::Colors::Teal); + lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); + settingsController.SetPTSColorBG(Controllers::Settings::Colors::Black); + lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Black)); + } + if (object == btnRandom) { + valueTime = static_cast<Controllers::Settings::Colors>(rand() % 17); + valueBar = static_cast<Controllers::Settings::Colors>(rand() % 17); + valueBG = static_cast<Controllers::Settings::Colors>(rand() % 17); + if (valueTime == valueBG) { + valueBG = GetNext(valueBG); + } + if (valueBar == Controllers::Settings::Colors::Black) { + valueBar = GetPrevious(valueBar); + } + if (valueBar == Controllers::Settings::Colors::White) { + needle_colors[0] = LV_COLOR_BLACK; + } else { + needle_colors[0] = LV_COLOR_WHITE; + } + settingsController.SetPTSColorTime(static_cast<Controllers::Settings::Colors>(valueTime)); + lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); + settingsController.SetPTSColorBar(static_cast<Controllers::Settings::Colors>(valueBar)); + lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBar)); + settingsController.SetPTSColorBG(static_cast<Controllers::Settings::Colors>(valueBG)); + lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBG)); + } + if (object == btnClose) { + CloseMenu(); + } + if (object == btnSet) { + lv_obj_set_hidden(btnSet, true); + lv_obj_set_hidden(btnNextTime, false); + lv_obj_set_hidden(btnPrevTime, false); + lv_obj_set_hidden(btnNextBar, false); + lv_obj_set_hidden(btnPrevBar, false); + lv_obj_set_hidden(btnNextBG, false); + lv_obj_set_hidden(btnPrevBG, false); + lv_obj_set_hidden(btnReset, false); + lv_obj_set_hidden(btnRandom, false); + lv_obj_set_hidden(btnClose, false); + } + } +} + +Pinetime::Controllers::Settings::Colors PineTimeStyle::GetNext(Pinetime::Controllers::Settings::Colors color) { + auto colorAsInt = static_cast<uint8_t>(color); + Pinetime::Controllers::Settings::Colors nextColor; + if (colorAsInt < 16) { + nextColor = static_cast<Controllers::Settings::Colors>(colorAsInt + 1); + } else { + nextColor = static_cast<Controllers::Settings::Colors>(0); + } + return nextColor; +} + +Pinetime::Controllers::Settings::Colors PineTimeStyle::GetPrevious(Pinetime::Controllers::Settings::Colors color) { + auto colorAsInt = static_cast<uint8_t>(color); + Pinetime::Controllers::Settings::Colors prevColor; + + if (colorAsInt > 0) { + prevColor = static_cast<Controllers::Settings::Colors>(colorAsInt - 1); + } else { + prevColor = static_cast<Controllers::Settings::Colors>(16); + } + return prevColor; } diff --git a/src/displayapp/screens/PineTimeStyle.h b/src/displayapp/screens/PineTimeStyle.h index ba473806..df8b7d5a 100644 --- a/src/displayapp/screens/PineTimeStyle.h +++ b/src/displayapp/screens/PineTimeStyle.h @@ -4,8 +4,8 @@ #include <chrono> #include <cstdint> #include <memory> -#include "Screen.h" -#include "ScreenList.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/Colors.h" #include "components/datetime/DateTimeController.h" namespace Pinetime { @@ -15,6 +15,7 @@ namespace Pinetime { class Ble; class NotificationManager; class HeartRateController; + class MotionController; } namespace Applications { @@ -30,8 +31,13 @@ namespace Pinetime { Controllers::MotionController& motionController); ~PineTimeStyle() override; + bool OnTouchEvent(TouchEvents event) override; + bool OnButtonPushed() override; + void Refresh() override; + void UpdateSelected(lv_obj_t *object, lv_event_t event); + private: char displayedChar[5]; @@ -39,6 +45,7 @@ namespace Pinetime { Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown; Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown; uint8_t currentDay = 0; + uint32_t savedTick = 0; DirtyValue<uint8_t> batteryPercentRemaining {}; DirtyValue<bool> isCharging {}; @@ -48,6 +55,18 @@ namespace Pinetime { DirtyValue<uint32_t> stepCount {}; DirtyValue<bool> notificationState {}; + static Pinetime::Controllers::Settings::Colors GetNext(Controllers::Settings::Colors color); + static Pinetime::Controllers::Settings::Colors GetPrevious(Controllers::Settings::Colors color); + + lv_obj_t* btnNextTime; + lv_obj_t* btnPrevTime; + lv_obj_t* btnNextBar; + lv_obj_t* btnPrevBar; + lv_obj_t* btnNextBG; + lv_obj_t* btnPrevBG; + lv_obj_t* btnReset; + lv_obj_t* btnRandom; + lv_obj_t* btnClose; lv_obj_t* timebar; lv_obj_t* sidebar; lv_obj_t* timeDD1; @@ -67,6 +86,8 @@ namespace Pinetime { lv_obj_t* calendarCrossBar2; lv_obj_t* notificationIcon; lv_obj_t* stepGauge; + lv_obj_t* btnSet; + lv_obj_t* lbl_btnSet; lv_color_t needle_colors[1]; Controllers::DateTime& dateTimeController; @@ -77,6 +98,8 @@ namespace Pinetime { Controllers::MotionController& motionController; void SetBatteryIcon(); + void CloseMenu(); + void AlignIcons(); lv_task_t* taskRefresh; }; diff --git a/src/displayapp/screens/Screen.cpp b/src/displayapp/screens/Screen.cpp index 6ae5b7bb..bc4cc438 100644 --- a/src/displayapp/screens/Screen.cpp +++ b/src/displayapp/screens/Screen.cpp @@ -1,4 +1,4 @@ -#include "Screen.h" +#include "displayapp/screens/Screen.h" using namespace Pinetime::Applications::Screens; void Screen::RefreshTaskCallback(lv_task_t* task) { diff --git a/src/displayapp/screens/Screen.h b/src/displayapp/screens/Screen.h index ce5741b2..04bb152c 100644 --- a/src/displayapp/screens/Screen.h +++ b/src/displayapp/screens/Screen.h @@ -1,7 +1,7 @@ #pragma once #include <cstdint> -#include "../TouchEvents.h" +#include "displayapp/TouchEvents.h" #include <lvgl/lvgl.h> namespace Pinetime { diff --git a/src/displayapp/screens/ScreenList.h b/src/displayapp/screens/ScreenList.h index a9d747aa..e316e360 100644 --- a/src/displayapp/screens/ScreenList.h +++ b/src/displayapp/screens/ScreenList.h @@ -3,8 +3,8 @@ #include <array> #include <functional> #include <memory> -#include "Screen.h" -#include "../DisplayApp.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/DisplayApp.h" namespace Pinetime { namespace Applications { diff --git a/src/displayapp/screens/Steps.cpp b/src/displayapp/screens/Steps.cpp index c41163ab..3e7f8201 100644 --- a/src/displayapp/screens/Steps.cpp +++ b/src/displayapp/screens/Steps.cpp @@ -1,10 +1,15 @@ -#include "Steps.h" +#include "displayapp/screens/Steps.h" #include <lvgl/lvgl.h> -#include "../DisplayApp.h" -#include "Symbols.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; +static void lap_event_handler(lv_obj_t* obj, lv_event_t event) { + auto* steps = static_cast<Steps*>(obj->user_data); + steps->lapBtnEventHandler(event); +} + Steps::Steps(Pinetime::Applications::DisplayApp* app, Controllers::MotionController& motionController, Controllers::Settings& settingsController) @@ -17,11 +22,12 @@ Steps::Steps(Pinetime::Applications::DisplayApp* app, lv_obj_set_style_local_radius(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 0); lv_obj_set_style_local_line_color(stepsArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0x0000FF)); lv_arc_set_end_angle(stepsArc, 200); - lv_obj_set_size(stepsArc, 220, 220); + lv_obj_set_size(stepsArc, 240, 240); lv_arc_set_range(stepsArc, 0, 500); lv_obj_align(stepsArc, nullptr, LV_ALIGN_CENTER, 0, 0); stepsCount = motionController.NbSteps(); + currentTripSteps = stepsCount - motionController.GetTripSteps(); lv_arc_set_value(stepsArc, int16_t(500 * stepsCount / settingsController.GetStepsGoal())); @@ -29,18 +35,18 @@ Steps::Steps(Pinetime::Applications::DisplayApp* app, lv_obj_set_style_local_text_color(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00)); lv_obj_set_style_local_text_font(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); lv_label_set_text_fmt(lSteps, "%li", stepsCount); - lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -20); + lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -40); lv_obj_t* lstepsL = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(lstepsL, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); lv_label_set_text_static(lstepsL, "Steps"); - lv_obj_align(lstepsL, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); + lv_obj_align(lstepsL, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 5); lv_obj_t* lstepsGoal = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(lstepsGoal, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN); - lv_label_set_text_fmt(lstepsGoal, "Goal\n%lu", settingsController.GetStepsGoal()); + lv_label_set_text_fmt(lstepsGoal, "Goal: %5lu", settingsController.GetStepsGoal()); lv_label_set_align(lstepsGoal, LV_LABEL_ALIGN_CENTER); - lv_obj_align(lstepsGoal, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 60); + lv_obj_align(lstepsGoal, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 40); lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr); lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); @@ -48,6 +54,22 @@ Steps::Steps(Pinetime::Applications::DisplayApp* app, lv_obj_set_pos(backgroundLabel, 0, 0); lv_label_set_text_static(backgroundLabel, ""); + resetBtn = lv_btn_create(lv_scr_act(), nullptr); + resetBtn->user_data = this; + lv_obj_set_event_cb(resetBtn, lap_event_handler); + lv_obj_set_height(resetBtn, 50); + lv_obj_set_width(resetBtn, 115); + lv_obj_align(resetBtn, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); + resetButtonLabel = lv_label_create(resetBtn, nullptr); + lv_label_set_text(resetButtonLabel, "Reset"); + + currentTripSteps = motionController.GetTripSteps(); + + tripLabel = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(tripLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW); + lv_label_set_text_fmt(tripLabel, "Trip: %5li", currentTripSteps); + lv_obj_align(tripLabel, lstepsGoal, LV_ALIGN_IN_LEFT_MID, 0, 20); + taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this); } @@ -58,9 +80,24 @@ Steps::~Steps() { void Steps::Refresh() { stepsCount = motionController.NbSteps(); + currentTripSteps = motionController.GetTripSteps(); lv_label_set_text_fmt(lSteps, "%li", stepsCount); - lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -20); + lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -40); + if (currentTripSteps < 100000) { + lv_label_set_text_fmt(tripLabel, "Trip: %5li", currentTripSteps); + } else { + lv_label_set_text_fmt(tripLabel, "Trip: 99999+"); + } lv_arc_set_value(stepsArc, int16_t(500 * stepsCount / settingsController.GetStepsGoal())); } + +void Steps::lapBtnEventHandler(lv_event_t event) { + if (event != LV_EVENT_CLICKED) { + return; + } + stepsCount = motionController.NbSteps(); + motionController.ResetTrip(); + Refresh(); +} diff --git a/src/displayapp/screens/Steps.h b/src/displayapp/screens/Steps.h index d7cf31e1..f109e0f2 100644 --- a/src/displayapp/screens/Steps.h +++ b/src/displayapp/screens/Steps.h @@ -2,7 +2,7 @@ #include <cstdint> #include <lvgl/lvgl.h> -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include <components/motion/MotionController.h> namespace Pinetime { @@ -20,14 +20,20 @@ namespace Pinetime { ~Steps() override; void Refresh() override; + void lapBtnEventHandler(lv_event_t event); private: Controllers::MotionController& motionController; Controllers::Settings& settingsController; + uint32_t currentTripSteps = 0; + lv_obj_t* lSteps; lv_obj_t* lStepsIcon; lv_obj_t* stepsArc; + lv_obj_t* resetBtn; + lv_obj_t* resetButtonLabel; + lv_obj_t* tripLabel; uint32_t stepsCount; diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 9b27a89d..a260d293 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -1,8 +1,8 @@ #include "StopWatch.h" -#include "Screen.h" -#include "Symbols.h" -#include "lvgl/lvgl.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/Symbols.h" +#include <lvgl/lvgl.h> #include "projdefs.h" #include "FreeRTOSConfig.h" #include "task.h" diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index 25634e92..0720a586 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -1,8 +1,8 @@ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "components/datetime/DateTimeController.h" -#include "../LittleVgl.h" +#include "displayapp/LittleVgl.h" #include "FreeRTOS.h" #include "portmacro_cmsis.h" diff --git a/src/displayapp/screens/Styles.cpp b/src/displayapp/screens/Styles.cpp new file mode 100644 index 00000000..7f43fb99 --- /dev/null +++ b/src/displayapp/screens/Styles.cpp @@ -0,0 +1,8 @@ +#include "Styles.h" + +void Pinetime::Applications::Screens::SetRadioButtonStyle(lv_obj_t* checkbox) { + lv_obj_set_style_local_radius(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); + lv_obj_set_style_local_border_width(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED, 9); + lv_obj_set_style_local_border_color(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED, LV_COLOR_GREEN); + lv_obj_set_style_local_bg_color(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED, LV_COLOR_WHITE); +} diff --git a/src/displayapp/screens/Styles.h b/src/displayapp/screens/Styles.h new file mode 100644 index 00000000..a5fbb9f6 --- /dev/null +++ b/src/displayapp/screens/Styles.h @@ -0,0 +1,9 @@ +#include <lvgl/lvgl.h> + +namespace Pinetime { + namespace Applications { + namespace Screens { + void SetRadioButtonStyle(lv_obj_t* checkbox); + } + } +} diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index 343b72bf..e0138f86 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -1,7 +1,9 @@ -#include "SystemInfo.h" +#include <FreeRTOS.h> +#include <task.h> +#include "displayapp/screens/SystemInfo.h" #include <lvgl/lvgl.h> -#include "../DisplayApp.h" -#include "Label.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Label.h" #include "Version.h" #include "BootloaderVersion.h" #include "components/battery/BatteryController.h" @@ -41,8 +43,8 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app, brightnessController {brightnessController}, bleController {bleController}, watchdog {watchdog}, - motionController{motionController}, - touchPanel{touchPanel}, + motionController {motionController}, + touchPanel {touchPanel}, screens {app, 0, {[this]() -> std::unique_ptr<Screen> { @@ -182,9 +184,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() { " #444444 used# %d (%d%%)\n" " #444444 max used# %lu\n" " #444444 frag# %d%%\n" - " #444444 free# %d" - "\n" - "#444444 Steps# %i", + " #444444 free# %d", bleAddr[5], bleAddr[4], bleAddr[3], @@ -195,8 +195,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() { mon.used_pct, mon.max_used, mon.frag_pct, - static_cast<int>(mon.free_biggest_size), - 0); + static_cast<int>(mon.free_biggest_size)); lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); return std::make_unique<Screens::Label>(2, 5, app, label); } @@ -209,7 +208,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen4() { static constexpr uint8_t maxTaskCount = 9; TaskStatus_t tasksStatus[maxTaskCount]; - lv_obj_t* infoTask = lv_table_create(lv_scr_act(), NULL); + lv_obj_t* infoTask = lv_table_create(lv_scr_act(), nullptr); lv_table_set_col_cnt(infoTask, 4); lv_table_set_row_cnt(infoTask, maxTaskCount + 1); lv_obj_set_style_local_pad_all(infoTask, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, 0); @@ -227,35 +226,37 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen4() { auto nb = uxTaskGetSystemState(tasksStatus, maxTaskCount, nullptr); std::sort(tasksStatus, tasksStatus + nb, sortById); for (uint8_t i = 0; i < nb && i < maxTaskCount; i++) { + char buffer[7] = {0}; - lv_table_set_cell_value(infoTask, i + 1, 0, std::to_string(tasksStatus[i].xTaskNumber).c_str()); - char state[2] = {0}; + sprintf(buffer, "%lu", tasksStatus[i].xTaskNumber); + lv_table_set_cell_value(infoTask, i + 1, 0, buffer); switch (tasksStatus[i].eCurrentState) { case eReady: case eRunning: - state[0] = 'R'; + buffer[0] = 'R'; break; case eBlocked: - state[0] = 'B'; + buffer[0] = 'B'; break; case eSuspended: - state[0] = 'S'; + buffer[0] = 'S'; break; case eDeleted: - state[0] = 'D'; + buffer[0] = 'D'; break; default: - state[0] = 'I'; // Invalid + buffer[0] = 'I'; // Invalid break; } - lv_table_set_cell_value(infoTask, i + 1, 1, state); + buffer[1] = '\0'; + lv_table_set_cell_value(infoTask, i + 1, 1, buffer); lv_table_set_cell_value(infoTask, i + 1, 2, tasksStatus[i].pcTaskName); if (tasksStatus[i].usStackHighWaterMark < 20) { - std::string str1 = std::to_string(tasksStatus[i].usStackHighWaterMark) + " low"; - lv_table_set_cell_value(infoTask, i + 1, 3, str1.c_str()); + sprintf(buffer, "%d low", tasksStatus[i].usStackHighWaterMark); } else { - lv_table_set_cell_value(infoTask, i + 1, 3, std::to_string(tasksStatus[i].usStackHighWaterMark).c_str()); + sprintf(buffer, "%d", tasksStatus[i].usStackHighWaterMark); } + lv_table_set_cell_value(infoTask, i + 1, 3, buffer); } return std::make_unique<Screens::Label>(3, 5, app, infoTask); } @@ -275,4 +276,4 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen5() { 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); -} +}
\ No newline at end of file diff --git a/src/displayapp/screens/SystemInfo.h b/src/displayapp/screens/SystemInfo.h index bfcc3aa4..a382ed8f 100644 --- a/src/displayapp/screens/SystemInfo.h +++ b/src/displayapp/screens/SystemInfo.h @@ -1,8 +1,8 @@ #pragma once #include <memory> -#include "Screen.h" -#include "ScreenList.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/ScreenList.h" namespace Pinetime { namespace Controllers { diff --git a/src/displayapp/screens/Tile.cpp b/src/displayapp/screens/Tile.cpp index 1d4f0d0e..ba764a2e 100644 --- a/src/displayapp/screens/Tile.cpp +++ b/src/displayapp/screens/Tile.cpp @@ -1,6 +1,6 @@ -#include "Tile.h" -#include "../DisplayApp.h" -#include "BatteryIcon.h" +#include "displayapp/screens/Tile.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/BatteryIcon.h" using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Tile.h b/src/displayapp/screens/Tile.h index 83d3fdf5..4869fef9 100644 --- a/src/displayapp/screens/Tile.h +++ b/src/displayapp/screens/Tile.h @@ -3,8 +3,8 @@ #include <lvgl/lvgl.h> #include <cstdint> #include <memory> -#include "Screen.h" -#include "../Apps.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/Apps.h" #include "components/datetime/DateTimeController.h" #include "components/settings/Settings.h" #include "components/datetime/DateTimeController.h" diff --git a/src/displayapp/screens/Timer.cpp b/src/displayapp/screens/Timer.cpp index ff3099d5..a5e40195 100644 --- a/src/displayapp/screens/Timer.cpp +++ b/src/displayapp/screens/Timer.cpp @@ -1,8 +1,8 @@ -#include "Timer.h" +#include "displayapp/screens/Timer.h" -#include "Screen.h" -#include "Symbols.h" -#include "lvgl/lvgl.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/Symbols.h" +#include <lvgl/lvgl.h> using namespace Pinetime::Applications::Screens; diff --git a/src/displayapp/screens/Timer.h b/src/displayapp/screens/Timer.h index d0fc8ed1..23c87345 100644 --- a/src/displayapp/screens/Timer.h +++ b/src/displayapp/screens/Timer.h @@ -1,9 +1,9 @@ #pragma once -#include "Screen.h" +#include "displayapp/screens/Screen.h" #include "components/datetime/DateTimeController.h" #include "systemtask/SystemTask.h" -#include "../LittleVgl.h" +#include "displayapp/LittleVgl.h" #include "components/timer/TimerController.h" diff --git a/src/displayapp/screens/Twos.cpp b/src/displayapp/screens/Twos.cpp index 4201d501..b15332f1 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 "displayapp/screens/Twos.h" #include <array> -#include <vector> +#include <cstdio> +#include <cstdlib> +#include <lvgl/lvgl.h> #include <utility> +#include <vector> using namespace Pinetime::Applications::Screens; @@ -129,7 +129,7 @@ bool Twos::placeNewTile() { return true; } -bool Twos::tryMerge(Tile grid[][4], int& newRow, int& newCol, int oldRow, int oldCol) { +bool Twos::tryMerge(TwosTile grid[][4], int& newRow, int& newCol, int oldRow, int oldCol) { if ((grid[newRow][newCol].value == grid[oldRow][oldCol].value)) { if ((newCol != oldCol) || (newRow != oldRow)) { if (!grid[newRow][newCol].merged) { @@ -146,7 +146,7 @@ bool Twos::tryMerge(Tile grid[][4], int& newRow, int& newCol, int oldRow, int ol return false; } -bool Twos::tryMove(Tile grid[][4], int newRow, int newCol, int oldRow, int oldCol) { +bool Twos::tryMove(TwosTile grid[][4], int newRow, int newCol, int oldRow, int oldCol) { if (((newCol >= 0) && (newCol != oldCol)) || ((newRow >= 0) && (newRow != oldRow))) { grid[newRow][newCol].value = grid[oldRow][oldCol].value; grid[oldRow][oldCol].value = 0; @@ -261,11 +261,13 @@ bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) { return false; } -void Twos::updateGridDisplay(Tile grid[][4]) { +void Twos::updateGridDisplay(TwosTile 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/Twos.h b/src/displayapp/screens/Twos.h index 6d85cff6..5a0c4350 100644 --- a/src/displayapp/screens/Twos.h +++ b/src/displayapp/screens/Twos.h @@ -1,11 +1,11 @@ #pragma once #include <lvgl/src/lv_core/lv_obj.h> -#include "Screen.h" +#include "displayapp/screens/Screen.h" namespace Pinetime { namespace Applications { - struct Tile { + struct TwosTile { bool merged = false; unsigned int value = 0; }; @@ -26,11 +26,11 @@ namespace Pinetime { lv_obj_t* scoreText; lv_obj_t* gridDisplay; - Tile grid[4][4]; + TwosTile grid[4][4]; unsigned int score = 0; - void updateGridDisplay(Tile grid[][4]); - bool tryMerge(Tile grid[][4], int& newRow, int& newCol, int oldRow, int oldCol); - bool tryMove(Tile grid[][4], int newRow, int newCol, int oldRow, int oldCol); + void updateGridDisplay(TwosTile grid[][4]); + bool tryMerge(TwosTile grid[][4], int& newRow, int& newCol, int oldRow, int oldCol); + bool tryMove(TwosTile grid[][4], int newRow, int newCol, int oldRow, int oldCol); bool placeNewTile(); }; } diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp index 53e7faf7..f027a744 100644 --- a/src/displayapp/screens/WatchFaceAnalog.cpp +++ b/src/displayapp/screens/WatchFaceAnalog.cpp @@ -1,9 +1,11 @@ -#include <libs/lvgl/lvgl.h> -#include "WatchFaceAnalog.h" -#include "BatteryIcon.h" -#include "BleIcon.h" -#include "Symbols.h" -#include "NotificationIcon.h" +#include "displayapp/screens/WatchFaceAnalog.h" +#include <cmath> +#include <lvgl/lvgl.h> +#include "displayapp/screens/BatteryIcon.h" +#include "displayapp/screens/BleIcon.h" +#include "displayapp/screens/Symbols.h" +#include "displayapp/screens/NotificationIcon.h" +#include "components/settings/Settings.h" LV_IMG_DECLARE(bg_clock); @@ -73,7 +75,7 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app, 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)); lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); - lv_obj_align(notificationIcon, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + lv_obj_align(notificationIcon, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); // Date - Day / Week day diff --git a/src/displayapp/screens/WatchFaceAnalog.h b/src/displayapp/screens/WatchFaceAnalog.h index 001414a6..a18eb299 100644 --- a/src/displayapp/screens/WatchFaceAnalog.h +++ b/src/displayapp/screens/WatchFaceAnalog.h @@ -4,8 +4,7 @@ #include <chrono> #include <cstdint> #include <memory> -#include "Screen.h" -#include "ScreenList.h" +#include "displayapp/screens/Screen.h" #include "components/datetime/DateTimeController.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index 2ecab609..4d9eaf37 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -1,17 +1,18 @@ -#include "WatchFaceDigital.h" +#include "displayapp/screens/WatchFaceDigital.h" #include <date/date.h> #include <lvgl/lvgl.h> #include <cstdio> -#include "BatteryIcon.h" -#include "BleIcon.h" -#include "NotificationIcon.h" -#include "Symbols.h" +#include "displayapp/screens/BatteryIcon.h" +#include "displayapp/screens/BleIcon.h" +#include "displayapp/screens/NotificationIcon.h" +#include "displayapp/screens/Symbols.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/heartrate/HeartRateController.h" #include "components/motion/MotionController.h" +#include "components/settings/Settings.h" using namespace Pinetime::Applications::Screens; WatchFaceDigital::WatchFaceDigital(DisplayApp* app, @@ -43,7 +44,7 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); 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(0x0000FF)); + lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x0082FC)); lv_label_set_text(bleIcon, Symbols::bluetooth); lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h index e27545f3..627154c8 100644 --- a/src/displayapp/screens/WatchFaceDigital.h +++ b/src/displayapp/screens/WatchFaceDigital.h @@ -4,8 +4,7 @@ #include <chrono> #include <cstdint> #include <memory> -#include "Screen.h" -#include "ScreenList.h" +#include "displayapp/screens/Screen.h" #include "components/datetime/DateTimeController.h" namespace Pinetime { diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp new file mode 100644 index 00000000..1d0a83bd --- /dev/null +++ b/src/displayapp/screens/Weather.cpp @@ -0,0 +1,222 @@ +/* Copyright (C) 2021 Avamander + + This file is part of InfiniTime. + + InfiniTime is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + InfiniTime is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +#include "Weather.h" +#include <lvgl/lvgl.h> +#include <components/ble/weather/WeatherService.h> +#include "Label.h" +#include "components/battery/BatteryController.h" +#include "components/ble/BleController.h" +#include "components/ble/weather/WeatherData.h" + +using namespace Pinetime::Applications::Screens; + +Weather::Weather(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::WeatherService& weather) + : Screen(app), + dateTimeController {dateTimeController}, + weatherService(weather), + screens {app, + 0, + {[this]() -> std::unique_ptr<Screen> { + return CreateScreenTemperature(); + }, + [this]() -> std::unique_ptr<Screen> { + return CreateScreenAir(); + }, + [this]() -> std::unique_ptr<Screen> { + return CreateScreenClouds(); + }, + [this]() -> std::unique_ptr<Screen> { + return CreateScreenPrecipitation(); + }, + [this]() -> std::unique_ptr<Screen> { + return CreateScreenHumidity(); + }}, + Screens::ScreenListModes::UpDown} { +} + +Weather::~Weather() { + lv_obj_clean(lv_scr_act()); +} + +void Weather::Refresh() { + if (running) { + // screens.Refresh(); + } +} + +bool Weather::OnButtonPushed() { + running = false; + return true; +} + +bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + return screens.OnTouchEvent(event); +} + +std::unique_ptr<Screen> Weather::CreateScreenTemperature() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr<Controllers::WeatherData::Temperature>& current = weatherService.GetCurrentTemperature(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Temperature#\n\n" + "#444444 %d#°C \n\n" + "#444444 %d#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0, + 0); + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Temperature#\n\n" + "#444444 %d#°C \n\n" + "#444444 %hd#\n\n" + "%llu\n" + "%lu\n", + current->temperature / 100, + current->dewPoint, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr<Screen> Weather::CreateScreenAir() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr<Controllers::WeatherData::AirQuality>& current = weatherService.GetCurrentQuality(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Air quality#\n\n" + "#444444 %s#\n" + "#444444 %d#\n\n" + "%d\n" + "%d\n", + "", + 0, + 0, + 0); + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Air quality#\n\n" + "#444444 %s#\n" + "#444444 %lu#\n\n" + "%llu\n" + "%lu\n", + current->polluter.c_str(), + (current->amount / 100), + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr<Screen> Weather::CreateScreenClouds() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr<Controllers::WeatherData::Clouds>& current = weatherService.GetCurrentClouds(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Clouds#\n\n" + "#444444 %d%%#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0); + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Clouds#\n\n" + "#444444 %hhu%%#\n\n" + "%llu\n" + "%lu\n", + current->amount, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr<Screen> Weather::CreateScreenPrecipitation() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr<Controllers::WeatherData::Precipitation>& current = weatherService.GetCurrentPrecipitation(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Precipitation#\n\n" + "#444444 %d%%#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0); + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Precipitation#\n\n" + "#444444 %hhu%%#\n\n" + "%llu\n" + "%lu\n", + current->amount, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); +} + +std::unique_ptr<Screen> Weather::CreateScreenHumidity() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr<Controllers::WeatherData::Humidity>& current = weatherService.GetCurrentHumidity(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Humidity#\n\n" + "#444444 %d%%#\n\n" + "%d\n" + "%d\n", + 0, + 0, + 0); + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Humidity#\n\n" + "#444444 %hhu%%#\n\n" + "%llu\n" + "%lu\n", + current->humidity, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); +} diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h new file mode 100644 index 00000000..34f95fce --- /dev/null +++ b/src/displayapp/screens/Weather.h @@ -0,0 +1,45 @@ +#pragma once + +#include <memory> +#include <components/ble/weather/WeatherService.h> +#include "Screen.h" +#include "ScreenList.h" + +namespace Pinetime { + namespace Applications { + class DisplayApp; + + namespace Screens { + class Weather : public Screen { + public: + explicit Weather(DisplayApp* app, Pinetime::Controllers::WeatherService& weather); + + ~Weather() override; + + void Refresh() override; + + bool OnButtonPushed() override; + + bool OnTouchEvent(TouchEvents event) override; + + private: + bool running = true; + + Pinetime::Controllers::DateTime& dateTimeController; + Controllers::WeatherService& weatherService; + + ScreenList<5> screens; + + std::unique_ptr<Screen> CreateScreenTemperature(); + + std::unique_ptr<Screen> CreateScreenAir(); + + std::unique_ptr<Screen> CreateScreenClouds(); + + std::unique_ptr<Screen> CreateScreenPrecipitation(); + + std::unique_ptr<Screen> CreateScreenHumidity(); + }; + } + } +} diff --git a/src/displayapp/screens/settings/QuickSettings.cpp b/src/displayapp/screens/settings/QuickSettings.cpp index dd626072..5d3a9834 100644 --- a/src/displayapp/screens/settings/QuickSettings.cpp +++ b/src/displayapp/screens/settings/QuickSettings.cpp @@ -1,4 +1,4 @@ -#include "QuickSettings.h" +#include "displayapp/screens/settings/QuickSettings.h" #include "displayapp/DisplayApp.h" #include "displayapp/screens/Symbols.h" #include "displayapp/screens/BatteryIcon.h" diff --git a/src/displayapp/screens/settings/QuickSettings.h b/src/displayapp/screens/settings/QuickSettings.h index c036fa5c..7913898d 100644 --- a/src/displayapp/screens/settings/QuickSettings.h +++ b/src/displayapp/screens/settings/QuickSettings.h @@ -1,8 +1,6 @@ #pragma once #include <cstdint> -#include <FreeRTOS.h> -#include <timers.h> #include "displayapp/screens/Screen.h" #include <lvgl/lvgl.h> #include "components/datetime/DateTimeController.h" diff --git a/src/displayapp/screens/settings/SettingDisplay.cpp b/src/displayapp/screens/settings/SettingDisplay.cpp index d8d6c767..9e972afc 100644 --- a/src/displayapp/screens/settings/SettingDisplay.cpp +++ b/src/displayapp/screens/settings/SettingDisplay.cpp @@ -1,7 +1,8 @@ -#include "SettingDisplay.h" +#include "displayapp/screens/settings/SettingDisplay.h" #include <lvgl/lvgl.h> #include "displayapp/DisplayApp.h" #include "displayapp/Messages.h" +#include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" @@ -14,6 +15,8 @@ namespace { } } +constexpr std::array<uint16_t, 4> SettingDisplay::options; + SettingDisplay::SettingDisplay(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController) : Screen(app), settingsController {settingsController} { @@ -40,39 +43,19 @@ SettingDisplay::SettingDisplay(Pinetime::Applications::DisplayApp* app, Pinetime lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); - optionsTotal = 0; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " 5 seconds"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetScreenTimeOut() == 5000) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); - } - optionsTotal++; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " 15 seconds"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetScreenTimeOut() == 15000) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); - } - optionsTotal++; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " 20 seconds"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetScreenTimeOut() == 20000) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); - } - optionsTotal++; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " 30 seconds"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetScreenTimeOut() == 30000) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); + char buffer[12]; + for (unsigned int i = 0; i < options.size(); i++) { + cbOption[i] = lv_checkbox_create(container1, nullptr); + sprintf(buffer, "%3d seconds", options[i] / 1000); + lv_checkbox_set_text(cbOption[i], buffer); + cbOption[i]->user_data = this; + lv_obj_set_event_cb(cbOption[i], event_handler); + SetRadioButtonStyle(cbOption[i]); + + if (settingsController.GetScreenTimeOut() == options[i]) { + lv_checkbox_set_checked(cbOption[i], true); + } } - optionsTotal++; } SettingDisplay::~SettingDisplay() { @@ -82,25 +65,11 @@ SettingDisplay::~SettingDisplay() { void SettingDisplay::UpdateSelected(lv_obj_t* object, lv_event_t event) { if (event == LV_EVENT_CLICKED) { - for (int i = 0; i < optionsTotal; i++) { + for (unsigned int i = 0; i < options.size(); i++) { if (object == cbOption[i]) { lv_checkbox_set_checked(cbOption[i], true); - - if (i == 0) { - settingsController.SetScreenTimeOut(5000); - }; - if (i == 1) { - settingsController.SetScreenTimeOut(15000); - }; - if (i == 2) { - settingsController.SetScreenTimeOut(20000); - }; - if (i == 3) { - settingsController.SetScreenTimeOut(30000); - }; - + settingsController.SetScreenTimeOut(options[i]); app->PushMessage(Applications::Display::Messages::UpdateTimeOut); - } else { lv_checkbox_set_checked(cbOption[i], false); } diff --git a/src/displayapp/screens/settings/SettingDisplay.h b/src/displayapp/screens/settings/SettingDisplay.h index 51b23aca..dc56419d 100644 --- a/src/displayapp/screens/settings/SettingDisplay.h +++ b/src/displayapp/screens/settings/SettingDisplay.h @@ -1,7 +1,9 @@ #pragma once +#include <array> #include <cstdint> #include <lvgl/lvgl.h> + #include "components/settings/Settings.h" #include "displayapp/screens/Screen.h" @@ -18,9 +20,10 @@ namespace Pinetime { void UpdateSelected(lv_obj_t* object, lv_event_t event); private: + static constexpr std::array<uint16_t, 4> options = {5000, 15000, 20000, 30000}; + Controllers::Settings& settingsController; - uint8_t optionsTotal; - lv_obj_t* cbOption[4]; + lv_obj_t* cbOption[options.size()]; }; } } diff --git a/src/displayapp/screens/settings/SettingPineTimeStyle.cpp b/src/displayapp/screens/settings/SettingPineTimeStyle.cpp deleted file mode 100644 index c9af19b6..00000000 --- a/src/displayapp/screens/settings/SettingPineTimeStyle.cpp +++ /dev/null @@ -1,318 +0,0 @@ -#include "SettingPineTimeStyle.h" -#include <lvgl/lvgl.h> -#include <displayapp/Colors.h> -#include "displayapp/DisplayApp.h" -#include "displayapp/screens/Symbols.h" - -using namespace Pinetime::Applications::Screens; - -namespace { - static void event_handler(lv_obj_t* obj, lv_event_t event) { - SettingPineTimeStyle* screen = static_cast<SettingPineTimeStyle*>(obj->user_data); - screen->UpdateSelected(obj, event); - } -} - -SettingPineTimeStyle::SettingPineTimeStyle(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController) - : Screen(app), settingsController {settingsController} { - timebar = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorBG())); - lv_obj_set_style_local_radius(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(timebar, 200, 240); - lv_obj_align(timebar, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 0); - - // Display the time - - timeDD1 = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_font(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light); - lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorTime())); - lv_label_set_text(timeDD1, "12"); - lv_obj_align(timeDD1, timebar, LV_ALIGN_IN_TOP_MID, 5, 5); - - timeDD2 = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_font(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light); - lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorTime())); - lv_label_set_text(timeDD2, "34"); - lv_obj_align(timeDD2, timebar, LV_ALIGN_IN_BOTTOM_MID, 5, -5); - - timeAMPM = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorTime())); - lv_obj_set_style_local_text_line_space(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, -3); - lv_label_set_text(timeAMPM, "A\nM"); - lv_obj_align(timeAMPM, timebar, LV_ALIGN_IN_BOTTOM_LEFT, 2, -20); - - // Create a 40px wide bar down the right side of the screen - - sidebar = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorBar())); - lv_obj_set_style_local_radius(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(sidebar, 40, 240); - lv_obj_align(sidebar, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0); - - // Display icons - - batteryIcon = lv_label_create(lv_scr_act(), nullptr); - 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); - - 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)); - lv_label_set_text(bleIcon, Symbols::bluetooth); - lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25); - - // Calendar icon - - calendarOuter = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_set_style_local_radius(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(calendarOuter, 34, 34); - lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 0); - - calendarInner = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xffffff)); - lv_obj_set_style_local_radius(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(calendarInner, 27, 27); - lv_obj_align(calendarInner, calendarOuter, LV_ALIGN_CENTER, 0, 0); - - calendarBar1 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_set_style_local_radius(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(calendarBar1, 3, 12); - lv_obj_align(calendarBar1, calendarOuter, LV_ALIGN_IN_TOP_MID, -6, -3); - - calendarBar2 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_set_style_local_radius(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(calendarBar2, 3, 12); - lv_obj_align(calendarBar2, calendarOuter, LV_ALIGN_IN_TOP_MID, 6, -3); - - calendarCrossBar1 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_set_style_local_radius(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(calendarCrossBar1, 8, 3); - lv_obj_align(calendarCrossBar1, calendarBar1, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - - calendarCrossBar2 = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_obj_set_style_local_radius(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_size(calendarCrossBar2, 8, 3); - lv_obj_align(calendarCrossBar2, calendarBar2, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - - // Display date - - dateDayOfWeek = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_label_set_text(dateDayOfWeek, "THU"); - lv_obj_align(dateDayOfWeek, sidebar, LV_ALIGN_CENTER, 0, -34); - - dateDay = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(dateDay, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_label_set_text(dateDay, "25"); - lv_obj_align(dateDay, sidebar, LV_ALIGN_CENTER, 0, 3); - - dateMonth = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(dateMonth, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); - lv_label_set_text(dateMonth, "MAR"); - lv_obj_align(dateMonth, sidebar, LV_ALIGN_CENTER, 0, 32); - - // Step count gauge - needle_colors[0] = LV_COLOR_WHITE; - stepGauge = lv_gauge_create(lv_scr_act(), nullptr); - lv_gauge_set_needle_count(stepGauge, 1, needle_colors); - lv_obj_set_size(stepGauge, 40, 40); - lv_obj_align(stepGauge, sidebar, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - lv_gauge_set_scale(stepGauge, 360, 11, 0); - lv_gauge_set_angle_offset(stepGauge, 180); - lv_gauge_set_critical_value(stepGauge, (100)); - lv_gauge_set_range(stepGauge, 0, (100)); - lv_gauge_set_value(stepGauge, 0, 0); - - lv_obj_set_style_local_pad_right(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_pad_left(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_pad_bottom(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_line_opa(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER); - lv_obj_set_style_local_scale_width(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 4); - lv_obj_set_style_local_line_width(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 4); - lv_obj_set_style_local_line_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); - lv_obj_set_style_local_line_opa(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, LV_OPA_COVER); - lv_obj_set_style_local_line_width(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_pad_inner(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, 4); - - backgroundLabel = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_click(backgroundLabel, true); - lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); - lv_obj_set_size(backgroundLabel, 240, 240); - lv_obj_set_pos(backgroundLabel, 0, 0); - lv_label_set_text(backgroundLabel, ""); - - btnNextTime = lv_btn_create(lv_scr_act(), nullptr); - btnNextTime->user_data = this; - lv_obj_set_size(btnNextTime, 60, 60); - lv_obj_align(btnNextTime, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, -80); - lv_obj_set_style_local_bg_opa(btnNextTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnNextTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">"); - lv_obj_set_event_cb(btnNextTime, event_handler); - - btnPrevTime = lv_btn_create(lv_scr_act(), nullptr); - btnPrevTime->user_data = this; - lv_obj_set_size(btnPrevTime, 60, 60); - lv_obj_align(btnPrevTime, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, -80); - lv_obj_set_style_local_bg_opa(btnPrevTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnPrevTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<"); - lv_obj_set_event_cb(btnPrevTime, event_handler); - - btnNextBar = lv_btn_create(lv_scr_act(), nullptr); - btnNextBar->user_data = this; - lv_obj_set_size(btnNextBar, 60, 60); - lv_obj_align(btnNextBar, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, 0); - lv_obj_set_style_local_bg_opa(btnNextBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnNextBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">"); - lv_obj_set_event_cb(btnNextBar, event_handler); - - btnPrevBar = lv_btn_create(lv_scr_act(), nullptr); - btnPrevBar->user_data = this; - lv_obj_set_size(btnPrevBar, 60, 60); - lv_obj_align(btnPrevBar, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, 0); - lv_obj_set_style_local_bg_opa(btnPrevBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnPrevBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<"); - lv_obj_set_event_cb(btnPrevBar, event_handler); - - btnNextBG = lv_btn_create(lv_scr_act(), nullptr); - btnNextBG->user_data = this; - lv_obj_set_size(btnNextBG, 60, 60); - lv_obj_align(btnNextBG, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, 80); - lv_obj_set_style_local_bg_opa(btnNextBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnNextBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">"); - lv_obj_set_event_cb(btnNextBG, event_handler); - - btnPrevBG = lv_btn_create(lv_scr_act(), nullptr); - btnPrevBG->user_data = this; - lv_obj_set_size(btnPrevBG, 60, 60); - lv_obj_align(btnPrevBG, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, 80); - lv_obj_set_style_local_bg_opa(btnPrevBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnPrevBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<"); - lv_obj_set_event_cb(btnPrevBG, event_handler); - - btnReset = lv_btn_create(lv_scr_act(), nullptr); - btnReset->user_data = this; - lv_obj_set_size(btnReset, 60, 60); - lv_obj_align(btnReset, lv_scr_act(), LV_ALIGN_CENTER, 0, 80); - lv_obj_set_style_local_bg_opa(btnReset, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnReset, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Rst"); - lv_obj_set_event_cb(btnReset, event_handler); - - btnRandom = lv_btn_create(lv_scr_act(), nullptr); - btnRandom->user_data = this; - lv_obj_set_size(btnRandom, 60, 60); - lv_obj_align(btnRandom, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - lv_obj_set_style_local_bg_opa(btnRandom, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_30); - lv_obj_set_style_local_value_str(btnRandom, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Rnd"); - lv_obj_set_event_cb(btnRandom, event_handler); -} - -SettingPineTimeStyle::~SettingPineTimeStyle() { - lv_obj_clean(lv_scr_act()); - settingsController.SaveSettings(); -} - -void SettingPineTimeStyle::UpdateSelected(lv_obj_t* object, lv_event_t event) { - auto valueTime = settingsController.GetPTSColorTime(); - auto valueBar = settingsController.GetPTSColorBar(); - auto valueBG = settingsController.GetPTSColorBG(); - - if (event == LV_EVENT_CLICKED) { - if (object == btnNextTime) { - valueTime = GetNext(valueTime); - - settingsController.SetPTSColorTime(valueTime); - lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); - lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); - lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); - } - if (object == btnPrevTime) { - valueTime = GetPrevious(valueTime); - settingsController.SetPTSColorTime(valueTime); - lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); - lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); - lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(valueTime)); - } - if (object == btnNextBar) { - valueBar = GetNext(valueBar); - if(valueBar == Controllers::Settings::Colors::Black) - valueBar = GetNext(valueBar); - settingsController.SetPTSColorBar(valueBar); - lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBar)); - } - if (object == btnPrevBar) { - valueBar = GetPrevious(valueBar); - if(valueBar == Controllers::Settings::Colors::Black) - valueBar = GetPrevious(valueBar); - settingsController.SetPTSColorBar(valueBar); - lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBar)); - } - if (object == btnNextBG) { - valueBG = GetNext(valueBG); - settingsController.SetPTSColorBG(valueBG); - lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBG)); - } - if (object == btnPrevBG) { - valueBG = GetPrevious(valueBG); - settingsController.SetPTSColorBG(valueBG); - lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(valueBG)); - } - if (object == btnReset) { - settingsController.SetPTSColorTime(Controllers::Settings::Colors::Teal); - lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); - lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); - lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); - settingsController.SetPTSColorBar(Controllers::Settings::Colors::Teal); - lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Teal)); - settingsController.SetPTSColorBG(Controllers::Settings::Colors::Black); - lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(Controllers::Settings::Colors::Black)); - } - if (object == btnRandom) { - uint8_t randTime = rand() % 17; - uint8_t randBar = rand() % 17; - uint8_t randBG = rand() % 17; - // Check if the time color is the same as its background, or if the sidebar is black. If so, change them to more useful values. - if (randTime == randBG) { - randBG += 1; - } - if (randBar == 3) { - randBar -= 1; - } - settingsController.SetPTSColorTime(static_cast<Controllers::Settings::Colors>(randTime)); - lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(static_cast<Controllers::Settings::Colors>(randTime))); - lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(static_cast<Controllers::Settings::Colors>(randTime))); - lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(static_cast<Controllers::Settings::Colors>(randTime))); - settingsController.SetPTSColorBar(static_cast<Controllers::Settings::Colors>(randBar)); - lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(static_cast<Controllers::Settings::Colors>(randBar))); - settingsController.SetPTSColorBG(static_cast<Controllers::Settings::Colors>(randBG)); - lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Convert(static_cast<Controllers::Settings::Colors>(randBG))); - } - } -} - -Pinetime::Controllers::Settings::Colors SettingPineTimeStyle::GetNext(Pinetime::Controllers::Settings::Colors color) { - auto colorAsInt = static_cast<uint8_t>(color); - Pinetime::Controllers::Settings::Colors nextColor; - if (colorAsInt < 16) { - nextColor = static_cast<Controllers::Settings::Colors>(colorAsInt + 1); - } else { - nextColor = static_cast<Controllers::Settings::Colors>(0); - } - return nextColor; -} - -Pinetime::Controllers::Settings::Colors SettingPineTimeStyle::GetPrevious(Pinetime::Controllers::Settings::Colors color) { - auto colorAsInt = static_cast<uint8_t>(color); - Pinetime::Controllers::Settings::Colors prevColor; - - if (colorAsInt > 0) { - prevColor = static_cast<Controllers::Settings::Colors>(colorAsInt - 1); - } else { - prevColor = static_cast<Controllers::Settings::Colors>(16); - } - return prevColor; -} diff --git a/src/displayapp/screens/settings/SettingPineTimeStyle.h b/src/displayapp/screens/settings/SettingPineTimeStyle.h deleted file mode 100644 index 397bd86d..00000000 --- a/src/displayapp/screens/settings/SettingPineTimeStyle.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include <cstdint> -#include <lvgl/lvgl.h> -#include "components/settings/Settings.h" -#include "displayapp/screens/Screen.h" - -namespace Pinetime { - - namespace Applications { - namespace Screens { - - class SettingPineTimeStyle : public Screen{ - public: - SettingPineTimeStyle(DisplayApp* app, Pinetime::Controllers::Settings &settingsController); - ~SettingPineTimeStyle() override; - - void UpdateSelected(lv_obj_t *object, lv_event_t event); - - private: - Controllers::Settings& settingsController; - - Pinetime::Controllers::Settings::Colors GetNext(Controllers::Settings::Colors color); - Pinetime::Controllers::Settings::Colors GetPrevious(Controllers::Settings::Colors color); - - lv_obj_t * btnNextTime; - lv_obj_t * btnPrevTime; - lv_obj_t * btnNextBar; - lv_obj_t * btnPrevBar; - lv_obj_t * btnNextBG; - lv_obj_t * btnPrevBG; - lv_obj_t * btnReset; - lv_obj_t * btnRandom; - lv_obj_t * timebar; - lv_obj_t * sidebar; - lv_obj_t * timeDD1; - lv_obj_t * timeDD2; - lv_obj_t * timeAMPM; - lv_obj_t * dateDayOfWeek; - lv_obj_t * dateDay; - lv_obj_t * dateMonth; - lv_obj_t * backgroundLabel; - lv_obj_t * batteryIcon; - lv_obj_t * bleIcon; - lv_obj_t * calendarOuter; - lv_obj_t * calendarInner; - lv_obj_t * calendarBar1; - lv_obj_t * calendarBar2; - lv_obj_t * calendarCrossBar1; - lv_obj_t * calendarCrossBar2; - lv_obj_t * stepGauge; - lv_color_t needle_colors[1]; - }; - } - } -} diff --git a/src/displayapp/screens/settings/SettingSetDate.cpp b/src/displayapp/screens/settings/SettingSetDate.cpp index ba3413ef..8bfded34 100644 --- a/src/displayapp/screens/settings/SettingSetDate.cpp +++ b/src/displayapp/screens/settings/SettingSetDate.cpp @@ -1,4 +1,4 @@ -#include "SettingSetDate.h" +#include "displayapp/screens/settings/SettingSetDate.h" #include <lvgl/lvgl.h> #include <hal/nrf_rtc.h> #include <nrf_log.h> diff --git a/src/displayapp/screens/settings/SettingSetTime.cpp b/src/displayapp/screens/settings/SettingSetTime.cpp index 194bf5eb..5351adeb 100644 --- a/src/displayapp/screens/settings/SettingSetTime.cpp +++ b/src/displayapp/screens/settings/SettingSetTime.cpp @@ -1,4 +1,4 @@ -#include "SettingSetTime.h" +#include "displayapp/screens/settings/SettingSetTime.h" #include <lvgl/lvgl.h> #include <hal/nrf_rtc.h> #include <nrf_log.h> diff --git a/src/displayapp/screens/settings/SettingSteps.cpp b/src/displayapp/screens/settings/SettingSteps.cpp index bec7972b..5ca3eecd 100644 --- a/src/displayapp/screens/settings/SettingSteps.cpp +++ b/src/displayapp/screens/settings/SettingSteps.cpp @@ -1,4 +1,4 @@ -#include "SettingSteps.h" +#include "displayapp/screens/settings/SettingSteps.h" #include <lvgl/lvgl.h> #include "displayapp/DisplayApp.h" #include "displayapp/screens/Symbols.h" @@ -6,8 +6,8 @@ using namespace Pinetime::Applications::Screens; namespace { - static void event_handler(lv_obj_t * obj, lv_event_t event) { - SettingSteps* screen = static_cast<SettingSteps *>(obj->user_data); + void event_handler(lv_obj_t* obj, lv_event_t event) { + SettingSteps* screen = static_cast<SettingSteps*>(obj->user_data); screen->UpdateSelected(obj, event); } } @@ -30,33 +30,32 @@ SettingSteps::SettingSteps( lv_obj_set_height(container1, LV_VER_RES - 60); lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); - lv_obj_t * title = lv_label_create(lv_scr_act(), NULL); + lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_static(title,"Daily steps goal"); 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(), NULL); + 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::shoe); lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); - - stepValue = lv_label_create(lv_scr_act(), NULL); + stepValue = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); lv_label_set_text_fmt(stepValue, "%lu", settingsController.GetStepsGoal()); lv_label_set_align(stepValue, LV_LABEL_ALIGN_CENTER); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_CENTER, 0, -10); - btnPlus = lv_btn_create(lv_scr_act(), NULL); + btnPlus = lv_btn_create(lv_scr_act(), nullptr); btnPlus->user_data = this; lv_obj_set_size(btnPlus, 80, 50); lv_obj_align(btnPlus, lv_scr_act(), LV_ALIGN_CENTER, 55, 80); lv_obj_set_style_local_value_str(btnPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+"); lv_obj_set_event_cb(btnPlus, event_handler); - btnMinus = lv_btn_create(lv_scr_act(), NULL); + btnMinus = lv_btn_create(lv_scr_act(), nullptr); btnMinus->user_data = this; lv_obj_set_size(btnMinus, 80, 50); lv_obj_set_event_cb(btnMinus, event_handler); diff --git a/src/displayapp/screens/settings/SettingTimeFormat.cpp b/src/displayapp/screens/settings/SettingTimeFormat.cpp index c99e3a0e..bd9af156 100644 --- a/src/displayapp/screens/settings/SettingTimeFormat.cpp +++ b/src/displayapp/screens/settings/SettingTimeFormat.cpp @@ -1,6 +1,7 @@ -#include "SettingTimeFormat.h" +#include "displayapp/screens/settings/SettingTimeFormat.h" #include <lvgl/lvgl.h> #include "displayapp/DisplayApp.h" +#include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" @@ -13,6 +14,8 @@ namespace { } } +constexpr std::array<const char*, 2> SettingTimeFormat::options; + SettingTimeFormat::SettingTimeFormat(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController) : Screen(app), settingsController {settingsController} { @@ -39,24 +42,19 @@ SettingTimeFormat::SettingTimeFormat(Pinetime::Applications::DisplayApp* app, Pi lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); - optionsTotal = 0; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " 12-hour"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); + for (unsigned int i = 0; i < options.size(); i++) { + cbOption[i] = lv_checkbox_create(container1, nullptr); + lv_checkbox_set_text(cbOption[i], options[i]); + cbOption[i]->user_data = this; + lv_obj_set_event_cb(cbOption[i], event_handler); + SetRadioButtonStyle(cbOption[i]); } - optionsTotal++; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " 24-hour"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); + if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { + lv_checkbox_set_checked(cbOption[0], true); + } else if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) { + lv_checkbox_set_checked(cbOption[1], true); } - optionsTotal++; } SettingTimeFormat::~SettingTimeFormat() { @@ -66,7 +64,7 @@ SettingTimeFormat::~SettingTimeFormat() { void SettingTimeFormat::UpdateSelected(lv_obj_t* object, lv_event_t event) { if (event == LV_EVENT_VALUE_CHANGED) { - for (int i = 0; i < optionsTotal; i++) { + for (unsigned int i = 0; i < options.size(); i++) { if (object == cbOption[i]) { lv_checkbox_set_checked(cbOption[i], true); diff --git a/src/displayapp/screens/settings/SettingTimeFormat.h b/src/displayapp/screens/settings/SettingTimeFormat.h index eac4bdc9..818edf0c 100644 --- a/src/displayapp/screens/settings/SettingTimeFormat.h +++ b/src/displayapp/screens/settings/SettingTimeFormat.h @@ -1,7 +1,9 @@ #pragma once +#include <array> #include <cstdint> #include <lvgl/lvgl.h> + #include "components/settings/Settings.h" #include "displayapp/screens/Screen.h" @@ -18,9 +20,9 @@ namespace Pinetime { void UpdateSelected(lv_obj_t* object, lv_event_t event); private: + static constexpr std::array<const char*, 2> options = {" 12-hour", " 24-hour"}; Controllers::Settings& settingsController; - uint8_t optionsTotal; - lv_obj_t* cbOption[2]; + lv_obj_t* cbOption[options.size()]; }; } } diff --git a/src/displayapp/screens/settings/SettingWakeUp.cpp b/src/displayapp/screens/settings/SettingWakeUp.cpp index d999004b..8339d9ad 100644 --- a/src/displayapp/screens/settings/SettingWakeUp.cpp +++ b/src/displayapp/screens/settings/SettingWakeUp.cpp @@ -1,4 +1,4 @@ -#include "SettingWakeUp.h" +#include "displayapp/screens/settings/SettingWakeUp.h" #include <lvgl/lvgl.h> #include "displayapp/DisplayApp.h" #include "displayapp/screens/Screen.h" diff --git a/src/displayapp/screens/settings/SettingWatchFace.cpp b/src/displayapp/screens/settings/SettingWatchFace.cpp index cdec704c..a24eaa15 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.cpp +++ b/src/displayapp/screens/settings/SettingWatchFace.cpp @@ -1,7 +1,8 @@ -#include "SettingWatchFace.h" +#include "displayapp/screens/settings/SettingWatchFace.h" #include <lvgl/lvgl.h> #include "displayapp/DisplayApp.h" #include "displayapp/screens/Screen.h" +#include "displayapp/screens/Styles.h" #include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; @@ -13,6 +14,8 @@ namespace { } } +constexpr std::array<const char*, 3> SettingWatchFace::options; + SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController) : Screen(app), settingsController {settingsController} { @@ -40,34 +43,17 @@ SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app, Pine lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); - optionsTotal = 0; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " Digital face"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetClockFace() == 0) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); - } - - optionsTotal++; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " Analog face"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetClockFace() == 1) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); - } + for (unsigned int i = 0; i < options.size(); i++) { + cbOption[i] = lv_checkbox_create(container1, nullptr); + lv_checkbox_set_text(cbOption[i], options[i]); + cbOption[i]->user_data = this; + lv_obj_set_event_cb(cbOption[i], event_handler); + SetRadioButtonStyle(cbOption[i]); - optionsTotal++; - cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[optionsTotal], " PineTimeStyle"); - cbOption[optionsTotal]->user_data = this; - lv_obj_set_event_cb(cbOption[optionsTotal], event_handler); - if (settingsController.GetClockFace() == 2) { - lv_checkbox_set_checked(cbOption[optionsTotal], true); + if (settingsController.GetClockFace() == i) { + lv_checkbox_set_checked(cbOption[i], true); + } } - - optionsTotal++; } SettingWatchFace::~SettingWatchFace() { @@ -77,7 +63,7 @@ SettingWatchFace::~SettingWatchFace() { void SettingWatchFace::UpdateSelected(lv_obj_t* object, lv_event_t event) { if (event == LV_EVENT_VALUE_CHANGED) { - for (uint8_t i = 0; i < optionsTotal; i++) { + for (unsigned int i = 0; i < options.size(); i++) { if (object == cbOption[i]) { lv_checkbox_set_checked(cbOption[i], true); settingsController.SetClockFace(i); diff --git a/src/displayapp/screens/settings/SettingWatchFace.h b/src/displayapp/screens/settings/SettingWatchFace.h index d4a96c6d..ccba7d13 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.h +++ b/src/displayapp/screens/settings/SettingWatchFace.h @@ -1,7 +1,9 @@ #pragma once +#include <array> #include <cstdint> #include <lvgl/lvgl.h> + #include "components/settings/Settings.h" #include "displayapp/screens/Screen.h" @@ -18,9 +20,10 @@ namespace Pinetime { void UpdateSelected(lv_obj_t* object, lv_event_t event); private: + static constexpr std::array<const char*, 3> options = {" Digital face", " Analog face", " PineTimeStyle"}; Controllers::Settings& settingsController; - uint8_t optionsTotal; - lv_obj_t* cbOption[2]; + + lv_obj_t* cbOption[options.size()]; }; } } diff --git a/src/displayapp/screens/settings/Settings.cpp b/src/displayapp/screens/settings/Settings.cpp index 678ad82e..3bc1372c 100644 --- a/src/displayapp/screens/settings/Settings.cpp +++ b/src/displayapp/screens/settings/Settings.cpp @@ -1,4 +1,4 @@ -#include "Settings.h" +#include "displayapp/screens/settings/Settings.h" #include <lvgl/lvgl.h> #include <array> #include "displayapp/screens/List.h" @@ -60,6 +60,7 @@ std::unique_ptr<Screen> Settings::CreateScreen2() { std::unique_ptr<Screen> Settings::CreateScreen3() { std::array<Screens::List::Applications, 4> applications {{ + {Symbols::clock, "Chimes", Apps::SettingChimes}, {Symbols::check, "Firmware", Apps::FirmwareValidation}, {Symbols::list, "About", Apps::SysInfo}, diff --git a/src/drivers/Bma421.cpp b/src/drivers/Bma421.cpp index dd284000..2f60f42f 100644 --- a/src/drivers/Bma421.cpp +++ b/src/drivers/Bma421.cpp @@ -1,7 +1,7 @@ +#include "drivers/Bma421.h" #include <libraries/delay/nrf_delay.h> #include <libraries/log/nrf_log.h> -#include "Bma421.h" -#include "TwiMaster.h" +#include "drivers/TwiMaster.h" #include <drivers/Bma421_C/bma423.h> using namespace Pinetime::Drivers; diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp index 7fc8eca4..e9573df1 100644 --- a/src/drivers/Cst816s.cpp +++ b/src/drivers/Cst816s.cpp @@ -1,4 +1,4 @@ -#include "Cst816s.h" +#include "drivers/Cst816s.h" #include <FreeRTOS.h> #include <legacy/nrf_drv_gpiote.h> #include <nrfx_log.h> @@ -32,6 +32,12 @@ bool Cst816S::Init() { twiMaster.Read(twiAddress, 0xa7, &dummy, 1); vTaskDelay(5); + // TODO This function check that the device IDs from the controller are equal to the ones + // we expect. However, it seems to return false positive (probably in case of communication issue). + // Also, it seems that some users have pinetimes that works correctly but that report different device IDs + // Until we know more about this, we'll just read the IDs but not take any action in case they are not 'valid' + CheckDeviceIds(); + /* [2] EnConLR - Continuous operation can slide around [1] EnConUD - Slide up and down to enable continuous operation @@ -50,21 +56,7 @@ bool Cst816S::Init() { static constexpr uint8_t irqCtl = 0b01110000; twiMaster.Write(twiAddress, 0xFA, &irqCtl, 1); - // 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; + return true; } Cst816S::TouchInfos Cst816S::GetTouchInfo() { @@ -79,18 +71,33 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() { // This can only be 0 or 1 uint8_t nbTouchPoints = touchData[touchPointNumIndex] & 0x0f; - uint8_t xHigh = touchData[touchXHighIndex] & 0x0f; uint8_t xLow = touchData[touchXLowIndex]; - info.x = (xHigh << 8) | xLow; - + uint16_t x = (xHigh << 8) | xLow; uint8_t yHigh = touchData[touchYHighIndex] & 0x0f; uint8_t yLow = touchData[touchYLowIndex]; - info.y = (yHigh << 8) | yLow; + 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; } @@ -108,3 +115,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 0fec8419..4a548d45 100644 --- a/src/drivers/Cst816s.h +++ b/src/drivers/Cst816s.h @@ -1,6 +1,6 @@ #pragma once -#include "TwiMaster.h" +#include "drivers/TwiMaster.h" namespace Pinetime { namespace Drivers { @@ -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); @@ -45,6 +45,8 @@ namespace Pinetime { return fwVersion; } private: + bool CheckDeviceIds(); + // Unused/Unavailable commented out static constexpr uint8_t gestureIndex = 1; static constexpr uint8_t touchPointNumIndex = 2; @@ -58,6 +60,9 @@ namespace Pinetime { //static constexpr uint8_t touchXYIndex = 7; //static constexpr uint8_t touchMiscIndex = 8; + static constexpr uint8_t maxX = 240; + static constexpr uint8_t maxY = 240; + TwiMaster& twiMaster; uint8_t twiAddress; diff --git a/src/drivers/DebugPins.cpp b/src/drivers/DebugPins.cpp index 56fd1458..92091280 100644 --- a/src/drivers/DebugPins.cpp +++ b/src/drivers/DebugPins.cpp @@ -1,4 +1,4 @@ -#include "DebugPins.h" +#include "drivers/DebugPins.h" #include <hal/nrf_gpio.h> #ifdef USE_DEBUG_PINS diff --git a/src/drivers/Hrs3300.cpp b/src/drivers/Hrs3300.cpp index edb9e81d..c14fe7aa 100644 --- a/src/drivers/Hrs3300.cpp +++ b/src/drivers/Hrs3300.cpp @@ -4,9 +4,9 @@ C++ port Copyright (C) 2021 Jean-François Milants */ +#include "drivers/Hrs3300.h" #include <algorithm> #include <nrf_gpio.h> -#include "Hrs3300.h" #include <FreeRTOS.h> #include <task.h> diff --git a/src/drivers/Hrs3300.h b/src/drivers/Hrs3300.h index c4f28900..01310c62 100644 --- a/src/drivers/Hrs3300.h +++ b/src/drivers/Hrs3300.h @@ -1,6 +1,6 @@ #pragma once -#include "TwiMaster.h" +#include "drivers/TwiMaster.h" namespace Pinetime { namespace Drivers { diff --git a/src/drivers/InternalFlash.cpp b/src/drivers/InternalFlash.cpp index 0840c6e5..ec5885d5 100644 --- a/src/drivers/InternalFlash.cpp +++ b/src/drivers/InternalFlash.cpp @@ -1,4 +1,4 @@ -#include "InternalFlash.h" +#include "drivers/InternalFlash.h" #include <mdk/nrf.h> using namespace Pinetime::Drivers; diff --git a/src/drivers/PinMap.h b/src/drivers/PinMap.h index 57964020..579bf38a 100644 --- a/src/drivers/PinMap.h +++ b/src/drivers/PinMap.h @@ -1,4 +1,5 @@ #pragma once +#include <cstdint> namespace Pinetime { namespace PinMap { diff --git a/src/drivers/Spi.cpp b/src/drivers/Spi.cpp index a55d2888..e477622b 100644 --- a/src/drivers/Spi.cpp +++ b/src/drivers/Spi.cpp @@ -1,4 +1,4 @@ -#include "Spi.h" +#include "drivers/Spi.h" #include <hal/nrf_gpio.h> #include <nrfx_log.h> diff --git a/src/drivers/Spi.h b/src/drivers/Spi.h index 6875710d..9b6a30f4 100644 --- a/src/drivers/Spi.h +++ b/src/drivers/Spi.h @@ -1,7 +1,7 @@ #pragma once #include <cstdint> #include <cstddef> -#include "SpiMaster.h" +#include "drivers/SpiMaster.h" namespace Pinetime { namespace Drivers { diff --git a/src/drivers/SpiMaster.cpp b/src/drivers/SpiMaster.cpp index c45e1294..747dbc84 100644 --- a/src/drivers/SpiMaster.cpp +++ b/src/drivers/SpiMaster.cpp @@ -1,4 +1,4 @@ -#include "SpiMaster.h" +#include "drivers/SpiMaster.h" #include <hal/nrf_gpio.h> #include <hal/nrf_spim.h> #include <nrfx_log.h> diff --git a/src/drivers/SpiNorFlash.cpp b/src/drivers/SpiNorFlash.cpp index 068d1d02..ebe3174c 100644 --- a/src/drivers/SpiNorFlash.cpp +++ b/src/drivers/SpiNorFlash.cpp @@ -1,8 +1,8 @@ -#include "SpiNorFlash.h" +#include "drivers/SpiNorFlash.h" #include <hal/nrf_gpio.h> #include <libraries/delay/nrf_delay.h> #include <libraries/log/nrf_log.h> -#include "Spi.h" +#include "drivers/Spi.h" using namespace Pinetime::Drivers; diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index 4d81cf27..fd1366f8 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -1,8 +1,8 @@ -#include "St7789.h" +#include "drivers/St7789.h" #include <hal/nrf_gpio.h> #include <libraries/delay/nrf_delay.h> #include <nrfx_log.h> -#include "Spi.h" +#include "drivers/Spi.h" using namespace Pinetime::Drivers; diff --git a/src/drivers/TwiMaster.cpp b/src/drivers/TwiMaster.cpp index 76009278..9b456d5f 100644 --- a/src/drivers/TwiMaster.cpp +++ b/src/drivers/TwiMaster.cpp @@ -1,4 +1,4 @@ -#include "TwiMaster.h" +#include "drivers/TwiMaster.h" #include <cstring> #include <hal/nrf_gpio.h> #include <nrfx_log.h> diff --git a/src/drivers/Watchdog.cpp b/src/drivers/Watchdog.cpp index a6ad263a..d0907a65 100644 --- a/src/drivers/Watchdog.cpp +++ b/src/drivers/Watchdog.cpp @@ -1,4 +1,4 @@ -#include "Watchdog.h" +#include "drivers/Watchdog.h" #include <mdk/nrf.h> using namespace Pinetime::Drivers; diff --git a/src/heartratetask/HeartRateTask.cpp b/src/heartratetask/HeartRateTask.cpp index fddc05d7..213ab4a7 100644 --- a/src/heartratetask/HeartRateTask.cpp +++ b/src/heartratetask/HeartRateTask.cpp @@ -1,4 +1,4 @@ -#include "HeartRateTask.h" +#include "heartratetask/HeartRateTask.h" #include <drivers/Hrs3300.h> #include <components/heartrate/HeartRateController.h> #include <nrf_log.h> diff --git a/src/libs/QCBOR b/src/libs/QCBOR new file mode 160000 +Subproject 9e2f70804393823cc6d16f9f1035ef7223faca0 diff --git a/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h b/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h index 94b72cb6..b3f23411 100644 --- a/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h +++ b/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h @@ -699,11 +699,11 @@ #endif #ifndef MYNEWT_VAL_BLE_SM_BONDING -#define MYNEWT_VAL_BLE_SM_BONDING (0) +#define MYNEWT_VAL_BLE_SM_BONDING (1) #endif #ifndef MYNEWT_VAL_BLE_SM_IO_CAP -#define MYNEWT_VAL_BLE_SM_IO_CAP (BLE_HS_IO_NO_INPUT_OUTPUT) +#define MYNEWT_VAL_BLE_SM_IO_CAP (BLE_HS_IO_DISPLAY_ONLY) #endif #ifndef MYNEWT_VAL_BLE_SM_KEYPRESS @@ -711,7 +711,7 @@ #endif #ifndef MYNEWT_VAL_BLE_SM_LEGACY -#define MYNEWT_VAL_BLE_SM_LEGACY (1) +#define MYNEWT_VAL_BLE_SM_LEGACY (0) #endif #ifndef MYNEWT_VAL_BLE_SM_MAX_PROCS @@ -719,7 +719,7 @@ #endif #ifndef MYNEWT_VAL_BLE_SM_MITM -#define MYNEWT_VAL_BLE_SM_MITM (0) +#define MYNEWT_VAL_BLE_SM_MITM (1) #endif #ifndef MYNEWT_VAL_BLE_SM_OOB_DATA_FLAG @@ -727,11 +727,11 @@ #endif #ifndef MYNEWT_VAL_BLE_SM_OUR_KEY_DIST -#define MYNEWT_VAL_BLE_SM_OUR_KEY_DIST (0) +#define MYNEWT_VAL_BLE_SM_OUR_KEY_DIST (7) #endif #ifndef MYNEWT_VAL_BLE_SM_SC -#define MYNEWT_VAL_BLE_SM_SC (0) +#define MYNEWT_VAL_BLE_SM_SC (1) #endif #ifndef MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS @@ -739,7 +739,7 @@ #endif #ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST -#define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0) +#define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (3) #endif #ifndef MYNEWT_VAL_BLE_STORE_MAX_BONDS @@ -1089,7 +1089,7 @@ /* Overridden by @apache-mynewt-nimble/targets/riot (defined by @apache-mynewt-nimble/nimble/controller) */ #ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_PRIVACY -#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_PRIVACY (0) +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_PRIVACY (1) #endif #ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG diff --git a/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c b/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c index 8ee3475a..b9902781 100644 --- a/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c +++ b/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c @@ -37,7 +37,7 @@ nimble_port_freertos_init(TaskFunction_t host_task_fn) * provided by NimBLE and in case of FreeRTOS it does not need to be wrapped * since it has compatible prototype. */ - xTaskCreate(nimble_port_ll_task_func, "ll", configMINIMAL_STACK_SIZE + 400, + xTaskCreate(nimble_port_ll_task_func, "ll", configMINIMAL_STACK_SIZE + 200, NULL, configMAX_PRIORITIES - 1, &ll_task_h); #endif @@ -46,6 +46,6 @@ nimble_port_freertos_init(TaskFunction_t host_task_fn) * have separate task for NimBLE host, but since something needs to handle * default queue it is just easier to make separate task which does this. */ - xTaskCreate(host_task_fn, "ble", configMINIMAL_STACK_SIZE + 400, + xTaskCreate(host_task_fn, "ble", configMINIMAL_STACK_SIZE + 600, NULL, tskIDLE_PRIORITY + 1, &host_task_h); } diff --git a/src/logging/DummyLogger.h b/src/logging/DummyLogger.h index 8732dff5..1b050b37 100644 --- a/src/logging/DummyLogger.h +++ b/src/logging/DummyLogger.h @@ -1,5 +1,5 @@ #pragma once -#include "Logger.h" +#include "logging/Logger.h" namespace Pinetime { namespace Logging { diff --git a/src/logging/NrfLogger.cpp b/src/logging/NrfLogger.cpp index 1c048f2c..f8d95a63 100644 --- a/src/logging/NrfLogger.cpp +++ b/src/logging/NrfLogger.cpp @@ -1,4 +1,4 @@ -#include "NrfLogger.h" +#include "logging/NrfLogger.h" #include <libraries/log/nrf_log.h> #include <libraries/log/nrf_log_ctrl.h> @@ -19,14 +19,13 @@ void NrfLogger::Init() { void NrfLogger::Process(void*) { NRF_LOG_INFO("Logger task started!"); -// Suppress endless loop diagnostic + #pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop" while (true) { NRF_LOG_FLUSH(); vTaskDelay(100); // Not good for power consumption, it will wake up every 100ms... } -// Clear diagnostic suppression #pragma clang diagnostic pop } diff --git a/src/logging/NrfLogger.h b/src/logging/NrfLogger.h index 060c4e76..21183a3d 100644 --- a/src/logging/NrfLogger.h +++ b/src/logging/NrfLogger.h @@ -1,5 +1,5 @@ #pragma once -#include "Logger.h" +#include "logging/Logger.h" #include <FreeRTOS.h> #include <task.h> diff --git a/src/main.cpp b/src/main.cpp index fc772355..53f78ce8 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/sdk_config.h b/src/sdk_config.h index 38d47a7f..7634dca1 100644 --- a/src/sdk_config.h +++ b/src/sdk_config.h @@ -12580,4 +12580,4 @@ #endif // <<< end of configuration section >>> -#endif // SDK_CONFIG_H
\ No newline at end of file +#endif // SDK_CONFIG_H diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index 1a9ad542..4d5ab4ce 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, @@ -23,10 +24,13 @@ namespace Pinetime { OnNewHour, OnNewHalfHour, OnChargingEvent, + OnPairing, SetOffAlarm, StopRinging, MeasureBatteryTimerExpired, BatteryPercentageUpdated, + StartFileTransfer, + StopFileTransfer, }; } } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index c48307af..b2926936 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -1,4 +1,4 @@ -#include "SystemTask.h" +#include "systemtask/SystemTask.h" #define min // workaround: nimble's min/max macros conflict with libstdc++ #define max #include <host/ble_gap.h> @@ -25,7 +25,6 @@ #include "main.h" #include "BootErrors.h" - #include <memory> using namespace Pinetime::System; @@ -77,7 +76,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, Pinetime::Applications::DisplayApp& displayApp, Pinetime::Applications::HeartRateTask& heartRateApp, Pinetime::Controllers::FS& fs, - Pinetime::Controllers::TouchHandler& touchHandler) + Pinetime::Controllers::TouchHandler& touchHandler, + Pinetime::Controllers::ButtonHandler& buttonHandler) : spi {spi}, lcd {lcd}, spiNorFlash {spiNorFlash}, @@ -101,14 +101,23 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, heartRateApp(heartRateApp), fs {fs}, touchHandler {touchHandler}, - nimbleController(*this, bleController, dateTimeController, notificationManager, - batteryController, spiNorFlash, heartRateController, motionController) { + buttonHandler {buttonHandler}, + nimbleController(*this, + bleController, + dateTimeController, + notificationManager, + batteryController, + spiNorFlash, + heartRateController, + motionController, + fs) { } void SystemTask::Start() { systemTasksMsgQueue = xQueueCreate(10, 1); - if (pdPASS != xTaskCreate(SystemTask::Process, "MAIN", 350, this, 0, &taskHandle)) + if (pdPASS != xTaskCreate(SystemTask::Process, "MAIN", 350, this, 0, &taskHandle)) { APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); + } } void SystemTask::Process(void* instance) { @@ -137,9 +146,15 @@ 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); motorController.Init(); @@ -163,6 +178,8 @@ void SystemTask::Work() { heartRateSensor.Disable(); heartRateApp.Start(); + buttonHandler.Init(this); + // Button nrf_gpio_cfg_output(15); nrf_gpio_pin_set(15); @@ -171,20 +188,22 @@ void SystemTask::Work() { pinConfig.skip_gpio_setup = false; pinConfig.hi_accuracy = false; pinConfig.is_watcher = false; - pinConfig.sense = (nrf_gpiote_polarity_t) NRF_GPIOTE_POLARITY_TOGGLE; - pinConfig.pull = (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pulldown; + pinConfig.sense = static_cast<nrf_gpiote_polarity_t>(NRF_GPIOTE_POLARITY_TOGGLE); + pinConfig.pull = static_cast<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); + nrf_gpio_cfg_sense_input(PinMap::Cst816sIrq, + static_cast<nrf_gpio_pin_pull_t>(GPIO_PIN_CNF_PULL_Pullup), + static_cast<nrf_gpio_pin_sense_t> GPIO_PIN_CNF_SENSE_Low); pinConfig.skip_gpio_setup = true; pinConfig.hi_accuracy = false; pinConfig.is_watcher = false; - pinConfig.sense = (nrf_gpiote_polarity_t) NRF_GPIOTE_POLARITY_HITOLO; - pinConfig.pull = (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup; + pinConfig.sense = static_cast<nrf_gpiote_polarity_t>(NRF_GPIOTE_POLARITY_HITOLO); + pinConfig.pull = static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Pullup; nrfx_gpiote_in_init(PinMap::Cst816sIrq, &pinConfig, nrfx_gpiote_evt_handler); @@ -205,7 +224,6 @@ void SystemTask::Work() { xTimerStart(dimTimer, 0); xTimerStart(measureBatteryTimer, portMAX_DELAY); -// Suppress endless loop diagnostic #pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop" while (true) { @@ -243,8 +261,9 @@ void SystemTask::Work() { displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning); heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp); - if (!bleController.IsConnected()) + if (!bleController.IsConnected()) { nimbleController.RestartFastAdv(); + } isSleeping = false; isWakingUp = false; @@ -263,6 +282,9 @@ void SystemTask::Work() { } } break; case Messages::GoToSleep: + if (doNotGoToSleep) { + break; + } isGoingToSleep = true; NRF_LOG_INFO("[systemtask] Going to sleep"); xTimerStop(idleTimer, 0); @@ -308,8 +330,9 @@ void SystemTask::Work() { break; case Messages::BleFirmwareUpdateStarted: doNotGoToSleep = true; - if (isSleeping && !isWakingUp) + if (isSleeping && !isWakingUp) { GoToRunning(); + } displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted); break; case Messages::BleFirmwareUpdateFinished: @@ -319,6 +342,19 @@ void SystemTask::Work() { doNotGoToSleep = false; xTimerStart(dimTimer, 0); break; + case Messages::StartFileTransfer: + NRF_LOG_INFO("[systemtask] FS Started"); + doNotGoToSleep = true; + if (isSleeping && !isWakingUp) + GoToRunning(); + //TODO add intent of fs access icon or something + break; + case Messages::StopFileTransfer: + NRF_LOG_INFO("[systemtask] FS Stopped"); + doNotGoToSleep = false; + xTimerStart(dimTimer, 0); + //TODO add intent of fs access icon or something + break; case Messages::OnTouchEvent: if (touchHandler.GetNewTouchInfo()) { touchHandler.UpdateLvglTouchPoint(); @@ -326,10 +362,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 @@ -386,6 +437,13 @@ void SystemTask::Work() { case Messages::BatteryPercentageUpdated: nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); break; + case Messages::OnPairing: + if (isSleeping && !isWakingUp) { + GoToRunning(); + } + motorController.RunForDuration(35); + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowPairingKey); + break; default: break; @@ -407,18 +465,21 @@ void SystemTask::Work() { uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG); dateTimeController.UpdateTime(systick_counter); NoInit_BackUpTime = dateTimeController.CurrentDateTime(); - if (!nrf_gpio_pin_read(PinMap::Button)) + if (!nrf_gpio_pin_read(PinMap::Button)) { watchdog.Kick(); + } } -// Clear diagnostic suppression #pragma clang diagnostic pop } + void SystemTask::UpdateMotion() { - if (isGoingToSleep or isWakingUp) + if (isGoingToSleep or isWakingUp) { return; + } - if (isSleeping && !settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist)) + if (isSleeping && !settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist)) { return; + } if (stepCounterMustBeReset) { motionSensor.ResetStepCounter(); @@ -434,30 +495,50 @@ 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() { - if (isGoingToSleep or (not isSleeping) or isWakingUp) + if (isGoingToSleep or (not isSleeping) or isWakingUp) { return; + } isWakingUp = true; PushMessage(Messages::GoToRunning); } void SystemTask::OnTouchEvent() { - if (isGoingToSleep) + if (isGoingToSleep) { return; + } if (!isSleeping) { PushMessage(Messages::OnTouchEvent); } else if (!isWakingUp) { @@ -469,7 +550,7 @@ void SystemTask::OnTouchEvent() { } void SystemTask::PushMessage(System::Messages msg) { - if (msg == Messages::GoToSleep) { + if (msg == Messages::GoToSleep && !doNotGoToSleep) { isGoingToSleep = true; } @@ -487,8 +568,9 @@ void SystemTask::PushMessage(System::Messages msg) { } void SystemTask::OnDim() { - if (doNotGoToSleep) + if (doNotGoToSleep) { return; + } NRF_LOG_INFO("Dim timeout -> Dim screen") displayApp.PushMessage(Pinetime::Applications::Display::Messages::DimScreen); xTimerStart(idleTimer, 0); @@ -496,15 +578,17 @@ void SystemTask::OnDim() { } void SystemTask::OnIdle() { - if (doNotGoToSleep) + if (doNotGoToSleep) { return; + } NRF_LOG_INFO("Idle timeout -> Going to sleep") PushMessage(Messages::GoToSleep); } void SystemTask::ReloadIdleTimer() { - if (isSleeping || isGoingToSleep) + if (isSleeping || isGoingToSleep) { return; + } if (isDimmed) { displayApp.PushMessage(Pinetime::Applications::Display::Messages::RestoreBrightness); isDimmed = false; diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 879c1be8..e2e6de7f 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -11,7 +11,7 @@ #include <drivers/PinMap.h> #include <components/motion/MotionController.h> -#include "SystemMonitor.h" +#include "systemtask/SystemMonitor.h" #include "components/battery/BatteryController.h" #include "components/ble/NimbleController.h" #include "components/ble/NotificationManager.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" @@ -31,7 +33,7 @@ #endif #include "drivers/Watchdog.h" -#include "Messages.h" +#include "systemtask/Messages.h" extern std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> NoInit_BackUpTime; namespace Pinetime { @@ -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); @@ -135,6 +139,9 @@ namespace Pinetime { TimerHandle_t measureBatteryTimer; bool doNotGoToSleep = false; + void HandleButtonAction(Controllers::ButtonActions action); + bool fastWakeUpDone = false; + void GoToRunning(); void UpdateMotion(); bool stepCounterMustBeReset = false; diff --git a/src/touchhandler/TouchHandler.cpp b/src/touchhandler/TouchHandler.cpp index 735b311a..0be33476 100644 --- a/src/touchhandler/TouchHandler.cpp +++ b/src/touchhandler/TouchHandler.cpp @@ -1,4 +1,4 @@ -#include "TouchHandler.h" +#include "touchhandler/TouchHandler.h" using namespace Pinetime::Controllers; diff --git a/src/touchhandler/TouchHandler.h b/src/touchhandler/TouchHandler.h index f5442939..ed452b3a 100644 --- a/src/touchhandler/TouchHandler.h +++ b/src/touchhandler/TouchHandler.h @@ -1,8 +1,6 @@ #pragma once #include "drivers/Cst816s.h" #include "systemtask/SystemTask.h" -#include <FreeRTOS.h> -#include <task.h> namespace Pinetime { namespace Components { @@ -11,9 +9,6 @@ namespace Pinetime { namespace Drivers { class Cst816S; } - namespace System { - class SystemTask; - } namespace Controllers { class TouchHandler { public: |