resolves conflicts
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Pinetime {
|
||||
class BootloaderVersion {
|
||||
public:
|
||||
|
@@ -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>
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
@VERSION_EDIT_WARNING@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Pinetime {
|
||||
class Version {
|
||||
public:
|
||||
|
7
src/buttonhandler/ButtonActions.h
Normal file
7
src/buttonhandler/ButtonActions.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
enum class ButtonActions { None, Click, DoubleClick, LongPress, LongerPress };
|
||||
}
|
||||
}
|
78
src/buttonhandler/ButtonHandler.cpp
Normal file
78
src/buttonhandler/ButtonHandler.cpp
Normal file
@@ -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;
|
||||
}
|
24
src/buttonhandler/ButtonHandler.h
Normal file
24
src/buttonhandler/ButtonHandler.h
Normal file
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
@@ -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"
|
||||
|
@@ -18,7 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "app_timer.h"
|
||||
#include "components/datetime/DateTimeController.h"
|
||||
|
||||
namespace Pinetime {
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include <host/ble_gap.h>
|
||||
#undef max
|
||||
#undef min
|
||||
#include "BleClient.h"
|
||||
#include "components/ble/BleClient.h"
|
||||
|
||||
namespace Pinetime {
|
||||
|
||||
|
@@ -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));
|
||||
|
@@ -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;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "BleController.h"
|
||||
#include "components/ble/BleController.h"
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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"
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#undef max
|
||||
#undef min
|
||||
#include <cstdint>
|
||||
#include "BleClient.h"
|
||||
#include "components/ble/BleClient.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "CurrentTimeService.h"
|
||||
#include "components/ble/CurrentTimeService.h"
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include <nrf_log.h>
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "DeviceInformationService.h"
|
||||
#include "components/ble/DeviceInformationService.h"
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "DfuService.h"
|
||||
#include "components/ble/DfuService.h"
|
||||
#include <cstring>
|
||||
#include "components/ble/BleController.h"
|
||||
#include "drivers/SpiNorFlash.h"
|
||||
|
330
src/components/ble/FSService.cpp
Normal file
330
src/components/ble/FSService.cpp
Normal file
@@ -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);
|
||||
}
|
||||
}
|
191
src/components/ble/FSService.h
Normal file
191
src/components/ble/FSService.h
Normal file
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#include "HeartRateService.h"
|
||||
#include "components/ble/HeartRateService.h"
|
||||
#include "components/heartrate/HeartRateController.h"
|
||||
#include "systemtask/SystemTask.h"
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "NotificationManager.h"
|
||||
#include "components/ble/NotificationManager.h"
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
385
src/components/ble/weather/WeatherData.h
Normal file
385
src/components/ble/weather/WeatherData.h
Normal file
@@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
604
src/components/ble/weather/WeatherService.cpp
Normal file
604
src/components/ble/weather/WeatherService.cpp
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
172
src/components/ble/weather/WeatherService.h
Normal file
172
src/components/ble/weather/WeatherService.h
Normal file
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
@@ -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"
|
||||
|
@@ -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>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "FirmwareValidator.h"
|
||||
#include "components/firmwarevalidator/FirmwareValidator.h"
|
||||
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include "drivers/InternalFlash.h"
|
||||
|
@@ -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,
|
||||
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,
|
||||
.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,
|
||||
}
|
||||
{ }
|
||||
.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);
|
||||
|
||||
}
|
@@ -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);
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "Gfx.h"
|
||||
#include "components/gfx/Gfx.h"
|
||||
#include "drivers/St7789.h"
|
||||
using namespace Pinetime::Components;
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "HeartRateController.h"
|
||||
#include "components/heartrate/HeartRateController.h"
|
||||
#include <heartratetask/HeartRateTask.h>
|
||||
#include <systemtask/SystemTask.h>
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "RleDecoder.h"
|
||||
#include "components/rle/RleDecoder.h"
|
||||
|
||||
using namespace Pinetime::Tools;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "Settings.h"
|
||||
#include "components/settings/Settings.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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"
|
||||
|
@@ -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,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "Colors.h"
|
||||
#include "displayapp/Colors.h"
|
||||
|
||||
using namespace Pinetime::Applications;
|
||||
using namespace Pinetime::Controllers;
|
||||
|
@@ -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 {
|
||||
|
@@ -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) {
|
||||
|
@@ -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 {
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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,
|
||||
|
@@ -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));
|
||||
|
@@ -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.");
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Applications {
|
||||
|
@@ -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;
|
||||
|
@@ -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 {
|
||||
|
@@ -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) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "Brightness.h"
|
||||
#include "displayapp/screens/Brightness.h"
|
||||
#include <lvgl/lvgl.h>
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
@@ -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 {
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "Error.h"
|
||||
#include "displayapp/screens/Error.h"
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.h"
|
||||
#include "displayapp/screens/Screen.h"
|
||||
#include "BootErrors.h"
|
||||
#include <lvgl/lvgl.h>
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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(),
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.h"
|
||||
#include "displayapp/screens/Screen.h"
|
||||
#include <lvgl/src/lv_core/lv_obj.h>
|
||||
|
||||
namespace Pinetime {
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "Label.h"
|
||||
#include "displayapp/screens/Label.h"
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.h"
|
||||
#include "displayapp/screens/Screen.h"
|
||||
#include <lvgl/lvgl.h>
|
||||
|
||||
namespace Pinetime {
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user