1
0
Fork 0

Merge pull request #293 from Avamander/patch-5

Reformatted all the files according to clang-format style
This commit is contained in:
JF002 2021-04-26 21:10:11 +02:00 committed by GitHub
commit 4706c99db4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
175 changed files with 10073 additions and 10642 deletions

View file

@ -21,8 +21,8 @@ AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true BinPackArguments: false
BinPackParameters: true BinPackParameters: false
BraceWrapping: BraceWrapping:
AfterCaseLabel: false AfterCaseLabel: false
AfterClass: false AfterClass: false
@ -52,14 +52,14 @@ BreakStringLiterals: true
ColumnLimit: 140 ColumnLimit: 140
CommentPragmas: '^ IWYU pragma:' CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 2 ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2 ContinuationIndentWidth: 2
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
DeriveLineEnding: false DeriveLineEnding: false
DerivePointerAlignment: false DerivePointerAlignment: false
DisableFormat: false DisableFormat: false
ExperimentalAutoDetectBinPacking: false ExperimentalAutoDetectBinPacking: true
FixNamespaceComments: false FixNamespaceComments: false
ForEachMacros: ForEachMacros:
- foreach - foreach
@ -90,10 +90,6 @@ MacroBlockBegin: ''
MacroBlockEnd: '' MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: All NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2 PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300 PenaltyBreakComment: 300

View file

@ -75,7 +75,7 @@ As of now, here is the list of achievements of this project:
- [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/gettingStarted.md) - [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/gettingStarted.md)
### Develop ### Develop
- [Generate the fonts and symbols](src/displayapp/fonts/Readme.md) - [Generate the fonts and symbols](src/displayapp/fonts/README.md)
- [Creating a stopwatch in Pinetime(article)](https://pankajraghav.com/2021/04/03/PINETIME-STOPCLOCK.html) - [Creating a stopwatch in Pinetime(article)](https://pankajraghav.com/2021/04/03/PINETIME-STOPCLOCK.html)
### Build, flash and debug ### Build, flash and debug

View file

@ -17,7 +17,7 @@ uint32_t BootloaderVersion::Patch() {
return 0; return 0;
} }
const char *BootloaderVersion::VersionString() { const char* BootloaderVersion::VersionString() {
return "0.0.0"; return "0.0.0";
} }

View file

@ -2,11 +2,11 @@
namespace Pinetime { namespace Pinetime {
class BootloaderVersion { class BootloaderVersion {
public: public:
static uint32_t Major(); static uint32_t Major();
static uint32_t Minor(); static uint32_t Minor();
static uint32_t Patch(); static uint32_t Patch();
static const char* VersionString(); static const char* VersionString();
static bool IsValid(); static bool IsValid();
}; };
} }

View file

@ -26,20 +26,19 @@
* 1 tab == 4 spaces! * 1 tab == 4 spaces!
*/ */
#ifndef FREERTOS_CONFIG_H #ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H
#ifdef SOFTDEVICE_PRESENT #ifdef SOFTDEVICE_PRESENT
#include "nrf_soc.h" #include "nrf_soc.h"
#endif #endif
#include "app_util_platform.h" #include "app_util_platform.h"
/*----------------------------------------------------------- /*-----------------------------------------------------------
* Possible configurations for system timer * Possible configurations for system timer
*/ */
#define FREERTOS_USE_RTC 0 /**< Use real time clock for the system */ #define FREERTOS_USE_RTC 0 /**< Use real time clock for the system */
#define FREERTOS_USE_SYSTICK 1 /**< Use SysTick timer for system */ #define FREERTOS_USE_SYSTICK 1 /**< Use SysTick timer for system */
/*----------------------------------------------------------- /*-----------------------------------------------------------
* Application specific definitions. * Application specific definitions.
@ -55,153 +54,150 @@
#define configTICK_SOURCE FREERTOS_USE_RTC #define configTICK_SOURCE FREERTOS_USE_RTC
#define configUSE_PREEMPTION 1 #define configUSE_PREEMPTION 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#define configUSE_TICKLESS_IDLE 1 #define configUSE_TICKLESS_IDLE 1
#define configUSE_TICKLESS_IDLE_SIMPLE_DEBUG 0 /* See into vPortSuppressTicksAndSleep source code for explanation */ #define configUSE_TICKLESS_IDLE_SIMPLE_DEBUG 0 /* See into vPortSuppressTicksAndSleep source code for explanation */
#define configCPU_CLOCK_HZ ( SystemCoreClock ) #define configCPU_CLOCK_HZ (SystemCoreClock)
#define configTICK_RATE_HZ 1024 #define configTICK_RATE_HZ 1024
#define configMAX_PRIORITIES ( 3 ) #define configMAX_PRIORITIES (3)
#define configMINIMAL_STACK_SIZE ( 120 ) #define configMINIMAL_STACK_SIZE (120)
#define configTOTAL_HEAP_SIZE ( 1024*16 ) #define configTOTAL_HEAP_SIZE (1024 * 16)
#define configMAX_TASK_NAME_LEN ( 4 ) #define configMAX_TASK_NAME_LEN (4)
#define configUSE_16_BIT_TICKS 0 #define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1 #define configIDLE_SHOULD_YIELD 1
#define configUSE_MUTEXES 1 #define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_COUNTING_SEMAPHORES 1
#define configUSE_ALTERNATIVE_API 0 /* Deprecated! */ #define configUSE_ALTERNATIVE_API 0 /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE 2 #define configQUEUE_REGISTRY_SIZE 2
#define configUSE_QUEUE_SETS 0 #define configUSE_QUEUE_SETS 0
#define configUSE_TIME_SLICING 0 #define configUSE_TIME_SLICING 0
#define configUSE_NEWLIB_REENTRANT 0 #define configUSE_NEWLIB_REENTRANT 0
#define configENABLE_BACKWARD_COMPATIBILITY 1 #define configENABLE_BACKWARD_COMPATIBILITY 1
/* Hook function related definitions. */ /* Hook function related definitions. */
#define configUSE_IDLE_HOOK 1 #define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0 #define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0 #define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_MALLOC_FAILED_HOOK 0
/* Run time and task stats gathering related definitions. */ /* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS 0 #define configGENERATE_RUN_TIME_STATS 0
#define configUSE_TRACE_FACILITY 1 #define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 0 #define configUSE_STATS_FORMATTING_FUNCTIONS 0
/* Co-routine definitions. */ /* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0 #define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) #define configMAX_CO_ROUTINE_PRIORITIES (2)
/* Software timer definitions. */ /* Software timer definitions. */
#define configUSE_TIMERS 1 #define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 0 ) #define configTIMER_TASK_PRIORITY (0)
#define configTIMER_QUEUE_LENGTH 32 #define configTIMER_QUEUE_LENGTH 32
#define configTIMER_TASK_STACK_DEPTH ( 300 ) #define configTIMER_TASK_STACK_DEPTH (300)
/* Tickless Idle configuration. */ /* Tickless Idle configuration. */
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
/* Tickless idle/low power functionality. */ /* Tickless idle/low power functionality. */
/* Define to trap errors during development. */ /* Define to trap errors during development. */
#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER) #if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)
#define configASSERT( x ) ASSERT(x) #define configASSERT(x) ASSERT(x)
#endif #endif
/* FreeRTOS MPU specific definitions. */ /* FreeRTOS MPU specific definitions. */
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 1 #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 1
/* Optional functions - most linkers will remove unused functions anyway. */ /* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1 #define INCLUDE_xResumeFromISR 1
#define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1 #define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1 #define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_xTaskGetIdleTaskHandle 1 #define INCLUDE_xTaskGetIdleTaskHandle 1
#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 #define INCLUDE_xTimerGetTimerDaemonTaskHandle 1
#define INCLUDE_pcTaskGetTaskName 1 #define INCLUDE_pcTaskGetTaskName 1
#define INCLUDE_eTaskGetState 1 #define INCLUDE_eTaskGetState 1
#define INCLUDE_xEventGroupSetBitFromISR 1 #define INCLUDE_xEventGroupSetBitFromISR 1
#define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_xTimerPendFunctionCall 1
/* The lowest interrupt priority that can be used in a call to a "set priority" /* The lowest interrupt priority that can be used in a call to a "set priority"
function. */ function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
/* The highest interrupt priority that can be used by any interrupt service /* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */ PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY _PRIO_APP_HIGH #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY _PRIO_APP_HIGH
/* Interrupt priorities used by the kernel port layer itself. These are generic /* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */ to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY configLIBRARY_LOWEST_INTERRUPT_PRIORITY #define configKERNEL_INTERRUPT_PRIORITY configLIBRARY_LOWEST_INTERRUPT_PRIORITY
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY #define configMAX_SYSCALL_INTERRUPT_PRIORITY configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names - or at least those used in the unmodified vector table. */ standard names - or at least those used in the unmodified vector table. */
#define vPortSVCHandler SVC_Handler #define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler #define xPortPendSVHandler PendSV_Handler
/*----------------------------------------------------------- /*-----------------------------------------------------------
* Settings that are generated automatically * Settings that are generated automatically
* basing on the settings above * basing on the settings above
*/ */
#if (configTICK_SOURCE == FREERTOS_USE_SYSTICK) #if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
// do not define configSYSTICK_CLOCK_HZ for SysTick to be configured automatically // do not define configSYSTICK_CLOCK_HZ for SysTick to be configured automatically
// to CPU clock source // to CPU clock source
#define xPortSysTickHandler SysTick_Handler #define xPortSysTickHandler SysTick_Handler
#elif (configTICK_SOURCE == FREERTOS_USE_RTC) #elif (configTICK_SOURCE == FREERTOS_USE_RTC)
#define configSYSTICK_CLOCK_HZ ( 32768UL ) #define configSYSTICK_CLOCK_HZ (32768UL)
#define xPortSysTickHandler RTC1_IRQHandler #define xPortSysTickHandler RTC1_IRQHandler
#else #else
#error Unsupported configTICK_SOURCE value #error Unsupported configTICK_SOURCE value
#endif #endif
/* Code below should be only used by the compiler, and not the assembler. */ /* Code below should be only used by the compiler, and not the assembler. */
#if !(defined(__ASSEMBLY__) || defined(__ASSEMBLER__)) #if !(defined(__ASSEMBLY__) || defined(__ASSEMBLER__))
#include "nrf.h" #include "nrf.h"
#include "nrf_assert.h" #include "nrf_assert.h"
/* This part of definitions may be problematic in assembly - it uses definitions from files that are not assembly compatible. */ /* This part of definitions may be problematic in assembly - it uses definitions from files that are not assembly compatible. */
/* Cortex-M specific definitions. */ /* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS #ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS #define configPRIO_BITS __NVIC_PRIO_BITS
#else #else
#error "This port requires __NVIC_PRIO_BITS to be defined" #error "This port requires __NVIC_PRIO_BITS to be defined"
#endif #endif
/* Access to current system core clock is required only if we are ticking the system by systimer */ /* Access to current system core clock is required only if we are ticking the system by systimer */
#if (configTICK_SOURCE == FREERTOS_USE_SYSTICK) #if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
#include <stdint.h> #include <stdint.h>
extern uint32_t SystemCoreClock; extern uint32_t SystemCoreClock;
#endif #endif
#endif /* !assembler */ #endif /* !assembler */
/** Implementation note: Use this with caution and set this to 1 ONLY for debugging /** Implementation note: Use this with caution and set this to 1 ONLY for debugging
* ---------------------------------------------------------- * ----------------------------------------------------------
* Set the value of configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to below for enabling or disabling RTOS tick auto correction: * Set the value of configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to below for enabling or disabling RTOS tick auto correction:
* 0. This is default. If the RTC tick interrupt is masked for more than 1 tick by higher priority interrupts, then most likely * 0. This is default. If the RTC tick interrupt is masked for more than 1 tick by higher priority interrupts, then most likely
* one or more RTC ticks are lost. The tick interrupt inside RTOS will detect this and make a correction needed. This is needed * one or more RTC ticks are lost. The tick interrupt inside RTOS will detect this and make a correction needed. This is needed
* for the RTOS internal timers to be more accurate. * for the RTOS internal timers to be more accurate.
* 1. The auto correction for RTOS tick is disabled even though few RTC tick interrupts were lost. This feature is desirable when debugging * 1. The auto correction for RTOS tick is disabled even though few RTC tick interrupts were lost. This feature is desirable when debugging
* the RTOS application and stepping though the code. After stepping when the application is continued in debug mode, the auto-corrections of * the RTOS application and stepping though the code. After stepping when the application is continued in debug mode, the
* RTOS tick might cause asserts. Setting configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to 1 will make RTC and RTOS go out of sync but could be * auto-corrections of RTOS tick might cause asserts. Setting configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to 1 will make RTC and RTOS go
* convenient for debugging. * out of sync but could be convenient for debugging.
*/ */
#define configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG 0 #define configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG 0
#endif /* FREERTOS_CONFIG_H */ #endif /* FREERTOS_CONFIG_H */

View file

@ -7,33 +7,33 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
Battery *Battery::instance = nullptr; Battery* Battery::instance = nullptr;
Battery::Battery() { Battery::Battery() {
instance = this; instance = this;
} }
void Battery::Init() { void Battery::Init() {
nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup); nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup);
nrf_gpio_cfg_input(powerPresentPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup); nrf_gpio_cfg_input(powerPresentPin, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup);
} }
void Battery::Update() { void Battery::Update() {
isCharging = !nrf_gpio_pin_read(chargingPin); isCharging = !nrf_gpio_pin_read(chargingPin);
isPowerPresent = !nrf_gpio_pin_read(powerPresentPin); isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
if ( isReading ) return; if (isReading)
return;
// Non blocking read // Non blocking read
samples = 0; samples = 0;
isReading = true; isReading = true;
SaadcInit(); SaadcInit();
nrfx_saadc_sample(); nrfx_saadc_sample();
} }
void Battery::adcCallbackStatic(nrfx_saadc_evt_t const *event) { void Battery::adcCallbackStatic(nrfx_saadc_evt_t const* event) {
instance->SaadcEventHandler(event); instance->SaadcEventHandler(event);
} }
@ -41,48 +41,44 @@ void Battery::SaadcInit() {
nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG; nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, adcCallbackStatic)); APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, adcCallbackStatic));
nrf_saadc_channel_config_t adcChannelConfig = { nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, .gain = NRF_SAADC_GAIN1_5,
.gain = NRF_SAADC_GAIN1_5, .reference = NRF_SAADC_REFERENCE_INTERNAL,
.reference = NRF_SAADC_REFERENCE_INTERNAL, .acq_time = NRF_SAADC_ACQTIME_3US,
.acq_time = NRF_SAADC_ACQTIME_3US, .mode = NRF_SAADC_MODE_SINGLE_ENDED,
.mode = NRF_SAADC_MODE_SINGLE_ENDED, .burst = NRF_SAADC_BURST_ENABLED,
.burst = NRF_SAADC_BURST_ENABLED, .pin_p = batteryVoltageAdcInput,
.pin_p = batteryVoltageAdcInput, .pin_n = NRF_SAADC_INPUT_DISABLED};
.pin_n = NRF_SAADC_INPUT_DISABLED
};
APP_ERROR_CHECK(nrfx_saadc_channel_init(0, &adcChannelConfig)); APP_ERROR_CHECK(nrfx_saadc_channel_init(0, &adcChannelConfig));
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1)); APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
} }
void Battery::SaadcEventHandler(nrfx_saadc_evt_t const * p_event) { void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
const float battery_max = 4.18; // maximum voltage of battery ( max charging voltage is 4.21 ) const float battery_max = 4.18; // maximum voltage of battery ( max charging voltage is 4.21 )
const float battery_min = 3.20; // minimum voltage of battery before shutdown ( depends on the battery ) const float battery_min = 3.20; // minimum voltage of battery before shutdown ( depends on the battery )
if (p_event->type == NRFX_SAADC_EVT_DONE) { if (p_event->type == NRFX_SAADC_EVT_DONE) {
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
voltage = (static_cast<float>(p_event->data.done.p_buffer[0]) * 2.04f) / (1024 / 3.0f); APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
voltage = roundf(voltage * 100) / 100;
percentRemaining = static_cast<int>(((voltage - battery_min) / (battery_max - battery_min)) * 100); voltage = (static_cast<float>(p_event->data.done.p_buffer[0]) * 2.04f) / (1024 / 3.0f);
voltage = roundf(voltage * 100) / 100;
percentRemaining = std::max(percentRemaining, 0); percentRemaining = static_cast<int>(((voltage - battery_min) / (battery_max - battery_min)) * 100);
percentRemaining = std::min(percentRemaining, 100);
percentRemainingBuffer.insert(percentRemaining); percentRemaining = std::max(percentRemaining, 0);
percentRemaining = std::min(percentRemaining, 100);
samples++; percentRemainingBuffer.insert(percentRemaining);
if ( samples > percentRemainingSamples ) {
nrfx_saadc_uninit(); samples++;
isReading = false; if (samples > percentRemainingSamples) {
} else { nrfx_saadc_uninit();
nrfx_saadc_sample(); isReading = false;
} } else {
nrfx_saadc_sample();
} }
} }
}

View file

@ -7,18 +7,18 @@
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
/** A simple circular buffer that can be used to average /** A simple circular buffer that can be used to average
out the sensor values. The total capacity of the CircBuffer out the sensor values. The total capacity of the CircBuffer
is given as the template parameter N. is given as the template parameter N.
*/ */
template <int N> template <int N> class CircBuffer {
class CircBuffer {
public: public:
CircBuffer() : arr{}, sz{}, cap{N}, head{} {} CircBuffer() : arr {}, sz {}, cap {N}, head {} {
}
/** /**
insert member function overwrites the next data to the current insert member function overwrites the next data to the current
HEAD and moves the HEAD to the newly inserted value. HEAD and moves the HEAD to the newly inserted value.
*/ */
void insert(const int num) { void insert(const int num) {
head %= cap; head %= cap;
arr[head++] = num; arr[head++] = num;
@ -34,49 +34,56 @@ namespace Pinetime {
private: private:
std::array<int, N> arr; /**< internal array used to store the values*/ std::array<int, N> arr; /**< internal array used to store the values*/
uint8_t sz; /**< The current size of the array.*/ uint8_t sz; /**< The current size of the array.*/
uint8_t cap; /**< Total capacity of the CircBuffer.*/ uint8_t cap; /**< Total capacity of the CircBuffer.*/
uint8_t head; /**< The current head of the CircBuffer*/ uint8_t head; /**< The current head of the CircBuffer*/
}; };
class Battery { class Battery {
public: public:
Battery();
Battery(); void Init();
void Update();
void Init(); int PercentRemaining() const {
void Update(); return percentRemainingBuffer.GetAverage();
}
int PercentRemaining() const { return percentRemainingBuffer.GetAverage(); }
float Voltage() const { return voltage; } float Voltage() const {
return voltage;
}
bool IsCharging() const { return isCharging; } bool IsCharging() const {
bool IsPowerPresent() const { return isPowerPresent; } return isCharging;
}
bool IsPowerPresent() const {
return isPowerPresent;
}
private: private:
static Battery *instance; static Battery* instance;
nrf_saadc_value_t saadc_value; nrf_saadc_value_t saadc_value;
static constexpr uint8_t percentRemainingSamples = 5;
CircBuffer<percentRemainingSamples> percentRemainingBuffer {};
static constexpr uint32_t chargingPin = 12; static constexpr uint8_t percentRemainingSamples = 5;
static constexpr uint32_t powerPresentPin = 19; CircBuffer<percentRemainingSamples> percentRemainingBuffer {};
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
float voltage = 0.0f;
int percentRemaining = -1;
bool isCharging = false; static constexpr uint32_t chargingPin = 12;
bool isPowerPresent = false; static constexpr uint32_t powerPresentPin = 19;
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
void SaadcInit(); float voltage = 0.0f;
int percentRemaining = -1;
void SaadcEventHandler(nrfx_saadc_evt_t const * p_event); bool isCharging = false;
static void adcCallbackStatic(nrfx_saadc_evt_t const *event); bool isPowerPresent = false;
bool isReading = false; void SaadcInit();
uint8_t samples = 0;
void SaadcEventHandler(nrfx_saadc_evt_t const* p_event);
static void adcCallbackStatic(nrfx_saadc_evt_t const* event);
bool isReading = false;
uint8_t samples = 0;
}; };
} }
} }

View file

@ -12,50 +12,42 @@ constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid;
constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid; constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid;
namespace { namespace {
int int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg) {
OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, auto client = static_cast<AlertNotificationClient*>(arg);
void *arg) {
auto client = static_cast<AlertNotificationClient *>(arg);
return client->OnDiscoveryEvent(conn_handle, error, service); return client->OnDiscoveryEvent(conn_handle, error, service);
} }
int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error, int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle,
const struct ble_gatt_chr *chr, void *arg) { const struct ble_gatt_error* error,
auto client = static_cast<AlertNotificationClient *>(arg); const struct ble_gatt_chr* chr,
void* arg) {
auto client = static_cast<AlertNotificationClient*>(arg);
return client->OnCharacteristicsDiscoveryEvent(conn_handle, error, chr); return client->OnCharacteristicsDiscoveryEvent(conn_handle, error, chr);
} }
int OnAlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle, int OnAlertNotificationDescriptorDiscoveryEventCallback(
const struct ble_gatt_error *error, uint16_t conn_handle, const struct ble_gatt_error* error, uint16_t chr_val_handle, const struct ble_gatt_dsc* dsc, void* arg) {
uint16_t chr_val_handle, auto client = static_cast<AlertNotificationClient*>(arg);
const struct ble_gatt_dsc *dsc,
void *arg) {
auto client = static_cast<AlertNotificationClient *>(arg);
return client->OnDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc); return client->OnDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
} }
int NewAlertSubcribeCallback(uint16_t conn_handle, int NewAlertSubcribeCallback(uint16_t conn_handle, const struct ble_gatt_error* error, struct ble_gatt_attr* attr, void* arg) {
const struct ble_gatt_error *error, auto client = static_cast<AlertNotificationClient*>(arg);
struct ble_gatt_attr *attr,
void *arg) {
auto client = static_cast<AlertNotificationClient *>(arg);
return client->OnNewAlertSubcribe(conn_handle, error, attr); return client->OnNewAlertSubcribe(conn_handle, error, attr);
} }
} }
AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask &systemTask, AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager &notificationManager) : Pinetime::Controllers::NotificationManager& notificationManager)
systemTask{systemTask}, notificationManager{notificationManager} { : systemTask {systemTask}, notificationManager {notificationManager} {
} }
bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
const ble_gatt_svc *service) {
if (service == nullptr && error->status == BLE_HS_EDONE) { if (service == nullptr && error->status == BLE_HS_EDONE) {
if (isDiscovered) { if (isDiscovered) {
NRF_LOG_INFO("ANS Discovery found, starting characteristics discovery"); NRF_LOG_INFO("ANS Discovery found, starting characteristics discovery");
ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle, ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle, OnAlertNotificationCharacteristicDiscoveredCallback, this);
OnAlertNotificationCharacteristicDiscoveredCallback, this);
} else { } else {
NRF_LOG_INFO("ANS not found"); NRF_LOG_INFO("ANS not found");
onServiceDiscovered(connectionHandle); onServiceDiscovered(connectionHandle);
@ -63,7 +55,7 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const
return true; return true;
} }
if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ansServiceUuid), &service->uuid.u) == 0) { if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ansServiceUuid), &service->uuid.u) == 0) {
NRF_LOG_INFO("ANS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); NRF_LOG_INFO("ANS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
ansStartHandle = service->start_handle; ansStartHandle = service->start_handle;
ansEndHandle = service->end_handle; ansEndHandle = service->end_handle;
@ -72,8 +64,9 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const
return false; return false;
} }
int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle,
const ble_gatt_chr *characteristic) { const ble_gatt_error* error,
const ble_gatt_chr* characteristic) {
if (error->status != 0 && error->status != BLE_HS_EDONE) { if (error->status != 0 && error->status != BLE_HS_EDONE) {
NRF_LOG_INFO("ANS Characteristic discovery ERROR"); NRF_LOG_INFO("ANS Characteristic discovery ERROR");
onServiceDiscovered(connectionHandle); onServiceDiscovered(connectionHandle);
@ -83,41 +76,34 @@ int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connection
if (characteristic == nullptr && error->status == BLE_HS_EDONE) { if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
NRF_LOG_INFO("ANS Characteristic discovery complete"); NRF_LOG_INFO("ANS Characteristic discovery complete");
if (isCharacteristicDiscovered) { if (isCharacteristicDiscovered) {
ble_gattc_disc_all_dscs(connectionHandle, ble_gattc_disc_all_dscs(connectionHandle, newAlertHandle, ansEndHandle, OnAlertNotificationDescriptorDiscoveryEventCallback, this);
newAlertHandle, ansEndHandle,
OnAlertNotificationDescriptorDiscoveryEventCallback, this);
} else } else
onServiceDiscovered(connectionHandle); onServiceDiscovered(connectionHandle);
} else { } else {
if (characteristic != nullptr && if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
ble_uuid_cmp(((ble_uuid_t *) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid"); NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid");
supportedNewAlertCategoryHandle = characteristic->val_handle; supportedNewAlertCategoryHandle = characteristic->val_handle;
} else if (characteristic != nullptr && } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
ble_uuid_cmp(((ble_uuid_t *) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid"); NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid");
supportedUnreadAlertCategoryHandle = characteristic->val_handle; supportedUnreadAlertCategoryHandle = characteristic->val_handle;
} else if (characteristic != nullptr && } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &characteristic->uuid.u) == 0) {
ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid"); NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid");
newAlertHandle = characteristic->val_handle; newAlertHandle = characteristic->val_handle;
newAlertDefHandle = characteristic->def_handle; newAlertDefHandle = characteristic->def_handle;
isCharacteristicDiscovered = true; isCharacteristicDiscovered = true;
} else if (characteristic != nullptr && } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
ble_uuid_cmp(((ble_uuid_t *) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid"); NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid");
unreadAlertStatusHandle = characteristic->val_handle; unreadAlertStatusHandle = characteristic->val_handle;
} else if (characteristic != nullptr && } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &controlPointUuid), &characteristic->uuid.u) == 0) {
ble_uuid_cmp(((ble_uuid_t *) &controlPointUuid), &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid"); NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid");
controlPointHandle = characteristic->val_handle; controlPointHandle = characteristic->val_handle;
} else NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle); } else
NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle);
} }
return 0; return 0;
} }
int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute) {
ble_gatt_attr *attribute) {
if (error->status == 0) { if (error->status == 0) {
NRF_LOG_INFO("ANS New alert subscribe OK"); NRF_LOG_INFO("ANS New alert subscribe OK");
} else { } else {
@ -128,12 +114,12 @@ int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const
return 0; return 0;
} }
int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
const ble_gatt_error* error,
uint16_t characteristicValueHandle, uint16_t characteristicValueHandle,
const ble_gatt_dsc *descriptor) { const ble_gatt_dsc* descriptor) {
if (error->status == 0) { if (error->status == 0) {
if (characteristicValueHandle == newAlertHandle && if (characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &descriptor->uuid.u)) {
ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &descriptor->uuid.u)) {
if (newAlertDescriptorHandle == 0) { if (newAlertDescriptorHandle == 0) {
NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle); NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle);
newAlertDescriptorHandle = descriptor->handle; newAlertDescriptorHandle = descriptor->handle;
@ -151,16 +137,17 @@ int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connect
return 0; return 0;
} }
void AlertNotificationClient::OnNotification(ble_gap_event *event) { void AlertNotificationClient::OnNotification(ble_gap_event* event) {
if (event->notify_rx.attr_handle == newAlertHandle) { if (event->notify_rx.attr_handle == newAlertHandle) {
constexpr size_t stringTerminatorSize = 1; // end of string '\0' constexpr size_t stringTerminatorSize = 1; // end of string '\0'
constexpr size_t headerSize = 3; constexpr size_t headerSize = 3;
const auto maxMessageSize{NotificationManager::MaximumMessageSize()}; const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
const auto maxBufferSize{maxMessageSize + headerSize}; const auto maxBufferSize {maxMessageSize + headerSize};
// Ignore notifications with empty message // Ignore notifications with empty message
const auto packetLen = OS_MBUF_PKTLEN(event->notify_rx.om); const auto packetLen = OS_MBUF_PKTLEN(event->notify_rx.om);
if(packetLen <= headerSize) return; if (packetLen <= headerSize)
return;
size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize); size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize);
auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize)); auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));

View file

@ -19,68 +19,52 @@ namespace Pinetime {
class NotificationManager; class NotificationManager;
class AlertNotificationClient : public BleClient { class AlertNotificationClient : public BleClient {
public: public:
explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask, explicit AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager &notificationManager); Pinetime::Controllers::NotificationManager& notificationManager);
bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service); bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service);
int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
const ble_gatt_chr *characteristic); int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute); int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_error* error,
uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor); uint16_t characteristicValueHandle,
void OnNotification(ble_gap_event *event); const ble_gatt_dsc* descriptor);
void Reset(); void OnNotification(ble_gap_event* event);
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override; void Reset();
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
private: private:
static constexpr uint16_t ansServiceId{0x1811}; static constexpr uint16_t ansServiceId {0x1811};
static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47; static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48; static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48;
static constexpr uint16_t newAlertId = 0x2a46; static constexpr uint16_t newAlertId = 0x2a46;
static constexpr uint16_t unreadAlertStatusId = 0x2a45; static constexpr uint16_t unreadAlertStatusId = 0x2a45;
static constexpr uint16_t controlPointId = 0x2a44; static constexpr uint16_t controlPointId = 0x2a44;
static constexpr ble_uuid16_t ansServiceUuid{ static constexpr ble_uuid16_t ansServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansServiceId};
.u {.type = BLE_UUID_TYPE_16}, static constexpr ble_uuid16_t supportedNewAlertCategoryUuid {.u {.type = BLE_UUID_TYPE_16}, .value = supportedNewAlertCategoryId};
.value = ansServiceId static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid {.u {.type = BLE_UUID_TYPE_16},
}; .value = supportedUnreadAlertCategoryId};
static constexpr ble_uuid16_t supportedNewAlertCategoryUuid{ static constexpr ble_uuid16_t newAlertUuid {.u {.type = BLE_UUID_TYPE_16}, .value = newAlertId};
.u {.type = BLE_UUID_TYPE_16}, static constexpr ble_uuid16_t unreadAlertStatusUuid {.u {.type = BLE_UUID_TYPE_16}, .value = unreadAlertStatusId};
.value = supportedNewAlertCategoryId static constexpr ble_uuid16_t controlPointUuid {.u {.type = BLE_UUID_TYPE_16}, .value = controlPointId};
};
static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid{
.u {.type = BLE_UUID_TYPE_16},
.value = supportedUnreadAlertCategoryId
};
static constexpr ble_uuid16_t newAlertUuid{
.u {.type = BLE_UUID_TYPE_16},
.value = newAlertId
};
static constexpr ble_uuid16_t unreadAlertStatusUuid{
.u {.type = BLE_UUID_TYPE_16},
.value = unreadAlertStatusId
};
static constexpr ble_uuid16_t controlPointUuid{
.u {.type = BLE_UUID_TYPE_16},
.value = controlPointId
};
uint16_t ansStartHandle = 0; uint16_t ansStartHandle = 0;
uint16_t ansEndHandle = 0; uint16_t ansEndHandle = 0;
uint16_t supportedNewAlertCategoryHandle = 0; uint16_t supportedNewAlertCategoryHandle = 0;
uint16_t supportedUnreadAlertCategoryHandle = 0; uint16_t supportedUnreadAlertCategoryHandle = 0;
uint16_t newAlertHandle = 0; uint16_t newAlertHandle = 0;
uint16_t newAlertDescriptorHandle = 0; uint16_t newAlertDescriptorHandle = 0;
uint16_t newAlertDefHandle = 0; uint16_t newAlertDefHandle = 0;
uint16_t unreadAlertStatusHandle = 0; uint16_t unreadAlertStatusHandle = 0;
uint16_t controlPointHandle = 0; uint16_t controlPointHandle = 0;
bool isDiscovered = false; bool isDiscovered = false;
Pinetime::System::SystemTask &systemTask; Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::NotificationManager &notificationManager; Pinetime::Controllers::NotificationManager& notificationManager;
std::function<void(uint16_t)> onServiceDiscovered; std::function<void(uint16_t)> onServiceDiscovered;
bool isCharacteristicDiscovered = false; bool isCharacteristicDiscovered = false;
bool isDescriptorFound = false; bool isDescriptorFound = false;
}; };
} }
} }

View file

@ -11,8 +11,7 @@ constexpr ble_uuid16_t AlertNotificationService::ansUuid;
constexpr ble_uuid16_t AlertNotificationService::ansCharUuid; constexpr ble_uuid16_t AlertNotificationService::ansCharUuid;
constexpr ble_uuid128_t AlertNotificationService::notificationEventUuid; constexpr ble_uuid128_t AlertNotificationService::notificationEventUuid;
int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto anService = static_cast<AlertNotificationService*>(arg); auto anService = static_cast<AlertNotificationService*>(arg);
return anService->OnAlert(conn_handle, attr_handle, ctxt); return anService->OnAlert(conn_handle, attr_handle, ctxt);
} }
@ -26,62 +25,52 @@ void AlertNotificationService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
AlertNotificationService::AlertNotificationService ( System::SystemTask& systemTask, NotificationManager& notificationManager ) AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager)
: characteristicDefinition{ : characteristicDefinition {{.uuid = (ble_uuid_t*) &ansCharUuid,
{ .access_cb = AlertNotificationCallback,
.uuid = (ble_uuid_t *) &ansCharUuid, .arg = this,
.access_cb = AlertNotificationCallback, .flags = BLE_GATT_CHR_F_WRITE},
.arg = this, {.uuid = (ble_uuid_t*) &notificationEventUuid,
.flags = BLE_GATT_CHR_F_WRITE .access_cb = AlertNotificationCallback,
}, .arg = this,
{ .flags = BLE_GATT_CHR_F_NOTIFY,
.uuid = (ble_uuid_t *) &notificationEventUuid, .val_handle = &eventHandle},
.access_cb = AlertNotificationCallback, {0}},
.arg = this, serviceDefinition {
.flags = BLE_GATT_CHR_F_NOTIFY, {/* Device Information Service */
.val_handle = &eventHandle .type = BLE_GATT_SVC_TYPE_PRIMARY,
}, .uuid = (ble_uuid_t*) &ansUuid,
{ .characteristics = characteristicDefinition},
0 {0},
} },
}, systemTask {systemTask},
serviceDefinition{ notificationManager {notificationManager} {
{
/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &ansUuid,
.characteristics = characteristicDefinition
},
{
0
},
}, systemTask{systemTask}, notificationManager{notificationManager} {
} }
int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
struct ble_gatt_access_ctxt *ctxt) {
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
constexpr size_t stringTerminatorSize = 1; // end of string '\0' constexpr size_t stringTerminatorSize = 1; // end of string '\0'
constexpr size_t headerSize = 3; constexpr size_t headerSize = 3;
const auto maxMessageSize {NotificationManager::MaximumMessageSize()}; const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
const auto maxBufferSize{maxMessageSize + headerSize}; const auto maxBufferSize {maxMessageSize + headerSize};
// Ignore notifications with empty message // Ignore notifications with empty message
const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); const auto packetLen = OS_MBUF_PKTLEN(ctxt->om);
if(packetLen <= headerSize) return 0; if (packetLen <= headerSize)
return 0;
size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize); size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize);
auto messageSize = std::min(maxMessageSize, (bufferSize-headerSize)); auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));
Categories category; Categories category;
NotificationManager::Notification notif; NotificationManager::Notification notif;
os_mbuf_copydata(ctxt->om, headerSize, messageSize-1, notif.message.data()); os_mbuf_copydata(ctxt->om, headerSize, messageSize - 1, notif.message.data());
os_mbuf_copydata(ctxt->om, 0, 1, &category); os_mbuf_copydata(ctxt->om, 0, 1, &category);
notif.message[messageSize-1] = '\0'; notif.message[messageSize - 1] = '\0';
notif.size = messageSize; notif.size = messageSize;
// TODO convert all ANS categories to NotificationController categories // TODO convert all ANS categories to NotificationController categories
switch(category) { switch (category) {
case Categories::Call: case Categories::Call:
notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall; notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall;
break; break;
@ -99,7 +88,7 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle
void AlertNotificationService::AcceptIncomingCall() { void AlertNotificationService::AcceptIncomingCall() {
auto response = IncomingCallResponses::Answer; auto response = IncomingCallResponses::Answer;
auto *om = ble_hs_mbuf_from_flat(&response, 1); auto* om = ble_hs_mbuf_from_flat(&response, 1);
uint16_t connectionHandle = systemTask.nimble().connHandle(); uint16_t connectionHandle = systemTask.nimble().connHandle();
@ -112,7 +101,7 @@ void AlertNotificationService::AcceptIncomingCall() {
void AlertNotificationService::RejectIncomingCall() { void AlertNotificationService::RejectIncomingCall() {
auto response = IncomingCallResponses::Reject; auto response = IncomingCallResponses::Reject;
auto *om = ble_hs_mbuf_from_flat(&response, 1); auto* om = ble_hs_mbuf_from_flat(&response, 1);
uint16_t connectionHandle = systemTask.nimble().connHandle(); uint16_t connectionHandle = systemTask.nimble().connHandle();
@ -125,7 +114,7 @@ void AlertNotificationService::RejectIncomingCall() {
void AlertNotificationService::MuteIncomingCall() { void AlertNotificationService::MuteIncomingCall() {
auto response = IncomingCallResponses::Mute; auto response = IncomingCallResponses::Mute;
auto *om = ble_hs_mbuf_from_flat(&response, 1); auto* om = ble_hs_mbuf_from_flat(&response, 1);
uint16_t connectionHandle = systemTask.nimble().connHandle(); uint16_t connectionHandle = systemTask.nimble().connHandle();

View file

@ -7,8 +7,9 @@
#undef max #undef max
#undef min #undef min
//00020001-78fc-48fe-8e23-433b3a1942d0 // 00020001-78fc-48fe-8e23-433b3a1942d0
#define NOTIFICATION_EVENT_SERVICE_UUID_BASE {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x01, 0x00, 0x02, 0x00} #define NOTIFICATION_EVENT_SERVICE_UUID_BASE \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x01, 0x00, 0x02, 0x00 }
namespace Pinetime { namespace Pinetime {
@ -19,64 +20,49 @@ namespace Pinetime {
class NotificationManager; class NotificationManager;
class AlertNotificationService { class AlertNotificationService {
public: public:
AlertNotificationService(Pinetime::System::SystemTask &systemTask, AlertNotificationService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager);
Pinetime::Controllers::NotificationManager &notificationManager); void Init();
void Init();
int OnAlert(uint16_t conn_handle, uint16_t attr_handle, int OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
struct ble_gatt_access_ctxt *ctxt);
void AcceptIncomingCall(); void AcceptIncomingCall();
void RejectIncomingCall(); void RejectIncomingCall();
void MuteIncomingCall(); void MuteIncomingCall();
enum class IncomingCallResponses : uint8_t { enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 };
Reject = 0x00,
Answer = 0x01,
Mute = 0x02
};
private: private:
enum class Categories : uint8_t { enum class Categories : uint8_t {
SimpleAlert = 0x00, SimpleAlert = 0x00,
Email = 0x01, Email = 0x01,
News = 0x02, News = 0x02,
Call = 0x03, Call = 0x03,
MissedCall = 0x04, MissedCall = 0x04,
MmsSms = 0x05, MmsSms = 0x05,
VoiceMail = 0x06, VoiceMail = 0x06,
Schedule = 0x07, Schedule = 0x07,
HighPrioritizedAlert = 0x08, HighPrioritizedAlert = 0x08,
InstantMessage = 0x09, InstantMessage = 0x09,
All = 0xff All = 0xff
}; };
static constexpr uint16_t ansId {0x1811}; static constexpr uint16_t ansId {0x1811};
static constexpr uint16_t ansCharId {0x2a46}; static constexpr uint16_t ansCharId {0x2a46};
static constexpr ble_uuid16_t ansUuid { static constexpr ble_uuid16_t ansUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansId};
.u { .type = BLE_UUID_TYPE_16 },
.value = ansId
};
static constexpr ble_uuid16_t ansCharUuid { static constexpr ble_uuid16_t ansCharUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansCharId};
.u { .type = BLE_UUID_TYPE_16 },
.value = ansCharId
};
static constexpr ble_uuid128_t notificationEventUuid { static constexpr ble_uuid128_t notificationEventUuid {.u {.type = BLE_UUID_TYPE_128}, .value = NOTIFICATION_EVENT_SERVICE_UUID_BASE};
.u { .type = BLE_UUID_TYPE_128 },
.value = NOTIFICATION_EVENT_SERVICE_UUID_BASE
};
struct ble_gatt_chr_def characteristicDefinition[3]; struct ble_gatt_chr_def characteristicDefinition[3];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
Pinetime::System::SystemTask &systemTask; Pinetime::System::SystemTask& systemTask;
NotificationManager &notificationManager; NotificationManager& notificationManager;
uint16_t eventHandle; uint16_t eventHandle;
}; };
} }
} }

View file

@ -7,39 +7,26 @@ using namespace Pinetime::Controllers;
constexpr ble_uuid16_t BatteryInformationService::batteryInformationServiceUuid; constexpr ble_uuid16_t BatteryInformationService::batteryInformationServiceUuid;
constexpr ble_uuid16_t BatteryInformationService::batteryLevelUuid; constexpr ble_uuid16_t BatteryInformationService::batteryLevelUuid;
int BatteryInformationServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
int BatteryInformationServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto* batteryInformationService = static_cast<BatteryInformationService*>(arg); auto* batteryInformationService = static_cast<BatteryInformationService*>(arg);
return batteryInformationService->OnBatteryServiceRequested(conn_handle, attr_handle, ctxt); return batteryInformationService->OnBatteryServiceRequested(conn_handle, attr_handle, ctxt);
} }
BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController) : BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController)
batteryController{batteryController}, : batteryController {batteryController},
characteristicDefinition{ characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid,
{ .access_cb = BatteryInformationServiceCallback,
.uuid = (ble_uuid_t *) &batteryLevelUuid, .arg = this,
.access_cb = BatteryInformationServiceCallback, .flags = BLE_GATT_CHR_F_READ,
.arg = this, .val_handle = &batteryLevelHandle},
.flags = BLE_GATT_CHR_F_READ, {0}},
.val_handle = &batteryLevelHandle serviceDefinition {
}, {/* Device Information Service */
{ .type = BLE_GATT_SVC_TYPE_PRIMARY,
0 .uuid = (ble_uuid_t*) &batteryInformationServiceUuid,
} .characteristics = characteristicDefinition},
}, {0},
serviceDefinition{ } {
{
/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &batteryInformationServiceUuid,
.characteristics = characteristicDefinition
},
{
0
},
}{
} }
void BatteryInformationService::Init() { void BatteryInformationService::Init() {
@ -51,9 +38,10 @@ void BatteryInformationService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle,
ble_gatt_access_ctxt *context) { uint16_t attributeHandle,
if(attributeHandle == batteryLevelHandle) { ble_gatt_access_ctxt* context) {
if (attributeHandle == batteryLevelHandle) {
NRF_LOG_INFO("BATTERY : handle = %d", batteryLevelHandle); NRF_LOG_INFO("BATTERY : handle = %d", batteryLevelHandle);
static uint8_t batteryValue = batteryController.PercentRemaining(); static uint8_t batteryValue = batteryController.PercentRemaining();
int res = os_mbuf_append(context->om, &batteryValue, 1); int res = os_mbuf_append(context->om, &batteryValue, 1);

View file

@ -12,33 +12,25 @@ namespace Pinetime {
namespace Controllers { namespace Controllers {
class Battery; class Battery;
class BatteryInformationService { class BatteryInformationService {
public: public:
BatteryInformationService(Controllers::Battery& batteryController); BatteryInformationService(Controllers::Battery& batteryController);
void Init(); void Init();
int int OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
private: private:
Controllers::Battery& batteryController; Controllers::Battery& batteryController;
static constexpr uint16_t batteryInformationServiceId {0x180F}; static constexpr uint16_t batteryInformationServiceId {0x180F};
static constexpr uint16_t batteryLevelId {0x2A19}; static constexpr uint16_t batteryLevelId {0x2A19};
static constexpr ble_uuid16_t batteryInformationServiceUuid { static constexpr ble_uuid16_t batteryInformationServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = batteryInformationServiceId};
.u {.type = BLE_UUID_TYPE_16},
.value = batteryInformationServiceId
};
static constexpr ble_uuid16_t batteryLevelUuid { static constexpr ble_uuid16_t batteryLevelUuid {.u {.type = BLE_UUID_TYPE_16}, .value = batteryLevelId};
.u {.type = BLE_UUID_TYPE_16},
.value = batteryLevelId
};
struct ble_gatt_chr_def characteristicDefinition[3]; struct ble_gatt_chr_def characteristicDefinition[3];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
uint16_t batteryLevelHandle;
uint16_t batteryLevelHandle;
}; };
} }
} }

View file

@ -3,10 +3,10 @@
#include <functional> #include <functional>
namespace Pinetime { namespace Pinetime {
namespace Controllers{ namespace Controllers {
class BleClient { class BleClient {
public: public:
virtual void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) = 0; virtual void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) = 0;
}; };
} }
} }

View file

@ -25,5 +25,3 @@ void Ble::FirmwareUpdateTotalBytes(uint32_t totalBytes) {
void Ble::FirmwareUpdateCurrentBytes(uint32_t currentBytes) { void Ble::FirmwareUpdateCurrentBytes(uint32_t currentBytes) {
firmwareUpdateCurrentBytes = currentBytes; firmwareUpdateCurrentBytes = currentBytes;
} }

View file

@ -6,39 +6,57 @@
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class Ble { class Ble {
public: public:
using BleAddress = std::array<uint8_t, 6>; using BleAddress = std::array<uint8_t, 6>;
enum class FirmwareUpdateStates {Idle, Running, Validated, Error}; enum class FirmwareUpdateStates { Idle, Running, Validated, Error };
enum class AddressTypes { Public, Random }; enum class AddressTypes { Public, Random };
Ble() = default; Ble() = default;
bool IsConnected() const {return isConnected;} bool IsConnected() const {
void Connect(); return isConnected;
void Disconnect(); }
void Connect();
void Disconnect();
void StartFirmwareUpdate(); void StartFirmwareUpdate();
void StopFirmwareUpdate(); void StopFirmwareUpdate();
void FirmwareUpdateTotalBytes(uint32_t totalBytes); void FirmwareUpdateTotalBytes(uint32_t totalBytes);
void FirmwareUpdateCurrentBytes(uint32_t currentBytes); void FirmwareUpdateCurrentBytes(uint32_t currentBytes);
void State(FirmwareUpdateStates state) { firmwareUpdateState = state; } void State(FirmwareUpdateStates state) {
firmwareUpdateState = state;
}
bool IsFirmwareUpdating() const { return isFirmwareUpdating; } bool IsFirmwareUpdating() const {
uint32_t FirmwareUpdateTotalBytes() const { return firmwareUpdateTotalBytes; } return isFirmwareUpdating;
uint32_t FirmwareUpdateCurrentBytes() const { return firmwareUpdateCurrentBytes; } }
FirmwareUpdateStates State() const { return firmwareUpdateState; } uint32_t FirmwareUpdateTotalBytes() const {
return firmwareUpdateTotalBytes;
}
uint32_t FirmwareUpdateCurrentBytes() const {
return firmwareUpdateCurrentBytes;
}
FirmwareUpdateStates State() const {
return firmwareUpdateState;
}
void Address(BleAddress&& addr) { address = addr; } void Address(BleAddress&& addr) {
const BleAddress& Address() const { return address; } address = addr;
void AddressType(AddressTypes t) { addressType = t;} }
private: const BleAddress& Address() const {
bool isConnected = false; return address;
bool isFirmwareUpdating = false; }
uint32_t firmwareUpdateTotalBytes = 0; void AddressType(AddressTypes t) {
uint32_t firmwareUpdateCurrentBytes = 0; addressType = t;
FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle; }
BleAddress address;
AddressTypes addressType;
private:
bool isConnected = false;
bool isFirmwareUpdating = false;
uint32_t firmwareUpdateTotalBytes = 0;
uint32_t firmwareUpdateCurrentBytes = 0;
FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle;
BleAddress address;
AddressTypes addressType;
}; };
} }
} }

View file

@ -9,39 +9,37 @@ constexpr ble_uuid16_t CurrentTimeClient::ctsServiceUuid;
constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid; constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid;
namespace { namespace {
int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) { int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg) {
auto client = static_cast<CurrentTimeClient *>(arg); auto client = static_cast<CurrentTimeClient*>(arg);
return client->OnDiscoveryEvent(conn_handle, error, service); return client->OnDiscoveryEvent(conn_handle, error, service);
} }
int OnCurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error, int OnCurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle,
const struct ble_gatt_chr *chr, void *arg) { const struct ble_gatt_error* error,
auto client = static_cast<CurrentTimeClient *>(arg); const struct ble_gatt_chr* chr,
void* arg) {
auto client = static_cast<CurrentTimeClient*>(arg);
return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr); return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr);
} }
int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error* error, struct ble_gatt_attr* attr, void* arg) {
auto client = static_cast<CurrentTimeClient *>(arg); auto client = static_cast<CurrentTimeClient*>(arg);
return client->OnCurrentTimeReadResult(conn_handle, error, attr); return client->OnCurrentTimeReadResult(conn_handle, error, attr);
} }
} }
CurrentTimeClient::CurrentTimeClient(DateTime &dateTimeController) : dateTimeController{dateTimeController} { CurrentTimeClient::CurrentTimeClient(DateTime& dateTimeController) : dateTimeController {dateTimeController} {
} }
void CurrentTimeClient::Init() { void CurrentTimeClient::Init() {
} }
bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
const ble_gatt_svc *service) {
if (service == nullptr && error->status == BLE_HS_EDONE) { if (service == nullptr && error->status == BLE_HS_EDONE) {
if (isDiscovered) { if (isDiscovered) {
NRF_LOG_INFO("CTS found, starting characteristics discovery"); NRF_LOG_INFO("CTS found, starting characteristics discovery");
ble_gattc_disc_all_chrs(connectionHandle, ctsStartHandle, ctsEndHandle, ble_gattc_disc_all_chrs(connectionHandle, ctsStartHandle, ctsEndHandle, OnCurrentTimeCharacteristicDiscoveredCallback, this);
OnCurrentTimeCharacteristicDiscoveredCallback, this);
} else { } else {
NRF_LOG_INFO("CTS not found"); NRF_LOG_INFO("CTS not found");
onServiceDiscovered(connectionHandle); onServiceDiscovered(connectionHandle);
@ -49,7 +47,7 @@ bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_ga
return true; return true;
} }
if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ctsServiceUuid), &service->uuid.u) == 0) { if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ctsServiceUuid), &service->uuid.u) == 0) {
NRF_LOG_INFO("CTS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); NRF_LOG_INFO("CTS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
isDiscovered = true; isDiscovered = true;
ctsStartHandle = service->start_handle; ctsStartHandle = service->start_handle;
@ -59,8 +57,9 @@ bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_ga
return false; return false;
} }
int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error, int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle,
const ble_gatt_chr *characteristic) { const ble_gatt_error* error,
const ble_gatt_chr* characteristic) {
if (characteristic == nullptr && error->status == BLE_HS_EDONE) { if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
if (isCharacteristicDiscovered) { if (isCharacteristicDiscovered) {
NRF_LOG_INFO("CTS Characteristic discovery complete, fetching time"); NRF_LOG_INFO("CTS Characteristic discovery complete, fetching time");
@ -73,8 +72,7 @@ int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, cons
return 0; return 0;
} }
if (characteristic != nullptr && if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &currentTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
ble_uuid_cmp(((ble_uuid_t *) &currentTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle); NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle);
isCharacteristicDiscovered = true; isCharacteristicDiscovered = true;
currentTimeHandle = characteristic->val_handle; currentTimeHandle = characteristic->val_handle;
@ -82,17 +80,15 @@ int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, cons
return 0; return 0;
} }
int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_attr* attribute) {
const ble_gatt_attr *attribute) {
if (error->status == 0) { if (error->status == 0) {
// TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
CtsData result; CtsData result;
os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result); os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year, NRF_LOG_INFO(
result.month, result.dayofmonth, "Received data: %d-%d-%d %d:%d:%d", result.year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
result.hour, result.minute, result.second); dateTimeController.SetTime(
dateTimeController.SetTime(result.year, result.month, result.dayofmonth, result.year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
} else { } else {
NRF_LOG_INFO("Error retrieving current time: %d", error->status); NRF_LOG_INFO("Error retrieving current time: %d", error->status);
} }

View file

@ -8,54 +8,51 @@
#include "BleClient.h" #include "BleClient.h"
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class DateTime; class DateTime;
class CurrentTimeClient : public BleClient { class CurrentTimeClient : public BleClient {
public: public:
explicit CurrentTimeClient(DateTime& dateTimeController); explicit CurrentTimeClient(DateTime& dateTimeController);
void Init(); void Init();
void Reset(); void Reset();
bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service); bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service);
int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error, int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
const ble_gatt_chr *characteristic); int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_attr* attribute);
int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute); static constexpr const ble_uuid16_t* Uuid() {
static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; } return &CurrentTimeClient::ctsServiceUuid;
static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; } }
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override; static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() {
return &CurrentTimeClient::currentTimeCharacteristicUuid;
}
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
private: private:
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint16_t year; uint16_t year;
uint8_t month; uint8_t month;
uint8_t dayofmonth; uint8_t dayofmonth;
uint8_t hour; uint8_t hour;
uint8_t minute; uint8_t minute;
uint8_t second; uint8_t second;
uint8_t millis; uint8_t millis;
uint8_t reason; uint8_t reason;
} CtsData; } CtsData;
static constexpr uint16_t ctsServiceId {0x1805}; static constexpr uint16_t ctsServiceId {0x1805};
static constexpr uint16_t currentTimeCharacteristicId {0x2a2b}; static constexpr uint16_t currentTimeCharacteristicId {0x2a2b};
static constexpr ble_uuid16_t ctsServiceUuid { static constexpr ble_uuid16_t ctsServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsServiceId};
.u { .type = BLE_UUID_TYPE_16 }, static constexpr ble_uuid16_t currentTimeCharacteristicUuid {.u {.type = BLE_UUID_TYPE_16}, .value = currentTimeCharacteristicId};
.value = ctsServiceId
};
static constexpr ble_uuid16_t currentTimeCharacteristicUuid {
.u { .type = BLE_UUID_TYPE_16 },
.value = currentTimeCharacteristicId
};
DateTime& dateTimeController; DateTime& dateTimeController;
bool isDiscovered = false; bool isDiscovered = false;
uint16_t ctsStartHandle; uint16_t ctsStartHandle;
uint16_t ctsEndHandle; uint16_t ctsEndHandle;
bool isCharacteristicDiscovered = false; bool isCharacteristicDiscovered = false;
uint16_t currentTimeHandle; uint16_t currentTimeHandle;
std::function<void(uint16_t)> onServiceDiscovered; std::function<void(uint16_t)> onServiceDiscovered;
}; };
} }
} }

View file

@ -7,8 +7,7 @@ using namespace Pinetime::Controllers;
constexpr ble_uuid16_t CurrentTimeService::ctsUuid; constexpr ble_uuid16_t CurrentTimeService::ctsUuid;
constexpr ble_uuid16_t CurrentTimeService::ctChrUuid; constexpr ble_uuid16_t CurrentTimeService::ctChrUuid;
int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto cts = static_cast<CurrentTimeService*>(arg); auto cts = static_cast<CurrentTimeService*>(arg);
return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt); return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt);
} }
@ -22,22 +21,19 @@ void CurrentTimeService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, NRF_LOG_INFO("Setting time...");
struct ble_gatt_access_ctxt *ctxt) {
NRF_LOG_INFO("Setting time...");
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
CtsData result; CtsData result;
os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result); os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result);
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year, NRF_LOG_INFO(
result.month, result.dayofmonth, "Received data: %d-%d-%d %d:%d:%d", result.year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
result.hour, result.minute, result.second);
m_dateTimeController.SetTime(result.year, result.month, result.dayofmonth, m_dateTimeController.SetTime(
0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG)); result.year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
CtsData currentDateTime; CtsData currentDateTime;
@ -49,39 +45,26 @@ int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handl
currentDateTime.second = m_dateTimeController.Seconds(); currentDateTime.second = m_dateTimeController.Seconds();
currentDateTime.millis = 0; currentDateTime.millis = 0;
int res = os_mbuf_append(ctxt->om, &currentDateTime, sizeof(CtsData)); int res = os_mbuf_append(ctxt->om, &currentDateTime, sizeof(CtsData));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
return 0; return 0;
} }
CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) : CurrentTimeService::CurrentTimeService(DateTime& dateTimeController)
characteristicDefinition{ : characteristicDefinition {{.uuid = (ble_uuid_t*) &ctChrUuid,
{ .access_cb = CTSCallback,
.uuid = (ble_uuid_t *) &ctChrUuid,
.access_cb = CTSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
},
{
0
}
},
serviceDefinition{
{
/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &ctsUuid,
.characteristics = characteristicDefinition
},
{
0
},
}, m_dateTimeController{dateTimeController} {
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
{0}},
serviceDefinition {
{/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t*) &ctsUuid,
.characteristics = characteristicDefinition},
{0},
},
m_dateTimeController {dateTimeController} {
} }

View file

@ -12,42 +12,35 @@
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class CurrentTimeService { class CurrentTimeService {
public: public:
CurrentTimeService(DateTime &dateTimeController); CurrentTimeService(DateTime& dateTimeController);
void Init(); void Init();
int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
struct ble_gatt_access_ctxt *ctxt);
private: private:
static constexpr uint16_t ctsId {0x1805}; static constexpr uint16_t ctsId {0x1805};
static constexpr uint16_t ctsCharId {0x2a2b}; static constexpr uint16_t ctsCharId {0x2a2b};
static constexpr ble_uuid16_t ctsUuid { static constexpr ble_uuid16_t ctsUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsId};
.u { .type = BLE_UUID_TYPE_16 },
.value = ctsId
};
static constexpr ble_uuid16_t ctChrUuid { static constexpr ble_uuid16_t ctChrUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsCharId};
.u { .type = BLE_UUID_TYPE_16 },
.value = ctsCharId
};
struct ble_gatt_chr_def characteristicDefinition[2]; struct ble_gatt_chr_def characteristicDefinition[2];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint16_t year; uint16_t year;
uint8_t month; uint8_t month;
uint8_t dayofmonth; uint8_t dayofmonth;
uint8_t hour; uint8_t hour;
uint8_t minute; uint8_t minute;
uint8_t second; uint8_t second;
uint8_t millis; uint8_t millis;
uint8_t reason; uint8_t reason;
} CtsData; } CtsData;
DateTime &m_dateTimeController; DateTime& m_dateTimeController;
}; };
} }
} }

View file

@ -10,8 +10,7 @@ constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid;
constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid; constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid;
constexpr ble_uuid16_t DeviceInformationService::swRevisionUuid; constexpr ble_uuid16_t DeviceInformationService::swRevisionUuid;
int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto deviceInformationService = static_cast<DeviceInformationService*>(arg); auto deviceInformationService = static_cast<DeviceInformationService*>(arg);
return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt); return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt);
} }
@ -25,10 +24,8 @@ void DeviceInformationService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, const char* str;
struct ble_gatt_access_ctxt *ctxt) {
const char *str;
switch (ble_uuid_u16(ctxt->chr->uuid)) { switch (ble_uuid_u16(ctxt->chr->uuid)) {
case manufacturerNameId: case manufacturerNameId:
@ -57,60 +54,49 @@ int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
DeviceInformationService::DeviceInformationService() : DeviceInformationService::DeviceInformationService()
characteristicDefinition{ : characteristicDefinition {{
{ .uuid = (ble_uuid_t*) &manufacturerNameUuid,
.uuid = (ble_uuid_t *) &manufacturerNameUuid, .access_cb = DeviceInformationCallback,
.access_cb = DeviceInformationCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_READ,
.flags = BLE_GATT_CHR_F_READ, },
}, {
{ .uuid = (ble_uuid_t*) &modelNumberUuid,
.uuid = (ble_uuid_t *) &modelNumberUuid, .access_cb = DeviceInformationCallback,
.access_cb = DeviceInformationCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_READ,
.flags = BLE_GATT_CHR_F_READ, },
}, {
{ .uuid = (ble_uuid_t*) &serialNumberUuid,
.uuid = (ble_uuid_t *) &serialNumberUuid, .access_cb = DeviceInformationCallback,
.access_cb = DeviceInformationCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_READ,
.flags = BLE_GATT_CHR_F_READ, },
}, {
{ .uuid = (ble_uuid_t*) &fwRevisionUuid,
.uuid = (ble_uuid_t *) &fwRevisionUuid, .access_cb = DeviceInformationCallback,
.access_cb = DeviceInformationCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_READ,
.flags = BLE_GATT_CHR_F_READ, },
}, {
{ .uuid = (ble_uuid_t*) &hwRevisionUuid,
.uuid = (ble_uuid_t *) &hwRevisionUuid, .access_cb = DeviceInformationCallback,
.access_cb = DeviceInformationCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_READ,
.flags = BLE_GATT_CHR_F_READ, },
}, {
{ .uuid = (ble_uuid_t*) &swRevisionUuid,
.uuid = (ble_uuid_t *) &swRevisionUuid, .access_cb = DeviceInformationCallback,
.access_cb = DeviceInformationCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_READ,
.flags = BLE_GATT_CHR_F_READ, },
}, {0}},
{ serviceDefinition {
0 {/* Device Information Service */
} .type = BLE_GATT_SVC_TYPE_PRIMARY,
}, .uuid = (ble_uuid_t*) &deviceInfoUuid,
serviceDefinition{ .characteristics = characteristicDefinition},
{ {0},
/* Device Information Service */ } {
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &deviceInfoUuid,
.characteristics = characteristicDefinition
},
{
0
},
}
{
} }

View file

@ -9,69 +9,44 @@
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class DeviceInformationService { class DeviceInformationService {
public: public:
DeviceInformationService(); DeviceInformationService();
void Init(); void Init();
int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
struct ble_gatt_access_ctxt *ctxt);
private: private:
static constexpr uint16_t deviceInfoId {0x180a}; static constexpr uint16_t deviceInfoId {0x180a};
static constexpr uint16_t manufacturerNameId {0x2a29}; static constexpr uint16_t manufacturerNameId {0x2a29};
static constexpr uint16_t modelNumberId {0x2a24}; static constexpr uint16_t modelNumberId {0x2a24};
static constexpr uint16_t serialNumberId {0x2a25}; static constexpr uint16_t serialNumberId {0x2a25};
static constexpr uint16_t fwRevisionId {0x2a26}; static constexpr uint16_t fwRevisionId {0x2a26};
static constexpr uint16_t hwRevisionId {0x2a27}; static constexpr uint16_t hwRevisionId {0x2a27};
static constexpr uint16_t swRevisionId {0x2a28}; static constexpr uint16_t swRevisionId {0x2a28};
static constexpr const char* manufacturerName = "PINE64"; static constexpr const char* manufacturerName = "PINE64";
static constexpr const char* modelNumber = "PineTime"; static constexpr const char* modelNumber = "PineTime";
static constexpr const char* hwRevision = "1.0.0"; static constexpr const char* hwRevision = "1.0.0";
static constexpr const char* serialNumber = "0"; static constexpr const char* serialNumber = "0";
static constexpr const char* fwRevision = Version::VersionString(); static constexpr const char* fwRevision = Version::VersionString();
static constexpr const char* swRevision = "InfiniTime"; static constexpr const char* swRevision = "InfiniTime";
static constexpr ble_uuid16_t deviceInfoUuid {.u {.type = BLE_UUID_TYPE_16}, .value = deviceInfoId};
static constexpr ble_uuid16_t deviceInfoUuid { static constexpr ble_uuid16_t manufacturerNameUuid {.u {.type = BLE_UUID_TYPE_16}, .value = manufacturerNameId};
.u { .type = BLE_UUID_TYPE_16 },
.value = deviceInfoId
};
static constexpr ble_uuid16_t manufacturerNameUuid { static constexpr ble_uuid16_t modelNumberUuid {.u {.type = BLE_UUID_TYPE_16}, .value = modelNumberId};
.u { .type = BLE_UUID_TYPE_16 },
.value = manufacturerNameId
};
static constexpr ble_uuid16_t modelNumberUuid { static constexpr ble_uuid16_t serialNumberUuid {.u {.type = BLE_UUID_TYPE_16}, .value = serialNumberId};
.u { .type = BLE_UUID_TYPE_16 },
.value = modelNumberId
};
static constexpr ble_uuid16_t serialNumberUuid { static constexpr ble_uuid16_t fwRevisionUuid {.u {.type = BLE_UUID_TYPE_16}, .value = fwRevisionId};
.u { .type = BLE_UUID_TYPE_16 },
.value = serialNumberId
};
static constexpr ble_uuid16_t fwRevisionUuid { static constexpr ble_uuid16_t hwRevisionUuid {.u {.type = BLE_UUID_TYPE_16}, .value = hwRevisionId};
.u { .type = BLE_UUID_TYPE_16 },
.value = fwRevisionId
};
static constexpr ble_uuid16_t hwRevisionUuid {
.u {.type = BLE_UUID_TYPE_16},
.value = hwRevisionId
};
static constexpr ble_uuid16_t swRevisionUuid {
.u {.type = BLE_UUID_TYPE_16},
.value = swRevisionId
};
struct ble_gatt_chr_def characteristicDefinition[7];
struct ble_gatt_svc_def serviceDefinition[2];
static constexpr ble_uuid16_t swRevisionUuid {.u {.type = BLE_UUID_TYPE_16}, .value = swRevisionId};
struct ble_gatt_chr_def characteristicDefinition[7];
struct ble_gatt_svc_def serviceDefinition[2];
}; };
} }
} }

View file

@ -11,67 +11,60 @@ constexpr ble_uuid128_t DfuService::controlPointCharacteristicUuid;
constexpr ble_uuid128_t DfuService::revisionCharacteristicUuid; constexpr ble_uuid128_t DfuService::revisionCharacteristicUuid;
constexpr ble_uuid128_t DfuService::packetCharacteristicUuid; constexpr ble_uuid128_t DfuService::packetCharacteristicUuid;
int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle, int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
struct ble_gatt_access_ctxt *ctxt, void *arg) { auto dfuService = static_cast<DfuService*>(arg);
auto dfuService = static_cast<DfuService *>(arg);
return dfuService->OnServiceData(conn_handle, attr_handle, ctxt); return dfuService->OnServiceData(conn_handle, attr_handle, ctxt);
} }
void NotificationTimerCallback(TimerHandle_t xTimer) { void NotificationTimerCallback(TimerHandle_t xTimer) {
auto notificationManager = static_cast<DfuService::NotificationManager *>(pvTimerGetTimerID(xTimer)); auto notificationManager = static_cast<DfuService::NotificationManager*>(pvTimerGetTimerID(xTimer));
notificationManager->OnNotificationTimer(); notificationManager->OnNotificationTimer();
} }
void TimeoutTimerCallback(TimerHandle_t xTimer) { void TimeoutTimerCallback(TimerHandle_t xTimer) {
auto dfuService = static_cast<DfuService *>(pvTimerGetTimerID(xTimer)); auto dfuService = static_cast<DfuService*>(pvTimerGetTimerID(xTimer));
dfuService->OnTimeout(); dfuService->OnTimeout();
} }
DfuService::DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController, DfuService::DfuService(Pinetime::System::SystemTask& systemTask,
Pinetime::Drivers::SpiNorFlash &spiNorFlash) : Pinetime::Controllers::Ble& bleController,
systemTask{systemTask}, Pinetime::Drivers::SpiNorFlash& spiNorFlash)
bleController{bleController}, : systemTask {systemTask},
dfuImage{spiNorFlash}, bleController {bleController},
characteristicDefinition{ dfuImage {spiNorFlash},
{ characteristicDefinition {{
.uuid = (ble_uuid_t *) &packetCharacteristicUuid, .uuid = (ble_uuid_t*) &packetCharacteristicUuid,
.access_cb = DfuServiceCallback, .access_cb = DfuServiceCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE_NO_RSP, .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
.val_handle = nullptr, .val_handle = nullptr,
}, },
{ {
.uuid = (ble_uuid_t *) &controlPointCharacteristicUuid, .uuid = (ble_uuid_t*) &controlPointCharacteristicUuid,
.access_cb = DfuServiceCallback, .access_cb = DfuServiceCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
.val_handle = nullptr, .val_handle = nullptr,
}, },
{ {
.uuid = (ble_uuid_t *) &revisionCharacteristicUuid, .uuid = (ble_uuid_t*) &revisionCharacteristicUuid,
.access_cb = DfuServiceCallback, .access_cb = DfuServiceCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_READ, .flags = BLE_GATT_CHR_F_READ,
.val_handle = &revision, .val_handle = &revision,
}, },
{ {0}
0
}
}, },
serviceDefinition{ serviceDefinition {
{ {/* Device Information Service */
/* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY,
.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &serviceUuid,
.uuid = (ble_uuid_t *) &serviceUuid, .characteristics = characteristicDefinition},
.characteristics = characteristicDefinition {0},
}, } {
{ timeoutTimer = xTimerCreate("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
0
},
} {
timeoutTimer = xTimerCreate ("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
} }
void DfuService::Init() { void DfuService::Init() {
@ -83,55 +76,54 @@ void DfuService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) { int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
if(bleController.IsFirmwareUpdating()){ if (bleController.IsFirmwareUpdating()) {
xTimerStart(timeoutTimer, 0); xTimerStart(timeoutTimer, 0);
} }
ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &packetCharacteristicUuid, nullptr, &packetCharacteristicHandle);
ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &packetCharacteristicUuid, nullptr, ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &controlPointCharacteristicUuid, nullptr, &controlPointCharacteristicHandle);
&packetCharacteristicHandle); ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &revisionCharacteristicUuid, nullptr, &revisionCharacteristicHandle);
ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &controlPointCharacteristicUuid, nullptr,
&controlPointCharacteristicHandle);
ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &revisionCharacteristicUuid, nullptr,
&revisionCharacteristicHandle);
if (attributeHandle == packetCharacteristicHandle) { if (attributeHandle == packetCharacteristicHandle) {
if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
return WritePacketHandler(connectionHandle, context->om); return WritePacketHandler(connectionHandle, context->om);
else return 0; else
return 0;
} else if (attributeHandle == controlPointCharacteristicHandle) { } else if (attributeHandle == controlPointCharacteristicHandle) {
if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
return ControlPointHandler(connectionHandle, context->om); return ControlPointHandler(connectionHandle, context->om);
else return 0; else
return 0;
} else if (attributeHandle == revisionCharacteristicHandle) { } else if (attributeHandle == revisionCharacteristicHandle) {
if (context->op == BLE_GATT_ACCESS_OP_READ_CHR) if (context->op == BLE_GATT_ACCESS_OP_READ_CHR)
return SendDfuRevision(context->om); return SendDfuRevision(context->om);
else return 0; else
return 0;
} else { } else {
NRF_LOG_INFO("[DFU] Unknown Characteristic : %d", attributeHandle); NRF_LOG_INFO("[DFU] Unknown Characteristic : %d", attributeHandle);
return 0; return 0;
} }
} }
int DfuService::SendDfuRevision(os_mbuf *om) const { int DfuService::SendDfuRevision(os_mbuf* om) const {
int res = os_mbuf_append(om, &revision, sizeof(revision)); int res = os_mbuf_append(om, &revision, sizeof(revision));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) { int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
switch (state) { switch (state) {
case States::Start: { case States::Start: {
softdeviceSize = om->om_data[0] + (om->om_data[1] << 8) + (om->om_data[2] << 16) + (om->om_data[3] << 24); softdeviceSize = om->om_data[0] + (om->om_data[1] << 8) + (om->om_data[2] << 16) + (om->om_data[3] << 24);
bootloaderSize = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24); bootloaderSize = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
applicationSize = om->om_data[8] + (om->om_data[9] << 8) + (om->om_data[10] << 16) + (om->om_data[11] << 24); applicationSize = om->om_data[8] + (om->om_data[9] << 8) + (om->om_data[10] << 16) + (om->om_data[11] << 24);
bleController.FirmwareUpdateTotalBytes(applicationSize); bleController.FirmwareUpdateTotalBytes(applicationSize);
NRF_LOG_INFO("[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, NRF_LOG_INFO(
bootloaderSize, applicationSize); "[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, bootloaderSize, applicationSize);
dfuImage.Erase(); dfuImage.Erase();
uint8_t data[]{16, 1, 1}; uint8_t data[] {16, 1, 1};
notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3); notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
state = States::Init; state = States::Init;
} }
@ -139,19 +131,22 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
case States::Init: { case States::Init: {
uint16_t deviceType = om->om_data[0] + (om->om_data[1] << 8); uint16_t deviceType = om->om_data[0] + (om->om_data[1] << 8);
uint16_t deviceRevision = om->om_data[2] + (om->om_data[3] << 8); uint16_t deviceRevision = om->om_data[2] + (om->om_data[3] << 8);
uint32_t applicationVersion = uint32_t applicationVersion = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
uint16_t softdeviceArrayLength = om->om_data[8] + (om->om_data[9] << 8); uint16_t softdeviceArrayLength = om->om_data[8] + (om->om_data[9] << 8);
uint16_t sd[softdeviceArrayLength]; uint16_t sd[softdeviceArrayLength];
for (int i = 0; i < softdeviceArrayLength; i++) { for (int i = 0; i < softdeviceArrayLength; i++) {
sd[i] = om->om_data[10 + (i * 2)] + (om->om_data[10 + (i * 2) + 1] << 8); sd[i] = om->om_data[10 + (i * 2)] + (om->om_data[10 + (i * 2) + 1] << 8);
} }
expectedCrc = expectedCrc = om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
NRF_LOG_INFO( NRF_LOG_INFO(
"[DFU] -> Init data received : deviceType = %d, deviceRevision = %d, applicationVersion = %d, nb SD = %d, First SD = %d, CRC = %u", "[DFU] -> Init data received : deviceType = %d, deviceRevision = %d, applicationVersion = %d, nb SD = %d, First SD = %d, CRC = %u",
deviceType, deviceRevision, applicationVersion, softdeviceArrayLength, sd[0], expectedCrc); deviceType,
deviceRevision,
applicationVersion,
softdeviceArrayLength,
sd[0],
expectedCrc);
return 0; return 0;
} }
@ -163,16 +158,18 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
bleController.FirmwareUpdateCurrentBytes(bytesReceived); bleController.FirmwareUpdateCurrentBytes(bytesReceived);
if ((nbPacketReceived % nbPacketsToNotify) == 0 && bytesReceived != applicationSize) { if ((nbPacketReceived % nbPacketsToNotify) == 0 && bytesReceived != applicationSize) {
uint8_t data[5]{static_cast<uint8_t>(Opcodes::PacketReceiptNotification), uint8_t data[5] {static_cast<uint8_t>(Opcodes::PacketReceiptNotification),
(uint8_t) (bytesReceived & 0x000000FFu), (uint8_t) (bytesReceived >> 8u), (uint8_t) (bytesReceived & 0x000000FFu),
(uint8_t) (bytesReceived >> 16u), (uint8_t) (bytesReceived >> 24u)}; (uint8_t) (bytesReceived >> 8u),
(uint8_t) (bytesReceived >> 16u),
(uint8_t) (bytesReceived >> 24u)};
NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received", bytesReceived); NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received", bytesReceived);
notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 5); notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 5);
} }
if (dfuImage.IsComplete()) { if (dfuImage.IsComplete()) {
uint8_t data[3]{static_cast<uint8_t>(Opcodes::Response), uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage), static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage),
static_cast<uint8_t>(ErrorCodes::NoError)}; static_cast<uint8_t>(ErrorCodes::NoError)};
NRF_LOG_INFO("[DFU] -> Send packet notification : all bytes received!"); NRF_LOG_INFO("[DFU] -> Send packet notification : all bytes received!");
notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3); notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
state = States::Validate; state = States::Validate;
@ -186,7 +183,7 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
return 0; return 0;
} }
int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) { int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
auto opcode = static_cast<Opcodes>(om->om_data[0]); auto opcode = static_cast<Opcodes>(om->om_data[0]);
NRF_LOG_INFO("[DFU] -> ControlPointHandler"); NRF_LOG_INFO("[DFU] -> ControlPointHandler");
@ -214,8 +211,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType); NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType);
return 0; return 0;
} }
} } break;
break;
case Opcodes::InitDFUParameters: { case Opcodes::InitDFUParameters: {
if (state != States::Init) { if (state != States::Init) {
NRF_LOG_INFO("[DFU] -> Init DFU requested, but we are not in Init state"); NRF_LOG_INFO("[DFU] -> Init DFU requested, but we are not in Init state");
@ -225,11 +221,9 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
NRF_LOG_INFO("[DFU] -> Init DFU parameters %s", isInitComplete ? " complete" : " not complete"); NRF_LOG_INFO("[DFU] -> Init DFU parameters %s", isInitComplete ? " complete" : " not complete");
if (isInitComplete) { if (isInitComplete) {
uint8_t data[3] { uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
static_cast<uint8_t>(Opcodes::Response), static_cast<uint8_t>(Opcodes::InitDFUParameters),
static_cast<uint8_t>(Opcodes::InitDFUParameters), (isInitComplete ? uint8_t {1} : uint8_t {0})};
(isInitComplete ? uint8_t{1} : uint8_t{0})
};
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3); notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
return 0; return 0;
} }
@ -257,26 +251,22 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
NRF_LOG_INFO("[DFU] -> Validate firmware image requested -- %d", connectionHandle); NRF_LOG_INFO("[DFU] -> Validate firmware image requested -- %d", connectionHandle);
if(dfuImage.Validate()){ if (dfuImage.Validate()) {
state = States::Validated; state = States::Validated;
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated); bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
NRF_LOG_INFO("Image OK"); NRF_LOG_INFO("Image OK");
uint8_t data[3] { uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
static_cast<uint8_t>(Opcodes::Response), static_cast<uint8_t>(Opcodes::ValidateFirmware),
static_cast<uint8_t>(Opcodes::ValidateFirmware), static_cast<uint8_t>(ErrorCodes::NoError)};
static_cast<uint8_t>(ErrorCodes::NoError)
};
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3); notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
} else { } else {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error); bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
NRF_LOG_INFO("Image Error : bad CRC"); NRF_LOG_INFO("Image Error : bad CRC");
uint8_t data[3] { uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
static_cast<uint8_t>(Opcodes::Response), static_cast<uint8_t>(Opcodes::ValidateFirmware),
static_cast<uint8_t>(Opcodes::ValidateFirmware), static_cast<uint8_t>(ErrorCodes::CrcError)};
static_cast<uint8_t>(ErrorCodes::CrcError)
};
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3); notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
} }
@ -318,11 +308,11 @@ void DfuService::Reset() {
} }
DfuService::NotificationManager::NotificationManager() { DfuService::NotificationManager::NotificationManager() {
timer = xTimerCreate ("notificationTimer", 1000, pdFALSE, this, NotificationTimerCallback); timer = xTimerCreate("notificationTimer", 1000, pdFALSE, this, NotificationTimerCallback);
} }
bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t s) { bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t* data, size_t s) {
if(size != 0 || s > 10) if (size != 0 || s > 10)
return false; return false;
connectionHandle = connection; connectionHandle = connection;
@ -334,14 +324,14 @@ bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t ch
} }
void DfuService::NotificationManager::OnNotificationTimer() { void DfuService::NotificationManager::OnNotificationTimer() {
if(size > 0) { if (size > 0) {
Send(connectionHandle, characteristicHandle, buffer, size); Send(connectionHandle, characteristicHandle, buffer, size);
size = 0; size = 0;
} }
} }
void DfuService::NotificationManager::Send(uint16_t connection, uint16_t charactHandle, const uint8_t *data, const size_t s) { void DfuService::NotificationManager::Send(uint16_t connection, uint16_t charactHandle, const uint8_t* data, const size_t s) {
auto *om = ble_hs_mbuf_from_flat(data, s); auto* om = ble_hs_mbuf_from_flat(data, s);
auto ret = ble_gattc_notify_custom(connection, charactHandle, om); auto ret = ble_gattc_notify_custom(connection, charactHandle, om);
ASSERT(ret == 0); ASSERT(ret == 0);
} }
@ -354,27 +344,29 @@ void DfuService::NotificationManager::Reset() {
} }
void DfuService::DfuImage::Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc) { void DfuService::DfuImage::Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc) {
if(chunkSize != 20) return; if (chunkSize != 20)
return;
this->chunkSize = chunkSize; this->chunkSize = chunkSize;
this->totalSize = totalSize; this->totalSize = totalSize;
this->expectedCrc = expectedCrc; this->expectedCrc = expectedCrc;
this->ready = true; this->ready = true;
} }
void DfuService::DfuImage::Append(uint8_t *data, size_t size) { void DfuService::DfuImage::Append(uint8_t* data, size_t size) {
if(!ready) return; if (!ready)
return;
ASSERT(size <= 20); ASSERT(size <= 20);
std::memcpy(tempBuffer + bufferWriteIndex, data, size); std::memcpy(tempBuffer + bufferWriteIndex, data, size);
bufferWriteIndex += size; bufferWriteIndex += size;
if(bufferWriteIndex == bufferSize) { if (bufferWriteIndex == bufferSize) {
spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex); spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
totalWriteIndex += bufferWriteIndex; totalWriteIndex += bufferWriteIndex;
bufferWriteIndex = 0; bufferWriteIndex = 0;
} }
if(bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) { if (bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) {
spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex); spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
totalWriteIndex += bufferWriteIndex; totalWriteIndex += bufferWriteIndex;
if (totalSize < maxSize) if (totalSize < maxSize)
@ -383,15 +375,16 @@ void DfuService::DfuImage::Append(uint8_t *data, size_t size) {
} }
void DfuService::DfuImage::WriteMagicNumber() { void DfuService::DfuImage::WriteMagicNumber() {
uint32_t magic[4] = { // TODO When this variable is a static constexpr, the values written to the memory are not correct. Why? uint32_t magic[4] = {
0xf395c277, // TODO When this variable is a static constexpr, the values written to the memory are not correct. Why?
0x7fefd260, 0xf395c277,
0x0f505235, 0x7fefd260,
0x8079b62c, 0x0f505235,
0x8079b62c,
}; };
uint32_t offset = writeOffset + (maxSize - (4 * sizeof(uint32_t))); uint32_t offset = writeOffset + (maxSize - (4 * sizeof(uint32_t)));
spiNorFlash.Write(offset, reinterpret_cast<const uint8_t *>(magic), 4 * sizeof(uint32_t)); spiNorFlash.Write(offset, reinterpret_cast<const uint8_t*>(magic), 4 * sizeof(uint32_t));
} }
void DfuService::DfuImage::Erase() { void DfuService::DfuImage::Erase() {
@ -421,7 +414,7 @@ bool DfuService::DfuImage::Validate() {
return (crc == expectedCrc); return (crc == expectedCrc);
} }
uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc) { uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t const* p_crc) {
uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc; uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
for (uint32_t i = 0; i < size; i++) { for (uint32_t i = 0; i < size; i++) {
@ -436,6 +429,7 @@ uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const *p_data, uint32_t size,
} }
bool DfuService::DfuImage::IsComplete() { bool DfuService::DfuImage::IsComplete() {
if(!ready) return false; if (!ready)
return false;
return totalWriteIndex == totalSize; return totalWriteIndex == totalSize;
} }

View file

@ -20,146 +20,139 @@ namespace Pinetime {
class Ble; class Ble;
class DfuService { class DfuService {
public:
DfuService(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash);
void Init();
int OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void OnTimeout();
void Reset();
class NotificationManager {
public: public:
DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController, NotificationManager();
Pinetime::Drivers::SpiNorFlash &spiNorFlash); bool AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t* data, size_t size);
void Init(); void Send(uint16_t connection, uint16_t characteristicHandle, const uint8_t* data, const size_t s);
int OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
void OnTimeout();
void Reset();
class NotificationManager {
public:
NotificationManager();
bool AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t size);
void Send(uint16_t connection, uint16_t characteristicHandle, const uint8_t *data, const size_t s);
private:
TimerHandle_t timer;
uint16_t connectionHandle = 0;
uint16_t characteristicHandle = 0;
size_t size = 0;
uint8_t buffer[10];
public:
void OnNotificationTimer();
void Reset();
};
class DfuImage {
public:
DfuImage(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash{spiNorFlash} {}
void Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc);
void Erase();
void Append(uint8_t* data, size_t size);
bool Validate();
bool IsComplete();
private:
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
static constexpr size_t bufferSize = 200;
bool ready = false;
size_t chunkSize = 0;
size_t totalSize = 0;
size_t maxSize = 475136;
size_t bufferWriteIndex = 0;
size_t totalWriteIndex = 0;
static constexpr size_t writeOffset = 0x40000;
uint8_t tempBuffer[bufferSize];
uint16_t expectedCrc = 0;
void WriteMagicNumber();
uint16_t ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc);
};
private: private:
Pinetime::System::SystemTask &systemTask; TimerHandle_t timer;
Pinetime::Controllers::Ble &bleController; uint16_t connectionHandle = 0;
DfuImage dfuImage; uint16_t characteristicHandle = 0;
NotificationManager notificationManager; size_t size = 0;
uint8_t buffer[10];
static constexpr uint16_t dfuServiceId{0x1530}; public:
static constexpr uint16_t packetCharacteristicId{0x1532}; void OnNotificationTimer();
static constexpr uint16_t controlPointCharacteristicId{0x1531}; void Reset();
static constexpr uint16_t revisionCharacteristicId{0x1534}; };
class DfuImage {
public:
DfuImage(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash {spiNorFlash} {
}
void Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc);
void Erase();
void Append(uint8_t* data, size_t size);
bool Validate();
bool IsComplete();
uint16_t revision{0x0008}; private:
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
static constexpr ble_uuid128_t serviceUuid{ static constexpr size_t bufferSize = 200;
.u {.type = BLE_UUID_TYPE_128}, bool ready = false;
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, size_t chunkSize = 0;
0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00} size_t totalSize = 0;
}; size_t maxSize = 475136;
size_t bufferWriteIndex = 0;
static constexpr ble_uuid128_t packetCharacteristicUuid{ size_t totalWriteIndex = 0;
.u {.type = BLE_UUID_TYPE_128}, static constexpr size_t writeOffset = 0x40000;
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, uint8_t tempBuffer[bufferSize];
0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00}
};
static constexpr ble_uuid128_t controlPointCharacteristicUuid{
.u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
0xDE, 0xEF, 0x12, 0x12, 0x31, 0x15, 0x00, 0x00}
};
static constexpr ble_uuid128_t revisionCharacteristicUuid{
.u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
0xDE, 0xEF, 0x12, 0x12, 0x34, 0x15, 0x00, 0x00}
};
struct ble_gatt_chr_def characteristicDefinition[4];
struct ble_gatt_svc_def serviceDefinition[2];
uint16_t packetCharacteristicHandle;
uint16_t controlPointCharacteristicHandle;
uint16_t revisionCharacteristicHandle;
enum class States : uint8_t {
Idle, Init, Start, Data, Validate, Validated
};
States state = States::Idle;
enum class ImageTypes : uint8_t {
NoImage = 0x00,
SoftDevice = 0x01,
Bootloader = 0x02,
SoftDeviceAndBootloader = 0x03,
Application = 0x04
};
enum class Opcodes : uint8_t {
StartDFU = 0x01,
InitDFUParameters = 0x02,
ReceiveFirmwareImage = 0x03,
ValidateFirmware = 0x04,
ActivateImageAndReset = 0x05,
PacketReceiptNotificationRequest = 0x08,
Response = 0x10,
PacketReceiptNotification = 0x11
};
enum class ErrorCodes {
NoError = 0x01,
InvalidState = 0x02,
NotSupported = 0x03,
DataSizeExceedsLimits = 0x04,
CrcError = 0x05,
OperationFailed = 0x06
};
uint8_t nbPacketsToNotify = 0;
uint32_t nbPacketReceived = 0;
uint32_t bytesReceived = 0;
uint32_t softdeviceSize = 0;
uint32_t bootloaderSize = 0;
uint32_t applicationSize = 0;
uint16_t expectedCrc = 0; uint16_t expectedCrc = 0;
int SendDfuRevision(os_mbuf *om) const; void WriteMagicNumber();
int WritePacketHandler(uint16_t connectionHandle, os_mbuf *om); uint16_t ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t const* p_crc);
int ControlPointHandler(uint16_t connectionHandle, os_mbuf *om); };
TimerHandle_t timeoutTimer; private:
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::Ble& bleController;
DfuImage dfuImage;
NotificationManager notificationManager;
static constexpr uint16_t dfuServiceId {0x1530};
static constexpr uint16_t packetCharacteristicId {0x1532};
static constexpr uint16_t controlPointCharacteristicId {0x1531};
static constexpr uint16_t revisionCharacteristicId {0x1534};
uint16_t revision {0x0008};
static constexpr ble_uuid128_t serviceUuid {
.u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
static constexpr ble_uuid128_t packetCharacteristicUuid {
.u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00}};
static constexpr ble_uuid128_t controlPointCharacteristicUuid {
.u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x31, 0x15, 0x00, 0x00}};
static constexpr ble_uuid128_t revisionCharacteristicUuid {
.u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x34, 0x15, 0x00, 0x00}};
struct ble_gatt_chr_def characteristicDefinition[4];
struct ble_gatt_svc_def serviceDefinition[2];
uint16_t packetCharacteristicHandle;
uint16_t controlPointCharacteristicHandle;
uint16_t revisionCharacteristicHandle;
enum class States : uint8_t { Idle, Init, Start, Data, Validate, Validated };
States state = States::Idle;
enum class ImageTypes : uint8_t {
NoImage = 0x00,
SoftDevice = 0x01,
Bootloader = 0x02,
SoftDeviceAndBootloader = 0x03,
Application = 0x04
};
enum class Opcodes : uint8_t {
StartDFU = 0x01,
InitDFUParameters = 0x02,
ReceiveFirmwareImage = 0x03,
ValidateFirmware = 0x04,
ActivateImageAndReset = 0x05,
PacketReceiptNotificationRequest = 0x08,
Response = 0x10,
PacketReceiptNotification = 0x11
};
enum class ErrorCodes {
NoError = 0x01,
InvalidState = 0x02,
NotSupported = 0x03,
DataSizeExceedsLimits = 0x04,
CrcError = 0x05,
OperationFailed = 0x06
};
uint8_t nbPacketsToNotify = 0;
uint32_t nbPacketReceived = 0;
uint32_t bytesReceived = 0;
uint32_t softdeviceSize = 0;
uint32_t bootloaderSize = 0;
uint32_t applicationSize = 0;
uint16_t expectedCrc = 0;
int SendDfuRevision(os_mbuf* om) const;
int WritePacketHandler(uint16_t connectionHandle, os_mbuf* om);
int ControlPointHandler(uint16_t connectionHandle, os_mbuf* om);
TimerHandle_t timeoutTimer;
}; };
} }
} }

View file

@ -8,39 +8,29 @@ constexpr ble_uuid16_t HeartRateService::heartRateServiceUuid;
constexpr ble_uuid16_t HeartRateService::heartRateMeasurementUuid; constexpr ble_uuid16_t HeartRateService::heartRateMeasurementUuid;
namespace { namespace {
int HeartRateServiceServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { int HeartRateServiceServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto* heartRateService = static_cast<HeartRateService*>(arg); auto* heartRateService = static_cast<HeartRateService*>(arg);
return heartRateService->OnHeartRateRequested(conn_handle, attr_handle, ctxt); return heartRateService->OnHeartRateRequested(conn_handle, attr_handle, ctxt);
} }
} }
// TODO Refactoring - remove dependency to SystemTask // TODO Refactoring - remove dependency to SystemTask
HeartRateService::HeartRateService(Pinetime::System::SystemTask &system, Controllers::HeartRateController& heartRateController) : HeartRateService::HeartRateService(Pinetime::System::SystemTask& system, Controllers::HeartRateController& heartRateController)
system{system}, : system {system},
heartRateController{heartRateController}, heartRateController {heartRateController},
characteristicDefinition{ characteristicDefinition {{.uuid = (ble_uuid_t*) &heartRateMeasurementUuid,
{ .access_cb = HeartRateServiceServiceCallback,
.uuid = (ble_uuid_t *) &heartRateMeasurementUuid, .arg = this,
.access_cb = HeartRateServiceServiceCallback, .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.arg = this, .val_handle = &heartRateMeasurementHandle},
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, {0}},
.val_handle = &heartRateMeasurementHandle serviceDefinition {
}, {/* Device Information Service */
{ .type = BLE_GATT_SVC_TYPE_PRIMARY,
0 .uuid = (ble_uuid_t*) &heartRateServiceUuid,
} .characteristics = characteristicDefinition},
}, {0},
serviceDefinition{ } {
{
/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &heartRateServiceUuid,
.characteristics = characteristicDefinition
},
{
0
},
}{
// TODO refactor to prevent this loop dependency (service depends on controller and controller depends on service) // TODO refactor to prevent this loop dependency (service depends on controller and controller depends on service)
heartRateController.SetService(this); heartRateController.SetService(this);
} }
@ -54,9 +44,8 @@ void HeartRateService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
int HeartRateService::OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, int HeartRateService::OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
ble_gatt_access_ctxt *context) { if (attributeHandle == heartRateMeasurementHandle) {
if(attributeHandle == heartRateMeasurementHandle) {
NRF_LOG_INFO("HEARTRATE : handle = %d", heartRateMeasurementHandle); NRF_LOG_INFO("HEARTRATE : handle = %d", heartRateMeasurementHandle);
uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value
@ -68,7 +57,7 @@ int HeartRateService::OnHeartRateRequested(uint16_t connectionHandle, uint16_t a
void HeartRateService::OnNewHeartRateValue(uint8_t heartRateValue) { void HeartRateService::OnNewHeartRateValue(uint8_t heartRateValue) {
uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value
auto *om = ble_hs_mbuf_from_flat(buffer, 2); auto* om = ble_hs_mbuf_from_flat(buffer, 2);
uint16_t connectionHandle = system.nimble().connHandle(); uint16_t connectionHandle = system.nimble().connHandle();

View file

@ -12,33 +12,26 @@ namespace Pinetime {
namespace Controllers { namespace Controllers {
class HeartRateController; class HeartRateController;
class HeartRateService { class HeartRateService {
public: public:
HeartRateService(Pinetime::System::SystemTask &system, Controllers::HeartRateController& heartRateController); HeartRateService(Pinetime::System::SystemTask& system, Controllers::HeartRateController& heartRateController);
void Init(); void Init();
int OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context); int OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void OnNewHeartRateValue(uint8_t hearRateValue); void OnNewHeartRateValue(uint8_t hearRateValue);
private: private:
Pinetime::System::SystemTask &system; Pinetime::System::SystemTask& system;
Controllers::HeartRateController& heartRateController; Controllers::HeartRateController& heartRateController;
static constexpr uint16_t heartRateServiceId {0x180D}; static constexpr uint16_t heartRateServiceId {0x180D};
static constexpr uint16_t heartRateMeasurementId {0x2A37}; static constexpr uint16_t heartRateMeasurementId {0x2A37};
static constexpr ble_uuid16_t heartRateServiceUuid { static constexpr ble_uuid16_t heartRateServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateServiceId};
.u {.type = BLE_UUID_TYPE_16},
.value = heartRateServiceId
};
static constexpr ble_uuid16_t heartRateMeasurementUuid { static constexpr ble_uuid16_t heartRateMeasurementUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateMeasurementId};
.u {.type = BLE_UUID_TYPE_16},
.value = heartRateMeasurementId
};
struct ble_gatt_chr_def characteristicDefinition[3]; struct ble_gatt_chr_def characteristicDefinition[3];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
uint16_t heartRateMeasurementHandle;
uint16_t heartRateMeasurementHandle;
}; };
} }
} }

View file

@ -9,49 +9,42 @@ constexpr ble_uuid16_t ImmediateAlertService::immediateAlertServiceUuid;
constexpr ble_uuid16_t ImmediateAlertService::alertLevelUuid; constexpr ble_uuid16_t ImmediateAlertService::alertLevelUuid;
namespace { namespace {
int AlertLevelCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { int AlertLevelCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto *immediateAlertService = static_cast<ImmediateAlertService *>(arg); auto* immediateAlertService = static_cast<ImmediateAlertService*>(arg);
return immediateAlertService->OnAlertLevelChanged(conn_handle, attr_handle, ctxt); return immediateAlertService->OnAlertLevelChanged(conn_handle, attr_handle, ctxt);
} }
const char* ToString(ImmediateAlertService::Levels level) { const char* ToString(ImmediateAlertService::Levels level) {
switch (level) { switch (level) {
case ImmediateAlertService::Levels::NoAlert: return "Alert : None"; case ImmediateAlertService::Levels::NoAlert:
case ImmediateAlertService::Levels::HighAlert: return "Alert : High"; return "Alert : None";
case ImmediateAlertService::Levels::MildAlert: return "Alert : Mild"; case ImmediateAlertService::Levels::HighAlert:
default: return ""; return "Alert : High";
case ImmediateAlertService::Levels::MildAlert:
return "Alert : Mild";
default:
return "";
} }
} }
} }
ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask &systemTask, ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager &notificationManager) : Pinetime::Controllers::NotificationManager& notificationManager)
systemTask{systemTask}, : systemTask {systemTask},
notificationManager{notificationManager}, notificationManager {notificationManager},
characteristicDefinition{ characteristicDefinition {{.uuid = (ble_uuid_t*) &alertLevelUuid,
{ .access_cb = AlertLevelCallback,
.uuid = (ble_uuid_t *) &alertLevelUuid, .arg = this,
.access_cb = AlertLevelCallback, .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
.arg = this, .val_handle = &alertLevelHandle},
.flags = BLE_GATT_CHR_F_WRITE_NO_RSP, {0}},
.val_handle = &alertLevelHandle serviceDefinition {
}, {/* Device Information Service */
{ .type = BLE_GATT_SVC_TYPE_PRIMARY,
0 .uuid = (ble_uuid_t*) &immediateAlertServiceUuid,
} .characteristics = characteristicDefinition},
}, {0},
serviceDefinition{ } {
{
/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &immediateAlertServiceUuid,
.characteristics = characteristicDefinition
},
{
0
},
}{
} }
void ImmediateAlertService::Init() { void ImmediateAlertService::Init() {
@ -63,9 +56,9 @@ void ImmediateAlertService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) { int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
if(attributeHandle == alertLevelHandle) { if (attributeHandle == alertLevelHandle) {
if(context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
auto alertLevel = static_cast<Levels>(context->om->om_data[0]); auto alertLevel = static_cast<Levels>(context->om->om_data[0]);
auto* alertString = ToString(alertLevel); auto* alertString = ToString(alertLevel);

View file

@ -12,39 +12,28 @@ namespace Pinetime {
namespace Controllers { namespace Controllers {
class NotificationManager; class NotificationManager;
class ImmediateAlertService { class ImmediateAlertService {
public: public:
enum class Levels : uint8_t { enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 };
NoAlert = 0,
MildAlert = 1,
HighAlert = 2
};
ImmediateAlertService(Pinetime::System::SystemTask &systemTask, ImmediateAlertService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager);
Pinetime::Controllers::NotificationManager &notificationManager); void Init();
void Init(); int OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
int OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
private: private:
Pinetime::System::SystemTask& systemTask; Pinetime::System::SystemTask& systemTask;
NotificationManager& notificationManager; NotificationManager& notificationManager;
static constexpr uint16_t immediateAlertServiceId {0x1802}; static constexpr uint16_t immediateAlertServiceId {0x1802};
static constexpr uint16_t alertLevelId {0x2A06}; static constexpr uint16_t alertLevelId {0x2A06};
static constexpr ble_uuid16_t immediateAlertServiceUuid { static constexpr ble_uuid16_t immediateAlertServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = immediateAlertServiceId};
.u {.type = BLE_UUID_TYPE_16},
.value = immediateAlertServiceId
};
static constexpr ble_uuid16_t alertLevelUuid { static constexpr ble_uuid16_t alertLevelUuid {.u {.type = BLE_UUID_TYPE_16}, .value = alertLevelId};
.u {.type = BLE_UUID_TYPE_16},
.value = alertLevelId
};
struct ble_gatt_chr_def characteristicDefinition[3]; struct ble_gatt_chr_def characteristicDefinition[3];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
uint16_t alertLevelHandle; uint16_t alertLevelHandle;
}; };
} }
} }

View file

@ -18,12 +18,12 @@
#include "MusicService.h" #include "MusicService.h"
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto musicService = static_cast<Pinetime::Controllers::MusicService *>(arg); auto musicService = static_cast<Pinetime::Controllers::MusicService*>(arg);
return musicService->OnCommand(conn_handle, attr_handle, ctxt); return musicService->OnCommand(conn_handle, attr_handle, ctxt);
} }
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &system) : m_system(system) { Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask& system) : m_system(system) {
msUuid.value[14] = msId[0]; msUuid.value[14] = msId[0];
msUuid.value[15] = msId[1]; msUuid.value[15] = msId[1];
@ -86,82 +86,51 @@ Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &
msShuffleCharUuid.value[13] = msShuffleCharId[1]; msShuffleCharUuid.value[13] = msShuffleCharId[1];
msShuffleCharUuid.value[14] = msId[0]; msShuffleCharUuid.value[14] = msId[0];
msShuffleCharUuid.value[15] = msId[1]; msShuffleCharUuid.value[15] = msId[1];
characteristicDefinition[0] = {.uuid = (ble_uuid_t *) (&msEventCharUuid), characteristicDefinition[0] = {.uuid = (ble_uuid_t*) (&msEventCharUuid),
.access_cb = MSCallback, .access_cb = MSCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY, .flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &eventHandle .val_handle = &eventHandle};
}; characteristicDefinition[1] = {
characteristicDefinition[1] = {.uuid = (ble_uuid_t *) (&msStatusCharUuid), .uuid = (ble_uuid_t*) (&msStatusCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.access_cb = MSCallback, characteristicDefinition[2] = {
.arg = this, .uuid = (ble_uuid_t*) (&msTrackCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ characteristicDefinition[3] = {
}; .uuid = (ble_uuid_t*) (&msArtistCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[2] = {.uuid = (ble_uuid_t *) (&msTrackCharUuid), characteristicDefinition[4] = {
.access_cb = MSCallback, .uuid = (ble_uuid_t*) (&msAlbumCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.arg = this, characteristicDefinition[5] = {
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ .uuid = (ble_uuid_t*) (&msPositionCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
}; characteristicDefinition[6] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid),
characteristicDefinition[3] = {.uuid = (ble_uuid_t *) (&msArtistCharUuid), .access_cb = MSCallback,
.access_cb = MSCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ characteristicDefinition[7] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid),
}; .access_cb = MSCallback,
characteristicDefinition[4] = {.uuid = (ble_uuid_t *) (&msAlbumCharUuid), .arg = this,
.access_cb = MSCallback, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.arg = this, characteristicDefinition[8] = {.uuid = (ble_uuid_t*) (&msTrackNumberCharUuid),
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ .access_cb = MSCallback,
}; .arg = this,
characteristicDefinition[5] = {.uuid = (ble_uuid_t *) (&msPositionCharUuid), .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.access_cb = MSCallback, characteristicDefinition[9] = {.uuid = (ble_uuid_t*) (&msTrackTotalCharUuid),
.arg = this, .access_cb = MSCallback,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ .arg = this,
}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[6] = {.uuid = (ble_uuid_t *) (&msTotalLengthCharUuid), characteristicDefinition[10] = {.uuid = (ble_uuid_t*) (&msPlaybackSpeedCharUuid),
.access_cb = MSCallback, .access_cb = MSCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
}; characteristicDefinition[11] = {
characteristicDefinition[7] = {.uuid = (ble_uuid_t *) (&msTotalLengthCharUuid), .uuid = (ble_uuid_t*) (&msRepeatCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.access_cb = MSCallback, characteristicDefinition[12] = {
.arg = this, .uuid = (ble_uuid_t*) (&msShuffleCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[8] = {.uuid = (ble_uuid_t *) (&msTrackNumberCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[9] = {.uuid = (ble_uuid_t *) (&msTrackTotalCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[10] = {.uuid = (ble_uuid_t *) (&msPlaybackSpeedCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[11] = {.uuid = (ble_uuid_t *) (&msRepeatCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[12] = {.uuid = (ble_uuid_t *) (&msShuffleCharUuid),
.access_cb = MSCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[13] = {0}; characteristicDefinition[13] = {0};
serviceDefinition[0] = { serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &msUuid, .characteristics = characteristicDefinition};
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &msUuid,
.characteristics = characteristicDefinition
};
serviceDefinition[1] = {0}; serviceDefinition[1] = {0};
artistName = "Waiting for"; artistName = "Waiting for";
albumName = ""; albumName = "";
trackName = "track information.."; trackName = "track information..";
@ -177,41 +146,40 @@ void Pinetime::Controllers::MusicService::Init() {
int res = 0; int res = 0;
res = ble_gatts_count_cfg(serviceDefinition); res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0); ASSERT(res == 0);
res = ble_gatts_add_svcs(serviceDefinition); res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0); ASSERT(res == 0);
} }
int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
struct ble_gatt_access_ctxt *ctxt) {
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
size_t notifSize = OS_MBUF_PKTLEN(ctxt->om); size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
uint8_t data[notifSize + 1]; uint8_t data[notifSize + 1];
data[notifSize] = '\0'; data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data); os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char *s = (char *) &data[0]; char* s = (char*) &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msArtistCharUuid) == 0) { if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msArtistCharUuid) == 0) {
artistName = s; artistName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTrackCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackCharUuid) == 0) {
trackName = s; trackName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msAlbumCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msAlbumCharUuid) == 0) {
albumName = s; albumName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msStatusCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msStatusCharUuid) == 0) {
playing = s[0]; playing = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msRepeatCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msRepeatCharUuid) == 0) {
repeat = s[0]; repeat = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msShuffleCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msShuffleCharUuid) == 0) {
shuffle = s[0]; shuffle = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msPositionCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msPositionCharUuid) == 0) {
trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTotalLengthCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTotalLengthCharUuid) == 0) {
trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTrackNumberCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackNumberCharUuid) == 0) {
trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msTrackTotalCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackTotalCharUuid) == 0) {
tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msPlaybackSpeedCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msPlaybackSpeedCharUuid) == 0) {
playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f; playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f;
} }
} }
@ -239,14 +207,14 @@ float Pinetime::Controllers::MusicService::getPlaybackSpeed() {
} }
void Pinetime::Controllers::MusicService::event(char event) { void Pinetime::Controllers::MusicService::event(char event) {
auto *om = ble_hs_mbuf_from_flat(&event, 1); auto* om = ble_hs_mbuf_from_flat(&event, 1);
uint16_t connectionHandle = m_system.nimble().connHandle(); uint16_t connectionHandle = m_system.nimble().connHandle();
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return; return;
} }
ble_gattc_notify_custom(connectionHandle, eventHandle, om); ble_gattc_notify_custom(connectionHandle, eventHandle, om);
} }
@ -257,4 +225,3 @@ int Pinetime::Controllers::MusicService::getProgress() {
int Pinetime::Controllers::MusicService::getTrackLength() { int Pinetime::Controllers::MusicService::getTrackLength() {
return trackLength; return trackLength;
} }

View file

@ -26,40 +26,40 @@
#undef max #undef max
#undef min #undef min
//00000000-78fc-48fe-8e23-433b3a1942d0 // 00000000-78fc-48fe-8e23-433b3a1942d0
#define MUSIC_SERVICE_UUID_BASE {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00} #define MUSIC_SERVICE_UUID_BASE \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 }
namespace Pinetime { namespace Pinetime {
namespace System { namespace System {
class SystemTask; class SystemTask;
} }
namespace Controllers { namespace Controllers {
class MusicService { class MusicService {
public: public:
explicit MusicService(Pinetime::System::SystemTask &system); explicit MusicService(Pinetime::System::SystemTask& system);
void Init(); void Init();
int OnCommand(uint16_t conn_handle, uint16_t attr_handle, int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
struct ble_gatt_access_ctxt *ctxt);
void event(char event); void event(char event);
std::string getArtist(); std::string getArtist();
std::string getTrack(); std::string getTrack();
std::string getAlbum(); std::string getAlbum();
int getProgress(); int getProgress();
int getTrackLength(); int getTrackLength();
float getPlaybackSpeed(); float getPlaybackSpeed();
bool isPlaying(); bool isPlaying();
static const char EVENT_MUSIC_OPEN = 0xe0; static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00; static const char EVENT_MUSIC_PLAY = 0x00;
static const char EVENT_MUSIC_PAUSE = 0x01; static const char EVENT_MUSIC_PAUSE = 0x01;
@ -67,11 +67,9 @@ namespace Pinetime {
static const char EVENT_MUSIC_PREV = 0x04; static const char EVENT_MUSIC_PREV = 0x04;
static const char EVENT_MUSIC_VOLUP = 0x05; static const char EVENT_MUSIC_VOLUP = 0x05;
static const char EVENT_MUSIC_VOLDOWN = 0x06; static const char EVENT_MUSIC_VOLDOWN = 0x06;
enum MusicStatus { enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
NotPlaying = 0x00,
Playing = 0x01
};
private: private:
static constexpr uint8_t msId[2] = {0x00, 0x00}; static constexpr uint8_t msId[2] = {0x00, 0x00};
static constexpr uint8_t msEventCharId[2] = {0x01, 0x00}; static constexpr uint8_t msEventCharId[2] = {0x01, 0x00};
@ -86,84 +84,44 @@ namespace Pinetime {
static constexpr uint8_t msPlaybackSpeedCharId[2] = {0x0a, 0x00}; static constexpr uint8_t msPlaybackSpeedCharId[2] = {0x0a, 0x00};
static constexpr uint8_t msRepeatCharId[2] = {0x0b, 0x00}; static constexpr uint8_t msRepeatCharId[2] = {0x0b, 0x00};
static constexpr uint8_t msShuffleCharId[2] = {0x0c, 0x00}; static constexpr uint8_t msShuffleCharId[2] = {0x0c, 0x00};
ble_uuid128_t msUuid{ ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE ble_uuid128_t msEventCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
}; ble_uuid128_t msStatusCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msArtistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msEventCharUuid{ ble_uuid128_t msTrackCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
.u = {.type = BLE_UUID_TYPE_128}, ble_uuid128_t msAlbumCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
.value = MUSIC_SERVICE_UUID_BASE ble_uuid128_t msPositionCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
}; ble_uuid128_t msTotalLengthCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msStatusCharUuid{ ble_uuid128_t msTrackNumberCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
.u = {.type = BLE_UUID_TYPE_128}, ble_uuid128_t msTrackTotalCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
.value = MUSIC_SERVICE_UUID_BASE ble_uuid128_t msPlaybackSpeedCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
}; ble_uuid128_t msRepeatCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msArtistCharUuid{ ble_uuid128_t msShuffleCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msTrackCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msAlbumCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msPositionCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msTotalLengthCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msTrackNumberCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msTrackTotalCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msPlaybackSpeedCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msRepeatCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
ble_uuid128_t msShuffleCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = MUSIC_SERVICE_UUID_BASE
};
struct ble_gatt_chr_def characteristicDefinition[14]; struct ble_gatt_chr_def characteristicDefinition[14];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
uint16_t eventHandle; uint16_t eventHandle;
std::string artistName; std::string artistName;
std::string albumName; std::string albumName;
std::string trackName; std::string trackName;
bool playing; bool playing;
int trackProgress; int trackProgress;
int trackLength; int trackLength;
int trackNumber; int trackNumber;
int tracksTotal; int tracksTotal;
float playbackSpeed; float playbackSpeed;
bool repeat; bool repeat;
bool shuffle; bool shuffle;
Pinetime::System::SystemTask &m_system; Pinetime::System::SystemTask& m_system;
}; };
} }
} }

View file

@ -20,12 +20,12 @@
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto navService = static_cast<Pinetime::Controllers::NavigationService *>(arg); auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg);
return navService->OnCommand(conn_handle, attr_handle, ctxt); return navService->OnCommand(conn_handle, attr_handle, ctxt);
} }
Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask &system) : m_system(system) { Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) {
navUuid.value[14] = navId[0]; navUuid.value[14] = navId[0];
navUuid.value[15] = navId[1]; navUuid.value[15] = navId[1];
@ -49,35 +49,25 @@ Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::Sy
navProgressCharUuid.value[14] = navId[0]; navProgressCharUuid.value[14] = navId[0];
navProgressCharUuid.value[15] = navId[1]; navProgressCharUuid.value[15] = navId[1];
characteristicDefinition[0] = {.uuid = (ble_uuid_t *) (&navFlagCharUuid), characteristicDefinition[0] = {
.access_cb = NAVCallback, .uuid = (ble_uuid_t*) (&navFlagCharUuid), .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[1] = {.uuid = (ble_uuid_t *) (&navNarrativeCharUuid), characteristicDefinition[1] = {.uuid = (ble_uuid_t*) (&navNarrativeCharUuid),
.access_cb = NAVCallback, .access_cb = NAVCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
}; characteristicDefinition[2] = {.uuid = (ble_uuid_t*) (&navManDistCharUuid),
characteristicDefinition[2] = {.uuid = (ble_uuid_t *) (&navManDistCharUuid), .access_cb = NAVCallback,
.access_cb = NAVCallback, .arg = this,
.arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ characteristicDefinition[3] = {.uuid = (ble_uuid_t*) (&navProgressCharUuid),
}; .access_cb = NAVCallback,
characteristicDefinition[3] = {.uuid = (ble_uuid_t *) (&navProgressCharUuid), .arg = this,
.access_cb = NAVCallback, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
};
characteristicDefinition[4] = {0}; characteristicDefinition[4] = {0};
serviceDefinition[0] = { serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &navUuid, .characteristics = characteristicDefinition};
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &navUuid,
.characteristics = characteristicDefinition
};
serviceDefinition[1] = {0}; serviceDefinition[1] = {0};
m_progress = 0; m_progress = 0;
@ -92,45 +82,39 @@ void Pinetime::Controllers::NavigationService::Init() {
ASSERT(res == 0); ASSERT(res == 0);
} }
int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
struct ble_gatt_access_ctxt *ctxt) {
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
size_t notifSize = OS_MBUF_PKTLEN(ctxt->om); size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
uint8_t data[notifSize + 1]; uint8_t data[notifSize + 1];
data[notifSize] = '\0'; data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data); os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char *s = (char *) &data[0]; char* s = (char*) &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navFlagCharUuid) == 0) { if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navFlagCharUuid) == 0) {
m_flag = s; m_flag = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navNarrativeCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navNarrativeCharUuid) == 0) {
m_narrative = s; m_narrative = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navManDistCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navManDistCharUuid) == 0) {
m_manDist = s; m_manDist = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navProgressCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navProgressCharUuid) == 0) {
m_progress = data[0]; m_progress = data[0];
} }
} }
return 0; return 0;
} }
std::string Pinetime::Controllers::NavigationService::getFlag() std::string Pinetime::Controllers::NavigationService::getFlag() {
{ return m_flag;
return m_flag;
} }
std::string Pinetime::Controllers::NavigationService::getNarrative() std::string Pinetime::Controllers::NavigationService::getNarrative() {
{ return m_narrative;
return m_narrative;
} }
std::string Pinetime::Controllers::NavigationService::getManDist() std::string Pinetime::Controllers::NavigationService::getManDist() {
{ return m_manDist;
return m_manDist;
} }
int Pinetime::Controllers::NavigationService::getProgress() int Pinetime::Controllers::NavigationService::getProgress() {
{ return m_progress;
return m_progress;
} }

View file

@ -26,8 +26,9 @@
#undef max #undef max
#undef min #undef min
//c7e60000-78fc-48fe-8e23-433b3a1942d0 // c7e60000-78fc-48fe-8e23-433b3a1942d0
#define NAVIGATION_SERVICE_UUID_BASE {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00} #define NAVIGATION_SERVICE_UUID_BASE \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 }
namespace Pinetime { namespace Pinetime {
namespace System { namespace System {
@ -37,12 +38,11 @@ namespace Pinetime {
class NavigationService { class NavigationService {
public: public:
explicit NavigationService(Pinetime::System::SystemTask &system); explicit NavigationService(Pinetime::System::SystemTask& system);
void Init(); void Init();
int OnCommand(uint16_t conn_handle, uint16_t attr_handle, int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
struct ble_gatt_access_ctxt *ctxt);
std::string getFlag(); std::string getFlag();
@ -59,27 +59,12 @@ namespace Pinetime {
static constexpr uint8_t navManDistCharId[2] = {0x03, 0x00}; static constexpr uint8_t navManDistCharId[2] = {0x03, 0x00};
static constexpr uint8_t navProgressCharId[2] = {0x04, 0x00}; static constexpr uint8_t navProgressCharId[2] = {0x04, 0x00};
ble_uuid128_t navUuid{ ble_uuid128_t navUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
.u = {.type = BLE_UUID_TYPE_128},
.value = NAVIGATION_SERVICE_UUID_BASE
};
ble_uuid128_t navFlagCharUuid{ ble_uuid128_t navFlagCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
.u = {.type = BLE_UUID_TYPE_128}, ble_uuid128_t navNarrativeCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
.value = NAVIGATION_SERVICE_UUID_BASE ble_uuid128_t navManDistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
}; ble_uuid128_t navProgressCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
ble_uuid128_t navNarrativeCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = NAVIGATION_SERVICE_UUID_BASE
};
ble_uuid128_t navManDistCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = NAVIGATION_SERVICE_UUID_BASE
};
ble_uuid128_t navProgressCharUuid{
.u = {.type = BLE_UUID_TYPE_128},
.value = NAVIGATION_SERVICE_UUID_BASE
};
struct ble_gatt_chr_def characteristicDefinition[5]; struct ble_gatt_chr_def characteristicDefinition[5];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
@ -89,8 +74,7 @@ namespace Pinetime {
std::string m_manDist; std::string m_manDist;
int m_progress; int m_progress;
Pinetime::System::SystemTask &m_system; Pinetime::System::SystemTask& m_system;
}; };
} }
} }

View file

@ -19,36 +19,37 @@ using namespace Pinetime::Controllers;
NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController, Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController, DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController, Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash, Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController) : Controllers::HeartRateController& heartRateController)
systemTask{systemTask}, : systemTask {systemTask},
bleController{bleController}, bleController {bleController},
dateTimeController{dateTimeController}, dateTimeController {dateTimeController},
notificationManager{notificationManager}, notificationManager {notificationManager},
spiNorFlash{spiNorFlash}, spiNorFlash {spiNorFlash},
dfuService{systemTask, bleController, spiNorFlash}, dfuService {systemTask, bleController, spiNorFlash},
currentTimeClient{dateTimeController}, currentTimeClient {dateTimeController},
anService{systemTask, notificationManager}, anService {systemTask, notificationManager},
alertNotificationClient{systemTask, notificationManager}, alertNotificationClient {systemTask, notificationManager},
currentTimeService{dateTimeController}, currentTimeService {dateTimeController},
musicService{systemTask}, musicService {systemTask},
navService{systemTask}, navService {systemTask},
batteryInformationService{batteryController}, batteryInformationService {batteryController},
immediateAlertService{systemTask, notificationManager}, immediateAlertService {systemTask, notificationManager},
heartRateService{systemTask, heartRateController}, heartRateService {systemTask, heartRateController},
serviceDiscovery({&currentTimeClient, &alertNotificationClient}) { serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
} }
int GAPEventCallback(struct ble_gap_event *event, void *arg) { int GAPEventCallback(struct ble_gap_event* event, void* arg) {
auto nimbleController = static_cast<NimbleController*>(arg); auto nimbleController = static_cast<NimbleController*>(arg);
return nimbleController->OnGAPEvent(event); return nimbleController->OnGAPEvent(event);
} }
void NimbleController::Init() { void NimbleController::Init() {
while (!ble_hs_synced()) {} while (!ble_hs_synced()) {
}
ble_svc_gap_init(); ble_svc_gap_init();
ble_svc_gatt_init(); ble_svc_gatt_init();
@ -81,7 +82,8 @@ void NimbleController::Init() {
} }
void NimbleController::StartAdvertising() { void NimbleController::StartAdvertising() {
if(bleController.IsConnected() || ble_gap_conn_active() || ble_gap_adv_active()) return; if (bleController.IsConnected() || ble_gap_conn_active() || ble_gap_adv_active())
return;
ble_svc_gap_device_name_set(deviceName); ble_svc_gap_device_name_set(deviceName);
@ -101,29 +103,27 @@ void NimbleController::StartAdvertising() {
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
fields.flags = BLE_HS_ADV_F_DISC_GEN | fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
BLE_HS_ADV_F_BREDR_UNSUP; // fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE(
// fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE( // 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
// 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
// 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
fields.uuids128 = &dfuServiceUuid; fields.uuids128 = &dfuServiceUuid;
fields.num_uuids128 = 1; fields.num_uuids128 = 1;
fields.uuids128_is_complete = 1; fields.uuids128_is_complete = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
rsp_fields.name = (uint8_t *)deviceName; rsp_fields.name = (uint8_t*) deviceName;
rsp_fields.name_len = strlen(deviceName); rsp_fields.name_len = strlen(deviceName);
rsp_fields.name_is_complete = 1; rsp_fields.name_is_complete = 1;
ble_gap_adv_set_fields(&fields); ble_gap_adv_set_fields(&fields);
// ASSERT(res == 0); // TODO this one sometimes fails with error 22 (notsync) // ASSERT(res == 0); // TODO this one sometimes fails with error 22 (notsync)
ble_gap_adv_rsp_set_fields(&rsp_fields); ble_gap_adv_rsp_set_fields(&rsp_fields);
// ASSERT(res == 0); // ASSERT(res == 0);
ble_gap_adv_start(addrType, NULL, 180000, ble_gap_adv_start(addrType, NULL, 180000, &adv_params, GAPEventCallback, this);
&adv_params, GAPEventCallback, this); // ASSERT(res == 0);// TODO I've disabled these ASSERT as they sometime asserts and reset the mcu.
// ASSERT(res == 0);// TODO I've disabled these ASSERT as they sometime asserts and reset the mcu.
// For now, the advertising is restarted as soon as it ends. There may be a race condition // For now, the advertising is restarted as soon as it ends. There may be a race condition
// that prevent the advertising from restarting reliably. // that prevent the advertising from restarting reliably.
// I remove the assert to prevent this uncesseray crash, but in the long term, the management of // I remove the assert to prevent this uncesseray crash, but in the long term, the management of
@ -131,7 +131,7 @@ void NimbleController::StartAdvertising() {
// the application has been woken up, for example. // the application has been woken up, for example.
} }
int NimbleController::OnGAPEvent(ble_gap_event *event) { int NimbleController::OnGAPEvent(ble_gap_event* event) {
switch (event->type) { switch (event->type) {
case BLE_GAP_EVENT_ADV_COMPLETE: case BLE_GAP_EVENT_ADV_COMPLETE:
NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE"); NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
@ -141,8 +141,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONNECT"); NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONNECT");
/* A new connection was established or a connection attempt failed. */ /* A new connection was established or a connection attempt failed. */
NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed", NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed", event->connect.status);
event->connect.status);
if (event->connect.status != 0) { if (event->connect.status != 0) {
/* Connection failed; resume advertising. */ /* Connection failed; resume advertising. */
@ -154,8 +153,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
connectionHandle = event->connect.conn_handle; connectionHandle = event->connect.conn_handle;
// Service discovery is deffered via systemtask // Service discovery is deffered via systemtask
} }
} } break;
break;
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_DISCONNECT"); NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_DISCONNECT");
NRF_LOG_INFO("disconnect; reason=%d", event->disconnect.reason); NRF_LOG_INFO("disconnect; reason=%d", event->disconnect.reason);
@ -178,19 +176,16 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
return 0; return 0;
case BLE_GAP_EVENT_SUBSCRIBE: 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", "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
event->subscribe.conn_handle, event->subscribe.conn_handle,
event->subscribe.attr_handle, event->subscribe.attr_handle,
event->subscribe.reason, event->subscribe.reason,
event->subscribe.prev_notify, event->subscribe.prev_notify,
event->subscribe.cur_notify, event->subscribe.cur_notify,
event->subscribe.prev_indicate); event->subscribe.prev_indicate);
return 0; return 0;
case BLE_GAP_EVENT_MTU: case BLE_GAP_EVENT_MTU:
NRF_LOG_INFO("mtu update event; conn_handle=%d cid=%d mtu=%d\n", 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);
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
return 0; return 0;
case BLE_GAP_EVENT_REPEAT_PAIRING: { case BLE_GAP_EVENT_REPEAT_PAIRING: {
@ -216,9 +211,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d " NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d "
"attr_len=%d", "attr_len=%d",
event->notify_rx.indication ? event->notify_rx.indication ? "indication" : "notification",
"indication" :
"notification",
event->notify_rx.conn_handle, event->notify_rx.conn_handle,
event->notify_rx.attr_handle, event->notify_rx.attr_handle,
notifSize); notifSize);
@ -229,7 +222,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
/* Attribute data is contained in event->notify_rx.attr_data. */ /* Attribute data is contained in event->notify_rx.attr_data. */
default: default:
// NRF_LOG_INFO("Advertising event : %d", event->type); // NRF_LOG_INFO("Advertising event : %d", event->type);
break; break;
} }
return 0; return 0;
@ -239,8 +232,6 @@ void NimbleController::StartDiscovery() {
serviceDiscovery.StartDiscovery(connectionHandle); serviceDiscovery.StartDiscovery(connectionHandle);
} }
uint16_t NimbleController::connHandle() { uint16_t NimbleController::connHandle() {
return connectionHandle; return connectionHandle;
} }

View file

@ -36,62 +36,69 @@ namespace Pinetime {
class NimbleController { class NimbleController {
public: public:
NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, NimbleController(Pinetime::System::SystemTask& systemTask,
DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::Ble& bleController,
Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash, DateTime& dateTimeController,
Controllers::HeartRateController& heartRateController); Pinetime::Controllers::NotificationManager& notificationManager,
void Init(); Controllers::Battery& batteryController,
void StartAdvertising(); Pinetime::Drivers::SpiNorFlash& spiNorFlash,
int OnGAPEvent(ble_gap_event *event); Controllers::HeartRateController& heartRateController);
void Init();
void StartAdvertising();
int OnGAPEvent(ble_gap_event* event);
int OnDiscoveryEvent(uint16_t i, const ble_gatt_error *pError, const ble_gatt_svc *pSvc); int OnDiscoveryEvent(uint16_t i, const ble_gatt_error* pError, const ble_gatt_svc* pSvc);
int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
const ble_gatt_chr *characteristic); int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
const ble_gatt_chr *characteristic); int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute); const ble_gatt_error* error,
int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, uint16_t characteristicValueHandle,
uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor); const ble_gatt_dsc* descriptor);
void StartDiscovery(); void StartDiscovery();
Pinetime::Controllers::MusicService& music() {return musicService;}; Pinetime::Controllers::MusicService& music() {
Pinetime::Controllers::NavigationService& navigation() {return navService;}; return musicService;
Pinetime::Controllers::AlertNotificationService& alertService() {return anService;}; };
Pinetime::Controllers::NavigationService& navigation() {
return navService;
};
Pinetime::Controllers::AlertNotificationService& alertService() {
return anService;
};
uint16_t connHandle(); uint16_t connHandle();
private: private:
static constexpr const char* deviceName = "InfiniTime"; static constexpr const char* deviceName = "InfiniTime";
Pinetime::System::SystemTask& systemTask; Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::Ble& bleController;
DateTime& dateTimeController; DateTime& dateTimeController;
Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Drivers::SpiNorFlash& spiNorFlash; Pinetime::Drivers::SpiNorFlash& spiNorFlash;
Pinetime::Controllers::DfuService dfuService; Pinetime::Controllers::DfuService dfuService;
DeviceInformationService deviceInformationService; DeviceInformationService deviceInformationService;
CurrentTimeClient currentTimeClient; CurrentTimeClient currentTimeClient;
AlertNotificationService anService; AlertNotificationService anService;
AlertNotificationClient alertNotificationClient; AlertNotificationClient alertNotificationClient;
CurrentTimeService currentTimeService; CurrentTimeService currentTimeService;
MusicService musicService; MusicService musicService;
NavigationService navService; NavigationService navService;
BatteryInformationService batteryInformationService; BatteryInformationService batteryInformationService;
ImmediateAlertService immediateAlertService; ImmediateAlertService immediateAlertService;
HeartRateService heartRateService; HeartRateService heartRateService;
uint8_t addrType; // 1 = Random, 0 = PUBLIC uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle = 0; uint16_t connectionHandle = 0;
ble_uuid128_t dfuServiceUuid { ble_uuid128_t dfuServiceUuid {
.u { .type = BLE_UUID_TYPE_128}, .u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}
};
ServiceDiscovery serviceDiscovery; ServiceDiscovery serviceDiscovery;
}; };
} }
} }

View file

@ -6,15 +6,15 @@ using namespace Pinetime::Controllers;
constexpr uint8_t NotificationManager::MessageSize; constexpr uint8_t NotificationManager::MessageSize;
void NotificationManager::Push(NotificationManager::Notification&& notif) {
void NotificationManager::Push(NotificationManager::Notification &&notif) {
notif.id = GetNextId(); notif.id = GetNextId();
notif.valid = true; notif.valid = true;
notifications[writeIndex] = std::move(notif); notifications[writeIndex] = std::move(notif);
writeIndex = (writeIndex + 1 < TotalNbNotifications) ? writeIndex + 1 : 0; writeIndex = (writeIndex + 1 < TotalNbNotifications) ? writeIndex + 1 : 0;
if(!empty) if (!empty)
readIndex = (readIndex + 1 < TotalNbNotifications) ? readIndex + 1 : 0; readIndex = (readIndex + 1 < TotalNbNotifications) ? readIndex + 1 : 0;
else empty = false; else
empty = false;
newNotification = true; newNotification = true;
} }
@ -30,40 +30,48 @@ NotificationManager::Notification::Id NotificationManager::GetNextId() {
} }
NotificationManager::Notification NotificationManager::GetNext(NotificationManager::Notification::Id id) { NotificationManager::Notification NotificationManager::GetNext(NotificationManager::Notification::Id id) {
auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n){return n.valid && n.id == id;}); auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n) {
if(currentIterator == notifications.end() || currentIterator->id != id) return Notification{}; return n.valid && n.id == id;
});
if (currentIterator == notifications.end() || currentIterator->id != id)
return Notification {};
auto& lastNotification = notifications[readIndex]; auto& lastNotification = notifications[readIndex];
NotificationManager::Notification result; NotificationManager::Notification result;
if(currentIterator == (notifications.end()-1)) if (currentIterator == (notifications.end() - 1))
result = *(notifications.begin()); result = *(notifications.begin());
else else
result = *(currentIterator+1); result = *(currentIterator + 1);
if(result.id <= id) return {}; if (result.id <= id)
return {};
result.index = (lastNotification.id - result.id)+1; result.index = (lastNotification.id - result.id) + 1;
return result; return result;
} }
NotificationManager::Notification NotificationManager::GetPrevious(NotificationManager::Notification::Id id) { NotificationManager::Notification NotificationManager::GetPrevious(NotificationManager::Notification::Id id) {
auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n){return n.valid && n.id == id;}); auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n) {
if(currentIterator == notifications.end() || currentIterator->id != id) return Notification{}; return n.valid && n.id == id;
});
if (currentIterator == notifications.end() || currentIterator->id != id)
return Notification {};
auto& lastNotification = notifications[readIndex]; auto& lastNotification = notifications[readIndex];
NotificationManager::Notification result; NotificationManager::Notification result;
if(currentIterator == notifications.begin()) if (currentIterator == notifications.begin())
result = *(notifications.end()-1); result = *(notifications.end() - 1);
else else
result = *(currentIterator-1); result = *(currentIterator - 1);
if(result.id >= id) return {}; if (result.id >= id)
return {};
result.index = (lastNotification.id - result.id)+1; result.index = (lastNotification.id - result.id) + 1;
return result; return result;
} }
@ -76,7 +84,7 @@ bool NotificationManager::IsVibrationEnabled() {
} }
void NotificationManager::ToggleVibrations() { void NotificationManager::ToggleVibrations() {
vibrationEnabled = !vibrationEnabled; vibrationEnabled = !vibrationEnabled;
} }
bool NotificationManager::ClearNewNotificationFlag() { bool NotificationManager::ClearNewNotificationFlag() {
@ -84,21 +92,23 @@ bool NotificationManager::ClearNewNotificationFlag() {
} }
size_t NotificationManager::NbNotifications() const { size_t NotificationManager::NbNotifications() const {
return std::count_if(notifications.begin(), notifications.end(), [](const Notification& n){ return n.valid;}); return std::count_if(notifications.begin(), notifications.end(), [](const Notification& n) {
return n.valid;
});
} }
const char* NotificationManager::Notification::Message() const { const char* NotificationManager::Notification::Message() const {
const char* itField = std::find(message.begin(), message.begin()+size-1, '\0'); const char* itField = std::find(message.begin(), message.begin() + size - 1, '\0');
if(itField != message.begin()+size-1) { if (itField != message.begin() + size - 1) {
const char* ptr = (itField)+1; const char* ptr = (itField) + 1;
return ptr; return ptr;
} }
return const_cast<char*>(message.data()); return const_cast<char*>(message.data());
} }
const char* NotificationManager::Notification::Title() const { const char* NotificationManager::Notification::Title() const {
const char * itField = std::find(message.begin(), message.begin()+size-1, '\0'); const char* itField = std::find(message.begin(), message.begin() + size - 1, '\0');
if(itField != message.begin()+size-1) { if (itField != message.begin() + size - 1) {
return message.data(); return message.data();
} }
return {}; return {};

View file

@ -8,23 +8,35 @@
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class NotificationManager { class NotificationManager {
public: public:
enum class Categories {Unknown, SimpleAlert, Email, News, IncomingCall, MissedCall, Sms, VoiceMail, Schedule, HighProriotyAlert, InstantMessage }; enum class Categories {
static constexpr uint8_t MessageSize{100}; Unknown,
SimpleAlert,
Email,
News,
IncomingCall,
MissedCall,
Sms,
VoiceMail,
Schedule,
HighProriotyAlert,
InstantMessage
};
static constexpr uint8_t MessageSize {100};
struct Notification { struct Notification {
using Id = uint8_t; using Id = uint8_t;
Id id; Id id;
bool valid = false; bool valid = false;
uint8_t index; uint8_t index;
uint8_t size; uint8_t size;
std::array<char, MessageSize+1> message; std::array<char, MessageSize + 1> message;
Categories category = Categories::Unknown; Categories category = Categories::Unknown;
const char* Message() const; const char* Message() const;
const char* Title() const; const char* Title() const;
}; };
Notification::Id nextId {0}; Notification::Id nextId {0};
void Push(Notification&& notif); void Push(Notification&& notif);
Notification GetLastNotification(); Notification GetLastNotification();
@ -35,18 +47,20 @@ namespace Pinetime {
bool IsVibrationEnabled(); bool IsVibrationEnabled();
void ToggleVibrations(); void ToggleVibrations();
static constexpr size_t MaximumMessageSize() { return MessageSize; }; static constexpr size_t MaximumMessageSize() {
return MessageSize;
};
size_t NbNotifications() const; size_t NbNotifications() const;
private: private:
Notification::Id GetNextId(); Notification::Id GetNextId();
static constexpr uint8_t TotalNbNotifications = 5; static constexpr uint8_t TotalNbNotifications = 5;
std::array<Notification, TotalNbNotifications> notifications; std::array<Notification, TotalNbNotifications> notifications;
uint8_t readIndex = 0; uint8_t readIndex = 0;
uint8_t writeIndex = 0; uint8_t writeIndex = 0;
bool empty = true; bool empty = true;
std::atomic<bool> newNotification{false}; std::atomic<bool> newNotification {false};
bool vibrationEnabled = true; bool vibrationEnabled = true;
}; };
} }
} }

View file

@ -4,8 +4,7 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
ServiceDiscovery::ServiceDiscovery(std::array<BleClient*, 2>&& clients) : clients{clients} { ServiceDiscovery::ServiceDiscovery(std::array<BleClient*, 2>&& clients) : clients {clients} {
} }
void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) { void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) {
@ -16,7 +15,7 @@ void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) {
void ServiceDiscovery::OnServiceDiscovered(uint16_t connectionHandle) { void ServiceDiscovery::OnServiceDiscovered(uint16_t connectionHandle) {
clientIterator++; clientIterator++;
if(clientIterator != clients.end()) { if (clientIterator != clients.end()) {
DiscoverNextService(connectionHandle); DiscoverNextService(connectionHandle);
} else { } else {
NRF_LOG_INFO("End of service discovery"); NRF_LOG_INFO("End of service discovery");
@ -26,7 +25,7 @@ void ServiceDiscovery::OnServiceDiscovered(uint16_t connectionHandle) {
void ServiceDiscovery::DiscoverNextService(uint16_t connectionHandle) { void ServiceDiscovery::DiscoverNextService(uint16_t connectionHandle) {
NRF_LOG_INFO("[Discovery] Discover next service"); NRF_LOG_INFO("[Discovery] Discover next service");
auto discoverNextService = [this](uint16_t connectionHandle){ auto discoverNextService = [this](uint16_t connectionHandle) {
this->OnServiceDiscovered(connectionHandle); this->OnServiceDiscovered(connectionHandle);
}; };
(*clientIterator)->Discover(connectionHandle, discoverNextService); (*clientIterator)->Discover(connectionHandle, discoverNextService);

View file

@ -8,17 +8,16 @@ namespace Pinetime {
class BleClient; class BleClient;
class ServiceDiscovery { class ServiceDiscovery {
public: public:
ServiceDiscovery(std::array<BleClient*, 2>&& bleClients); ServiceDiscovery(std::array<BleClient*, 2>&& bleClients);
void StartDiscovery(uint16_t connectionHandle); void StartDiscovery(uint16_t connectionHandle);
private:
private: BleClient** clientIterator;
BleClient** clientIterator; std::array<BleClient*, 2> clients;
std::array<BleClient*, 2> clients; void OnServiceDiscovered(uint16_t connectionHandle);
void OnServiceDiscovered(uint16_t connectionHandle); void DiscoverNextService(uint16_t connectionHandle);
void DiscoverNextService(uint16_t connectionHandle);
}; };
} }
} }

View file

@ -4,7 +4,6 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
void BrightnessController::Init() { void BrightnessController::Init() {
nrf_gpio_cfg_output(pinLcdBacklight1); nrf_gpio_cfg_output(pinLcdBacklight1);
nrf_gpio_cfg_output(pinLcdBacklight2); nrf_gpio_cfg_output(pinLcdBacklight2);
@ -14,7 +13,7 @@ void BrightnessController::Init() {
void BrightnessController::Set(BrightnessController::Levels level) { void BrightnessController::Set(BrightnessController::Levels level) {
this->level = level; this->level = level;
switch(level) { switch (level) {
default: default:
case Levels::High: case Levels::High:
nrf_gpio_pin_clear(pinLcdBacklight1); nrf_gpio_pin_clear(pinLcdBacklight1);
@ -40,20 +39,34 @@ void BrightnessController::Set(BrightnessController::Levels level) {
} }
void BrightnessController::Lower() { void BrightnessController::Lower() {
switch(level) { switch (level) {
case Levels::High: Set(Levels::Medium); break; case Levels::High:
case Levels::Medium: Set(Levels::Low); break; Set(Levels::Medium);
case Levels::Low: Set(Levels::Off); break; break;
default: break; case Levels::Medium:
Set(Levels::Low);
break;
case Levels::Low:
Set(Levels::Off);
break;
default:
break;
} }
} }
void BrightnessController::Higher() { void BrightnessController::Higher() {
switch(level) { switch (level) {
case Levels::Off: Set(Levels::Low); break; case Levels::Off:
case Levels::Low: Set(Levels::Medium); break; Set(Levels::Low);
case Levels::Medium: Set(Levels::High); break; break;
default: break; case Levels::Low:
Set(Levels::Medium);
break;
case Levels::Medium:
Set(Levels::High);
break;
default:
break;
} }
} }
@ -70,29 +83,44 @@ void BrightnessController::Restore() {
} }
void BrightnessController::Step() { void BrightnessController::Step() {
switch(level) { switch (level) {
case Levels::Low: Set(Levels::Medium); break; case Levels::Low:
case Levels::Medium: Set(Levels::High); break; Set(Levels::Medium);
case Levels::High: Set(Levels::Low); break; break;
default: break; case Levels::Medium:
Set(Levels::High);
break;
case Levels::High:
Set(Levels::Low);
break;
default:
break;
} }
} }
const char* BrightnessController::GetIcon() { const char* BrightnessController::GetIcon() {
switch(level) { switch (level) {
case Levels::Medium: return Applications::Screens::Symbols::brightnessMedium; case Levels::Medium:
case Levels::High: return Applications::Screens::Symbols::brightnessHigh; return Applications::Screens::Symbols::brightnessMedium;
default: break; case Levels::High:
return Applications::Screens::Symbols::brightnessHigh;
default:
break;
} }
return Applications::Screens::Symbols::brightnessLow; return Applications::Screens::Symbols::brightnessLow;
} }
const char* BrightnessController::ToString() { const char* BrightnessController::ToString() {
switch(level) { switch (level) {
case Levels::Off: return "Off"; case Levels::Off:
case Levels::Low: return "Low"; return "Off";
case Levels::Medium: return "Medium"; case Levels::Low:
case Levels::High: return "High"; return "Low";
default : return "???"; case Levels::Medium:
return "Medium";
case Levels::High:
return "High";
default:
return "???";
} }
} }

View file

@ -6,7 +6,7 @@ namespace Pinetime {
namespace Controllers { namespace Controllers {
class BrightnessController { class BrightnessController {
public: public:
enum class Levels {Off, Low, Medium, High}; enum class Levels { Off, Low, Medium, High };
void Init(); void Init();
void Set(Levels level); void Set(Levels level);

View file

@ -5,22 +5,21 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
DateTime::DateTime(System::SystemTask& systemTask) : systemTask{systemTask} { DateTime::DateTime(System::SystemTask& systemTask) : systemTask {systemTask} {
} }
void DateTime::SetTime(
void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter) {
uint8_t second, uint32_t systickCounter) { std::tm tm = {
std::tm tm = { /* .tm_sec = */ second, /* .tm_sec = */ second,
/* .tm_min = */ minute, /* .tm_min = */ minute,
/* .tm_hour = */ hour, /* .tm_hour = */ hour,
/* .tm_mday = */ day, /* .tm_mday = */ day,
/* .tm_mon = */ month - 1, /* .tm_mon = */ month - 1,
/* .tm_year = */ year - 1900, /* .tm_year = */ year - 1900,
}; };
tm.tm_isdst = -1; // Use DST value from local time zone tm.tm_isdst = -1; // Use DST value from local time zone
currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm)); currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm));
NRF_LOG_INFO("%d %d %d ", day, month, year); NRF_LOG_INFO("%d %d %d ", day, month, year);
NRF_LOG_INFO("%d %d %d ", hour, minute, second); NRF_LOG_INFO("%d %d %d ", hour, minute, second);
@ -34,7 +33,7 @@ void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfW
void DateTime::UpdateTime(uint32_t systickCounter) { void DateTime::UpdateTime(uint32_t systickCounter) {
// Handle systick counter overflow // Handle systick counter overflow
uint32_t systickDelta = 0; uint32_t systickDelta = 0;
if(systickCounter < previousSystickCounter) { if (systickCounter < previousSystickCounter) {
systickDelta = 0xffffff - previousSystickCounter; systickDelta = 0xffffff - previousSystickCounter;
systickDelta += systickCounter + 1; systickDelta += systickCounter + 1;
} else { } else {
@ -42,11 +41,11 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
} }
/* /*
* 1000 ms = 1024 ticks * 1000 ms = 1024 ticks
*/ */
auto correctedDelta = systickDelta / 1024; auto correctedDelta = systickDelta / 1024;
auto rest = (systickDelta - (correctedDelta*1024)); auto rest = (systickDelta - (correctedDelta * 1024));
if(systickCounter >= rest) { if (systickCounter >= rest) {
previousSystickCounter = systickCounter - rest; previousSystickCounter = systickCounter - rest;
} else { } else {
previousSystickCounter = 0xffffff - (rest - systickCounter); previousSystickCounter = 0xffffff - (rest - systickCounter);
@ -56,12 +55,12 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
uptime += std::chrono::seconds(correctedDelta); uptime += std::chrono::seconds(correctedDelta);
auto dp = date::floor<date::days>(currentDateTime); auto dp = date::floor<date::days>(currentDateTime);
auto time = date::make_time(currentDateTime-dp); auto time = date::make_time(currentDateTime - dp);
auto yearMonthDay = date::year_month_day(dp); auto yearMonthDay = date::year_month_day(dp);
year = (int)yearMonthDay.year(); year = (int) yearMonthDay.year();
month = static_cast<Months>((unsigned)yearMonthDay.month()); month = static_cast<Months>((unsigned) yearMonthDay.month());
day = (unsigned)yearMonthDay.day(); day = (unsigned) yearMonthDay.day();
dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding()); dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding());
hour = time.hours().count(); hour = time.hours().count();
@ -69,7 +68,7 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
second = time.seconds().count(); second = time.seconds().count();
// Notify new day to SystemTask // Notify new day to SystemTask
if(hour == 0 and not isMidnightAlreadyNotified) { if (hour == 0 and not isMidnightAlreadyNotified) {
isMidnightAlreadyNotified = true; isMidnightAlreadyNotified = true;
systemTask.PushMessage(System::SystemTask::Messages::OnNewDay); systemTask.PushMessage(System::SystemTask::Messages::OnNewDay);
} else if (hour != 0) { } else if (hour != 0) {
@ -77,123 +76,45 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
} }
} }
const char *DateTime::MonthShortToString() { const char* DateTime::MonthShortToString() {
return DateTime::MonthsString[(uint8_t)month]; return DateTime::MonthsString[(uint8_t) month];
} }
const char *DateTime::MonthShortToStringLow() { const char* DateTime::MonthShortToStringLow() {
return DateTime::MonthsStringLow[(uint8_t)month]; return DateTime::MonthsStringLow[(uint8_t) month];
} }
const char *DateTime::MonthsToStringLow() { const char* DateTime::MonthsToStringLow() {
return DateTime::MonthsLow[(uint8_t)month]; return DateTime::MonthsLow[(uint8_t) month];
} }
const char *DateTime::DayOfWeekToString() { const char* DateTime::DayOfWeekToString() {
return DateTime::DaysString[(uint8_t)dayOfWeek]; return DateTime::DaysString[(uint8_t) dayOfWeek];
} }
const char *DateTime::DayOfWeekShortToString() { const char* DateTime::DayOfWeekShortToString() {
return DateTime::DaysStringShort[(uint8_t)dayOfWeek]; return DateTime::DaysStringShort[(uint8_t) dayOfWeek];
} }
const char *DateTime::DayOfWeekToStringLow() { const char* DateTime::DayOfWeekToStringLow() {
return DateTime::DaysStringLow[(uint8_t)dayOfWeek]; return DateTime::DaysStringLow[(uint8_t) dayOfWeek];
} }
const char *DateTime::DayOfWeekShortToStringLow() { const char* DateTime::DayOfWeekShortToStringLow() {
return DateTime::DaysStringShortLow[(uint8_t)dayOfWeek]; return DateTime::DaysStringShortLow[(uint8_t) dayOfWeek];
} }
char const* DateTime::DaysStringLow[] = {"--", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
char const *DateTime::DaysStringLow[] = { char const* DateTime::DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
"--",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
};
char const *DateTime::DaysStringShortLow[] = { char const* DateTime::DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
"--",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun"
};
char const *DateTime::DaysStringShort[] = { char const* DateTime::DaysString[] = {"--", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"};
"--",
"MON",
"TUE",
"WED",
"THU",
"FRI",
"SAT",
"SUN"
};
char const *DateTime::DaysString[] = { char const* DateTime::MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
"--",
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
"SUNDAY"
};
char const *DateTime::MonthsString[] = { char const* DateTime::MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
"--",
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC"
};
char const *DateTime::MonthsStringLow[] = { char const* DateTime::MonthsLow[] = {
"--", "--", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
char const *DateTime::MonthsLow[] = {
"--",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};

View file

@ -9,56 +9,95 @@ namespace Pinetime {
} }
namespace Controllers { namespace Controllers {
class DateTime { class DateTime {
public: public:
enum class Days : uint8_t {Unknown, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}; enum class Days : uint8_t { Unknown, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
enum class Months : uint8_t {Unknown, January, February, March, April, May, June, July, August, September, October, November, December}; enum class Months : uint8_t {
Unknown,
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
};
DateTime(System::SystemTask& systemTask); DateTime(System::SystemTask& systemTask);
void SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter); void SetTime(uint16_t year,
void UpdateTime(uint32_t systickCounter); uint8_t month,
uint16_t Year() const { return year; } uint8_t day,
Months Month() const { return month; } uint8_t dayOfWeek,
uint8_t Day() const { return day; } uint8_t hour,
Days DayOfWeek() const { return dayOfWeek; } uint8_t minute,
uint8_t Hours() const { return hour; } uint8_t second,
uint8_t Minutes() const { return minute; } uint32_t systickCounter);
uint8_t Seconds() const { return second; } void UpdateTime(uint32_t systickCounter);
uint16_t Year() const {
return year;
}
Months Month() const {
return month;
}
uint8_t Day() const {
return day;
}
Days DayOfWeek() const {
return dayOfWeek;
}
uint8_t Hours() const {
return hour;
}
uint8_t Minutes() const {
return minute;
}
uint8_t Seconds() const {
return second;
}
const char *MonthShortToString(); const char* MonthShortToString();
const char *MonthShortToStringLow(); const char* MonthShortToStringLow();
const char *MonthsToStringLow(); const char* MonthsToStringLow();
const char *DayOfWeekToString(); const char* DayOfWeekToString();
const char *DayOfWeekShortToString(); const char* DayOfWeekShortToString();
const char *DayOfWeekToStringLow(); const char* DayOfWeekToStringLow();
const char *DayOfWeekShortToStringLow(); const char* DayOfWeekShortToStringLow();
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const { return currentDateTime; } std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
std::chrono::seconds Uptime() const { return uptime; } return currentDateTime;
private: }
System::SystemTask& systemTask; std::chrono::seconds Uptime() const {
uint16_t year = 0; return uptime;
Months month = Months::Unknown; }
uint8_t day = 0;
Days dayOfWeek = Days::Unknown;
uint8_t hour = 0;
uint8_t minute = 0;
uint8_t second = 0;
uint32_t previousSystickCounter = 0; private:
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime; System::SystemTask& systemTask;
std::chrono::seconds uptime {0}; uint16_t year = 0;
Months month = Months::Unknown;
uint8_t day = 0;
Days dayOfWeek = Days::Unknown;
uint8_t hour = 0;
uint8_t minute = 0;
uint8_t second = 0;
bool isMidnightAlreadyNotified = false; uint32_t previousSystickCounter = 0;
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
std::chrono::seconds uptime {0};
static char const *DaysString[]; bool isMidnightAlreadyNotified = false;
static char const *DaysStringShort[];
static char const *DaysStringLow[];
static char const *DaysStringShortLow[];
static char const *MonthsString[];
static char const *MonthsStringLow[];
static char const *MonthsLow[];
static char const* DaysString[];
static char const* DaysStringShort[];
static char const* DaysStringLow[];
static char const* DaysStringShortLow[];
static char const* MonthsString[];
static char const* MonthsStringLow[];
static char const* MonthsLow[];
}; };
} }
} }

View file

@ -6,12 +6,12 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
bool FirmwareValidator::IsValidated() const { bool FirmwareValidator::IsValidated() const {
auto* imageOkPtr = reinterpret_cast<uint32_t *>(validBitAdress); auto* imageOkPtr = reinterpret_cast<uint32_t*>(validBitAdress);
return (*imageOkPtr) == validBitValue; return (*imageOkPtr) == validBitValue;
} }
void FirmwareValidator::Validate() { void FirmwareValidator::Validate() {
if(!IsValidated()) if (!IsValidated())
Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue); Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue);
} }

View file

@ -5,14 +5,15 @@
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class FirmwareValidator { class FirmwareValidator {
public: public:
void Validate(); void Validate();
bool IsValidated() const; bool IsValidated() const;
void Reset(); void Reset();
private:
static constexpr uint32_t validBitAdress {0x7BFE8}; private:
static constexpr uint32_t validBitValue {1}; static constexpr uint32_t validBitAdress {0x7BFE8};
static constexpr uint32_t validBitValue {1};
}; };
} }
} }

View file

@ -2,11 +2,10 @@
#include "drivers/St7789.h" #include "drivers/St7789.h"
using namespace Pinetime::Components; using namespace Pinetime::Components;
Gfx::Gfx(Pinetime::Drivers::St7789 &lcd) : lcd{lcd} { Gfx::Gfx(Pinetime::Drivers::St7789& lcd) : lcd {lcd} {
} }
void Gfx::Init() { void Gfx::Init() {
} }
void Gfx::ClearScreen() { void Gfx::ClearScreen() {
@ -17,10 +16,9 @@ void Gfx::ClearScreen() {
state.busy = true; state.busy = true;
state.action = Action::FillRectangle; state.action = Action::FillRectangle;
state.taskToNotify = xTaskGetCurrentTaskHandle(); state.taskToNotify = xTaskGetCurrentTaskHandle();
lcd.DrawBuffer(0, 0, width, height, reinterpret_cast<const uint8_t *>(buffer), width * 2);
WaitTransferFinished();
lcd.DrawBuffer(0, 0, width, height, reinterpret_cast<const uint8_t*>(buffer), width * 2);
WaitTransferFinished();
} }
void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) { void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
@ -33,7 +31,7 @@ void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t col
state.color = color; state.color = color;
state.taskToNotify = xTaskGetCurrentTaskHandle(); state.taskToNotify = xTaskGetCurrentTaskHandle();
lcd.DrawBuffer(x, y, w, h, reinterpret_cast<const uint8_t *>(buffer), width * 2); lcd.DrawBuffer(x, y, w, h, reinterpret_cast<const uint8_t*>(buffer), width * 2);
WaitTransferFinished(); WaitTransferFinished();
} }
@ -46,12 +44,12 @@ void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b)
state.color = 0x00; state.color = 0x00;
state.taskToNotify = xTaskGetCurrentTaskHandle(); state.taskToNotify = xTaskGetCurrentTaskHandle();
lcd.DrawBuffer(x, y, w, h, reinterpret_cast<const uint8_t *>(b), width * 2); lcd.DrawBuffer(x, y, w, h, reinterpret_cast<const uint8_t*>(b), width * 2);
WaitTransferFinished(); WaitTransferFinished();
} }
void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, const FONT_INFO *p_font, bool wrap) { void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO* p_font, bool wrap) {
if (y > (height - p_font->height)) { if (y > (height - p_font->height)) {
// Not enough space to write even single char. // Not enough space to write even single char.
return; return;
@ -86,7 +84,7 @@ void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, con
} }
} }
void Gfx::DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color) { void Gfx::DrawChar(const FONT_INFO* font, uint8_t c, uint8_t* x, uint8_t y, uint16_t color) {
uint8_t char_idx = c - font->startChar; uint8_t char_idx = c - font->startChar;
uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8); uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8);
uint16_t bg = 0x0000; uint16_t bg = 0x0000;
@ -100,10 +98,9 @@ void Gfx::DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint
for (uint16_t j = 0; j < bytes_in_line; j++) { for (uint16_t j = 0; j < bytes_in_line; j++) {
for (uint8_t k = 0; k < 8; k++) { for (uint8_t k = 0; k < 8; k++) {
if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) { if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) {
buffer[(j*8)+k] = color; buffer[(j * 8) + k] = color;
} } else {
else { buffer[(j * 8) + k] = bg;
buffer[(j*8)+k] = bg;
} }
} }
} }
@ -112,12 +109,12 @@ void Gfx::DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint
state.currentIteration = 0; state.currentIteration = 0;
state.busy = true; state.busy = true;
state.action = Action::DrawChar; state.action = Action::DrawChar;
state.font = const_cast<FONT_INFO *>(font); state.font = const_cast<FONT_INFO*>(font);
state.character = c; state.character = c;
state.color = color; state.color = color;
state.taskToNotify = xTaskGetCurrentTaskHandle(); state.taskToNotify = xTaskGetCurrentTaskHandle();
lcd.DrawBuffer(*x, y, bytes_in_line*8, font->height, reinterpret_cast<const uint8_t *>(&buffer), bytes_in_line*8*2); lcd.DrawBuffer(*x, y, bytes_in_line * 8, font->height, reinterpret_cast<const uint8_t*>(&buffer), bytes_in_line * 8 * 2);
WaitTransferFinished(); WaitTransferFinished();
*x += font->charInfo[char_idx].widthBits + font->spacePixels; *x += font->charInfo[char_idx].widthBits + font->spacePixels;
@ -136,13 +133,14 @@ void Gfx::Wakeup() {
} }
void Gfx::SetBackgroundColor(uint16_t color) { void Gfx::SetBackgroundColor(uint16_t color) {
for(int i = 0; i < width; i++) { for (int i = 0; i < width; i++) {
buffer[i] = color; buffer[i] = color;
} }
} }
bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) { bool Gfx::GetNextBuffer(uint8_t** data, size_t& size) {
if(!state.busy) return false; if (!state.busy)
return false;
state.remainingIterations--; state.remainingIterations--;
if (state.remainingIterations == 0) { if (state.remainingIterations == 0) {
state.busy = false; state.busy = false;
@ -150,27 +148,26 @@ bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
return false; return false;
} }
if(state.action == Action::FillRectangle) { if (state.action == Action::FillRectangle) {
*data = reinterpret_cast<uint8_t *>(buffer); *data = reinterpret_cast<uint8_t*>(buffer);
size = width * 2; size = width * 2;
} else if(state.action == Action::DrawChar) { } else if (state.action == Action::DrawChar) {
uint16_t bg = 0x0000; uint16_t bg = 0x0000;
uint8_t char_idx = state.character - state.font->startChar; uint8_t char_idx = state.character - state.font->startChar;
uint16_t bytes_in_line = CEIL_DIV(state.font->charInfo[char_idx].widthBits, 8); uint16_t bytes_in_line = CEIL_DIV(state.font->charInfo[char_idx].widthBits, 8);
for (uint16_t j = 0; j < bytes_in_line; j++) { for (uint16_t j = 0; j < bytes_in_line; j++) {
for (uint8_t k = 0; k < 8; k++) { for (uint8_t k = 0; k < 8; k++) {
if ((1 << (7 - k)) & state.font->data[state.font->charInfo[char_idx].offset + ((state.currentIteration+1) * bytes_in_line) + j]) { if ((1 << (7 - k)) & state.font->data[state.font->charInfo[char_idx].offset + ((state.currentIteration + 1) * bytes_in_line) + j]) {
buffer[(j*8)+k] = state.color; buffer[(j * 8) + k] = state.color;
} } else {
else { buffer[(j * 8) + k] = bg;
buffer[(j*8)+k] = bg;
} }
} }
} }
*data = reinterpret_cast<uint8_t *>(buffer); *data = reinterpret_cast<uint8_t*>(buffer);
size = bytes_in_line*8*2; size = bytes_in_line * 8 * 2;
} }
state.currentIteration++; state.currentIteration++;
@ -179,7 +176,7 @@ bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
} }
void Gfx::NotifyEndOfTransfer(TaskHandle_t task) { void Gfx::NotifyEndOfTransfer(TaskHandle_t task) {
if(task != nullptr) { if (task != nullptr) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken); vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

View file

@ -12,49 +12,48 @@ namespace Pinetime {
} }
namespace Components { namespace Components {
class Gfx : public Pinetime::Drivers::BufferProvider { class Gfx : public Pinetime::Drivers::BufferProvider {
public: public:
explicit Gfx(Drivers::St7789& lcd); explicit Gfx(Drivers::St7789& lcd);
void Init(); void Init();
void ClearScreen(); void ClearScreen();
void DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO *p_font, bool wrap); void DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO* p_font, bool wrap);
void DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color); void DrawChar(const FONT_INFO* font, uint8_t c, uint8_t* x, uint8_t y, uint16_t color);
void FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color); void FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color);
void FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b); void FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b);
void SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines); void SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
void SetScrollStartLine(uint16_t line); void SetScrollStartLine(uint16_t line);
void Sleep();
void Wakeup();
bool GetNextBuffer(uint8_t** buffer, size_t& size) override;
void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
void Sleep(); private:
void Wakeup(); static constexpr uint8_t width = 240;
bool GetNextBuffer(uint8_t **buffer, size_t &size) override; static constexpr uint8_t height = 240;
void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
enum class Action { None, FillRectangle, DrawChar };
struct State {
State() : busy {false}, action {Action::None}, remainingIterations {0}, currentIteration {0} {
}
volatile bool busy;
volatile Action action;
volatile uint16_t remainingIterations;
volatile uint16_t currentIteration;
volatile FONT_INFO* font;
volatile uint16_t color;
volatile uint8_t character;
volatile TaskHandle_t taskToNotify = nullptr;
};
private: volatile State state;
static constexpr uint8_t width = 240;
static constexpr uint8_t height = 240;
enum class Action { None, FillRectangle, DrawChar}; uint16_t buffer[width]; // 1 line buffer
struct State { Drivers::St7789& lcd;
State() : busy{false}, action{Action::None}, remainingIterations{0}, currentIteration{0} {}
volatile bool busy;
volatile Action action;
volatile uint16_t remainingIterations;
volatile uint16_t currentIteration;
volatile FONT_INFO *font;
volatile uint16_t color;
volatile uint8_t character;
volatile TaskHandle_t taskToNotify = nullptr;
};
volatile State state; void SetBackgroundColor(uint16_t color);
void WaitTransferFinished() const;
uint16_t buffer[width]; // 1 line buffer void NotifyEndOfTransfer(TaskHandle_t task);
Drivers::St7789& lcd;
void SetBackgroundColor(uint16_t color);
void WaitTransferFinished() const;
void NotifyEndOfTransfer(TaskHandle_t task);
}; };
} }
} }

View file

@ -9,8 +9,7 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
/** Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/ppg.py */ /** Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/ppg.py */
Biquad::Biquad(float b0, float b1, float b2, float a1, float a2) : b0{b0}, b1{b1}, b2{b2}, a1{a1}, a2{a2} { Biquad::Biquad(float b0, float b1, float b2, float a1, float a2) : b0 {b0}, b1 {b1}, b2 {b2}, a1 {a1}, a2 {a2} {
} }
float Biquad::Step(float x) { float Biquad::Step(float x) {

View file

@ -5,7 +5,7 @@ namespace Pinetime {
/// Direct Form II Biquad Filter /// Direct Form II Biquad Filter
class Biquad { class Biquad {
public: public:
Biquad(float b0, float b1, float b2, float a1, float a2); Biquad(float b0, float b1, float b2, float a1, float a2);
float Step(float x); float Step(float x);
private: private:

View file

@ -4,38 +4,35 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
HeartRateController::HeartRateController(Pinetime::System::SystemTask &systemTask) : systemTask{systemTask} { HeartRateController::HeartRateController(Pinetime::System::SystemTask& systemTask) : systemTask {systemTask} {
} }
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) { void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
this->state = newState; this->state = newState;
if(this->heartRate != heartRate) { if (this->heartRate != heartRate) {
this->heartRate = heartRate; this->heartRate = heartRate;
service->OnNewHeartRateValue(heartRate); service->OnNewHeartRateValue(heartRate);
} }
} }
void HeartRateController::Start() { void HeartRateController::Start() {
if(task != nullptr) { if (task != nullptr) {
state = States::NotEnoughData; state = States::NotEnoughData;
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StartMeasurement); task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StartMeasurement);
} }
} }
void HeartRateController::Stop() { void HeartRateController::Stop() {
if(task != nullptr) { if (task != nullptr) {
state = States::Stopped; state = States::Stopped;
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StopMeasurement); task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StopMeasurement);
} }
} }
void HeartRateController::SetHeartRateTask(Pinetime::Applications::HeartRateTask *task) { void HeartRateController::SetHeartRateTask(Pinetime::Applications::HeartRateTask* task) {
this->task = task; this->task = task;
} }
void HeartRateController::SetService(Pinetime::Controllers::HeartRateService *service) { void HeartRateController::SetService(Pinetime::Controllers::HeartRateService* service) {
this->service = service; this->service = service;
} }

View file

@ -13,7 +13,7 @@ namespace Pinetime {
namespace Controllers { namespace Controllers {
class HeartRateController { class HeartRateController {
public: public:
enum class States { Stopped, NotEnoughData, NoTouch, Running}; enum class States { Stopped, NotEnoughData, NoTouch, Running };
explicit HeartRateController(System::SystemTask& systemTask); explicit HeartRateController(System::SystemTask& systemTask);
@ -22,10 +22,14 @@ namespace Pinetime {
void Update(States newState, uint8_t heartRate); void Update(States newState, uint8_t heartRate);
void SetHeartRateTask(Applications::HeartRateTask* task); void SetHeartRateTask(Applications::HeartRateTask* task);
States State() const { return state; } States State() const {
uint8_t HeartRate() const { return heartRate; } return state;
}
uint8_t HeartRate() const {
return heartRate;
}
void SetService(Pinetime::Controllers::HeartRateService *service); void SetService(Pinetime::Controllers::HeartRateService* service);
private: private:
System::SystemTask& systemTask; System::SystemTask& systemTask;

View file

@ -13,7 +13,7 @@ using namespace Pinetime::Controllers;
namespace { namespace {
int Compare(int* d1, int* d2, size_t count) { int Compare(int* d1, int* d2, size_t count) {
int e = 0; int e = 0;
for(size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
auto d = d1[i] - d2[i]; auto d = d1[i] - d2[i];
e += d * d; e += d * d;
} }
@ -21,15 +21,15 @@ namespace {
} }
int CompareShift(int* d, int shift, size_t count) { int CompareShift(int* d, int shift, size_t count) {
return Compare(d +shift, d, count - shift); return Compare(d + shift, d, count - shift);
} }
int Trough(int* d, size_t size, float mn, float mx) { int Trough(int* d, size_t size, float mn, float mx) {
auto z2 = CompareShift(d, mn-2, size); auto z2 = CompareShift(d, mn - 2, size);
auto z1 = CompareShift(d, mn-1, size); auto z1 = CompareShift(d, mn - 1, size);
for(int i = mn; i < mx + 1; i++) { for (int i = mn; i < mx + 1; i++) {
auto z = CompareShift(d, i, size); auto z = CompareShift(d, i, size);
if(z2 > z1 && z1 < z) if (z2 > z1 && z1 < z)
return i; return i;
z2 = z1; z2 = z1;
z1 = z; z1 = z;
@ -38,11 +38,11 @@ namespace {
} }
} }
Ppg::Ppg(float spl) : offset{spl}, Ppg::Ppg(float spl)
hpf{0.87033078, -1.74066156, 0.87033078,-1.72377617, 0.75754694}, : offset {spl},
agc{20, 0.971, 2}, hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
lpf{0.11595249, 0.23190498, 0.11595249,-0.72168143, 0.18549138} { agc {20, 0.971, 2},
lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} {
} }
int Ppg::Preprocess(float spl) { int Ppg::Preprocess(float spl) {
@ -53,13 +53,13 @@ int Ppg::Preprocess(float spl) {
auto spl_int = static_cast<int>(spl); auto spl_int = static_cast<int>(spl);
if(dataIndex < 200) if (dataIndex < 200)
data[dataIndex++] = spl_int; data[dataIndex++] = spl_int;
return spl_int; return spl_int;
} }
float Ppg::HeartRate() { float Ppg::HeartRate() {
if(dataIndex < 200) if (dataIndex < 200)
return 0; return 0;
NRF_LOG_INFO("PREPROCESS, offset = %d", offset); NRF_LOG_INFO("PREPROCESS, offset = %d", offset);
@ -71,26 +71,26 @@ float Ppg::HeartRate() {
int cccount = 0; int cccount = 0;
float Ppg::ProcessHeartRate() { float Ppg::ProcessHeartRate() {
if(cccount > 2) if (cccount > 2)
asm("nop"); asm("nop");
cccount ++; cccount++;
auto t0 = Trough(data.data(), dataIndex, 7, 48); auto t0 = Trough(data.data(), dataIndex, 7, 48);
if(t0 < 0) if (t0 < 0)
return 0; return 0;
float t1 = t0 * 2; float t1 = t0 * 2;
t1 = Trough(data.data(), dataIndex, t1-5, t1+5); t1 = Trough(data.data(), dataIndex, t1 - 5, t1 + 5);
if(t1 < 0) if (t1 < 0)
return 0; return 0;
float t2 = static_cast<int>(t1 * 3) / 2; float t2 = static_cast<int>(t1 * 3) / 2;
t2 = Trough(data.data(), dataIndex, t2 - 5, t2 + 5); t2 = Trough(data.data(), dataIndex, t2 - 5, t2 + 5);
if(t2 < 0) if (t2 < 0)
return 0; return 0;
float t3 = static_cast<int>(t2 * 4) / 3; float t3 = static_cast<int>(t2 * 4) / 3;
t3 = Trough(data.data(), dataIndex, t3 - 4, t3 + 4); t3 = Trough(data.data(), dataIndex, t3 - 4, t3 + 4);
if(t3 < 0) if (t3 < 0)
return static_cast<int>(60 * 24 * 3) / static_cast<int>(t2); return static_cast<int>(60 * 24 * 3) / static_cast<int>(t2);
return static_cast<int>(60 * 24 * 4) / static_cast<int>(t3); return static_cast<int>(60 * 24 * 4) / static_cast<int>(t3);

View file

@ -24,7 +24,6 @@ namespace Pinetime {
Ptagc agc; Ptagc agc;
Biquad lpf; Biquad lpf;
float ProcessHeartRate(); float ProcessHeartRate();
}; };
} }

View file

@ -10,17 +10,16 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
/** Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/ppg.py */ /** Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/ppg.py */
Ptagc::Ptagc(float start, float decay, float threshold) : peak{start}, decay{decay}, boost{1.0f/decay}, threshold{threshold} { Ptagc::Ptagc(float start, float decay, float threshold) : peak {start}, decay {decay}, boost {1.0f / decay}, threshold {threshold} {
} }
float Ptagc::Step(float spl) { float Ptagc::Step(float spl) {
if(std::abs(spl) > peak) if (std::abs(spl) > peak)
peak *= boost; peak *= boost;
else else
peak *= decay; peak *= decay;
if((spl > (peak * threshold)) || (spl < (peak * -threshold))) if ((spl > (peak * threshold)) || (spl < (peak * -threshold)))
return 0.0f; return 0.0f;
spl = 100.0f * spl / (2.0f * peak); spl = 100.0f * spl / (2.0f * peak);

View file

@ -12,7 +12,6 @@ namespace Pinetime {
float decay; float decay;
float boost; float boost;
float threshold; float threshold;
}; };
} }
} }

View file

@ -8,14 +8,24 @@ namespace Pinetime {
public: public:
void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps); void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
uint16_t X() const { return x; } uint16_t X() const {
uint16_t Y() const { return y; } return x;
uint16_t Z() const { return z; } }
uint32_t NbSteps() const { return nbSteps; } uint16_t Y() const {
return y;
}
uint16_t Z() const {
return z;
}
uint32_t NbSteps() const {
return nbSteps;
}
bool ShouldWakeUp(bool isSleeping); bool ShouldWakeUp(bool isSleeping);
void IsSensorOk(bool isOk); void IsSensorOk(bool isOk);
bool IsSensorOk() const { return isSensorOk; } bool IsSensorOk() const {
return isSensorOk;
}
private: private:
uint32_t nbSteps; uint32_t nbSteps;

View file

@ -7,24 +7,26 @@ APP_TIMER_DEF(vibTimer);
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
MotorController::MotorController( Controllers::Settings &settingsController ) : settingsController{settingsController} {} MotorController::MotorController(Controllers::Settings& settingsController) : settingsController {settingsController} {
}
void MotorController::Init() { void MotorController::Init() {
nrf_gpio_cfg_output(pinMotor); nrf_gpio_cfg_output(pinMotor);
nrf_gpio_pin_set(pinMotor); nrf_gpio_pin_set(pinMotor);
app_timer_init(); app_timer_init();
app_timer_create(&vibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate); app_timer_create(&vibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate);
} }
void MotorController::SetDuration(uint8_t motorDuration) { void MotorController::SetDuration(uint8_t motorDuration) {
if ( settingsController.GetVibrationStatus() == Controllers::Settings::Vibration::OFF ) return; if (settingsController.GetVibrationStatus() == Controllers::Settings::Vibration::OFF)
return;
nrf_gpio_pin_clear(pinMotor);
/* Start timer for motorDuration miliseconds and timer triggers vibrate() when it finishes*/ nrf_gpio_pin_clear(pinMotor);
app_timer_start(vibTimer, APP_TIMER_TICKS(motorDuration), NULL); /* Start timer for motorDuration miliseconds and timer triggers vibrate() when it finishes*/
app_timer_start(vibTimer, APP_TIMER_TICKS(motorDuration), NULL);
} }
void MotorController::vibrate(void * p_context) { void MotorController::vibrate(void* p_context) {
nrf_gpio_pin_set(pinMotor); nrf_gpio_pin_set(pinMotor);
} }

View file

@ -9,14 +9,14 @@ namespace Pinetime {
static constexpr uint8_t pinMotor = 16; static constexpr uint8_t pinMotor = 16;
class MotorController { class MotorController {
public: public:
MotorController( Controllers::Settings &settingsController ); MotorController(Controllers::Settings& settingsController);
void Init(); void Init();
void SetDuration(uint8_t motorDuration); void SetDuration(uint8_t motorDuration);
private: private:
Controllers::Settings& settingsController; Controllers::Settings& settingsController;
static void vibrate(void * p_context); static void vibrate(void* p_context);
}; };
} }
} }

View file

@ -2,18 +2,16 @@
using namespace Pinetime::Tools; using namespace Pinetime::Tools;
RleDecoder::RleDecoder(const uint8_t *buffer, size_t size) : buffer{buffer}, size{size} { RleDecoder::RleDecoder(const uint8_t* buffer, size_t size) : buffer {buffer}, size {size} {
} }
RleDecoder::RleDecoder(const uint8_t *buffer, size_t size, uint16_t foregroundColor, uint16_t backgroundColor) : RleDecoder{buffer, size} { RleDecoder::RleDecoder(const uint8_t* buffer, size_t size, uint16_t foregroundColor, uint16_t backgroundColor) : RleDecoder {buffer, size} {
this->foregroundColor = foregroundColor; this->foregroundColor = foregroundColor;
this->backgroundColor = backgroundColor; this->backgroundColor = backgroundColor;
} }
void RleDecoder::DecodeNext(uint8_t* output, size_t maxBytes) {
void RleDecoder::DecodeNext(uint8_t *output, size_t maxBytes) { for (; encodedBufferIndex < size; encodedBufferIndex++) {
for (;encodedBufferIndex<size; encodedBufferIndex++) {
uint8_t rl = buffer[encodedBufferIndex] - processedCount; uint8_t rl = buffer[encodedBufferIndex] - processedCount;
while (rl) { while (rl) {
output[bp] = color >> 8; output[bp] = color >> 8;
@ -36,4 +34,3 @@ void RleDecoder::DecodeNext(uint8_t *output, size_t maxBytes) {
color = backgroundColor; color = backgroundColor;
} }
} }

View file

@ -5,110 +5,107 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
struct SettingsHeader { struct SettingsHeader {
uint8_t isActive; // 0xF1 = Block is active, 0xF0 = Block is inactive uint8_t isActive; // 0xF1 = Block is active, 0xF0 = Block is inactive
uint16_t version; // Current version, to verify if the saved data is for the current Version uint16_t version; // Current version, to verify if the saved data is for the current Version
}; };
#define HEADER_SIZE sizeof(SettingsHeader) #define HEADER_SIZE sizeof(SettingsHeader)
Settings::Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash {spiNorFlash} {
Settings::Settings( Pinetime::Drivers::SpiNorFlash &spiNorFlash ) : spiNorFlash{spiNorFlash} {} }
void Settings::Init() { void Settings::Init() {
// Load default settings from Flash // Load default settings from Flash
LoadSettingsFromFlash(); LoadSettingsFromFlash();
} }
void Settings::SaveSettings() { void Settings::SaveSettings() {
// verify if is necessary to save // verify if is necessary to save
if ( settingsChanged ) { if (settingsChanged) {
SaveSettingsToFlash(); SaveSettingsToFlash();
} }
settingsChanged = false; settingsChanged = false;
} }
bool Settings::FindHeader() { bool Settings::FindHeader() {
SettingsHeader settingsHeader; SettingsHeader settingsHeader;
uint8_t bufferHead[sizeof(settingsHeader)]; uint8_t bufferHead[sizeof(settingsHeader)];
for (uint8_t block = 0; block < 10; block++) { for (uint8_t block = 0; block < 10; block++) {
spiNorFlash.Read( settingsBaseAddr + (block * 0x1000), bufferHead, sizeof(settingsHeader) ); spiNorFlash.Read(settingsBaseAddr + (block * 0x1000), bufferHead, sizeof(settingsHeader));
std::memcpy(&settingsHeader, bufferHead, sizeof(settingsHeader)); std::memcpy(&settingsHeader, bufferHead, sizeof(settingsHeader));
if ( settingsHeader.isActive == 0xF1 && settingsHeader.version == settingsVersion ) { if (settingsHeader.isActive == 0xF1 && settingsHeader.version == settingsVersion) {
settingsFlashBlock = block; settingsFlashBlock = block;
return true; return true;
}
} }
return false; }
return false;
} }
void Settings::ReadSettingsData() { void Settings::ReadSettingsData() {
uint8_t bufferSettings[sizeof(settings)]; uint8_t bufferSettings[sizeof(settings)];
spiNorFlash.Read( settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings) ); spiNorFlash.Read(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
std::memcpy(&settings, bufferSettings, sizeof(settings)); std::memcpy(&settings, bufferSettings, sizeof(settings));
} }
void Settings::EraseBlock() { void Settings::EraseBlock() {
spiNorFlash.SectorErase(settingsBaseAddr + (settingsFlashBlock * 0x1000)); spiNorFlash.SectorErase(settingsBaseAddr + (settingsFlashBlock * 0x1000));
} }
void Settings::SetHeader( bool state ) { void Settings::SetHeader(bool state) {
SettingsHeader settingsHeader; SettingsHeader settingsHeader;
uint8_t bufferHead[sizeof(settingsHeader)]; uint8_t bufferHead[sizeof(settingsHeader)];
settingsHeader.isActive = state ? 0xF1 : 0xF0; settingsHeader.isActive = state ? 0xF1 : 0xF0;
settingsHeader.version = settingsVersion; settingsHeader.version = settingsVersion;
std::memcpy(bufferHead, &settingsHeader, sizeof(settingsHeader));
spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000), bufferHead, sizeof(settingsHeader));
std::memcpy(bufferHead, &settingsHeader, sizeof(settingsHeader));
spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000), bufferHead, sizeof(settingsHeader));
} }
void Settings::SaveSettingsData() { void Settings::SaveSettingsData() {
uint8_t bufferSettings[sizeof(settings)]; uint8_t bufferSettings[sizeof(settings)];
std::memcpy(bufferSettings, &settings, sizeof(settings)); std::memcpy(bufferSettings, &settings, sizeof(settings));
spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings)); spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
} }
void Settings::LoadSettingsFromFlash() { void Settings::LoadSettingsFromFlash() {
if ( settingsFlashBlock == 99 ) { if (settingsFlashBlock == 99) {
// Find current Block, if can't find use default settings and set block to 0 ans save ! // Find current Block, if can't find use default settings and set block to 0 ans save !
if ( FindHeader() ) { if (FindHeader()) {
ReadSettingsData(); ReadSettingsData();
} else {
SaveSettingsToFlash();
}
} else { } else {
// Read Settings from flash... SaveSettingsToFlash();
// never used :)
ReadSettingsData();
} }
} else {
// Read Settings from flash...
// never used :)
ReadSettingsData();
}
} }
void Settings::SaveSettingsToFlash() { void Settings::SaveSettingsToFlash() {
// calculate where to save...
// mark current to inactive
// erase the new location and save
// set settingsFlashBlock
// if first time hever, only saves to block 0 and set settingsFlashBlock // calculate where to save...
// mark current to inactive
// erase the new location and save
// set settingsFlashBlock
if ( settingsFlashBlock != 99 ) { // if first time hever, only saves to block 0 and set settingsFlashBlock
SetHeader( false );
}
settingsFlashBlock++; if (settingsFlashBlock != 99) {
if ( settingsFlashBlock > 9 ) settingsFlashBlock = 0; SetHeader(false);
}
EraseBlock(); settingsFlashBlock++;
SetHeader( true ); if (settingsFlashBlock > 9)
SaveSettingsData(); settingsFlashBlock = 0;
EraseBlock();
SetHeader(true);
SaveSettingsData();
} }

View file

@ -8,98 +8,121 @@
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class Settings { class Settings {
public: public:
enum class ClockType {H24, H12}; enum class ClockType { H24, H12 };
enum class Vibration {ON, OFF}; enum class Vibration { ON, OFF };
enum class WakeUpMode {None, SingleTap, DoubleTap, RaiseWrist}; enum class WakeUpMode { None, SingleTap, DoubleTap, RaiseWrist };
Settings( Pinetime::Drivers::SpiNorFlash &spiNorFlash ); Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash);
void Init(); void Init();
void SaveSettings(); void SaveSettings();
void SetClockFace( uint8_t face ) { void SetClockFace(uint8_t face) {
if ( face != settings.clockFace ) settingsChanged = true; if (face != settings.clockFace)
settings.clockFace = face; settingsChanged = true;
}; settings.clockFace = face;
uint8_t GetClockFace() const { return settings.clockFace; }; };
uint8_t GetClockFace() const {
return settings.clockFace;
};
void SetAppMenu( uint8_t menu ) { appMenu = menu; }; void SetAppMenu(uint8_t menu) {
uint8_t GetAppMenu() { return appMenu; }; appMenu = menu;
};
uint8_t GetAppMenu() {
return appMenu;
};
void SetSettingsMenu( uint8_t menu ) { settingsMenu = menu; }; void SetSettingsMenu(uint8_t menu) {
uint8_t GetSettingsMenu() const { return settingsMenu; }; settingsMenu = menu;
};
uint8_t GetSettingsMenu() const {
return settingsMenu;
};
void SetClockType( ClockType clocktype ) { void SetClockType(ClockType clocktype) {
if ( clocktype != settings.clockType ) settingsChanged = true; if (clocktype != settings.clockType)
settings.clockType = clocktype; settingsChanged = true;
}; settings.clockType = clocktype;
ClockType GetClockType() const { return settings.clockType; }; };
ClockType GetClockType() const {
return settings.clockType;
};
void SetVibrationStatus( Vibration status ) { void SetVibrationStatus(Vibration status) {
if ( status != settings.vibrationStatus ) settingsChanged = true; if (status != settings.vibrationStatus)
settings.vibrationStatus = status; settingsChanged = true;
}; settings.vibrationStatus = status;
Vibration GetVibrationStatus() const { return settings.vibrationStatus; }; };
Vibration GetVibrationStatus() const {
return settings.vibrationStatus;
};
void SetScreenTimeOut( uint32_t timeout ) { void SetScreenTimeOut(uint32_t timeout) {
if ( timeout != settings.screenTimeOut ) settingsChanged = true; if (timeout != settings.screenTimeOut)
settings.screenTimeOut = timeout; settingsChanged = true;
}; settings.screenTimeOut = timeout;
uint32_t GetScreenTimeOut() const { return settings.screenTimeOut; }; };
uint32_t GetScreenTimeOut() const {
return settings.screenTimeOut;
};
void setWakeUpMode( WakeUpMode wakeUp ) { void setWakeUpMode(WakeUpMode wakeUp) {
if ( wakeUp != settings.wakeUpMode ) settingsChanged = true; if (wakeUp != settings.wakeUpMode)
settings.wakeUpMode = wakeUp; settingsChanged = true;
}; settings.wakeUpMode = wakeUp;
WakeUpMode getWakeUpMode() const { return settings.wakeUpMode; }; };
WakeUpMode getWakeUpMode() const {
return settings.wakeUpMode;
};
void SetBrightness( Controllers::BrightnessController::Levels level ) { void SetBrightness(Controllers::BrightnessController::Levels level) {
if ( level != settings.brightLevel ) settingsChanged = true; if (level != settings.brightLevel)
settings.brightLevel = level; settingsChanged = true;
}; settings.brightLevel = level;
Controllers::BrightnessController::Levels GetBrightness() const { return settings.brightLevel; }; };
Controllers::BrightnessController::Levels GetBrightness() const {
return settings.brightLevel;
};
private: private:
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
struct SettingsData {
Pinetime::Drivers::SpiNorFlash& spiNorFlash; ClockType clockType = ClockType::H24;
struct SettingsData { Vibration vibrationStatus = Vibration::ON;
ClockType clockType = ClockType::H24; uint8_t clockFace = 0;
Vibration vibrationStatus = Vibration::ON;
uint8_t clockFace = 0; uint32_t stepsGoal = 1000;
uint32_t screenTimeOut = 15000;
uint32_t stepsGoal = 1000; WakeUpMode wakeUpMode = WakeUpMode::None;
uint32_t screenTimeOut = 15000;
WakeUpMode wakeUpMode = WakeUpMode::None; Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
};
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium; SettingsData settings;
bool settingsChanged = false;
}; uint8_t appMenu = 0;
uint8_t settingsMenu = 0;
SettingsData settings; // There are 10 blocks of reserved flash to save settings
bool settingsChanged = false; // to minimize wear, the recording is done in a rotating way by the 10 blocks
uint8_t settingsFlashBlock = 99; // default to indicate it needs to find the active block
uint8_t appMenu = 0; static constexpr uint32_t settingsBaseAddr = 0x3F6000; // Flash Settings Location
uint8_t settingsMenu = 0; static constexpr uint16_t settingsVersion = 0x0100; // Flash Settings Version
// There are 10 blocks of reserved flash to save settings
// to minimize wear, the recording is done in a rotating way by the 10 blocks
uint8_t settingsFlashBlock = 99; // default to indicate it needs to find the active block
static constexpr uint32_t settingsBaseAddr = 0x3F6000; // Flash Settings Location
static constexpr uint16_t settingsVersion = 0x0100; // Flash Settings Version
bool FindHeader();
void ReadSettingsData();
void EraseBlock();
void SetHeader( bool state );
void SaveSettingsData();
void LoadSettingsFromFlash();
void SaveSettingsToFlash();
bool FindHeader();
void ReadSettingsData();
void EraseBlock();
void SetHeader(bool state);
void SaveSettingsData();
void LoadSettingsFromFlash();
void SaveSettingsToFlash();
}; };
} }
} }

View file

@ -3,9 +3,30 @@
namespace Pinetime { namespace Pinetime {
namespace Applications { namespace Applications {
enum class Apps { enum class Apps {
None, Launcher, Clock, SysInfo, FirmwareUpdate, FirmwareValidation, NotificationsPreview, Notifications, FlashLight, BatteryInfo, None,
Music, Paint, Paddle, Twos, HeartRate, Navigation, StopWatch, Motion, Launcher,
QuickSettings, Settings, SettingWatchFace, SettingTimeFormat, SettingDisplay, SettingWakeUp Clock,
SysInfo,
FirmwareUpdate,
FirmwareValidation,
NotificationsPreview,
Notifications,
FlashLight,
BatteryInfo,
Music,
Paint,
Paddle,
Twos,
HeartRate,
Navigation,
StopWatch,
Motion,
QuickSettings,
Settings,
SettingWatchFace,
SettingTimeFormat,
SettingDisplay,
SettingWakeUp
}; };
} }
} }

View file

@ -40,29 +40,33 @@
using namespace Pinetime::Applications; using namespace Pinetime::Applications;
using namespace Pinetime::Applications::Display; using namespace Pinetime::Applications::Display;
DisplayApp::DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &touchPanel, DisplayApp::DisplayApp(Drivers::St7789& lcd,
Controllers::Battery &batteryController, Controllers::Ble &bleController, Components::LittleVgl& lvgl,
Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog, Drivers::Cst816S& touchPanel,
System::SystemTask &systemTask, Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings &settingsController, Controllers::Settings& settingsController,
Pinetime::Controllers::MotionController& motionController) : Pinetime::Controllers::MotionController& motionController)
lcd{lcd}, : lcd {lcd},
lvgl{lvgl}, lvgl {lvgl},
touchPanel{touchPanel}, touchPanel {touchPanel},
batteryController{batteryController}, batteryController {batteryController},
bleController{bleController}, bleController {bleController},
dateTimeController{dateTimeController}, dateTimeController {dateTimeController},
watchdog{watchdog}, watchdog {watchdog},
systemTask{systemTask}, systemTask {systemTask},
notificationManager{notificationManager}, notificationManager {notificationManager},
heartRateController{heartRateController}, heartRateController {heartRateController},
settingsController{settingsController}, settingsController {settingsController},
motionController{motionController} { motionController {motionController} {
msgQueue = xQueueCreate(queueSize, itemSize); msgQueue = xQueueCreate(queueSize, itemSize);
// Start clock when smartwatch boots // Start clock when smartwatch boots
LoadApp( Apps::Clock, DisplayApp::FullRefreshDirections::None ); LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
} }
void DisplayApp::Start() { void DisplayApp::Start() {
@ -70,8 +74,8 @@ void DisplayApp::Start() {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
} }
void DisplayApp::Process(void *instance) { void DisplayApp::Process(void* instance) {
auto *app = static_cast<DisplayApp *>(instance); auto* app = static_cast<DisplayApp*>(instance);
NRF_LOG_INFO("displayapp task started!"); NRF_LOG_INFO("displayapp task started!");
app->InitHw(); app->InitHw();
@ -112,7 +116,7 @@ void DisplayApp::Refresh() {
switch (msg) { switch (msg) {
case Messages::GoToSleep: case Messages::GoToSleep:
brightnessController.Backup(); brightnessController.Backup();
while(brightnessController.Level() != Controllers::BrightnessController::Levels::Off) { while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
brightnessController.Lower(); brightnessController.Lower();
vTaskDelay(100); vTaskDelay(100);
} }
@ -127,30 +131,32 @@ void DisplayApp::Refresh() {
break; break;
case Messages::UpdateTimeOut: case Messages::UpdateTimeOut:
systemTask.PushMessage(System::SystemTask::Messages::UpdateTimeOut); systemTask.PushMessage(System::SystemTask::Messages::UpdateTimeOut);
break; break;
case Messages::UpdateBleConnection: case Messages::UpdateBleConnection:
// clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected : Screens::Clock::BleConnectionStates::NotConnected); // clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected :
// Screens::Clock::BleConnectionStates::NotConnected);
break; break;
case Messages::UpdateBatteryLevel: case Messages::UpdateBatteryLevel:
batteryController.Update(); batteryController.Update();
break; break;
case Messages::NewNotification: case Messages::NewNotification:
LoadApp( Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down ); LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down);
break; break;
case Messages::TouchEvent: { case Messages::TouchEvent: {
if (state != States::Running) break; if (state != States::Running)
break;
auto gesture = OnTouchEvent(); auto gesture = OnTouchEvent();
if(!currentScreen->OnTouchEvent(gesture)) { if (!currentScreen->OnTouchEvent(gesture)) {
if ( currentApp == Apps::Clock ) { if (currentApp == Apps::Clock) {
switch (gesture) { switch (gesture) {
case TouchEvents::SwipeUp: case TouchEvents::SwipeUp:
LoadApp( Apps::Launcher, DisplayApp::FullRefreshDirections::Up ); LoadApp(Apps::Launcher, DisplayApp::FullRefreshDirections::Up);
break; break;
case TouchEvents::SwipeDown: case TouchEvents::SwipeDown:
LoadApp( Apps::Notifications, DisplayApp::FullRefreshDirections::Down ); LoadApp(Apps::Notifications, DisplayApp::FullRefreshDirections::Down);
break; break;
case TouchEvents::SwipeRight: case TouchEvents::SwipeRight:
LoadApp( Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim ); LoadApp(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim);
break; break;
case TouchEvents::DoubleTap: case TouchEvents::DoubleTap:
systemTask.PushMessage(System::SystemTask::Messages::GoToSleep); systemTask.PushMessage(System::SystemTask::Messages::GoToSleep);
@ -158,36 +164,35 @@ void DisplayApp::Refresh() {
default: default:
break; break;
} }
} else if ( returnTouchEvent == gesture ) { } else if (returnTouchEvent == gesture) {
LoadApp( returnToApp, returnDirection ); LoadApp(returnToApp, returnDirection);
} }
} }
} } break;
break;
case Messages::ButtonPushed: case Messages::ButtonPushed:
if( currentApp == Apps::Clock ) { if (currentApp == Apps::Clock) {
systemTask.PushMessage(System::SystemTask::Messages::GoToSleep); systemTask.PushMessage(System::SystemTask::Messages::GoToSleep);
} else { } else {
if ( !currentScreen->OnButtonPushed() ) { if (!currentScreen->OnButtonPushed()) {
LoadApp( returnToApp, returnDirection ); LoadApp(returnToApp, returnDirection);
} }
} }
break; break;
case Messages::BleFirmwareUpdateStarted: case Messages::BleFirmwareUpdateStarted:
LoadApp( Apps::FirmwareUpdate, DisplayApp::FullRefreshDirections::Down ); LoadApp(Apps::FirmwareUpdate, DisplayApp::FullRefreshDirections::Down);
break;
case Messages::UpdateDateTime:
// Added to remove warning
// What should happen here?
break; break;
case Messages::UpdateDateTime:
// Added to remove warning
// What should happen here?
break;
} }
} }
if(state != States::Idle && touchMode == TouchModes::Polling) { if (state != States::Idle && touchMode == TouchModes::Polling) {
auto info = touchPanel.GetTouchInfo(); auto info = touchPanel.GetTouchInfo();
if(info.action == 2) {// 2 = contact if (info.action == 2) { // 2 = contact
if(!currentScreen->OnTouchEvent(info.x, info.y)) { if (!currentScreen->OnTouchEvent(info.x, info.y)) {
lvgl.SetNewTapEvent(info.x, info.y); lvgl.SetNewTapEvent(info.x, info.y);
} }
} }
@ -195,14 +200,14 @@ void DisplayApp::Refresh() {
} }
void DisplayApp::RunningState() { void DisplayApp::RunningState() {
if(!currentScreen->Refresh()) { if (!currentScreen->Refresh()) {
LoadApp( returnToApp, returnDirection ); LoadApp(returnToApp, returnDirection);
} }
lv_task_handler(); lv_task_handler();
} }
void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) { void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) {
LoadApp( app, direction ); LoadApp(app, direction);
} }
void DisplayApp::returnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent) { void DisplayApp::returnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent) {
@ -213,19 +218,26 @@ void DisplayApp::returnApp(Apps app, DisplayApp::FullRefreshDirections direction
void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) { void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) {
currentScreen.reset(nullptr); currentScreen.reset(nullptr);
SetFullRefresh( direction ); SetFullRefresh(direction);
// default return to launcher // default return to launcher
returnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::SwipeDown); returnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::SwipeDown);
switch(app) { switch (app) {
case Apps::Launcher: case Apps::Launcher:
currentScreen = std::make_unique<Screens::ApplicationList>(this, settingsController, batteryController, dateTimeController); currentScreen = std::make_unique<Screens::ApplicationList>(this, settingsController, batteryController, dateTimeController);
returnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::SwipeDown); returnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
case Apps::None: case Apps::None:
case Apps::Clock: case Apps::Clock:
currentScreen = std::make_unique<Screens::Clock>(this, dateTimeController, batteryController, bleController, notificationManager, settingsController, heartRateController, motionController); currentScreen = std::make_unique<Screens::Clock>(this,
dateTimeController,
batteryController,
bleController,
notificationManager,
settingsController,
heartRateController,
motionController);
break; break;
case Apps::FirmwareValidation: case Apps::FirmwareValidation:
@ -237,17 +249,20 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
break; break;
case Apps::Notifications: case Apps::Notifications:
currentScreen = std::make_unique<Screens::Notifications>(this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Normal); currentScreen = std::make_unique<Screens::Notifications>(
this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Normal);
returnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); returnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
break; break;
case Apps::NotificationsPreview: case Apps::NotificationsPreview:
currentScreen = std::make_unique<Screens::Notifications>(this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Preview); currentScreen = std::make_unique<Screens::Notifications>(
this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Preview);
returnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); returnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
break; break;
// Settings // Settings
case Apps::QuickSettings: case Apps::QuickSettings:
currentScreen = std::make_unique<Screens::QuickSettings>(this, batteryController, dateTimeController, brightnessController, settingsController); currentScreen =
std::make_unique<Screens::QuickSettings>(this, batteryController, dateTimeController, brightnessController, settingsController);
returnApp(Apps::Clock, FullRefreshDirections::LeftAnim, TouchEvents::SwipeLeft); returnApp(Apps::Clock, FullRefreshDirections::LeftAnim, TouchEvents::SwipeLeft);
break; break;
case Apps::Settings: case Apps::Settings:
@ -275,10 +290,11 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
returnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); returnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
case Apps::SysInfo: case Apps::SysInfo:
currentScreen = std::make_unique<Screens::SystemInfo>(this, dateTimeController, batteryController, brightnessController, bleController, watchdog); currentScreen =
std::make_unique<Screens::SystemInfo>(this, dateTimeController, batteryController, brightnessController, bleController, watchdog);
returnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); returnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
// //
case Apps::FlashLight: case Apps::FlashLight:
currentScreen = std::make_unique<Screens::FlashLight>(this, systemTask, brightnessController); currentScreen = std::make_unique<Screens::FlashLight>(this, systemTask, brightnessController);
@ -313,7 +329,6 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
} }
void DisplayApp::IdleState() { void DisplayApp::IdleState() {
} }
void DisplayApp::PushMessage(Messages msg) { void DisplayApp::PushMessage(Messages msg) {
@ -328,10 +343,10 @@ void DisplayApp::PushMessage(Messages msg) {
TouchEvents DisplayApp::OnTouchEvent() { TouchEvents DisplayApp::OnTouchEvent() {
auto info = touchPanel.GetTouchInfo(); auto info = touchPanel.GetTouchInfo();
if(info.isTouch) { if (info.isTouch) {
switch(info.gesture) { switch (info.gesture) {
case Pinetime::Drivers::Cst816S::Gestures::SingleTap: case Pinetime::Drivers::Cst816S::Gestures::SingleTap:
if(touchMode == TouchModes::Gestures) if (touchMode == TouchModes::Gestures)
lvgl.SetNewTapEvent(info.x, info.y); lvgl.SetNewTapEvent(info.x, info.y);
return TouchEvents::Tap; return TouchEvents::Tap;
case Pinetime::Drivers::Cst816S::Gestures::LongPress: case Pinetime::Drivers::Cst816S::Gestures::LongPress:
@ -355,7 +370,7 @@ TouchEvents DisplayApp::OnTouchEvent() {
} }
void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) { void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
switch(direction){ switch (direction) {
case DisplayApp::FullRefreshDirections::Down: case DisplayApp::FullRefreshDirections::Down:
lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Down); lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Down);
break; break;
@ -374,11 +389,11 @@ void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
case DisplayApp::FullRefreshDirections::RightAnim: case DisplayApp::FullRefreshDirections::RightAnim:
lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::RightAnim); lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::RightAnim);
break; break;
default: break; default:
break;
} }
} }
void DisplayApp::SetTouchMode(DisplayApp::TouchModes mode) { void DisplayApp::SetTouchMode(DisplayApp::TouchModes mode) {
touchMode = mode; touchMode = mode;
} }

View file

@ -35,74 +35,73 @@ namespace Pinetime {
}; };
namespace Applications { namespace Applications {
class DisplayApp { class DisplayApp {
public: public:
enum class States {Idle, Running}; enum class States { Idle, Running };
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim }; enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
enum class TouchModes { Gestures, Polling }; enum class TouchModes { Gestures, Polling };
DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &, DisplayApp(Drivers::St7789& lcd,
Controllers::Battery &batteryController, Controllers::Ble &bleController, Components::LittleVgl& lvgl,
Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog, Drivers::Cst816S&,
System::SystemTask &systemTask, Controllers::Battery& batteryController,
Pinetime::Controllers::NotificationManager& notificationManager, Controllers::Ble& bleController,
Pinetime::Controllers::HeartRateController& heartRateController, Controllers::DateTime& dateTimeController,
Controllers::Settings &settingsController, Drivers::WatchdogView& watchdog,
Pinetime::Controllers::MotionController& motionController System::SystemTask& systemTask,
); Pinetime::Controllers::NotificationManager& notificationManager,
void Start(); Pinetime::Controllers::HeartRateController& heartRateController,
void PushMessage(Display::Messages msg); Controllers::Settings& settingsController,
Pinetime::Controllers::MotionController& motionController);
void Start();
void PushMessage(Display::Messages msg);
void StartApp(Apps app, DisplayApp::FullRefreshDirections direction); void StartApp(Apps app, DisplayApp::FullRefreshDirections direction);
void SetFullRefresh(FullRefreshDirections direction); void SetFullRefresh(FullRefreshDirections direction);
void SetTouchMode(TouchModes mode); void SetTouchMode(TouchModes mode);
private: private:
Pinetime::Drivers::St7789& lcd;
Pinetime::Components::LittleVgl& lvgl;
Pinetime::Drivers::Cst816S& touchPanel;
Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Drivers::WatchdogView& watchdog;
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::HeartRateController& heartRateController;
Pinetime::Controllers::Settings& settingsController;
Pinetime::Controllers::MotionController& motionController;
Pinetime::Drivers::St7789& lcd; Pinetime::Controllers::FirmwareValidator validator;
Pinetime::Components::LittleVgl& lvgl; Controllers::BrightnessController brightnessController;
Pinetime::Drivers::Cst816S& touchPanel;
Pinetime::Controllers::Battery &batteryController;
Pinetime::Controllers::Ble &bleController;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Drivers::WatchdogView& watchdog;
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::HeartRateController& heartRateController;
Pinetime::Controllers::Settings& settingsController;
Pinetime::Controllers::MotionController& motionController;
Pinetime::Controllers::FirmwareValidator validator; TaskHandle_t taskHandle;
Controllers::BrightnessController brightnessController;
TaskHandle_t taskHandle; States state = States::Running;
QueueHandle_t msgQueue;
States state = States::Running; static constexpr uint8_t queueSize = 10;
QueueHandle_t msgQueue; static constexpr uint8_t itemSize = 1;
static constexpr uint8_t queueSize = 10; std::unique_ptr<Screens::Screen> currentScreen;
static constexpr uint8_t itemSize = 1;
std::unique_ptr<Screens::Screen> currentScreen; Apps currentApp = Apps::None;
Apps returnToApp = Apps::None;
FullRefreshDirections returnDirection = FullRefreshDirections::None;
TouchEvents returnTouchEvent = TouchEvents::None;
Apps currentApp = Apps::None; TouchModes touchMode = TouchModes::Gestures;
Apps returnToApp = Apps::None;
FullRefreshDirections returnDirection = FullRefreshDirections::None;
TouchEvents returnTouchEvent = TouchEvents::None;
TouchModes touchMode = TouchModes::Gestures;
TouchEvents OnTouchEvent();
void RunningState();
void IdleState();
static void Process(void* instance);
void InitHw();
void Refresh();
void returnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent);
void LoadApp(Apps app, DisplayApp::FullRefreshDirections direction);
TouchEvents OnTouchEvent();
void RunningState();
void IdleState();
static void Process(void* instance);
void InitHw();
void Refresh();
void returnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent);
void LoadApp(Apps app, DisplayApp::FullRefreshDirections direction);
}; };
} }
} }

View file

@ -7,17 +7,20 @@
using namespace Pinetime::Applications; using namespace Pinetime::Applications;
DisplayApp::DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &touchPanel, DisplayApp::DisplayApp(Drivers::St7789& lcd,
Controllers::Battery &batteryController, Controllers::Ble &bleController, Components::LittleVgl& lvgl,
Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog, Drivers::Cst816S& touchPanel,
System::SystemTask &systemTask, Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Controllers::Settings& settingsController, Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::MotionController& motionController): Pinetime::Controllers::MotionController& motionController)
lcd{lcd}, bleController{bleController} { : lcd {lcd}, bleController {bleController} {
msgQueue = xQueueCreate(queueSize, itemSize); msgQueue = xQueueCreate(queueSize, itemSize);
} }
void DisplayApp::Start() { void DisplayApp::Start() {
@ -25,8 +28,8 @@ void DisplayApp::Start() {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
} }
void DisplayApp::Process(void *instance) { void DisplayApp::Process(void* instance) {
auto *app = static_cast<DisplayApp *>(instance); auto* app = static_cast<DisplayApp*>(instance);
NRF_LOG_INFO("displayapp task started!"); NRF_LOG_INFO("displayapp task started!");
// Send a dummy notification to unlock the lvgl display driver for the first iteration // Send a dummy notification to unlock the lvgl display driver for the first iteration
@ -61,8 +64,9 @@ void DisplayApp::Refresh() {
} }
if (bleController.IsFirmwareUpdating()) { if (bleController.IsFirmwareUpdating()) {
uint8_t percent = (static_cast<float>(bleController.FirmwareUpdateCurrentBytes()) / uint8_t percent =
static_cast<float>(bleController.FirmwareUpdateTotalBytes())) * 100.0f; (static_cast<float>(bleController.FirmwareUpdateCurrentBytes()) / static_cast<float>(bleController.FirmwareUpdateTotalBytes())) *
100.0f;
switch (bleController.State()) { switch (bleController.State()) {
case Controllers::Ble::FirmwareUpdateStates::Running: case Controllers::Ble::FirmwareUpdateStates::Running:
DisplayOtaProgress(percent, colorWhite); DisplayOtaProgress(percent, colorWhite);
@ -81,20 +85,20 @@ void DisplayApp::Refresh() {
void DisplayApp::DisplayLogo(uint16_t color) { void DisplayApp::DisplayLogo(uint16_t color) {
Pinetime::Tools::RleDecoder rleDecoder(infinitime_nb, sizeof(infinitime_nb), color, colorBlack); Pinetime::Tools::RleDecoder rleDecoder(infinitime_nb, sizeof(infinitime_nb), color, colorBlack);
for(int i = 0; i < displayWidth; i++) { for (int i = 0; i < displayWidth; i++) {
rleDecoder.DecodeNext(displayBuffer, displayWidth * bytesPerPixel); rleDecoder.DecodeNext(displayBuffer, displayWidth * bytesPerPixel);
ulTaskNotifyTake(pdTRUE, 500); ulTaskNotifyTake(pdTRUE, 500);
lcd.DrawBuffer(0, i, displayWidth, 1, reinterpret_cast<const uint8_t *>(displayBuffer), displayWidth * bytesPerPixel); lcd.DrawBuffer(0, i, displayWidth, 1, reinterpret_cast<const uint8_t*>(displayBuffer), displayWidth * bytesPerPixel);
} }
} }
void DisplayApp::DisplayOtaProgress(uint8_t percent, uint16_t color) { void DisplayApp::DisplayOtaProgress(uint8_t percent, uint16_t color) {
const uint8_t barHeight = 20; const uint8_t barHeight = 20;
std::fill(displayBuffer, displayBuffer+(displayWidth * bytesPerPixel), color); std::fill(displayBuffer, displayBuffer + (displayWidth * bytesPerPixel), color);
for(int i = 0; i < barHeight; i++) { for (int i = 0; i < barHeight; i++) {
ulTaskNotifyTake(pdTRUE, 500); ulTaskNotifyTake(pdTRUE, 500);
uint16_t barWidth = std::min(static_cast<float>(percent) * 2.4f, static_cast<float>(displayWidth)); uint16_t barWidth = std::min(static_cast<float>(percent) * 2.4f, static_cast<float>(displayWidth));
lcd.DrawBuffer(0, displayWidth - barHeight + i, barWidth, 1, reinterpret_cast<const uint8_t *>(displayBuffer), barWidth * bytesPerPixel); lcd.DrawBuffer(0, displayWidth - barHeight + i, barWidth, 1, reinterpret_cast<const uint8_t*>(displayBuffer), barWidth * bytesPerPixel);
} }
} }

View file

@ -30,10 +30,14 @@ namespace Pinetime {
namespace Applications { namespace Applications {
class DisplayApp { class DisplayApp {
public: public:
DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &, DisplayApp(Drivers::St7789& lcd,
Controllers::Battery &batteryController, Controllers::Ble &bleController, Components::LittleVgl& lvgl,
Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog, Drivers::Cst816S&,
System::SystemTask &systemTask, Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Controllers::Settings& settingsController, Pinetime::Controllers::Settings& settingsController,
@ -49,7 +53,7 @@ namespace Pinetime {
void InitHw(); void InitHw();
void Refresh(); void Refresh();
Pinetime::Drivers::St7789& lcd; Pinetime::Drivers::St7789& lcd;
Controllers::Ble &bleController; Controllers::Ble& bleController;
static constexpr uint8_t queueSize = 10; static constexpr uint8_t queueSize = 10;
static constexpr uint8_t itemSize = 1; static constexpr uint8_t itemSize = 1;
@ -66,10 +70,6 @@ namespace Pinetime {
static constexpr uint16_t colorRedSwapped = 0x00ff; static constexpr uint16_t colorRedSwapped = 0x00ff;
static constexpr uint16_t colorBlack = 0x0000; static constexpr uint16_t colorBlack = 0x0000;
uint8_t displayBuffer[displayWidth * bytesPerPixel]; uint8_t displayBuffer[displayWidth * bytesPerPixel];
}; };
} }
} }

View file

@ -9,22 +9,25 @@
namespace Pinetime { namespace Pinetime {
namespace Components { namespace Components {
class LittleVgl { class LittleVgl {
public: public:
enum class FullRefreshDirections { None, Up, Down }; enum class FullRefreshDirections { None, Up, Down };
LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel) {} LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel) {
}
LittleVgl(const LittleVgl&) = delete;
LittleVgl& operator=(const LittleVgl&) = delete;
LittleVgl(LittleVgl&&) = delete;
LittleVgl& operator=(LittleVgl&&) = delete;
void FlushDisplay(const lv_area_t * area, lv_color_t * color_p) {}
bool GetTouchPadInfo(lv_indev_data_t *ptr) {return false;}
void SetFullRefresh(FullRefreshDirections direction) {}
void SetNewTapEvent(uint16_t x, uint16_t y) {}
LittleVgl(const LittleVgl&) = delete;
LittleVgl& operator=(const LittleVgl&) = delete;
LittleVgl(LittleVgl&&) = delete;
LittleVgl& operator=(LittleVgl&&) = delete;
void FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
}
bool GetTouchPadInfo(lv_indev_data_t* ptr) {
return false;
}
void SetFullRefresh(FullRefreshDirections direction) {
}
void SetNewTapEvent(uint16_t x, uint16_t y) {
}
}; };
} }
} }

View file

@ -11,17 +11,18 @@ using namespace Pinetime::Components;
lv_style_t* LabelBigStyle = nullptr; lv_style_t* LabelBigStyle = nullptr;
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { static void disp_flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) {
auto* lvgl = static_cast<LittleVgl*>(disp_drv->user_data); auto* lvgl = static_cast<LittleVgl*>(disp_drv->user_data);
lvgl->FlushDisplay(area, color_p); lvgl->FlushDisplay(area, color_p);
} }
bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) { bool touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data) {
auto* lvgl = static_cast<LittleVgl*>(indev_drv->user_data); auto* lvgl = static_cast<LittleVgl*>(indev_drv->user_data);
return lvgl->GetTouchPadInfo(data); return lvgl->GetTouchPadInfo(data);
} }
LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel) : lcd{lcd}, touchPanel{touchPanel}, previousClick{0,0} { LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel)
: lcd {lcd}, touchPanel {touchPanel}, previousClick {0, 0} {
lv_init(); lv_init();
InitTheme(); InitTheme();
InitDisplay(); InitDisplay();
@ -29,8 +30,8 @@ LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S&
} }
void LittleVgl::InitDisplay() { void LittleVgl::InitDisplay() {
lv_disp_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 4); /*Initialize the display buffer*/ lv_disp_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 4); /*Initialize the display buffer*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/ lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/ /*Set up the functions to access to your display*/
@ -59,7 +60,7 @@ void LittleVgl::InitTouchpad() {
} }
void LittleVgl::SetFullRefresh(FullRefreshDirections direction) { void LittleVgl::SetFullRefresh(FullRefreshDirections direction) {
if(scrollDirection == FullRefreshDirections::None) { if (scrollDirection == FullRefreshDirections::None) {
scrollDirection = direction; scrollDirection = direction;
if (scrollDirection == FullRefreshDirections::Down) { if (scrollDirection == FullRefreshDirections::Down) {
lv_disp_set_direction(lv_disp_get_default(), 1); lv_disp_set_direction(lv_disp_get_default(), 1);
@ -75,16 +76,16 @@ void LittleVgl::SetFullRefresh(FullRefreshDirections direction) {
} }
} }
void LittleVgl::FlushDisplay(const lv_area_t *area, lv_color_t *color_p) { void LittleVgl::FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
uint16_t y1, y2, width, height = 0; uint16_t y1, y2, width, height = 0;
ulTaskNotifyTake(pdTRUE, 200); ulTaskNotifyTake(pdTRUE, 200);
// NOtification is still needed (even if there is a mutex on SPI) because of the DataCommand pin // NOtification is still needed (even if there is a mutex on SPI) because of the DataCommand pin
// which cannot be set/clear during a transfert. // which cannot be set/clear during a transfert.
if( (scrollDirection == LittleVgl::FullRefreshDirections::Down) && (area->y2 == visibleNbLines - 1)) { if ((scrollDirection == LittleVgl::FullRefreshDirections::Down) && (area->y2 == visibleNbLines - 1)) {
writeOffset = ((writeOffset + totalNbLines) - visibleNbLines) % totalNbLines; writeOffset = ((writeOffset + totalNbLines) - visibleNbLines) % totalNbLines;
} else if( (scrollDirection == FullRefreshDirections::Up) && (area->y1 == 0) ) { } else if ((scrollDirection == FullRefreshDirections::Up) && (area->y1 == 0)) {
writeOffset = (writeOffset + visibleNbLines) % totalNbLines; writeOffset = (writeOffset + visibleNbLines) % totalNbLines;
} }
@ -94,11 +95,11 @@ void LittleVgl::FlushDisplay(const lv_area_t *area, lv_color_t *color_p) {
width = (area->x2 - area->x1) + 1; width = (area->x2 - area->x1) + 1;
height = (area->y2 - area->y1) + 1; height = (area->y2 - area->y1) + 1;
if(scrollDirection == LittleVgl::FullRefreshDirections::Down) { if (scrollDirection == LittleVgl::FullRefreshDirections::Down) {
if(area->y2 < visibleNbLines - 1) { if (area->y2 < visibleNbLines - 1) {
uint16_t toScroll = 0; uint16_t toScroll = 0;
if(area->y1 == 0) { if (area->y1 == 0) {
toScroll = height * 2; toScroll = height * 2;
scrollDirection = FullRefreshDirections::None; scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0); lv_disp_set_direction(lv_disp_get_default(), 0);
@ -106,19 +107,19 @@ void LittleVgl::FlushDisplay(const lv_area_t *area, lv_color_t *color_p) {
toScroll = height; toScroll = height;
} }
if(scrollOffset >= toScroll) if (scrollOffset >= toScroll)
scrollOffset -= toScroll; scrollOffset -= toScroll;
else { else {
toScroll -= scrollOffset; toScroll -= scrollOffset;
scrollOffset = (totalNbLines) - toScroll; scrollOffset = (totalNbLines) -toScroll;
} }
lcd.VerticalScrollStartAddress(scrollOffset); lcd.VerticalScrollStartAddress(scrollOffset);
} }
} else if(scrollDirection == FullRefreshDirections::Up) { } else if (scrollDirection == FullRefreshDirections::Up) {
if(area->y1 > 0) { if (area->y1 > 0) {
if(area->y2 == visibleNbLines - 1) { if (area->y2 == visibleNbLines - 1) {
scrollOffset += (height * 2); scrollOffset += (height * 2);
scrollDirection = FullRefreshDirections::None; scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0); lv_disp_set_direction(lv_disp_get_default(), 0);
@ -128,13 +129,13 @@ void LittleVgl::FlushDisplay(const lv_area_t *area, lv_color_t *color_p) {
scrollOffset = scrollOffset % totalNbLines; scrollOffset = scrollOffset % totalNbLines;
lcd.VerticalScrollStartAddress(scrollOffset); lcd.VerticalScrollStartAddress(scrollOffset);
} }
} else if(scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) { } else if (scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) {
if(area->x2 == visibleNbLines - 1) { if (area->x2 == visibleNbLines - 1) {
scrollDirection = FullRefreshDirections::None; scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0); lv_disp_set_direction(lv_disp_get_default(), 0);
} }
} else if(scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) { } else if (scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) {
if(area->x1 == 0) { if (area->x1 == 0) {
scrollDirection = FullRefreshDirections::None; scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0); lv_disp_set_direction(lv_disp_get_default(), 0);
} }
@ -143,17 +144,17 @@ void LittleVgl::FlushDisplay(const lv_area_t *area, lv_color_t *color_p) {
if (y2 < y1) { if (y2 < y1) {
height = totalNbLines - y1; height = totalNbLines - y1;
if ( height > 0 ) { if (height > 0) {
lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t *>(color_p), width * height * 2); lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
ulTaskNotifyTake(pdTRUE, 100); ulTaskNotifyTake(pdTRUE, 100);
} }
uint16_t pixOffset = width * height; uint16_t pixOffset = width * height;
height = y2 + 1; height = y2 + 1;
lcd.DrawBuffer(area->x1, 0, width, height, reinterpret_cast<const uint8_t *>(color_p + pixOffset), width * height * 2); lcd.DrawBuffer(area->x1, 0, width, height, reinterpret_cast<const uint8_t*>(color_p + pixOffset), width * height * 2);
} else { } else {
lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t *>(color_p), width * height * 2); lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
} }
// IMPORTANT!!! // IMPORTANT!!!
@ -167,8 +168,8 @@ void LittleVgl::SetNewTapEvent(uint16_t x, uint16_t y) {
tapped = true; tapped = true;
} }
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t *ptr) { bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
if(tapped) { if (tapped) {
ptr->point.x = tap_x; ptr->point.x = tap_x;
ptr->point.y = tap_y; ptr->point.y = tap_y;
ptr->state = LV_INDEV_STATE_PR; ptr->state = LV_INDEV_STATE_PR;
@ -202,14 +203,8 @@ bool LittleVgl::GetTouchPadInfo(lv_indev_data_t *ptr) {
void LittleVgl::InitTheme() { void LittleVgl::InitTheme() {
lv_theme_t * th = lv_pinetime_theme_init( lv_theme_t* th = lv_pinetime_theme_init(
LV_COLOR_WHITE, LV_COLOR_SILVER, LV_COLOR_WHITE, LV_COLOR_SILVER, 0, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20);
0,
&jetbrains_mono_bold_20,
&jetbrains_mono_bold_20,
&jetbrains_mono_bold_20,
&jetbrains_mono_bold_20);
lv_theme_set_act(th); lv_theme_set_act(th);
} }

View file

@ -10,50 +10,49 @@ namespace Pinetime {
namespace Components { namespace Components {
class LittleVgl { class LittleVgl {
public: public:
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim }; enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel); LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel);
LittleVgl(const LittleVgl&) = delete; LittleVgl(const LittleVgl&) = delete;
LittleVgl& operator=(const LittleVgl&) = delete; LittleVgl& operator=(const LittleVgl&) = delete;
LittleVgl(LittleVgl&&) = delete; LittleVgl(LittleVgl&&) = delete;
LittleVgl& operator=(LittleVgl&&) = delete; LittleVgl& operator=(LittleVgl&&) = delete;
void FlushDisplay(const lv_area_t * area, lv_color_t * color_p); void FlushDisplay(const lv_area_t* area, lv_color_t* color_p);
bool GetTouchPadInfo(lv_indev_data_t *ptr); bool GetTouchPadInfo(lv_indev_data_t* ptr);
void SetFullRefresh(FullRefreshDirections direction); void SetFullRefresh(FullRefreshDirections direction);
void SetNewTapEvent(uint16_t x, uint16_t y); void SetNewTapEvent(uint16_t x, uint16_t y);
private: private:
void InitDisplay(); void InitDisplay();
void InitTouchpad(); void InitTouchpad();
void InitTheme(); void InitTheme();
Pinetime::Drivers::St7789& lcd;
Pinetime::Drivers::Cst816S& touchPanel;
Pinetime::Drivers::St7789& lcd; lv_disp_buf_t disp_buf_2;
Pinetime::Drivers::Cst816S& touchPanel; lv_color_t buf2_1[LV_HOR_RES_MAX * 4];
lv_color_t buf2_2[LV_HOR_RES_MAX * 4];
lv_disp_drv_t disp_drv;
lv_point_t previousClick;
lv_disp_buf_t disp_buf_2; bool firstTouch = true;
lv_color_t buf2_1[LV_HOR_RES_MAX * 4]; static constexpr uint8_t nbWriteLines = 4;
lv_color_t buf2_2[LV_HOR_RES_MAX * 4]; static constexpr uint16_t totalNbLines = 320;
static constexpr uint16_t visibleNbLines = 240;
static constexpr uint8_t MaxScrollOffset() {
return LV_VER_RES_MAX - nbWriteLines;
}
FullRefreshDirections scrollDirection = FullRefreshDirections::None;
uint16_t writeOffset = 0;
uint16_t scrollOffset = 0;
lv_disp_drv_t disp_drv; uint16_t tap_x = 0;
lv_point_t previousClick; uint16_t tap_y = 0;
bool tapped = false;
bool firstTouch = true;
static constexpr uint8_t nbWriteLines = 4;
static constexpr uint16_t totalNbLines = 320;
static constexpr uint16_t visibleNbLines = 240;
static constexpr uint8_t MaxScrollOffset() { return LV_VER_RES_MAX - nbWriteLines; }
FullRefreshDirections scrollDirection = FullRefreshDirections::None;
uint16_t writeOffset = 0;
uint16_t scrollOffset = 0;
uint16_t tap_x = 0;
uint16_t tap_y = 0;
bool tapped = false;
}; };
} }
} }

View file

@ -3,8 +3,16 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Display { namespace Display {
enum class Messages : uint8_t { enum class Messages : uint8_t {
GoToSleep, GoToRunning, UpdateDateTime, UpdateBleConnection, UpdateBatteryLevel, TouchEvent, ButtonPushed, GoToSleep,
NewNotification, BleFirmwareUpdateStarted, UpdateTimeOut GoToRunning,
UpdateDateTime,
UpdateBleConnection,
UpdateBatteryLevel,
TouchEvent,
ButtonPushed,
NewNotification,
BleFirmwareUpdateStarted,
UpdateTimeOut
}; };
} }
} }

View file

@ -3,6 +3,6 @@
namespace Pinetime { namespace Pinetime {
namespace Applications { namespace Applications {
enum class TouchEvents { None, Tap, SwipeLeft, SwipeRight, SwipeUp, SwipeDown, LongTap, DoubleTap}; enum class TouchEvents { None, Tap, SwipeLeft, SwipeRight, SwipeUp, SwipeDown, LongTap, DoubleTap };
} }
} }

View file

@ -0,0 +1,44 @@
# Fonts
* [Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)
* [Awesome font from LVGL](https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff)
## Generate the fonts:
* Open the [LVGL font converter](https://lvgl.io/tools/fontconverter)
* Name : jetbrains_mono_bold_20
* Size : 20
* Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting
* Load the file `JetBrainsMono-Bold.tff` and specify the following range : `0x20-0x7f, 0x410-0x44f`
* Add a 2nd font, load the file `FontAwesome5-Solid+Brands+Regular.woff` and specify the following
range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf069, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf029, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024`
* Click on Convert, and download the file `jetbrains_mono_bold_20.c` and copy it in `src/DisplayApp/Fonts`
Add new symbols:
* Browse the [cheatsheet](https://fontawesome.com/cheatsheet/free/solid) and find your new symbols
* For each symbol, add its hex code (0xf641 for the 'Ad' icon, for example) to the *Range* list (Remember to keep this
readme updated with newest range list)
* Convert this hex value into a UTF-8 code
using [this site](http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=f185&mode=hex)
* Define the new symbols in `src/DisplayApp/Screens/Symbols.h`:
```
static constex char* newSymbol = "\xEF\x86\x85";
```
#### Navigation font
To create the navigtion.ttf I use the web app [icomoon](https://icomoon.io/app)
this app can import the svg files from the folder *src/displayapp/icons/navigation/unique* and creat a ttf file the
project for the site is *lv_font_navi_80.json* you can import it to add or remove icons
You can also use the online LVGL tool to create the .c
ttf file : navigation.ttf name : lv_font_navi_80 size : 80px Bpp : 2 bit-per-pixel range : 0xe900-0xe929
$lv_font_conv --font navigation.ttf -r '0xe900-0xe929' --size 80 --format lvgl --bpp 2 --no-prefilter -o
lv_font_navi_80.c
#### I use the method above to create the other ttf

View file

@ -1,41 +0,0 @@
#Fonts
* [Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)
* [Awesome font from LVGL](https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff)
## Generate the fonts:
* Open the [LVGL font converter](https://lvgl.io/tools/fontconverter)
* Name : jetbrains_mono_bold_20
* Size : 20
* Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting
* Load the file `JetBrainsMono-Bold.tff` and specify the following range : `0x20-0x7f, 0x410-0x44f`
* Add a 2nd font, load the file `FontAwesome5-Solid+Brands+Regular.woff` and specify the following range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf069, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf029, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024`
* Click on Convert, and download the file `jetbrains_mono_bold_20.c` and copy it in `src/DisplayApp/Fonts`
Add new symbols:
* Browse the [cheatsheet](https://fontawesome.com/cheatsheet/free/solid) and find your new symbols
* For each symbol, add its hex code (0xf641 for the 'Ad' icon, for example) to the *Range* list (Remember to keep this readme updated with newest range list)
* Convert this hex value into a UTF-8 code using [this site](http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=f185&mode=hex)
* Define the new symbols in `src/DisplayApp/Screens/Symbols.h`:
```
static constex char* newSymbol = "\xEF\x86\x85";
```
#### Navigation font
To create the navigtion.ttf I use the web app [icomoon](https://icomoon.io/app)
this app can import the svg files from the folder *src/displayapp/icons/navigation/unique* and creat a ttf file
the project for the site is *lv_font_navi_80.json* you can import it to add or remove icons
You can also use the online LVGL tool to create the .c
ttf file : navigation.ttf
name : lv_font_navi_80
size : 80px
Bpp : 2 bit-per-pixel
range : 0xe900-0xe929
$lv_font_conv --font navigation.ttf -r '0xe900-0xe929' --size 80 --format lvgl --bpp 2 --no-prefilter -o lv_font_navi_80.c
#### I use the method above to create the other ttf

View file

@ -19,7 +19,7 @@
/********************** /**********************
* STATIC PROTOTYPES * STATIC PROTOTYPES
**********************/ **********************/
static void theme_apply(lv_obj_t * obj, lv_theme_style_t name); static void theme_apply(lv_obj_t* obj, lv_theme_style_t name);
/********************** /**********************
* STATIC VARIABLES * STATIC VARIABLES
@ -67,244 +67,240 @@ static bool inited;
* STATIC FUNCTIONS * STATIC FUNCTIONS
**********************/ **********************/
static void style_init_reset(lv_style_t * style) static void style_init_reset(lv_style_t* style) {
{ if (inited)
if(inited) lv_style_reset(style); lv_style_reset(style);
else lv_style_init(style); else
lv_style_init(style);
} }
static void basic_init(void) {
static void basic_init(void) style_init_reset(&style_pad);
{ lv_style_set_pad_top(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 30);
lv_style_set_pad_bottom(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 30);
lv_style_set_pad_left(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 40);
lv_style_set_pad_right(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 40);
style_init_reset(&style_pad); style_init_reset(&style_circle);
lv_style_set_pad_top(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 30); lv_style_set_radius(&style_circle, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_pad_bottom(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 30);
lv_style_set_pad_left(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 40);
lv_style_set_pad_right(&style_pad, LV_STATE_DEFAULT, LV_VER_RES / 40);
style_init_reset(&style_circle); style_init_reset(&style_bg);
lv_style_set_radius(&style_circle, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_style_set_bg_opa(&style_bg, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_bg, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_style_set_text_font(&style_bg, LV_STATE_DEFAULT, theme.font_normal);
style_init_reset(&style_bg); style_init_reset(&style_box);
lv_style_set_bg_opa(&style_bg, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_box, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_bg, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_style_set_radius(&style_box, LV_STATE_DEFAULT, 10);
lv_style_set_text_font(&style_bg, LV_STATE_DEFAULT, theme.font_normal); lv_style_set_value_color(&style_box, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_value_font(&style_box, LV_STATE_DEFAULT, theme.font_normal);
style_init_reset(&style_box); style_init_reset(&style_box_border);
lv_style_set_bg_opa(&style_box, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_box_border, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_radius(&style_box, LV_STATE_DEFAULT, 10); lv_style_set_border_width(&style_box_border, LV_STATE_DEFAULT, 2);
lv_style_set_value_color(&style_box, LV_STATE_DEFAULT, LV_PINETIME_BLUE); lv_style_set_border_color(&style_box_border, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_value_font(&style_box, LV_STATE_DEFAULT, theme.font_normal); lv_style_set_text_color(&style_box, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
style_init_reset(&style_box_border); style_init_reset(&style_title);
lv_style_set_bg_opa(&style_box_border, LV_STATE_DEFAULT, LV_OPA_TRANSP); lv_style_set_text_color(&style_title, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_border_width(&style_box_border, LV_STATE_DEFAULT, 2); lv_style_set_text_font(&style_title, LV_STATE_DEFAULT, theme.font_subtitle);
lv_style_set_border_color(&style_box_border, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_text_color(&style_box, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
style_init_reset(&style_label_white);
lv_style_set_text_color(&style_label_white, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
style_init_reset(&style_title); style_init_reset(&style_btn);
lv_style_set_text_color(&style_title, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_radius(&style_btn, LV_STATE_DEFAULT, 10);
lv_style_set_text_font(&style_title, LV_STATE_DEFAULT, theme.font_subtitle); lv_style_set_bg_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0x2F3540));
lv_style_set_bg_color(&style_btn, LV_STATE_CHECKED, LV_COLOR_GREEN);
lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x2F3540));
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);
style_init_reset(&style_label_white); lv_style_set_text_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
lv_style_set_text_color(&style_label_white, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
lv_style_set_text_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888));
style_init_reset(&style_btn); lv_style_set_value_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
lv_style_set_radius(&style_btn, LV_STATE_DEFAULT, 10); lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
lv_style_set_bg_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
lv_style_set_bg_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0x2F3540)); lv_style_set_value_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888));
lv_style_set_bg_color(&style_btn, LV_STATE_CHECKED, LV_COLOR_GREEN);
lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x2F3540));
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_pad_left(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff)); lv_style_set_pad_right(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff)); lv_style_set_pad_top(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_text_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888)); lv_style_set_pad_bottom(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_pad_inner(&style_btn, LV_STATE_DEFAULT, LV_DPX(15));
lv_style_set_outline_width(&style_btn, LV_STATE_DEFAULT, LV_DPX(2));
lv_style_set_outline_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_0);
lv_style_set_outline_color(&style_btn, LV_STATE_DEFAULT, theme.color_primary);
lv_style_set_transition_time(&style_btn, LV_STATE_DEFAULT, 0);
lv_style_set_transition_delay(&style_btn, LV_STATE_DEFAULT, 0);
lv_style_set_value_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff)); style_init_reset(&style_btn_border);
lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff)); lv_style_set_radius(&style_btn_border, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff)); lv_style_set_border_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_value_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888)); lv_style_set_border_width(&style_btn_border, LV_STATE_DEFAULT, 2);
lv_style_set_bg_opa(&style_btn_border, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_bg_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_text_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_value_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_transition_prop_3(&style_btn_border, LV_STATE_DEFAULT, LV_STYLE_BG_OPA);
lv_style_set_pad_left(&style_btn, LV_STATE_DEFAULT, LV_DPX(20)); style_init_reset(&style_icon);
lv_style_set_pad_right(&style_btn, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_text_color(&style_icon, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_pad_top(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_pad_bottom(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_pad_inner(&style_btn, LV_STATE_DEFAULT, LV_DPX(15));
lv_style_set_outline_width(&style_btn, LV_STATE_DEFAULT, LV_DPX(2));
lv_style_set_outline_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_0);
lv_style_set_outline_color(&style_btn, LV_STATE_DEFAULT, theme.color_primary);
lv_style_set_transition_time(&style_btn, LV_STATE_DEFAULT, 0);
lv_style_set_transition_delay(&style_btn, LV_STATE_DEFAULT, 0);
style_init_reset(&style_btn_border); style_init_reset(&style_back);
lv_style_set_radius(&style_btn_border, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_style_set_value_color(&style_back, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_border_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_value_str(&style_back, LV_STATE_DEFAULT, LV_SYMBOL_LEFT);
lv_style_set_border_width(&style_btn_border, LV_STATE_DEFAULT, 2); lv_style_set_value_font(&style_back, LV_STATE_DEFAULT, theme.font_subtitle);
lv_style_set_bg_opa(&style_btn_border, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_bg_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_text_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_value_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_transition_prop_3(&style_btn_border, LV_STATE_DEFAULT, LV_STYLE_BG_OPA);
style_init_reset(&style_icon); style_init_reset(&style_bar_indic);
lv_style_set_text_color(&style_icon, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_bg_opa(&style_bar_indic, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_radius(&style_bar_indic, LV_STATE_DEFAULT, 10);
style_init_reset(&style_back); style_init_reset(&style_scrollbar);
lv_style_set_value_color(&style_back, LV_STATE_DEFAULT, LV_PINETIME_GRAY); lv_style_set_bg_opa(&style_scrollbar, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_value_str(&style_back, LV_STATE_DEFAULT, LV_SYMBOL_LEFT); lv_style_set_radius(&style_scrollbar, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_value_font(&style_back, LV_STATE_DEFAULT, theme.font_subtitle); lv_style_set_bg_color(&style_scrollbar, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_GRAY);
lv_style_set_size(&style_scrollbar, LV_STATE_DEFAULT, LV_HOR_RES / 80);
lv_style_set_pad_right(&style_scrollbar, LV_STATE_DEFAULT, LV_HOR_RES / 60);
style_init_reset(&style_bar_indic); style_init_reset(&style_list_btn);
lv_style_set_bg_opa(&style_bar_indic, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_list_btn, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_radius(&style_bar_indic, LV_STATE_DEFAULT, 10); lv_style_set_bg_color(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_bg_color(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_GRAY);
lv_style_set_bg_color(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, lv_color_darken(LV_PINETIME_GRAY, LV_OPA_20));
lv_style_set_text_color(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_WHITE);
lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, LV_PINETIME_WHITE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_WHITE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, LV_PINETIME_WHITE);
lv_style_set_pad_left(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25);
lv_style_set_pad_right(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25);
lv_style_set_pad_top(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 100);
lv_style_set_pad_bottom(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 100);
lv_style_set_pad_inner(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 50);
style_init_reset(&style_scrollbar); style_init_reset(&style_ddlist_list);
lv_style_set_bg_opa(&style_scrollbar, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25);
lv_style_set_radius(&style_scrollbar, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_style_set_shadow_width(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 20);
lv_style_set_bg_color(&style_scrollbar, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_GRAY); lv_style_set_shadow_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_size(&style_scrollbar, LV_STATE_DEFAULT, LV_HOR_RES / 80);
lv_style_set_pad_right(&style_scrollbar, LV_STATE_DEFAULT, LV_HOR_RES / 60);
style_init_reset(&style_list_btn); style_init_reset(&style_ddlist_selected);
lv_style_set_bg_opa(&style_list_btn, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_bg_color(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_GRAY);
lv_style_set_bg_color(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, lv_color_darken(LV_PINETIME_GRAY, LV_OPA_20));
lv_style_set_text_color(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_WHITE);
lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, LV_PINETIME_WHITE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_WHITE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, LV_PINETIME_WHITE);
lv_style_set_pad_left(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25);
lv_style_set_pad_right(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25);
lv_style_set_pad_top(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 100);
lv_style_set_pad_bottom(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 100);
lv_style_set_pad_inner(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 50);
style_init_reset(&style_ddlist_list); style_init_reset(&style_sw_bg);
lv_style_set_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25); lv_style_set_bg_opa(&style_sw_bg, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_shadow_width(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 20); lv_style_set_bg_color(&style_sw_bg, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_GRAY);
lv_style_set_shadow_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY); lv_style_set_radius(&style_sw_bg, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_value_color(&style_sw_bg, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
style_init_reset(&style_ddlist_selected); style_init_reset(&style_sw_indic);
lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_sw_indic, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, LV_PINETIME_BLUE); lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, LV_PINETIME_GREEN);
style_init_reset(&style_sw_bg); style_init_reset(&style_sw_knob);
lv_style_set_bg_opa(&style_sw_bg, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_sw_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_sw_bg, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_GRAY); lv_style_set_bg_color(&style_sw_knob, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_radius(&style_sw_bg, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_style_set_radius(&style_sw_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_value_color(&style_sw_bg, LV_STATE_DEFAULT, LV_PINETIME_BLUE); lv_style_set_pad_top(&style_sw_knob, LV_STATE_DEFAULT, -4);
lv_style_set_pad_bottom(&style_sw_knob, LV_STATE_DEFAULT, -4);
lv_style_set_pad_left(&style_sw_knob, LV_STATE_DEFAULT, -4);
lv_style_set_pad_right(&style_sw_knob, LV_STATE_DEFAULT, -4);
style_init_reset(&style_sw_indic); style_init_reset(&style_slider_knob);
lv_style_set_bg_opa(&style_sw_indic, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_slider_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, LV_PINETIME_GREEN); lv_style_set_bg_color(&style_slider_knob, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_style_set_border_color(&style_slider_knob, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_border_width(&style_slider_knob, LV_STATE_DEFAULT, 6);
lv_style_set_radius(&style_slider_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_pad_top(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_bottom(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_left(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_right(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_top(&style_slider_knob, LV_STATE_PRESSED, 14);
lv_style_set_pad_bottom(&style_slider_knob, LV_STATE_PRESSED, 14);
lv_style_set_pad_left(&style_slider_knob, LV_STATE_PRESSED, 14);
lv_style_set_pad_right(&style_slider_knob, LV_STATE_PRESSED, 14);
style_init_reset(&style_sw_knob); style_init_reset(&style_arc_indic);
lv_style_set_bg_opa(&style_sw_knob, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_line_color(&style_arc_indic, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_bg_color(&style_sw_knob, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_line_width(&style_arc_indic, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_radius(&style_sw_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_style_set_line_rounded(&style_arc_indic, LV_STATE_DEFAULT, true);
lv_style_set_pad_top(&style_sw_knob, LV_STATE_DEFAULT, - 4);
lv_style_set_pad_bottom(&style_sw_knob, LV_STATE_DEFAULT, - 4);
lv_style_set_pad_left(&style_sw_knob, LV_STATE_DEFAULT, - 4);
lv_style_set_pad_right(&style_sw_knob, LV_STATE_DEFAULT, - 4);
style_init_reset(&style_slider_knob); style_init_reset(&style_arc_bg);
lv_style_set_bg_opa(&style_slider_knob, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_bg_color(&style_slider_knob, LV_STATE_DEFAULT, LV_COLOR_RED); lv_style_set_line_width(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_border_color(&style_slider_knob, LV_STATE_DEFAULT, LV_COLOR_WHITE); lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true);
lv_style_set_border_width(&style_slider_knob, LV_STATE_DEFAULT, 6);
lv_style_set_radius(&style_slider_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_pad_top(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_bottom(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_left(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_right(&style_slider_knob, LV_STATE_DEFAULT, 10);
lv_style_set_pad_top(&style_slider_knob, LV_STATE_PRESSED, 14);
lv_style_set_pad_bottom(&style_slider_knob, LV_STATE_PRESSED, 14);
lv_style_set_pad_left(&style_slider_knob, LV_STATE_PRESSED, 14);
lv_style_set_pad_right(&style_slider_knob, LV_STATE_PRESSED, 14);
style_init_reset(&style_arc_indic); style_init_reset(&style_table_cell);
lv_style_set_line_color(&style_arc_indic, LV_STATE_DEFAULT, LV_PINETIME_BLUE); lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_line_width(&style_arc_indic, LV_STATE_DEFAULT, LV_DPX(25)); lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1);
lv_style_set_line_rounded(&style_arc_indic, LV_STATE_DEFAULT, true); lv_style_set_border_side(&style_table_cell, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL);
lv_style_set_pad_left(&style_table_cell, LV_STATE_DEFAULT, 5);
lv_style_set_pad_right(&style_table_cell, LV_STATE_DEFAULT, 5);
lv_style_set_pad_top(&style_table_cell, LV_STATE_DEFAULT, 2);
lv_style_set_pad_bottom(&style_table_cell, LV_STATE_DEFAULT, 2);
style_init_reset(&style_arc_bg); style_init_reset(&style_pad_small);
lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, LV_PINETIME_GRAY); lv_style_int_t pad_small_value = 10;
lv_style_set_line_width(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(25)); lv_style_set_pad_left(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true); lv_style_set_pad_right(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
lv_style_set_pad_top(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
lv_style_set_pad_bottom(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
lv_style_set_pad_inner(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
style_init_reset(&style_table_cell); style_init_reset(&style_bg_grad);
lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY); lv_style_set_bg_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 40));
lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1); lv_style_set_bg_grad_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 20));
lv_style_set_border_side(&style_table_cell, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); lv_style_set_bg_grad_dir(&style_bg_grad, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
lv_style_set_pad_left(&style_table_cell, LV_STATE_DEFAULT, 5);
lv_style_set_pad_right(&style_table_cell, LV_STATE_DEFAULT, 5);
lv_style_set_pad_top(&style_table_cell, LV_STATE_DEFAULT, 2);
lv_style_set_pad_bottom(&style_table_cell, LV_STATE_DEFAULT, 2);
style_init_reset(&style_pad_small); style_init_reset(&style_lmeter);
lv_style_int_t pad_small_value = 10; lv_style_set_radius(&style_lmeter, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_pad_left(&style_pad_small, LV_STATE_DEFAULT, pad_small_value); lv_style_set_pad_left(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_pad_right(&style_pad_small, LV_STATE_DEFAULT, pad_small_value); lv_style_set_pad_right(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_pad_top(&style_pad_small, LV_STATE_DEFAULT, pad_small_value); lv_style_set_pad_top(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_pad_bottom(&style_pad_small, LV_STATE_DEFAULT, pad_small_value); lv_style_set_pad_inner(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(30));
lv_style_set_pad_inner(&style_pad_small, LV_STATE_DEFAULT, pad_small_value); lv_style_set_scale_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(25));
style_init_reset(&style_bg_grad); lv_style_set_line_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary);
lv_style_set_bg_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 40)); lv_style_set_scale_grad_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary);
lv_style_set_bg_grad_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 20)); lv_style_set_scale_end_color(&style_lmeter, LV_STATE_DEFAULT, lv_color_hex3(0x888));
lv_style_set_bg_grad_dir(&style_bg_grad, LV_STATE_DEFAULT, LV_GRAD_DIR_VER); lv_style_set_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(10));
lv_style_set_scale_end_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(7));
style_init_reset(&style_lmeter); style_init_reset(&style_chart_serie);
lv_style_set_radius(&style_lmeter, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_style_set_line_color(&style_chart_serie, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_pad_left(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_line_width(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_pad_right(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_size(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_pad_top(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_bg_opa(&style_chart_serie, LV_STATE_DEFAULT, 0);
lv_style_set_pad_inner(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(30));
lv_style_set_scale_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_line_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary); lv_style_reset(&style_cb_bg);
lv_style_set_scale_grad_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary); lv_style_set_radius(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(4));
lv_style_set_scale_end_color(&style_lmeter, LV_STATE_DEFAULT, lv_color_hex3(0x888)); lv_style_set_pad_inner(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(10));
lv_style_set_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(10)); lv_style_set_outline_color(&style_cb_bg, LV_STATE_DEFAULT, theme.color_primary);
lv_style_set_scale_end_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(7)); lv_style_set_outline_width(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(2));
lv_style_set_outline_pad(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(20));
style_init_reset(&style_chart_serie); lv_style_set_transition_time(&style_cb_bg, LV_STATE_DEFAULT, 0);
lv_style_set_line_color(&style_chart_serie, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_transition_prop_6(&style_cb_bg, LV_STATE_DEFAULT, LV_STYLE_OUTLINE_OPA);
lv_style_set_line_width(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_size(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_bg_opa(&style_chart_serie, LV_STATE_DEFAULT, 0);
lv_style_reset(&style_cb_bg);
lv_style_set_radius(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(4));
lv_style_set_pad_inner(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(10));
lv_style_set_outline_color(&style_cb_bg, LV_STATE_DEFAULT, theme.color_primary);
lv_style_set_outline_width(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(2));
lv_style_set_outline_pad(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_transition_time(&style_cb_bg, LV_STATE_DEFAULT, 0);
lv_style_set_transition_prop_6(&style_cb_bg, LV_STATE_DEFAULT, LV_STYLE_OUTLINE_OPA);
lv_style_reset(&style_cb_bullet);
lv_style_set_outline_opa(&style_cb_bullet, LV_STATE_FOCUSED, LV_OPA_TRANSP);
lv_style_set_radius(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(4));
lv_style_set_pattern_recolor(&style_cb_bullet, LV_STATE_CHECKED, LV_COLOR_WHITE);
lv_style_set_pad_left(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_set_pad_right(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_set_pad_top(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_set_pad_bottom(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_reset(&style_cb_bullet);
lv_style_set_outline_opa(&style_cb_bullet, LV_STATE_FOCUSED, LV_OPA_TRANSP);
lv_style_set_radius(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(4));
lv_style_set_pattern_recolor(&style_cb_bullet, LV_STATE_CHECKED, LV_COLOR_WHITE);
lv_style_set_pad_left(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_set_pad_right(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_set_pad_top(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_set_pad_bottom(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
} }
/********************** /**********************
* GLOBAL FUNCTIONS * GLOBAL FUNCTIONS
**********************/ **********************/
@ -320,221 +316,216 @@ static void basic_init(void)
* @param font_title pointer to a extra large font * @param font_title pointer to a extra large font
* @return a pointer to reference this theme later * @return a pointer to reference this theme later
*/ */
lv_theme_t * lv_pinetime_theme_init(lv_color_t color_primary, lv_color_t color_secondary, uint32_t flags, lv_theme_t* lv_pinetime_theme_init(lv_color_t color_primary,
const lv_font_t * font_small, const lv_font_t * font_normal, const lv_font_t * font_subtitle, lv_color_t color_secondary,
const lv_font_t * font_title) uint32_t flags,
{ const lv_font_t* font_small,
theme.color_primary = color_primary; const lv_font_t* font_normal,
theme.color_secondary = color_secondary; const lv_font_t* font_subtitle,
theme.font_small = font_small; const lv_font_t* font_title) {
theme.font_normal = font_normal; theme.color_primary = color_primary;
theme.font_subtitle = font_subtitle; theme.color_secondary = color_secondary;
theme.font_title = font_title; theme.font_small = font_small;
theme.flags = flags; theme.font_normal = font_normal;
theme.font_subtitle = font_subtitle;
theme.font_title = font_title;
theme.flags = flags;
basic_init(); basic_init();
theme.apply_xcb = theme_apply; theme.apply_xcb = theme_apply;
inited = true; inited = true;
return &theme; return &theme;
} }
static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
lv_style_list_t* list;
static void theme_apply(lv_obj_t * obj, lv_theme_style_t name) /*To avoid warnings*/
{ uint32_t name_int = (uint32_t) name;
lv_style_list_t * list; switch (name_int) {
case LV_THEME_NONE:
break;
/*To avoid warnings*/ case LV_THEME_SCR:
uint32_t name_int = (uint32_t) name; lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN);
switch(name_int) { list = lv_obj_get_style_list(obj, LV_OBJ_PART_MAIN);
case LV_THEME_NONE: _lv_style_list_add_style(list, &style_bg);
break; _lv_style_list_add_style(list, &style_label_white);
break;
case LV_THEME_SCR: case LV_THEME_OBJ:
lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN); lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN);
list = lv_obj_get_style_list(obj, LV_OBJ_PART_MAIN); list = lv_obj_get_style_list(obj, LV_OBJ_PART_MAIN);
_lv_style_list_add_style(list, &style_bg); _lv_style_list_add_style(list, &style_box);
_lv_style_list_add_style(list, &style_label_white); break;
break;
case LV_THEME_OBJ: case LV_THEME_CONT:
lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN); lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN);
list = lv_obj_get_style_list(obj, LV_OBJ_PART_MAIN); list = lv_obj_get_style_list(obj, LV_CONT_PART_MAIN);
_lv_style_list_add_style(list, &style_box); _lv_style_list_add_style(list, &style_box);
break; break;
case LV_THEME_CONT: case LV_THEME_BTN:
lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN); lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN);
list = lv_obj_get_style_list(obj, LV_CONT_PART_MAIN); list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN);
_lv_style_list_add_style(list, &style_box); _lv_style_list_add_style(list, &style_btn);
break; //_lv_style_list_add_style(list, &style_bg_grad);
break;
case LV_THEME_BTN: case LV_THEME_BTNMATRIX:
lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN); list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BG);
list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN); _lv_style_list_add_style(list, &style_bg);
_lv_style_list_add_style(list, &style_btn); _lv_style_list_add_style(list, &style_pad_small);
//_lv_style_list_add_style(list, &style_bg_grad);
break;
case LV_THEME_BTNMATRIX: list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BTN);
list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BG); _lv_style_list_add_style(list, &style_btn);
_lv_style_list_add_style(list, &style_bg); //_lv_style_list_add_style(list, &style_bg_grad);
_lv_style_list_add_style(list, &style_pad_small); //_lv_style_list_add_style(list, &style_bg_click);
break;
list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BTN); case LV_THEME_BAR:
_lv_style_list_add_style(list, &style_btn); lv_obj_clean_style_list(obj, LV_BAR_PART_BG);
//_lv_style_list_add_style(list, &style_bg_grad); list = lv_obj_get_style_list(obj, LV_BAR_PART_BG);
//_lv_style_list_add_style(list, &style_bg_click);
break;
case LV_THEME_BAR: lv_obj_clean_style_list(obj, LV_BAR_PART_INDIC);
lv_obj_clean_style_list(obj, LV_BAR_PART_BG); list = lv_obj_get_style_list(obj, LV_BAR_PART_INDIC);
list = lv_obj_get_style_list(obj, LV_BAR_PART_BG); _lv_style_list_add_style(list, &style_bar_indic);
break;
lv_obj_clean_style_list(obj, LV_BAR_PART_INDIC); case LV_THEME_IMAGE:
list = lv_obj_get_style_list(obj, LV_BAR_PART_INDIC); lv_obj_clean_style_list(obj, LV_IMG_PART_MAIN);
_lv_style_list_add_style(list, &style_bar_indic); list = lv_obj_get_style_list(obj, LV_IMG_PART_MAIN);
break; _lv_style_list_add_style(list, &style_icon);
break;
case LV_THEME_IMAGE: case LV_THEME_LABEL:
lv_obj_clean_style_list(obj, LV_IMG_PART_MAIN); lv_obj_clean_style_list(obj, LV_LABEL_PART_MAIN);
list = lv_obj_get_style_list(obj, LV_IMG_PART_MAIN); list = lv_obj_get_style_list(obj, LV_LABEL_PART_MAIN);
_lv_style_list_add_style(list, &style_icon); _lv_style_list_add_style(list, &style_label_white);
break; break;
case LV_THEME_LABEL: case LV_THEME_SLIDER:
lv_obj_clean_style_list(obj, LV_LABEL_PART_MAIN); lv_obj_clean_style_list(obj, LV_SLIDER_PART_BG);
list = lv_obj_get_style_list(obj, LV_LABEL_PART_MAIN); list = lv_obj_get_style_list(obj, LV_SLIDER_PART_BG);
_lv_style_list_add_style(list, &style_label_white); _lv_style_list_add_style(list, &style_sw_bg);
break;
case LV_THEME_SLIDER: lv_obj_clean_style_list(obj, LV_SLIDER_PART_INDIC);
lv_obj_clean_style_list(obj, LV_SLIDER_PART_BG); list = lv_obj_get_style_list(obj, LV_SLIDER_PART_INDIC);
list = lv_obj_get_style_list(obj, LV_SLIDER_PART_BG);
_lv_style_list_add_style(list, &style_sw_bg);
lv_obj_clean_style_list(obj, LV_SLIDER_PART_INDIC); lv_obj_clean_style_list(obj, LV_SLIDER_PART_KNOB);
list = lv_obj_get_style_list(obj, LV_SLIDER_PART_INDIC); list = lv_obj_get_style_list(obj, LV_SLIDER_PART_KNOB);
_lv_style_list_add_style(list, &style_slider_knob);
break;
lv_obj_clean_style_list(obj, LV_SLIDER_PART_KNOB); case LV_THEME_LIST:
list = lv_obj_get_style_list(obj, LV_SLIDER_PART_KNOB); lv_obj_clean_style_list(obj, LV_LIST_PART_BG);
_lv_style_list_add_style(list, &style_slider_knob); list = lv_obj_get_style_list(obj, LV_LIST_PART_BG);
break; _lv_style_list_add_style(list, &style_box);
case LV_THEME_LIST: lv_obj_clean_style_list(obj, LV_LIST_PART_SCROLLABLE);
lv_obj_clean_style_list(obj, LV_LIST_PART_BG); list = lv_obj_get_style_list(obj, LV_LIST_PART_SCROLLABLE);
list = lv_obj_get_style_list(obj, LV_LIST_PART_BG);
_lv_style_list_add_style(list, &style_box);
lv_obj_clean_style_list(obj, LV_LIST_PART_SCROLLABLE); lv_obj_clean_style_list(obj, LV_LIST_PART_SCROLLBAR);
list = lv_obj_get_style_list(obj, LV_LIST_PART_SCROLLABLE); list = lv_obj_get_style_list(obj, LV_LIST_PART_SCROLLBAR);
_lv_style_list_add_style(list, &style_scrollbar);
break;
lv_obj_clean_style_list(obj, LV_LIST_PART_SCROLLBAR); case LV_THEME_LIST_BTN:
list = lv_obj_get_style_list(obj, LV_LIST_PART_SCROLLBAR); lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN);
_lv_style_list_add_style(list, &style_scrollbar); list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN);
break; _lv_style_list_add_style(list, &style_list_btn);
break;
case LV_THEME_LIST_BTN: case LV_THEME_ARC:
lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN); lv_obj_clean_style_list(obj, LV_ARC_PART_BG);
list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN); list = lv_obj_get_style_list(obj, LV_ARC_PART_BG);
_lv_style_list_add_style(list, &style_list_btn); _lv_style_list_add_style(list, &style_arc_bg);
break;
lv_obj_clean_style_list(obj, LV_ARC_PART_INDIC);
list = lv_obj_get_style_list(obj, LV_ARC_PART_INDIC);
_lv_style_list_add_style(list, &style_arc_indic);
break;
case LV_THEME_ARC: case LV_THEME_SWITCH:
lv_obj_clean_style_list(obj, LV_ARC_PART_BG); lv_obj_clean_style_list(obj, LV_SWITCH_PART_BG);
list = lv_obj_get_style_list(obj, LV_ARC_PART_BG); list = lv_obj_get_style_list(obj, LV_SWITCH_PART_BG);
_lv_style_list_add_style(list, &style_arc_bg); _lv_style_list_add_style(list, &style_sw_bg);
lv_obj_clean_style_list(obj, LV_ARC_PART_INDIC); lv_obj_clean_style_list(obj, LV_SWITCH_PART_INDIC);
list = lv_obj_get_style_list(obj, LV_ARC_PART_INDIC); list = lv_obj_get_style_list(obj, LV_SWITCH_PART_INDIC);
_lv_style_list_add_style(list, &style_arc_indic); _lv_style_list_add_style(list, &style_sw_indic);
break;
lv_obj_clean_style_list(obj, LV_SWITCH_PART_KNOB);
list = lv_obj_get_style_list(obj, LV_SWITCH_PART_KNOB);
_lv_style_list_add_style(list, &style_sw_knob);
break;
case LV_THEME_SWITCH: case LV_THEME_DROPDOWN:
lv_obj_clean_style_list(obj, LV_SWITCH_PART_BG); lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_MAIN);
list = lv_obj_get_style_list(obj, LV_SWITCH_PART_BG); list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_MAIN);
_lv_style_list_add_style(list, &style_sw_bg); _lv_style_list_add_style(list, &style_btn);
_lv_style_list_add_style(list, &style_pad);
lv_obj_clean_style_list(obj, LV_SWITCH_PART_INDIC); lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_LIST);
list = lv_obj_get_style_list(obj, LV_SWITCH_PART_INDIC); list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_LIST);
_lv_style_list_add_style(list, &style_sw_indic); _lv_style_list_add_style(list, &style_box);
_lv_style_list_add_style(list, &style_ddlist_list);
_lv_style_list_add_style(list, &style_pad);
lv_obj_clean_style_list(obj, LV_SWITCH_PART_KNOB); lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_SELECTED);
list = lv_obj_get_style_list(obj, LV_SWITCH_PART_KNOB); list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SELECTED);
_lv_style_list_add_style(list, &style_sw_knob); _lv_style_list_add_style(list, &style_ddlist_selected);
break;
case LV_THEME_DROPDOWN: lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_SCROLLBAR);
lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_MAIN); list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SCROLLBAR);
list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_MAIN); _lv_style_list_add_style(list, &style_scrollbar);
_lv_style_list_add_style(list, &style_btn); break;
_lv_style_list_add_style(list, &style_pad);
lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_LIST); case LV_THEME_TABLE:
list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_LIST); list = lv_obj_get_style_list(obj, LV_TABLE_PART_BG);
_lv_style_list_add_style(list, &style_box); _lv_style_list_add_style(list, &style_bg);
_lv_style_list_add_style(list, &style_ddlist_list);
_lv_style_list_add_style(list, &style_pad);
lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_SELECTED); int idx = 1; /* start value should be 1, not zero, since cell styles
list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SELECTED); start at 1 due to presence of LV_TABLE_PART_BG=0
_lv_style_list_add_style(list, &style_ddlist_selected); in the enum (lv_table.h) */
/* declaring idx outside loop to work with older compilers */
for (; idx <= LV_TABLE_CELL_STYLE_CNT; idx++) {
list = lv_obj_get_style_list(obj, idx);
_lv_style_list_add_style(list, &style_table_cell);
_lv_style_list_add_style(list, &style_label_white);
}
break;
lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_SCROLLBAR); case LV_THEME_LINEMETER:
list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SCROLLBAR); list = lv_obj_get_style_list(obj, LV_LINEMETER_PART_MAIN);
_lv_style_list_add_style(list, &style_scrollbar); _lv_style_list_add_style(list, &style_bg);
break; _lv_style_list_add_style(list, &style_lmeter);
break;
case LV_THEME_TABLE: case LV_THEME_CHART:
list = lv_obj_get_style_list(obj, LV_TABLE_PART_BG); lv_obj_clean_style_list(obj, LV_CHART_PART_SERIES);
_lv_style_list_add_style(list, &style_bg); list = lv_obj_get_style_list(obj, LV_CHART_PART_SERIES);
_lv_style_list_add_style(list, &style_btn);
_lv_style_list_add_style(list, &style_chart_serie);
break;
int idx = 1; /* start value should be 1, not zero, since cell styles case LV_THEME_CHECKBOX:
start at 1 due to presence of LV_TABLE_PART_BG=0 list = lv_obj_get_style_list(obj, LV_CHECKBOX_PART_BG);
in the enum (lv_table.h) */ _lv_style_list_add_style(list, &style_cb_bg);
/* declaring idx outside loop to work with older compilers */
for(; idx <= LV_TABLE_CELL_STYLE_CNT; idx ++) {
list = lv_obj_get_style_list(obj, idx);
_lv_style_list_add_style(list, &style_table_cell);
_lv_style_list_add_style(list, &style_label_white);
}
break;
case LV_THEME_LINEMETER: list = lv_obj_get_style_list(obj, LV_CHECKBOX_PART_BULLET);
list = lv_obj_get_style_list(obj, LV_LINEMETER_PART_MAIN); _lv_style_list_add_style(list, &style_btn);
_lv_style_list_add_style(list, &style_bg); _lv_style_list_add_style(list, &style_cb_bullet);
_lv_style_list_add_style(list, &style_lmeter); break;
break;
case LV_THEME_CHART:
lv_obj_clean_style_list(obj, LV_CHART_PART_SERIES);
list = lv_obj_get_style_list(obj, LV_CHART_PART_SERIES);
_lv_style_list_add_style(list, &style_btn);
_lv_style_list_add_style(list, &style_chart_serie);
break;
case LV_THEME_CHECKBOX:
list = lv_obj_get_style_list(obj, LV_CHECKBOX_PART_BG);
_lv_style_list_add_style(list, &style_cb_bg);
list = lv_obj_get_style_list(obj, LV_CHECKBOX_PART_BULLET);
_lv_style_list_add_style(list, &style_btn);
_lv_style_list_add_style(list, &style_cb_bullet);
break;
default:
break;
}
lv_obj_refresh_style(obj, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
default:
break;
}
lv_obj_refresh_style(obj, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
} }
/********************** /**********************

View file

@ -19,24 +19,22 @@ extern "C" {
* DEFINES * DEFINES
*********************/ *********************/
/*Colors*/ /*Colors*/
#define LV_PINETIME_WHITE lv_color_hex(0xffffff) #define LV_PINETIME_WHITE lv_color_hex(0xffffff)
#define LV_PINETIME_LIGHT lv_color_hex(0xf3f8fe) #define LV_PINETIME_LIGHT lv_color_hex(0xf3f8fe)
#define LV_PINETIME_GRAY lv_color_hex(0x8a8a8a) #define LV_PINETIME_GRAY lv_color_hex(0x8a8a8a)
#define LV_PINETIME_LIGHT_GRAY lv_color_hex(0xc4c4c4) #define LV_PINETIME_LIGHT_GRAY lv_color_hex(0xc4c4c4)
#define LV_PINETIME_BLUE lv_color_hex(0x2f3243) //006fb6 #define LV_PINETIME_BLUE lv_color_hex(0x2f3243) // 006fb6
#define LV_PINETIME_GREEN lv_color_hex(0x4cb242) #define LV_PINETIME_GREEN lv_color_hex(0x4cb242)
#define LV_PINETIME_RED lv_color_hex(0xd51732) #define LV_PINETIME_RED lv_color_hex(0xd51732)
/********************** /**********************
* TYPEDEFS * TYPEDEFS
**********************/ **********************/
/********************** /**********************
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
/** /**
* Initialize the default * Initialize the default
* @param color_primary the primary color of the theme * @param color_primary the primary color of the theme
@ -48,9 +46,13 @@ extern "C" {
* @param font_title pointer to a extra large font * @param font_title pointer to a extra large font
* @return a pointer to reference this theme later * @return a pointer to reference this theme later
*/ */
lv_theme_t * lv_pinetime_theme_init(lv_color_t color_primary, lv_color_t color_secondary, uint32_t flags, lv_theme_t* lv_pinetime_theme_init(lv_color_t color_primary,
const lv_font_t * font_small, const lv_font_t * font_normal, const lv_font_t * font_subtitle, lv_color_t color_secondary,
const lv_font_t * font_title); uint32_t flags,
const lv_font_t* font_small,
const lv_font_t* font_normal,
const lv_font_t* font_subtitle,
const lv_font_t* font_title);
/********************** /**********************
* MACROS * MACROS
**********************/ **********************/

View file

@ -8,31 +8,34 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp *app, ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app,
Pinetime::Controllers::Settings &settingsController, Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::Battery& batteryController, Pinetime::Controllers::Battery& batteryController,
Controllers::DateTime& dateTimeController) : Controllers::DateTime& dateTimeController)
Screen(app), : Screen(app),
settingsController{settingsController}, settingsController {settingsController},
batteryController{batteryController}, batteryController {batteryController},
dateTimeController{dateTimeController}, dateTimeController {dateTimeController},
screens{app, screens {app,
settingsController.GetAppMenu(), settingsController.GetAppMenu(),
{ {
[this]() -> std::unique_ptr<Screen> { return CreateScreen1(); }, [this]() -> std::unique_ptr<Screen> {
[this]() -> std::unique_ptr<Screen> { return CreateScreen2(); }, return CreateScreen1();
//[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); } },
}, [this]() -> std::unique_ptr<Screen> {
Screens::ScreenListModes::UpDown return CreateScreen2();
} {} },
//[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
},
Screens::ScreenListModes::UpDown} {
}
ApplicationList::~ApplicationList() { ApplicationList::~ApplicationList() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
bool ApplicationList::Refresh() { bool ApplicationList::Refresh() {
if(running) if (running)
running = screens.Refresh(); running = screens.Refresh();
return running; return running;
} }
@ -42,31 +45,27 @@ bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
} }
std::unique_ptr<Screen> ApplicationList::CreateScreen1() { std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
std::array<Screens::Tile::Applications, 6> applications { std::array<Screens::Tile::Applications, 6> applications {{
{ {Symbols::stopWatch, Apps::StopWatch},
{Symbols::stopWatch, Apps::StopWatch}, {Symbols::music, Apps::Music},
{Symbols::music, Apps::Music}, {Symbols::map, Apps::Navigation},
{Symbols::map, Apps::Navigation}, {Symbols::shoe, Apps::Motion},
{Symbols::shoe, Apps::Motion}, {Symbols::heartBeat, Apps::HeartRate},
{Symbols::heartBeat, Apps::HeartRate}, {"", Apps::None},
{"", Apps::None}, }};
}
};
return std::make_unique<Screens::Tile>(0, 2, app, settingsController, batteryController, dateTimeController, applications); return std::make_unique<Screens::Tile>(0, 2, app, settingsController, batteryController, dateTimeController, applications);
} }
std::unique_ptr<Screen> ApplicationList::CreateScreen2() { std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
std::array<Screens::Tile::Applications, 6> applications { std::array<Screens::Tile::Applications, 6> applications {{
{ {Symbols::paintbrush, Apps::Paint},
{Symbols::paintbrush, Apps::Paint}, {Symbols::paddle, Apps::Paddle},
{Symbols::paddle, Apps::Paddle}, {"2", Apps::Twos},
{"2", Apps::Twos}, {"", Apps::None},
{"", Apps::None}, {"", Apps::None},
{"", Apps::None}, {"", Apps::None},
{"", Apps::None}, }};
}
};
return std::make_unique<Screens::Tile>(1, 2, app, settingsController, batteryController, dateTimeController, applications); return std::make_unique<Screens::Tile>(1, 2, app, settingsController, batteryController, dateTimeController, applications);
} }
@ -84,4 +83,3 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
return std::make_unique<Screens::Tile>(2, 3, app, settingsController, batteryController, dateTimeController, applications); return std::make_unique<Screens::Tile>(2, 3, app, settingsController, batteryController, dateTimeController, applications);
}*/ }*/

View file

@ -12,24 +12,24 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class ApplicationList : public Screen { class ApplicationList : public Screen {
public: public:
explicit ApplicationList(DisplayApp* app, explicit ApplicationList(DisplayApp* app,
Pinetime::Controllers::Settings &settingsController, Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::Battery& batteryController, Pinetime::Controllers::Battery& batteryController,
Controllers::DateTime& dateTimeController); Controllers::DateTime& dateTimeController);
~ApplicationList() override; ~ApplicationList() override;
bool Refresh() override; bool Refresh() override;
bool OnTouchEvent(TouchEvents event) override; bool OnTouchEvent(TouchEvents event) override;
private:
Controllers::Settings& settingsController; private:
Pinetime::Controllers::Battery& batteryController; Controllers::Settings& settingsController;
Controllers::DateTime& dateTimeController; Pinetime::Controllers::Battery& batteryController;
Controllers::DateTime& dateTimeController;
ScreenList<2> screens; ScreenList<2> screens;
std::unique_ptr<Screen> CreateScreen1(); std::unique_ptr<Screen> CreateScreen1();
std::unique_ptr<Screen> CreateScreen2(); std::unique_ptr<Screen> CreateScreen2();
//std::unique_ptr<Screen> CreateScreen3(); // std::unique_ptr<Screen> CreateScreen3();
}; };
} }
} }

View file

@ -4,10 +4,14 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
const char* BatteryIcon::GetBatteryIcon(int batteryPercent) { const char* BatteryIcon::GetBatteryIcon(int batteryPercent) {
if(batteryPercent > 90) return Symbols::batteryFull; if (batteryPercent > 90)
if(batteryPercent > 75) return Symbols::batteryThreeQuarter; return Symbols::batteryFull;
if(batteryPercent > 50) return Symbols::batteryHalf; if (batteryPercent > 75)
if(batteryPercent > 25) return Symbols::batteryOneQuarter; return Symbols::batteryThreeQuarter;
if (batteryPercent > 50)
return Symbols::batteryHalf;
if (batteryPercent > 25)
return Symbols::batteryOneQuarter;
return Symbols::batteryEmpty; return Symbols::batteryEmpty;
} }
@ -15,8 +19,9 @@ const char* BatteryIcon::GetUnknownIcon() {
return Symbols::batteryEmpty; return Symbols::batteryEmpty;
} }
const char *BatteryIcon::GetPlugIcon(bool isCharging) { const char* BatteryIcon::GetPlugIcon(bool isCharging) {
if(isCharging) if (isCharging)
return Symbols::plug; return Symbols::plug;
else return ""; else
return "";
} }

View file

@ -6,8 +6,8 @@ namespace Pinetime {
class BatteryIcon { class BatteryIcon {
public: public:
static const char* GetUnknownIcon(); static const char* GetUnknownIcon();
static const char* GetBatteryIcon(int batteryPercent); static const char* GetBatteryIcon(int batteryPercent);
static const char* GetPlugIcon(bool isCharging); static const char* GetPlugIcon(bool isCharging);
}; };
} }
} }

View file

@ -4,22 +4,18 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
static void lv_update_task(struct _lv_task_t *task) { static void lv_update_task(struct _lv_task_t* task) {
auto user_data = static_cast<BatteryInfo *>(task->user_data); auto user_data = static_cast<BatteryInfo*>(task->user_data);
user_data->UpdateScreen(); user_data->UpdateScreen();
} }
static void lv_anim_task(struct _lv_task_t *task) { static void lv_anim_task(struct _lv_task_t* task) {
auto user_data = static_cast<BatteryInfo *>(task->user_data); auto user_data = static_cast<BatteryInfo*>(task->user_data);
user_data->UpdateAnim(); user_data->UpdateAnim();
} }
BatteryInfo::BatteryInfo( BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Battery& batteryController)
Pinetime::Applications::DisplayApp *app, : Screen(app), batteryController {batteryController} {
Pinetime::Controllers::Battery& batteryController) :
Screen(app),
batteryController{batteryController}
{
batteryPercent = batteryController.PercentRemaining(); batteryPercent = batteryController.PercentRemaining();
batteryVoltage = batteryController.Voltage(); batteryVoltage = batteryController.Voltage();
@ -32,37 +28,38 @@ BatteryInfo::BatteryInfo(
lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, lv_color_hex(0x222222)); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, lv_color_hex(0x222222));
lv_obj_set_style_local_bg_opa(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_100); lv_obj_set_style_local_bg_opa(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_100);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC , LV_STATE_DEFAULT, lv_color_hex(0xFF0000)); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0xFF0000));
lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_OFF); lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_OFF);
status = lv_label_create(lv_scr_act(), nullptr); status = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(status,"Reading Battery status"); lv_label_set_text_static(status, "Reading Battery status");
lv_label_set_align(status, LV_LABEL_ALIGN_CENTER); lv_label_set_align(status, LV_LABEL_ALIGN_CENTER);
lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
percent = lv_label_create(lv_scr_act(), nullptr); percent = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
if ( batteryPercent >= 0) { if (batteryPercent >= 0) {
lv_label_set_text_fmt(percent,"%02i%%", batteryPercent); lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
} else { } else {
lv_label_set_text(percent,"--%"); lv_label_set_text(percent, "--%");
} }
lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT); lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT);
lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60); lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60);
// hack to not use the flot functions from printf // hack to not use the flot functions from printf
uint8_t batteryVoltageBytes[2]; uint8_t batteryVoltageBytes[2];
batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); //truncate whole numbers batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); // truncate whole numbers
batteryVoltageBytes[0] = static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); //remove whole part of flt and shift 2 places over batteryVoltageBytes[0] =
static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over
// //
voltage = lv_label_create(lv_scr_act(), nullptr); voltage = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xC6A600)); lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xC6A600));
lv_label_set_text_fmt(voltage,"%1i.%02i volts", batteryVoltageBytes[1], batteryVoltageBytes[0]); lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltageBytes[1], batteryVoltageBytes[0]);
lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER); lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER);
lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95); lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95);
lv_obj_t * backgroundLabel = lv_label_create(lv_scr_act(), nullptr); lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, 240, 240); lv_obj_set_size(backgroundLabel, 240, 240);
lv_obj_set_pos(backgroundLabel, 0, 0); lv_obj_set_pos(backgroundLabel, 0, 0);
@ -73,7 +70,6 @@ BatteryInfo::BatteryInfo(
UpdateScreen(); UpdateScreen();
} }
BatteryInfo::~BatteryInfo() { BatteryInfo::~BatteryInfo() {
lv_task_del(taskUpdate); lv_task_del(taskUpdate);
lv_task_del(taskAnim); lv_task_del(taskAnim);
@ -83,9 +79,9 @@ BatteryInfo::~BatteryInfo() {
void BatteryInfo::UpdateAnim() { void BatteryInfo::UpdateAnim() {
batteryPercent = batteryController.PercentRemaining(); batteryPercent = batteryController.PercentRemaining();
if ( batteryPercent >= 0 ) { if (batteryPercent >= 0) {
if ( batteryController.IsCharging() and batteryPercent < 100 ) { if (batteryController.IsCharging() and batteryPercent < 100) {
animation +=1; animation += 1;
if (animation >= 100) { if (animation >= 100) {
animation = 0; animation = 0;
} }
@ -110,40 +106,39 @@ void BatteryInfo::UpdateScreen() {
batteryPercent = batteryController.PercentRemaining(); batteryPercent = batteryController.PercentRemaining();
batteryVoltage = batteryController.Voltage(); batteryVoltage = batteryController.Voltage();
if ( batteryPercent >= 0 ) { if (batteryPercent >= 0) {
if ( batteryController.IsCharging() and batteryPercent < 100 ) { if (batteryController.IsCharging() and batteryPercent < 100) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC , LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_label_set_text_static(status,"Battery charging"); lv_label_set_text_static(status, "Battery charging");
} else if ( batteryPercent == 100 ) { } else if (batteryPercent == 100) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC , LV_STATE_DEFAULT, LV_COLOR_BLUE); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE);
lv_label_set_text_static(status,"Battery is fully charged"); lv_label_set_text_static(status, "Battery is fully charged");
} else if ( batteryPercent < 10 ) { } else if (batteryPercent < 10) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC , LV_STATE_DEFAULT, LV_COLOR_YELLOW); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_label_set_text_static(status,"Battery is low"); lv_label_set_text_static(status, "Battery is low");
} else { } else {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC , LV_STATE_DEFAULT, LV_COLOR_GREEN); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_label_set_text_static(status,"Battery discharging"); lv_label_set_text_static(status, "Battery discharging");
} }
lv_label_set_text_fmt(percent,"%02i%%", batteryPercent); lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
} else { } else {
lv_label_set_text_static(status,"Reading Battery status"); lv_label_set_text_static(status, "Reading Battery status");
lv_label_set_text(percent,"--%"); lv_label_set_text(percent, "--%");
} }
lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
// hack to not use the flot functions from printf // hack to not use the flot functions from printf
uint8_t batteryVoltageBytes[2]; uint8_t batteryVoltageBytes[2];
batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); //truncate whole numbers batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); // truncate whole numbers
batteryVoltageBytes[0] = static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); //remove whole part of flt and shift 2 places over batteryVoltageBytes[0] =
static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over
// //
lv_label_set_text_fmt(voltage,"%1i.%02i volts", batteryVoltageBytes[1], batteryVoltageBytes[0]); lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltageBytes[1], batteryVoltageBytes[0]);
} }
bool BatteryInfo::Refresh() { bool BatteryInfo::Refresh() {
return running; return running;
} }

View file

@ -6,7 +6,6 @@
#include "Screen.h" #include "Screen.h"
#include <lvgl/lvgl.h> #include <lvgl/lvgl.h>
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class Battery; class Battery;
@ -15,34 +14,30 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class BatteryInfo : public Screen{ class BatteryInfo : public Screen {
public: public:
BatteryInfo(DisplayApp* app, BatteryInfo(DisplayApp* app, Pinetime::Controllers::Battery& batteryController);
Pinetime::Controllers::Battery& batteryController); ~BatteryInfo() override;
~BatteryInfo() override;
bool Refresh() override; bool Refresh() override;
void UpdateScreen(); void UpdateScreen();
void UpdateAnim(); void UpdateAnim();
private: private:
Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::Battery& batteryController; lv_obj_t* voltage;
lv_obj_t* percent;
lv_obj_t* charging_bar;
lv_obj_t* status;
lv_obj_t* voltage; lv_task_t* taskUpdate;
lv_obj_t* percent; lv_task_t* taskAnim;
lv_obj_t* charging_bar;
lv_obj_t* status;
lv_task_t* taskUpdate;
lv_task_t* taskAnim;
int8_t animation = 0;
int8_t batteryPercent = -1;
float batteryVoltage = 0.0f;
int8_t animation = 0;
int8_t batteryPercent = -1;
float batteryVoltage = 0.0f;
}; };
} }
} }

View file

@ -3,6 +3,8 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
const char* BleIcon::GetIcon(bool isConnected) { const char* BleIcon::GetIcon(bool isConnected) {
if(isConnected) return Symbols::bluetooth; if (isConnected)
else return ""; return Symbols::bluetooth;
else
return "";
} }

View file

@ -3,14 +3,15 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
void slider_event_cb(lv_obj_t * slider, lv_event_t event) { void slider_event_cb(lv_obj_t* slider, lv_event_t event) {
if(event == LV_EVENT_VALUE_CHANGED) { if (event == LV_EVENT_VALUE_CHANGED) {
auto* brightnessSlider = static_cast<Brightness*>(slider->user_data); auto* brightnessSlider = static_cast<Brightness*>(slider->user_data);
brightnessSlider->OnValueChanged(); brightnessSlider->OnValueChanged();
} }
} }
Brightness::Brightness(Pinetime::Applications::DisplayApp *app, Controllers::BrightnessController& brightness) : Screen(app), brightness{brightness} { Brightness::Brightness(Pinetime::Applications::DisplayApp* app, Controllers::BrightnessController& brightness)
: Screen(app), brightness {brightness} {
slider = lv_slider_create(lv_scr_act(), nullptr); slider = lv_slider_create(lv_scr_act(), nullptr);
lv_obj_set_user_data(slider, this); lv_obj_set_user_data(slider, this);
lv_obj_set_width(slider, LV_DPI * 2); lv_obj_set_width(slider, LV_DPI * 2);
@ -33,13 +34,18 @@ bool Brightness::Refresh() {
return running; return running;
} }
const char *Brightness::LevelToString(Pinetime::Controllers::BrightnessController::Levels level) { const char* Brightness::LevelToString(Pinetime::Controllers::BrightnessController::Levels level) {
switch(level) { switch (level) {
case Pinetime::Controllers::BrightnessController::Levels::Off: return "Off"; case Pinetime::Controllers::BrightnessController::Levels::Off:
case Pinetime::Controllers::BrightnessController::Levels::Low: return "Low"; return "Off";
case Pinetime::Controllers::BrightnessController::Levels::Medium: return "Medium"; case Pinetime::Controllers::BrightnessController::Levels::Low:
case Pinetime::Controllers::BrightnessController::Levels::High: return "High"; return "Low";
default : return "???"; case Pinetime::Controllers::BrightnessController::Levels::Medium:
return "Medium";
case Pinetime::Controllers::BrightnessController::Levels::High:
return "High";
default:
return "???";
} }
} }
@ -48,29 +54,40 @@ void Brightness::OnValueChanged() {
} }
void Brightness::SetValue(uint8_t value) { void Brightness::SetValue(uint8_t value) {
switch(value) { switch (value) {
case 0: brightness.Set(Controllers::BrightnessController::Levels::Low); break; case 0:
case 1: brightness.Set(Controllers::BrightnessController::Levels::Medium); break; brightness.Set(Controllers::BrightnessController::Levels::Low);
case 2: brightness.Set(Controllers::BrightnessController::Levels::High); break; break;
case 1:
brightness.Set(Controllers::BrightnessController::Levels::Medium);
break;
case 2:
brightness.Set(Controllers::BrightnessController::Levels::High);
break;
} }
lv_label_set_text(slider_label, LevelToString(brightness.Level())); lv_label_set_text(slider_label, LevelToString(brightness.Level()));
} }
uint8_t Brightness::LevelToInt(Pinetime::Controllers::BrightnessController::Levels level) { uint8_t Brightness::LevelToInt(Pinetime::Controllers::BrightnessController::Levels level) {
switch(level) { switch (level) {
case Pinetime::Controllers::BrightnessController::Levels::Off: return 0; case Pinetime::Controllers::BrightnessController::Levels::Off:
case Pinetime::Controllers::BrightnessController::Levels::Low: return 0; return 0;
case Pinetime::Controllers::BrightnessController::Levels::Medium: return 1; case Pinetime::Controllers::BrightnessController::Levels::Low:
case Pinetime::Controllers::BrightnessController::Levels::High: return 2; return 0;
default : return 0; case Pinetime::Controllers::BrightnessController::Levels::Medium:
return 1;
case Pinetime::Controllers::BrightnessController::Levels::High:
return 2;
default:
return 0;
} }
} }
bool Brightness::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool Brightness::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
switch(event) { switch (event) {
case TouchEvents::SwipeLeft: case TouchEvents::SwipeLeft:
brightness.Lower(); brightness.Lower();
if ( brightness.Level() == Pinetime::Controllers::BrightnessController::Levels::Off) { if (brightness.Level() == Pinetime::Controllers::BrightnessController::Levels::Off) {
brightness.Set(Controllers::BrightnessController::Levels::Low); brightness.Set(Controllers::BrightnessController::Levels::Low);
} }
SetValue(); SetValue();

View file

@ -9,25 +9,25 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class Brightness : public Screen { class Brightness : public Screen {
public: public:
Brightness(DisplayApp* app, Controllers::BrightnessController& brightness); Brightness(DisplayApp* app, Controllers::BrightnessController& brightness);
~Brightness() override; ~Brightness() override;
bool Refresh() override; bool Refresh() override;
bool OnTouchEvent(TouchEvents event) override;
void OnValueChanged(); bool OnTouchEvent(TouchEvents event) override;
private:
Controllers::BrightnessController& brightness;
lv_obj_t * slider_label; void OnValueChanged();
lv_obj_t * slider;
const char* LevelToString(Controllers::BrightnessController::Levels level); private:
uint8_t LevelToInt(Controllers::BrightnessController::Levels level); Controllers::BrightnessController& brightness;
void SetValue(uint8_t value);
void SetValue(); lv_obj_t* slider_label;
lv_obj_t* slider;
const char* LevelToString(Controllers::BrightnessController::Levels level);
uint8_t LevelToInt(Controllers::BrightnessController::Levels level);
void SetValue(uint8_t value);
void SetValue();
}; };
} }
} }

View file

@ -15,45 +15,48 @@
#include "WatchFaceDigital.h" #include "WatchFaceDigital.h"
#include "WatchFaceAnalog.h" #include "WatchFaceAnalog.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
Clock::Clock(DisplayApp* app, Clock::Clock(DisplayApp* app,
Controllers::DateTime& dateTimeController, Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController, Controllers::Battery& batteryController,
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings &settingsController, Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController, Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController) : Screen(app), Controllers::MotionController& motionController)
dateTimeController{dateTimeController}, batteryController{batteryController}, : Screen(app),
bleController{bleController}, notificatioManager{notificatioManager}, dateTimeController {dateTimeController},
settingsController{settingsController}, batteryController {batteryController},
heartRateController{heartRateController}, bleController {bleController},
motionController{motionController}, notificatioManager {notificatioManager},
screens{app, settingsController {settingsController},
settingsController.GetClockFace(), heartRateController {heartRateController},
{ motionController {motionController},
[this]() -> std::unique_ptr<Screen> { return WatchFaceDigitalScreen(); }, screens {app,
[this]() -> std::unique_ptr<Screen> { return WatchFaceAnalogScreen(); }, settingsController.GetClockFace(),
// Examples for more watch faces {
//[this]() -> std::unique_ptr<Screen> { return WatchFaceMinimalScreen(); }, [this]() -> std::unique_ptr<Screen> {
//[this]() -> std::unique_ptr<Screen> { return WatchFaceCustomScreen(); } return WatchFaceDigitalScreen();
}, },
Screens::ScreenListModes::LongPress [this]() -> std::unique_ptr<Screen> {
} { return WatchFaceAnalogScreen();
},
// Examples for more watch faces
//[this]() -> std::unique_ptr<Screen> { return WatchFaceMinimalScreen(); },
//[this]() -> std::unique_ptr<Screen> { return WatchFaceCustomScreen(); }
},
Screens::ScreenListModes::LongPress} {
settingsController.SetAppMenu(0); settingsController.SetAppMenu(0);
}
}
Clock::~Clock() { Clock::~Clock() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
bool Clock::Refresh() {
bool Clock::Refresh() { screens.Refresh();
screens.Refresh();
return running; return running;
} }
@ -61,21 +64,31 @@ bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return screens.OnTouchEvent(event); return screens.OnTouchEvent(event);
} }
std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() { std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {
return std::make_unique<Screens::WatchFaceDigital>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController, motionController); return std::make_unique<Screens::WatchFaceDigital>(app,
dateTimeController,
batteryController,
bleController,
notificatioManager,
settingsController,
heartRateController,
motionController);
} }
std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() { std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {
return std::make_unique<Screens::WatchFaceAnalog>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController); return std::make_unique<Screens::WatchFaceAnalog>(
app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
} }
/* /*
// Examples for more watch faces // Examples for more watch faces
std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() { std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() {
return std::make_unique<Screens::WatchFaceMinimal>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController); return std::make_unique<Screens::WatchFaceMinimal>(app, dateTimeController, batteryController, bleController, notificatioManager,
settingsController);
} }
std::unique_ptr<Screen> Clock::WatchFaceCustomScreen() { std::unique_ptr<Screen> Clock::WatchFaceCustomScreen() {
return std::make_unique<Screens::WatchFaceCustom>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController); return std::make_unique<Screens::WatchFaceCustom>(app, dateTimeController, batteryController, bleController, notificatioManager,
settingsController);
} }
*/ */

View file

@ -23,42 +23,37 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class Clock : public Screen { class Clock : public Screen {
public: public:
Clock(DisplayApp* app, Clock(DisplayApp* app,
Controllers::DateTime& dateTimeController, Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController, Controllers::Battery& batteryController,
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings &settingsController, Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController, Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController); Controllers::MotionController& motionController);
~Clock() override; ~Clock() override;
bool Refresh() override; bool Refresh() override;
bool OnTouchEvent(TouchEvents event) override;
private: bool OnTouchEvent(TouchEvents event) override;
Controllers::DateTime& dateTimeController; private:
Controllers::Battery& batteryController; Controllers::DateTime& dateTimeController;
Controllers::Ble& bleController; Controllers::Battery& batteryController;
Controllers::NotificationManager& notificatioManager; Controllers::Ble& bleController;
Controllers::Settings& settingsController; Controllers::NotificationManager& notificatioManager;
Controllers::HeartRateController& heartRateController; Controllers::Settings& settingsController;
Controllers::MotionController& motionController; Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
ScreenList<2> screens;
std::unique_ptr<Screen> WatchFaceDigitalScreen();
std::unique_ptr<Screen> WatchFaceAnalogScreen();
ScreenList<2> screens; // Examples for more watch faces
std::unique_ptr<Screen> WatchFaceDigitalScreen(); // std::unique_ptr<Screen> WatchFaceMinimalScreen();
std::unique_ptr<Screen> WatchFaceAnalogScreen(); // std::unique_ptr<Screen> WatchFaceCustomScreen();
// Examples for more watch faces
//std::unique_ptr<Screen> WatchFaceMinimalScreen();
//std::unique_ptr<Screen> WatchFaceCustomScreen();
}; };
} }
} }

View file

@ -5,20 +5,21 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
DropDownDemo::DropDownDemo(Pinetime::Applications::DisplayApp *app) : Screen(app) { DropDownDemo::DropDownDemo(Pinetime::Applications::DisplayApp* app) : Screen(app) {
// Create the dropdown object, with many item, and fix its height // Create the dropdown object, with many item, and fix its height
ddlist = lv_ddlist_create(lv_scr_act(), nullptr); ddlist = lv_ddlist_create(lv_scr_act(), nullptr);
lv_ddlist_set_options(ddlist, "Apple\n" lv_ddlist_set_options(ddlist,
"Banana\n" "Apple\n"
"Orange\n" "Banana\n"
"Melon\n" "Orange\n"
"Grape\n" "Melon\n"
"Raspberry\n" "Grape\n"
"A\n" "Raspberry\n"
"B\n" "A\n"
"C\n" "B\n"
"D\n" "C\n"
"E"); "D\n"
"E");
lv_ddlist_set_fix_width(ddlist, 150); lv_ddlist_set_fix_width(ddlist, 150);
lv_ddlist_set_draw_arrow(ddlist, true); lv_ddlist_set_draw_arrow(ddlist, true);
lv_ddlist_set_fix_height(ddlist, 150); lv_ddlist_set_fix_height(ddlist, 150);
@ -32,12 +33,12 @@ DropDownDemo::~DropDownDemo() {
} }
bool DropDownDemo::Refresh() { bool DropDownDemo::Refresh() {
auto* list = static_cast<lv_ddlist_ext_t *>(ddlist->ext_attr); auto* list = static_cast<lv_ddlist_ext_t*>(ddlist->ext_attr);
// Switch touchmode to Polling if the dropdown is opened. This will allow to scroll inside the // Switch touchmode to Polling if the dropdown is opened. This will allow to scroll inside the
// dropdown while it is opened. // dropdown while it is opened.
// Disable the polling mode when the dropdown is closed to be able to handle the gestures. // Disable the polling mode when the dropdown is closed to be able to handle the gestures.
if(list->opened) if (list->opened)
app->SetTouchMode(DisplayApp::TouchModes::Polling); app->SetTouchMode(DisplayApp::TouchModes::Polling);
else else
app->SetTouchMode(DisplayApp::TouchModes::Gestures); app->SetTouchMode(DisplayApp::TouchModes::Gestures);
@ -47,11 +48,10 @@ bool DropDownDemo::Refresh() {
bool DropDownDemo::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool DropDownDemo::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
// If the dropdown is opened, notify Display app that it doesn't need to handle the event // If the dropdown is opened, notify Display app that it doesn't need to handle the event
// (this will prevent displayApp from going back to the menu or clock scree). // (this will prevent displayApp from going back to the menu or clock scree).
auto* list = static_cast<lv_ddlist_ext_t *>(ddlist->ext_attr); auto* list = static_cast<lv_ddlist_ext_t*>(ddlist->ext_attr);
if(list->opened) { if (list->opened) {
return true; return true;
} else { } else {
return false; return false;
} }
} }

View file

@ -9,18 +9,18 @@ namespace Pinetime {
namespace Screens { namespace Screens {
class DropDownDemo : public Screen { class DropDownDemo : public Screen {
public: public:
DropDownDemo(DisplayApp* app); DropDownDemo(DisplayApp* app);
~DropDownDemo() override; ~DropDownDemo() override;
bool Refresh() override; bool Refresh() override;
bool OnTouchEvent(TouchEvents event) override;
private: bool OnTouchEvent(TouchEvents event) override;
lv_obj_t * ddlist;
private:
bool isDropDownOpened = false; lv_obj_t* ddlist;
bool isDropDownOpened = false;
}; };
} }
} }

View file

@ -5,11 +5,10 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Ble& bleController)
: Screen(app), bleController {bleController} {
FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::Ble& bleController) : lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
Screen(app), bleController{bleController} {
lv_obj_t * backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, 240, 240); lv_obj_set_size(backgroundLabel, 240, 240);
lv_obj_set_pos(backgroundLabel, 0, 0); lv_obj_set_pos(backgroundLabel, 0, 0);
@ -38,21 +37,21 @@ FirmwareUpdate::~FirmwareUpdate() {
} }
bool FirmwareUpdate::Refresh() { bool FirmwareUpdate::Refresh() {
switch(bleController.State()) { switch (bleController.State()) {
default: default:
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Idle: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Idle:
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Running: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Running:
if(state != States::Running) if (state != States::Running)
state = States::Running; state = States::Running;
return DisplayProgression(); return DisplayProgression();
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated:
if(state != States::Validated) { if (state != States::Validated) {
UpdateValidated(); UpdateValidated();
state = States::Validated; state = States::Validated;
} }
return running; return running;
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Error: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Error:
if(state != States::Error) { if (state != States::Error) {
UpdateError(); UpdateError();
state = States::Error; state = States::Error;
} }

View file

@ -10,29 +10,28 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class FirmwareUpdate : public Screen{ class FirmwareUpdate : public Screen {
public: public:
FirmwareUpdate(DisplayApp* app, Pinetime::Controllers::Ble& bleController); FirmwareUpdate(DisplayApp* app, Pinetime::Controllers::Ble& bleController);
~FirmwareUpdate() override; ~FirmwareUpdate() override;
bool Refresh() override; bool Refresh() override;
private: private:
enum class States { Idle, Running, Validated, Error }; enum class States { Idle, Running, Validated, Error };
Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::Ble& bleController;
lv_obj_t* bar1; lv_obj_t* bar1;
lv_obj_t* percentLabel; lv_obj_t* percentLabel;
lv_obj_t* titleLabel; lv_obj_t* titleLabel;
mutable char percentStr[10]; mutable char percentStr[10];
States state;
bool DisplayProgression() const; States state;
void UpdateValidated(); bool DisplayProgression() const;
void UpdateError(); void UpdateValidated();
void UpdateError();
}; };
} }
} }

View file

@ -7,23 +7,20 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
namespace { namespace {
static void ButtonEventHandler(lv_obj_t * obj, lv_event_t event) static void ButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
{ FirmwareValidation* screen = static_cast<FirmwareValidation*>(obj->user_data);
FirmwareValidation* screen = static_cast<FirmwareValidation *>(obj->user_data);
screen->OnButtonEvent(obj, event); screen->OnButtonEvent(obj, event);
} }
} }
FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app, FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator)
Pinetime::Controllers::FirmwareValidator &validator) : Screen {app}, validator {validator} {
: Screen{app}, validator{validator} {
labelVersionInfo = lv_label_create(lv_scr_act(), nullptr); labelVersionInfo = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelVersionInfo, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); lv_obj_align(labelVersionInfo, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_label_set_text(labelVersionInfo, "Version : "); lv_label_set_text(labelVersionInfo, "Version : ");
lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT); lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT);
labelVersionValue = lv_label_create(lv_scr_act(), nullptr); labelVersionValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0); lv_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_label_set_recolor(labelVersionValue, true); lv_label_set_recolor(labelVersionValue, true);
@ -36,11 +33,10 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app,
lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK); lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK);
lv_obj_set_width(labelIsValidated, 240); lv_obj_set_width(labelIsValidated, 240);
if(validator.IsValidated()) if (validator.IsValidated())
lv_label_set_text(labelIsValidated, "You have already\n#00ff00 validated# this firmware#"); lv_label_set_text(labelIsValidated, "You have already\n#00ff00 validated# this firmware#");
else { else {
lv_label_set_text(labelIsValidated, lv_label_set_text(labelIsValidated, "Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version.");
"Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version.");
buttonValidate = lv_btn_create(lv_scr_act(), nullptr); buttonValidate = lv_btn_create(lv_scr_act(), nullptr);
lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
@ -49,19 +45,18 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app,
lv_obj_set_style_local_bg_color(buttonValidate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x009900)); lv_obj_set_style_local_bg_color(buttonValidate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x009900));
labelButtonValidate = lv_label_create(buttonValidate, nullptr); labelButtonValidate = lv_label_create(buttonValidate, nullptr);
lv_label_set_text_static(labelButtonValidate, "Validate"); lv_label_set_text_static(labelButtonValidate, "Validate");
buttonReset = lv_btn_create(lv_scr_act(), nullptr); buttonReset = lv_btn_create(lv_scr_act(), nullptr);
buttonReset->user_data = this; buttonReset->user_data = this;
lv_obj_align(buttonReset, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); lv_obj_align(buttonReset, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
lv_obj_set_style_local_bg_color(buttonReset, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x990000)); lv_obj_set_style_local_bg_color(buttonReset, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x990000));
lv_obj_set_event_cb(buttonReset, ButtonEventHandler); lv_obj_set_event_cb(buttonReset, ButtonEventHandler);
labelButtonReset = lv_label_create(buttonReset, nullptr);
lv_label_set_text_static(labelButtonReset, "Reset");
}
}
labelButtonReset = lv_label_create(buttonReset, nullptr);
lv_label_set_text_static(labelButtonReset, "Reset");
}
}
FirmwareValidation::~FirmwareValidation() { FirmwareValidation::~FirmwareValidation() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
@ -71,12 +66,11 @@ bool FirmwareValidation::Refresh() {
return running; return running;
} }
void FirmwareValidation::OnButtonEvent(lv_obj_t *object, lv_event_t event) { void FirmwareValidation::OnButtonEvent(lv_obj_t* object, lv_event_t event) {
if(object == buttonValidate && event == LV_EVENT_PRESSED) { if (object == buttonValidate && event == LV_EVENT_PRESSED) {
validator.Validate(); validator.Validate();
running = false; running = false;
} else if(object == buttonReset && event == LV_EVENT_PRESSED) { } else if (object == buttonReset && event == LV_EVENT_PRESSED) {
validator.Reset(); validator.Reset();
} }
} }

View file

@ -11,28 +11,26 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class FirmwareValidation : public Screen{ class FirmwareValidation : public Screen {
public: public:
FirmwareValidation(DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator); FirmwareValidation(DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator);
~FirmwareValidation() override; ~FirmwareValidation() override;
bool Refresh() override; bool Refresh() override;
void OnButtonEvent(lv_obj_t *object, lv_event_t event); void OnButtonEvent(lv_obj_t* object, lv_event_t event);
private: private:
Pinetime::Controllers::FirmwareValidator& validator; Pinetime::Controllers::FirmwareValidator& validator;
lv_obj_t* labelVersionInfo; lv_obj_t* labelVersionInfo;
lv_obj_t* labelVersionValue; lv_obj_t* labelVersionValue;
char version[9]; char version[9];
lv_obj_t* labelIsValidated; lv_obj_t* labelIsValidated;
lv_obj_t* buttonValidate; lv_obj_t* buttonValidate;
lv_obj_t* labelButtonValidate; lv_obj_t* labelButtonValidate;
lv_obj_t* buttonReset; lv_obj_t* buttonReset;
lv_obj_t* labelButtonReset; lv_obj_t* labelButtonReset;
}; };
} }
} }

View file

@ -5,46 +5,43 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
namespace { namespace {
static void event_handler(lv_obj_t * obj, lv_event_t event) { static void event_handler(lv_obj_t* obj, lv_event_t event) {
FlashLight* screen = static_cast<FlashLight *>(obj->user_data); FlashLight* screen = static_cast<FlashLight*>(obj->user_data);
screen->OnClickEvent(obj, event); screen->OnClickEvent(obj, event);
} }
} }
FlashLight::FlashLight( FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app,
Pinetime::Applications::DisplayApp *app, System::SystemTask& systemTask,
System::SystemTask &systemTask, Controllers::BrightnessController& brightness)
Controllers::BrightnessController& brightness) : : Screen(app),
Screen(app), systemTask {systemTask},
systemTask{systemTask}, brightness {brightness}
brightness{brightness}
{ {
brightness.Backup(); brightness.Backup();
brightness.Set(Controllers::BrightnessController::Levels::High); brightness.Set(Controllers::BrightnessController::Levels::High);
// Set the background // Set the background
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF));
flashLight = lv_label_create(lv_scr_act(), NULL); flashLight = lv_label_create(lv_scr_act(), NULL);
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_text_font(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48); lv_obj_set_style_local_text_font(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
lv_label_set_text_static(flashLight, Symbols::highlight); lv_label_set_text_static(flashLight, Symbols::highlight);
lv_obj_align(flashLight, NULL, LV_ALIGN_CENTER, 0, 0); lv_obj_align(flashLight, NULL, LV_ALIGN_CENTER, 0, 0);
backgroundAction = lv_label_create(lv_scr_act(), nullptr); backgroundAction = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(backgroundAction, LV_LABEL_LONG_CROP); lv_label_set_long_mode(backgroundAction, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundAction, 240, 240); lv_obj_set_size(backgroundAction, 240, 240);
lv_obj_set_pos(backgroundAction, 0, 0); lv_obj_set_pos(backgroundAction, 0, 0);
lv_label_set_text(backgroundAction, ""); lv_label_set_text(backgroundAction, "");
lv_obj_set_click(backgroundAction, true); lv_obj_set_click(backgroundAction, true);
backgroundAction->user_data = this; backgroundAction->user_data = this;
lv_obj_set_event_cb(backgroundAction, event_handler); lv_obj_set_event_cb(backgroundAction, event_handler);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
} }
FlashLight::~FlashLight() { FlashLight::~FlashLight() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
@ -52,19 +49,18 @@ FlashLight::~FlashLight() {
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping); systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping);
} }
void FlashLight::OnClickEvent(lv_obj_t *obj, lv_event_t event) { void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) {
if(obj == backgroundAction) { if (obj == backgroundAction) {
if (event == LV_EVENT_CLICKED) { if (event == LV_EVENT_CLICKED) {
isOn = !isOn; isOn = !isOn;
if ( isOn ) { if (isOn) {
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF));
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
} else { } else {
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF));
} }
} }
} }
} }
@ -76,4 +72,3 @@ bool FlashLight::Refresh() {
bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return true; return true;
} }

View file

@ -6,30 +6,28 @@
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
#include "components/brightness/BrightnessController.h" #include "components/brightness/BrightnessController.h"
namespace Pinetime { namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class FlashLight : public Screen{ class FlashLight : public Screen {
public: public:
FlashLight(DisplayApp* app, System::SystemTask &systemTask, Controllers::BrightnessController& brightness); FlashLight(DisplayApp* app, System::SystemTask& systemTask, Controllers::BrightnessController& brightness);
~FlashLight() override; ~FlashLight() override;
bool Refresh() override; bool Refresh() override;
bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;
void OnClickEvent(lv_obj_t *obj, lv_event_t event);
private: bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;
Pinetime::System::SystemTask& systemTask; void OnClickEvent(lv_obj_t* obj, lv_event_t event);
Controllers::BrightnessController& brightness;
lv_obj_t* flashLight; private:
lv_obj_t* backgroundAction; Pinetime::System::SystemTask& systemTask;
bool isOn = true; Controllers::BrightnessController& brightness;
lv_obj_t* flashLight;
lv_obj_t* backgroundAction;
bool isOn = true;
}; };
} }
} }

View file

@ -7,7 +7,7 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
namespace { namespace {
const char *ToString(Pinetime::Controllers::HeartRateController::States s) { const char* ToString(Pinetime::Controllers::HeartRateController::States s) {
switch (s) { switch (s) {
case Pinetime::Controllers::HeartRateController::States::NotEnoughData: case Pinetime::Controllers::HeartRateController::States::NotEnoughData:
return "Not enough data,\nplease wait..."; return "Not enough data,\nplease wait...";
@ -21,35 +21,37 @@ namespace {
return ""; return "";
} }
static void btnStartStopEventHandler(lv_obj_t *obj, lv_event_t event) { static void btnStartStopEventHandler(lv_obj_t* obj, lv_event_t event) {
HeartRate *screen = static_cast<HeartRate *>(obj->user_data); HeartRate* screen = static_cast<HeartRate*>(obj->user_data);
screen->OnStartStopEvent(event); screen->OnStartStopEvent(event);
} }
} }
HeartRate::HeartRate(Pinetime::Applications::DisplayApp *app, Controllers::HeartRateController& heartRateController, System::SystemTask &systemTask) : HeartRate::HeartRate(Pinetime::Applications::DisplayApp* app,
Screen(app), heartRateController{heartRateController}, systemTask{systemTask} { Controllers::HeartRateController& heartRateController,
System::SystemTask& systemTask)
: Screen(app), heartRateController {heartRateController}, systemTask {systemTask} {
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
label_hr = lv_label_create(lv_scr_act(), nullptr); label_hr = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); lv_obj_set_style_local_text_font(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
if(isHrRunning) if (isHrRunning)
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
else else
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_label_set_text(label_hr, "000"); lv_label_set_text(label_hr, "000");
lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40); lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40);
label_bpm = lv_label_create(lv_scr_act(), nullptr); label_bpm = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text(label_bpm, "Heart rate BPM"); lv_label_set_text(label_bpm, "Heart rate BPM");
lv_obj_align(label_bpm, label_hr, LV_ALIGN_OUT_TOP_MID, 0, -20); lv_obj_align(label_bpm, label_hr, LV_ALIGN_OUT_TOP_MID, 0, -20);
label_status = lv_label_create(lv_scr_act(), nullptr); label_status = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(label_status, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x222222)); lv_obj_set_style_local_text_color(label_status, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x222222));
lv_label_set_text(label_status, ToString(Pinetime::Controllers::HeartRateController::States::NotEnoughData)); lv_label_set_text(label_status, ToString(Pinetime::Controllers::HeartRateController::States::NotEnoughData));
lv_obj_align(label_status, label_hr, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); lv_obj_align(label_status, label_hr, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
btn_startStop = lv_btn_create(lv_scr_act(), nullptr); btn_startStop = lv_btn_create(lv_scr_act(), nullptr);
@ -60,7 +62,7 @@ HeartRate::HeartRate(Pinetime::Applications::DisplayApp *app, Controllers::Heart
label_startStop = lv_label_create(btn_startStop, nullptr); label_startStop = lv_label_create(btn_startStop, nullptr);
UpdateStartStopButton(isHrRunning); UpdateStartStopButton(isHrRunning);
if(isHrRunning) if (isHrRunning)
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
} }
@ -72,10 +74,10 @@ HeartRate::~HeartRate() {
bool HeartRate::Refresh() { bool HeartRate::Refresh() {
auto state = heartRateController.State(); auto state = heartRateController.State();
switch(state) { switch (state) {
case Controllers::HeartRateController::States::NoTouch: case Controllers::HeartRateController::States::NoTouch:
case Controllers::HeartRateController::States::NotEnoughData: case Controllers::HeartRateController::States::NotEnoughData:
//case Controllers::HeartRateController::States::Stopped: // case Controllers::HeartRateController::States::Stopped:
lv_label_set_text(label_hr, "000"); lv_label_set_text(label_hr, "000");
break; break;
default: default:
@ -90,13 +92,12 @@ bool HeartRate::Refresh() {
void HeartRate::OnStartStopEvent(lv_event_t event) { void HeartRate::OnStartStopEvent(lv_event_t event) {
if (event == LV_EVENT_CLICKED) { if (event == LV_EVENT_CLICKED) {
if(heartRateController.State() == Controllers::HeartRateController::States::Stopped) { if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
heartRateController.Start(); heartRateController.Start();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
} } else {
else {
heartRateController.Stop(); heartRateController.Stop();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping); systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping);
@ -106,7 +107,7 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
} }
void HeartRate::UpdateStartStopButton(bool isRunning) { void HeartRate::UpdateStartStopButton(bool isRunning) {
if(isRunning) if (isRunning)
lv_label_set_text(label_startStop, "Stop"); lv_label_set_text(label_startStop, "Stop");
else else
lv_label_set_text(label_startStop, "Start"); lv_label_set_text(label_startStop, "Start");

View file

@ -15,13 +15,13 @@ namespace Pinetime {
namespace Applications { namespace Applications {
namespace Screens { namespace Screens {
class HeartRate : public Screen{ class HeartRate : public Screen {
public: public:
HeartRate(DisplayApp* app, Controllers::HeartRateController& HeartRateController, System::SystemTask &systemTask); HeartRate(DisplayApp* app, Controllers::HeartRateController& HeartRateController, System::SystemTask& systemTask);
~HeartRate() override; ~HeartRate() override;
bool Refresh() override; bool Refresh() override;
void OnStartStopEvent(lv_event_t event); void OnStartStopEvent(lv_event_t event);
private: private:
@ -33,9 +33,6 @@ namespace Pinetime {
lv_obj_t* label_status; lv_obj_t* label_status;
lv_obj_t* btn_startStop; lv_obj_t* btn_startStop;
lv_obj_t* label_startStop; lv_obj_t* label_startStop;
}; };
} }
} }

View file

@ -4,7 +4,7 @@
using namespace Pinetime::Applications::Screens; 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) : Screen(app), lvgl {lvgl} {
app->SetTouchMode(DisplayApp::TouchModes::Polling); app->SetTouchMode(DisplayApp::TouchModes::Polling);
std::fill(b, b + bufferSize, selectColor); std::fill(b, b + bufferSize, selectColor);
} }
@ -20,8 +20,8 @@ bool InfiniPaint::Refresh() {
} }
bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
switch(event) { switch (event) {
case Pinetime::Applications::TouchEvents::LongTap: case Pinetime::Applications::TouchEvents::LongTap:
switch (color) { switch (color) {
case 0: case 0:
selectColor = LV_COLOR_MAGENTA; selectColor = LV_COLOR_MAGENTA;
@ -47,13 +47,13 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
case 7: case 7:
selectColor = LV_COLOR_BLACK; selectColor = LV_COLOR_BLACK;
break; break;
default: default:
color = 0; color = 0;
break; break;
} }
std::fill(b, b + bufferSize, selectColor); std::fill(b, b + bufferSize, selectColor);
color++; color++;
return true; return true;
default: default:
@ -72,4 +72,3 @@ bool InfiniPaint::OnTouchEvent(uint16_t x, uint16_t y) {
lvgl.FlushDisplay(&area, b); lvgl.FlushDisplay(&area, b);
return true; return true;
} }

View file

@ -31,7 +31,6 @@ namespace Pinetime {
lv_color_t b[bufferSize]; lv_color_t b[bufferSize];
lv_color_t selectColor = LV_COLOR_WHITE; lv_color_t selectColor = LV_COLOR_WHITE;
uint8_t color = 2; uint8_t color = 2;
}; };
} }
} }

View file

@ -2,24 +2,21 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
Label::Label(uint8_t screenID, uint8_t numScreens, Label::Label(uint8_t screenID, uint8_t numScreens, Pinetime::Applications::DisplayApp* app, lv_obj_t* labelText)
Pinetime::Applications::DisplayApp *app, lv_obj_t* labelText) : : Screen(app), labelText {labelText} {
Screen(app),
labelText{labelText} { if (numScreens > 1) {
if ( numScreens > 1 ) {
pageIndicatorBasePoints[0].x = 240 - 1; pageIndicatorBasePoints[0].x = 240 - 1;
pageIndicatorBasePoints[0].y = 6; pageIndicatorBasePoints[0].y = 6;
pageIndicatorBasePoints[1].x = 240 - 1; pageIndicatorBasePoints[1].x = 240 - 1;
pageIndicatorBasePoints[1].y = 240 - 6; pageIndicatorBasePoints[1].y = 240 - 6;
pageIndicatorBase = lv_line_create(lv_scr_act(), NULL); pageIndicatorBase = lv_line_create(lv_scr_act(), NULL);
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true); lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2); lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
uint16_t indicatorSize = 228 / numScreens; uint16_t indicatorSize = 228 / numScreens;
uint16_t indicatorPos = indicatorSize * screenID; uint16_t indicatorPos = indicatorSize * screenID;
@ -34,7 +31,6 @@ Label::Label(uint8_t screenID, uint8_t numScreens,
lv_obj_set_style_local_line_rounded(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true); lv_obj_set_style_local_line_rounded(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
lv_line_set_points(pageIndicator, pageIndicatorPoints, 2); lv_line_set_points(pageIndicator, pageIndicatorPoints, 2);
} }
} }
Label::~Label() { Label::~Label() {

View file

@ -8,23 +8,20 @@ namespace Pinetime {
namespace Screens { namespace Screens {
class Label : public Screen { class Label : public Screen {
public: public:
Label( uint8_t screenID, uint8_t numScreens, Label(uint8_t screenID, uint8_t numScreens, DisplayApp* app, lv_obj_t* labelText);
DisplayApp* app, lv_obj_t* labelText ); ~Label() override;
~Label() override;
bool Refresh() override;
private: bool Refresh() override;
bool running = true; private:
bool running = true;
lv_obj_t * labelText = nullptr;
lv_point_t pageIndicatorBasePoints[2]; lv_obj_t* labelText = nullptr;
lv_point_t pageIndicatorPoints[2]; lv_point_t pageIndicatorBasePoints[2];
lv_obj_t* pageIndicatorBase; lv_point_t pageIndicatorPoints[2];
lv_obj_t* pageIndicator; lv_obj_t* pageIndicatorBase;
lv_obj_t* pageIndicator;
}; };
} }
} }

View file

@ -5,39 +5,37 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
namespace { namespace {
static void ButtonEventHandler(lv_obj_t * obj, lv_event_t event) { static void ButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
List* screen = static_cast<List *>(obj->user_data); List* screen = static_cast<List*>(obj->user_data);
screen->OnButtonEvent(obj, event); screen->OnButtonEvent(obj, event);
} }
} }
List::List(uint8_t screenID, uint8_t numScreens, List::List(uint8_t screenID,
DisplayApp* app, uint8_t numScreens,
Controllers::Settings &settingsController, DisplayApp* app,
std::array<Applications, MAXLISTITEMS>& applications) : Controllers::Settings& settingsController,
Screen(app), std::array<Applications, MAXLISTITEMS>& applications)
settingsController{settingsController} : Screen(app), settingsController {settingsController} {
{
// Set the background to Black // Set the background to Black
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_make(0, 0, 0)); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_make(0, 0, 0));
settingsController.SetSettingsMenu(screenID); settingsController.SetSettingsMenu(screenID);
if ( numScreens > 1 ) { if (numScreens > 1) {
pageIndicatorBasePoints[0].x = 240 - 1; pageIndicatorBasePoints[0].x = 240 - 1;
pageIndicatorBasePoints[0].y = 6; pageIndicatorBasePoints[0].y = 6;
pageIndicatorBasePoints[1].x = 240 - 1; pageIndicatorBasePoints[1].x = 240 - 1;
pageIndicatorBasePoints[1].y = 240 - 6; pageIndicatorBasePoints[1].y = 240 - 6;
pageIndicatorBase = lv_line_create(lv_scr_act(), NULL); pageIndicatorBase = lv_line_create(lv_scr_act(), NULL);
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true); lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2); lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
uint16_t indicatorSize = 228 / numScreens; uint16_t indicatorSize = 228 / numScreens;
uint16_t indicatorPos = indicatorSize * screenID; uint16_t indicatorPos = indicatorSize * screenID;
@ -53,10 +51,9 @@ List::List(uint8_t screenID, uint8_t numScreens,
lv_line_set_points(pageIndicator, pageIndicatorPoints, 2); lv_line_set_points(pageIndicator, pageIndicatorPoints, 2);
} }
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr); lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
//lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); // lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10);
lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
@ -67,12 +64,12 @@ List::List(uint8_t screenID, uint8_t numScreens,
lv_obj_set_height(container1, LV_VER_RES); lv_obj_set_height(container1, LV_VER_RES);
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
lv_obj_t * labelBt; lv_obj_t* labelBt;
lv_obj_t * labelBtIco; lv_obj_t* labelBtIco;
for(int i = 0; i < MAXLISTITEMS; i++) { for (int i = 0; i < MAXLISTITEMS; i++) {
apps[i] = applications[i].application; apps[i] = applications[i].application;
if ( applications[i].application != Apps::None) { if (applications[i].application != Apps::None) {
itemApps[i] = lv_btn_create(container1, nullptr); itemApps[i] = lv_btn_create(container1, nullptr);
lv_obj_set_style_local_bg_opa(itemApps[i], LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20); lv_obj_set_style_local_bg_opa(itemApps[i], LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20);
@ -91,19 +88,17 @@ List::List(uint8_t screenID, uint8_t numScreens,
labelBt = lv_label_create(itemApps[i], nullptr); labelBt = lv_label_create(itemApps[i], nullptr);
lv_label_set_text_fmt(labelBt, " %s", applications[i].name); lv_label_set_text_fmt(labelBt, " %s", applications[i].name);
} }
} }
lv_obj_t * backgroundLabel = lv_label_create(lv_scr_act(), nullptr); lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, LV_HOR_RES, LV_VER_RES); lv_obj_set_size(backgroundLabel, LV_HOR_RES, LV_VER_RES);
lv_obj_set_pos(backgroundLabel, 0, 0); lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, ""); lv_label_set_text_static(backgroundLabel, "");
} }
List::~List() { List::~List() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
@ -112,10 +107,10 @@ bool List::Refresh() {
return running; return running;
} }
void List::OnButtonEvent(lv_obj_t * object, lv_event_t event) { void List::OnButtonEvent(lv_obj_t* object, lv_event_t event) {
if ( event == LV_EVENT_RELEASED ) { if (event == LV_EVENT_RELEASED) {
for(int i = 0; i < MAXLISTITEMS; i++) { for (int i = 0; i < MAXLISTITEMS; i++) {
if ( apps[i] != Apps::None && object == itemApps[i] ) { if (apps[i] != Apps::None && object == itemApps[i]) {
app->StartApp(apps[i], DisplayApp::FullRefreshDirections::Up); app->StartApp(apps[i], DisplayApp::FullRefreshDirections::Up);
running = false; running = false;
return; return;
@ -123,4 +118,3 @@ void List::OnButtonEvent(lv_obj_t * object, lv_event_t event) {
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show more