Merge branch 'develop' of
https://github.com/JF002/InfiniTime into StepsApp
This commit is contained in:
		
						commit
						cd0d85dff9
					
				@ -21,8 +21,8 @@ AlwaysBreakAfterDefinitionReturnType: None
 | 
			
		||||
AlwaysBreakAfterReturnType: None
 | 
			
		||||
AlwaysBreakBeforeMultilineStrings: false
 | 
			
		||||
AlwaysBreakTemplateDeclarations: MultiLine
 | 
			
		||||
BinPackArguments: true
 | 
			
		||||
BinPackParameters: true
 | 
			
		||||
BinPackArguments: false
 | 
			
		||||
BinPackParameters: false
 | 
			
		||||
BraceWrapping:
 | 
			
		||||
  AfterCaseLabel: false
 | 
			
		||||
  AfterClass: false
 | 
			
		||||
@ -52,14 +52,14 @@ BreakStringLiterals: true
 | 
			
		||||
ColumnLimit: 140
 | 
			
		||||
CommentPragmas: '^ IWYU pragma:'
 | 
			
		||||
CompactNamespaces: false
 | 
			
		||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
 | 
			
		||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
 | 
			
		||||
ConstructorInitializerIndentWidth: 2
 | 
			
		||||
ContinuationIndentWidth: 2
 | 
			
		||||
Cpp11BracedListStyle: true
 | 
			
		||||
DeriveLineEnding: false
 | 
			
		||||
DerivePointerAlignment: false
 | 
			
		||||
DisableFormat: false
 | 
			
		||||
ExperimentalAutoDetectBinPacking: false
 | 
			
		||||
ExperimentalAutoDetectBinPacking: true
 | 
			
		||||
FixNamespaceComments: false
 | 
			
		||||
ForEachMacros:
 | 
			
		||||
  - foreach
 | 
			
		||||
@ -90,10 +90,6 @@ MacroBlockBegin: ''
 | 
			
		||||
MacroBlockEnd: ''
 | 
			
		||||
MaxEmptyLinesToKeep: 1
 | 
			
		||||
NamespaceIndentation: All
 | 
			
		||||
ObjCBinPackProtocolList: Auto
 | 
			
		||||
ObjCBlockIndentWidth: 2
 | 
			
		||||
ObjCSpaceAfterProperty: false
 | 
			
		||||
ObjCSpaceBeforeProtocolList: true
 | 
			
		||||
PenaltyBreakAssignment: 2
 | 
			
		||||
PenaltyBreakBeforeFirstCallParameter: 19
 | 
			
		||||
PenaltyBreakComment: 300
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,4 +1,7 @@
 | 
			
		||||
.idea/
 | 
			
		||||
# Python virtual environment for DFU images
 | 
			
		||||
.venv/
 | 
			
		||||
 | 
			
		||||
# CMake
 | 
			
		||||
cmake-build-*
 | 
			
		||||
cmake-*
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
								
							@ -24,8 +24,7 @@ The goal of this project is to design an open-source firmware for the Pinetime s
 | 
			
		||||
 | 
			
		||||
## Overview
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
As of now, here is the list of achievements of this project:
 | 
			
		||||
 | 
			
		||||
@ -37,10 +36,15 @@ As of now, here is the list of achievements of this project:
 | 
			
		||||
 - Heart rate measurements
 | 
			
		||||
 - Step counting
 | 
			
		||||
 - Wake-up on wrist rotation
 | 
			
		||||
 - Multiple 'apps' : 
 | 
			
		||||
    * Clock (displays the date, time, battery level, ble connection status, heart rate)
 | 
			
		||||
    * System info (displays various info : BLE MAC, build date/time, uptime, version,...)
 | 
			
		||||
    * Brightess (allows the user to configure the brightness of the display)
 | 
			
		||||
 - Quick actions 
 | 
			
		||||
    * Disable vibration on notification
 | 
			
		||||
    * Brightness settings
 | 
			
		||||
    * Flashlight
 | 
			
		||||
    * Settings
 | 
			
		||||
 - 2 watch faces:
 | 
			
		||||
    * Digital
 | 
			
		||||
    * Analog
 | 
			
		||||
 - Multiple 'apps' :
 | 
			
		||||
    * Music (control the playback of the music on your phone)
 | 
			
		||||
    * Heart rate (controls the heart rate sensor and display current heartbeat)
 | 
			
		||||
    * Navigation (displays navigation instructions coming from the companion app)
 | 
			
		||||
@ -48,13 +52,22 @@ As of now, here is the list of achievements of this project:
 | 
			
		||||
    * Paddle (single player pong-like game)
 | 
			
		||||
    * Two (2048 clone game)
 | 
			
		||||
    * Stopwatch (with all the necessary functions such as play, pause, lap, stop)
 | 
			
		||||
    * Motion sensor and step counter (displays the number of steps and the state of the motion sensor in real-time)
 | 
			
		||||
 - User settings:
 | 
			
		||||
    * Display timeout
 | 
			
		||||
    * Wake-up condition
 | 
			
		||||
    * Time format (12/24h)
 | 
			
		||||
    * Default watch face
 | 
			
		||||
    * Battery status
 | 
			
		||||
    * Firmware validation
 | 
			
		||||
    * System information
 | 
			
		||||
 - Supported by 3 companion apps (development is in progress):
 | 
			
		||||
    * [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android)
 | 
			
		||||
    * [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS and Linux)
 | 
			
		||||
    * [Siglo](https://github.com/alexr4535/siglo) (on Linux)
 | 
			
		||||
    * **[Experimental]** [WebBLEWatch](https://hubmartin.github.io/WebBLEWatch/) Synchronize time directly from your web browser. [video](https://youtu.be/IakiuhVDdrY)
 | 
			
		||||
 - **[Experimental]** OTA (Over-the-air) update via BLE
 | 
			
		||||
 - **[Experimental]** Bootloader based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/)
 | 
			
		||||
 - OTA (Over-the-air) update via BLE
 | 
			
		||||
 - [Bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader) based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/)
 | 
			
		||||
 
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
@ -62,7 +75,7 @@ As of now, here is the list of achievements of this project:
 | 
			
		||||
 - [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/gettingStarted.md)
 | 
			
		||||
 | 
			
		||||
### 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)
 | 
			
		||||
 | 
			
		||||
### Build, flash and debug
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								images/1.0.0/collage.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								images/1.0.0/collage.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.4 MiB  | 
@ -17,7 +17,7 @@ uint32_t BootloaderVersion::Patch() {
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *BootloaderVersion::VersionString() {
 | 
			
		||||
const char* BootloaderVersion::VersionString() {
 | 
			
		||||
  return "0.0.0";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,11 @@
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  class BootloaderVersion {
 | 
			
		||||
    public:
 | 
			
		||||
      static uint32_t Major();
 | 
			
		||||
      static uint32_t Minor();
 | 
			
		||||
      static uint32_t Patch();
 | 
			
		||||
      static const char* VersionString();
 | 
			
		||||
      static bool IsValid();
 | 
			
		||||
  public:
 | 
			
		||||
    static uint32_t Major();
 | 
			
		||||
    static uint32_t Minor();
 | 
			
		||||
    static uint32_t Patch();
 | 
			
		||||
    static const char* VersionString();
 | 
			
		||||
    static bool IsValid();
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -26,20 +26,19 @@
 | 
			
		||||
 * 1 tab == 4 spaces!
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifndef FREERTOS_CONFIG_H
 | 
			
		||||
#define FREERTOS_CONFIG_H
 | 
			
		||||
 | 
			
		||||
#ifdef SOFTDEVICE_PRESENT
 | 
			
		||||
#include "nrf_soc.h"
 | 
			
		||||
  #include "nrf_soc.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "app_util_platform.h"
 | 
			
		||||
 | 
			
		||||
/*-----------------------------------------------------------
 | 
			
		||||
 * Possible configurations for system timer
 | 
			
		||||
 */
 | 
			
		||||
#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_RTC     0 /**< Use real time clock for the system */
 | 
			
		||||
#define FREERTOS_USE_SYSTICK 1 /**< Use SysTick timer for system */
 | 
			
		||||
 | 
			
		||||
/*-----------------------------------------------------------
 | 
			
		||||
 * Application specific definitions.
 | 
			
		||||
@ -55,153 +54,150 @@
 | 
			
		||||
 | 
			
		||||
#define configTICK_SOURCE FREERTOS_USE_RTC
 | 
			
		||||
 | 
			
		||||
#define configUSE_PREEMPTION 1
 | 
			
		||||
#define configUSE_PREEMPTION                    1
 | 
			
		||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
 | 
			
		||||
#define configUSE_TICKLESS_IDLE 1
 | 
			
		||||
#define configUSE_TICKLESS_IDLE_SIMPLE_DEBUG                                      0 /* See into vPortSuppressTicksAndSleep source code for explanation */
 | 
			
		||||
#define configCPU_CLOCK_HZ                                                        ( SystemCoreClock )
 | 
			
		||||
#define configTICK_RATE_HZ                                                        1024
 | 
			
		||||
#define configMAX_PRIORITIES                                                      ( 3 )
 | 
			
		||||
#define configMINIMAL_STACK_SIZE                                                  ( 120 )
 | 
			
		||||
#define configTOTAL_HEAP_SIZE                                                     ( 1024*16 )
 | 
			
		||||
#define configMAX_TASK_NAME_LEN                                                   ( 4 )
 | 
			
		||||
#define configUSE_16_BIT_TICKS                                                    0
 | 
			
		||||
#define configIDLE_SHOULD_YIELD                                                   1
 | 
			
		||||
#define configUSE_MUTEXES                                                         1
 | 
			
		||||
#define configUSE_RECURSIVE_MUTEXES                                               1
 | 
			
		||||
#define configUSE_COUNTING_SEMAPHORES                                             1
 | 
			
		||||
#define configUSE_ALTERNATIVE_API                                                 0    /* Deprecated! */
 | 
			
		||||
#define configQUEUE_REGISTRY_SIZE                                                 2
 | 
			
		||||
#define configUSE_QUEUE_SETS                                                      0
 | 
			
		||||
#define configUSE_TIME_SLICING                                                    0
 | 
			
		||||
#define configUSE_NEWLIB_REENTRANT                                                0
 | 
			
		||||
#define configENABLE_BACKWARD_COMPATIBILITY                                       1
 | 
			
		||||
#define configUSE_TICKLESS_IDLE                 1
 | 
			
		||||
#define configUSE_TICKLESS_IDLE_SIMPLE_DEBUG    0 /* See into vPortSuppressTicksAndSleep source code for explanation */
 | 
			
		||||
#define configCPU_CLOCK_HZ                      (SystemCoreClock)
 | 
			
		||||
#define configTICK_RATE_HZ                      1024
 | 
			
		||||
#define configMAX_PRIORITIES                    (3)
 | 
			
		||||
#define configMINIMAL_STACK_SIZE                (120)
 | 
			
		||||
#define configTOTAL_HEAP_SIZE                   (1024 * 16)
 | 
			
		||||
#define configMAX_TASK_NAME_LEN                 (4)
 | 
			
		||||
#define configUSE_16_BIT_TICKS                  0
 | 
			
		||||
#define configIDLE_SHOULD_YIELD                 1
 | 
			
		||||
#define configUSE_MUTEXES                       1
 | 
			
		||||
#define configUSE_RECURSIVE_MUTEXES             1
 | 
			
		||||
#define configUSE_COUNTING_SEMAPHORES           1
 | 
			
		||||
#define configUSE_ALTERNATIVE_API               0 /* Deprecated! */
 | 
			
		||||
#define configQUEUE_REGISTRY_SIZE               2
 | 
			
		||||
#define configUSE_QUEUE_SETS                    0
 | 
			
		||||
#define configUSE_TIME_SLICING                  0
 | 
			
		||||
#define configUSE_NEWLIB_REENTRANT              0
 | 
			
		||||
#define configENABLE_BACKWARD_COMPATIBILITY     1
 | 
			
		||||
 | 
			
		||||
/* Hook function related definitions. */
 | 
			
		||||
#define configUSE_IDLE_HOOK                                                       1
 | 
			
		||||
#define configUSE_TICK_HOOK                                                       0
 | 
			
		||||
#define configCHECK_FOR_STACK_OVERFLOW                                            0
 | 
			
		||||
#define configUSE_MALLOC_FAILED_HOOK                                              0
 | 
			
		||||
#define configUSE_IDLE_HOOK            1
 | 
			
		||||
#define configUSE_TICK_HOOK            0
 | 
			
		||||
#define configCHECK_FOR_STACK_OVERFLOW 0
 | 
			
		||||
#define configUSE_MALLOC_FAILED_HOOK   0
 | 
			
		||||
 | 
			
		||||
/* Run time and task stats gathering related definitions. */
 | 
			
		||||
#define configGENERATE_RUN_TIME_STATS                                             0
 | 
			
		||||
#define configUSE_TRACE_FACILITY                                                  1
 | 
			
		||||
#define configUSE_STATS_FORMATTING_FUNCTIONS                                      0
 | 
			
		||||
#define configGENERATE_RUN_TIME_STATS        0
 | 
			
		||||
#define configUSE_TRACE_FACILITY             1
 | 
			
		||||
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
 | 
			
		||||
 | 
			
		||||
/* Co-routine definitions. */
 | 
			
		||||
#define configUSE_CO_ROUTINES                                                     0
 | 
			
		||||
#define configMAX_CO_ROUTINE_PRIORITIES                                           ( 2 )
 | 
			
		||||
#define configUSE_CO_ROUTINES           0
 | 
			
		||||
#define configMAX_CO_ROUTINE_PRIORITIES (2)
 | 
			
		||||
 | 
			
		||||
/* Software timer definitions. */
 | 
			
		||||
#define configUSE_TIMERS 1
 | 
			
		||||
#define configTIMER_TASK_PRIORITY                                                 ( 0 )
 | 
			
		||||
#define configTIMER_QUEUE_LENGTH                                                  32
 | 
			
		||||
#define configTIMER_TASK_STACK_DEPTH                                              ( 300 )
 | 
			
		||||
#define configUSE_TIMERS             1
 | 
			
		||||
#define configTIMER_TASK_PRIORITY    (0)
 | 
			
		||||
#define configTIMER_QUEUE_LENGTH     32
 | 
			
		||||
#define configTIMER_TASK_STACK_DEPTH (300)
 | 
			
		||||
 | 
			
		||||
/* Tickless Idle configuration. */
 | 
			
		||||
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP                                     2
 | 
			
		||||
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
 | 
			
		||||
 | 
			
		||||
/* Tickless idle/low power functionality. */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Define to trap errors during development. */
 | 
			
		||||
#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)
 | 
			
		||||
#define configASSERT( x )                                                         ASSERT(x)
 | 
			
		||||
  #define configASSERT(x) ASSERT(x)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* 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. */
 | 
			
		||||
#define INCLUDE_vTaskPrioritySet                                                  1
 | 
			
		||||
#define INCLUDE_uxTaskPriorityGet                                                 1
 | 
			
		||||
#define INCLUDE_vTaskDelete                                                       1
 | 
			
		||||
#define INCLUDE_vTaskSuspend                                                      1
 | 
			
		||||
#define INCLUDE_xResumeFromISR                                                    1
 | 
			
		||||
#define INCLUDE_vTaskDelayUntil                                                   1
 | 
			
		||||
#define INCLUDE_vTaskDelay                                                        1
 | 
			
		||||
#define INCLUDE_xTaskGetSchedulerState                                            1
 | 
			
		||||
#define INCLUDE_xTaskGetCurrentTaskHandle                                         1
 | 
			
		||||
#define INCLUDE_uxTaskGetStackHighWaterMark                                       1
 | 
			
		||||
#define INCLUDE_xTaskGetIdleTaskHandle                                            1
 | 
			
		||||
#define INCLUDE_xTimerGetTimerDaemonTaskHandle                                    1
 | 
			
		||||
#define INCLUDE_pcTaskGetTaskName                                                 1
 | 
			
		||||
#define INCLUDE_eTaskGetState                                                     1
 | 
			
		||||
#define INCLUDE_xEventGroupSetBitFromISR                                          1
 | 
			
		||||
#define INCLUDE_xTimerPendFunctionCall                                            1
 | 
			
		||||
#define INCLUDE_vTaskPrioritySet               1
 | 
			
		||||
#define INCLUDE_uxTaskPriorityGet              1
 | 
			
		||||
#define INCLUDE_vTaskDelete                    1
 | 
			
		||||
#define INCLUDE_vTaskSuspend                   1
 | 
			
		||||
#define INCLUDE_xResumeFromISR                 1
 | 
			
		||||
#define INCLUDE_vTaskDelayUntil                1
 | 
			
		||||
#define INCLUDE_vTaskDelay                     1
 | 
			
		||||
#define INCLUDE_xTaskGetSchedulerState         1
 | 
			
		||||
#define INCLUDE_xTaskGetCurrentTaskHandle      1
 | 
			
		||||
#define INCLUDE_uxTaskGetStackHighWaterMark    1
 | 
			
		||||
#define INCLUDE_xTaskGetIdleTaskHandle         1
 | 
			
		||||
#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1
 | 
			
		||||
#define INCLUDE_pcTaskGetTaskName              1
 | 
			
		||||
#define INCLUDE_eTaskGetState                  1
 | 
			
		||||
#define INCLUDE_xEventGroupSetBitFromISR       1
 | 
			
		||||
#define INCLUDE_xTimerPendFunctionCall         1
 | 
			
		||||
 | 
			
		||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
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 !!!!
 | 
			
		||||
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
 | 
			
		||||
standard names - or at least those used in the unmodified vector table. */
 | 
			
		||||
 | 
			
		||||
#define vPortSVCHandler                                                           SVC_Handler
 | 
			
		||||
#define xPortPendSVHandler                                                        PendSV_Handler
 | 
			
		||||
 | 
			
		||||
#define vPortSVCHandler    SVC_Handler
 | 
			
		||||
#define xPortPendSVHandler PendSV_Handler
 | 
			
		||||
 | 
			
		||||
/*-----------------------------------------------------------
 | 
			
		||||
 * Settings that are generated automatically
 | 
			
		||||
 * basing on the settings above
 | 
			
		||||
 */
 | 
			
		||||
#if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
 | 
			
		||||
    // do not define configSYSTICK_CLOCK_HZ for SysTick to be configured automatically
 | 
			
		||||
    // to CPU clock source
 | 
			
		||||
    #define xPortSysTickHandler     SysTick_Handler
 | 
			
		||||
  // do not define configSYSTICK_CLOCK_HZ for SysTick to be configured automatically
 | 
			
		||||
  // to CPU clock source
 | 
			
		||||
  #define xPortSysTickHandler SysTick_Handler
 | 
			
		||||
#elif (configTICK_SOURCE == FREERTOS_USE_RTC)
 | 
			
		||||
    #define configSYSTICK_CLOCK_HZ  ( 32768UL )
 | 
			
		||||
    #define xPortSysTickHandler     RTC1_IRQHandler
 | 
			
		||||
  #define configSYSTICK_CLOCK_HZ (32768UL)
 | 
			
		||||
  #define xPortSysTickHandler    RTC1_IRQHandler
 | 
			
		||||
#else
 | 
			
		||||
    #error  Unsupported configTICK_SOURCE value
 | 
			
		||||
  #error Unsupported configTICK_SOURCE value
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Code below should be only used by the compiler, and not the assembler. */
 | 
			
		||||
#if !(defined(__ASSEMBLY__) || defined(__ASSEMBLER__))
 | 
			
		||||
    #include "nrf.h"
 | 
			
		||||
    #include "nrf_assert.h"
 | 
			
		||||
  #include "nrf.h"
 | 
			
		||||
  #include "nrf_assert.h"
 | 
			
		||||
 | 
			
		||||
    /* This part of definitions may be problematic in assembly - it uses definitions from files that are not assembly compatible. */
 | 
			
		||||
    /* Cortex-M specific definitions. */
 | 
			
		||||
    #ifdef __NVIC_PRIO_BITS
 | 
			
		||||
        /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
 | 
			
		||||
        #define configPRIO_BITS             __NVIC_PRIO_BITS
 | 
			
		||||
    #else
 | 
			
		||||
        #error "This port requires __NVIC_PRIO_BITS to be defined"
 | 
			
		||||
    #endif
 | 
			
		||||
  /* This part of definitions may be problematic in assembly - it uses definitions from files that are not assembly compatible. */
 | 
			
		||||
  /* Cortex-M specific definitions. */
 | 
			
		||||
  #ifdef __NVIC_PRIO_BITS
 | 
			
		||||
    /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
 | 
			
		||||
    #define configPRIO_BITS __NVIC_PRIO_BITS
 | 
			
		||||
  #else
 | 
			
		||||
    #error "This port requires __NVIC_PRIO_BITS to be defined"
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
    /* Access to current system core clock is required only if we are ticking the system by systimer */
 | 
			
		||||
    #if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
 | 
			
		||||
        #include <stdint.h>
 | 
			
		||||
        extern uint32_t SystemCoreClock;
 | 
			
		||||
    #endif
 | 
			
		||||
  /* Access to current system core clock is required only if we are ticking the system by systimer */
 | 
			
		||||
  #if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
 | 
			
		||||
    #include <stdint.h>
 | 
			
		||||
extern uint32_t SystemCoreClock;
 | 
			
		||||
  #endif
 | 
			
		||||
#endif /* !assembler */
 | 
			
		||||
 | 
			
		||||
/** 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:
 | 
			
		||||
     * 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
 | 
			
		||||
     *    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
 | 
			
		||||
     *    the RTOS application and stepping though the code. After stepping when the application is continued in debug mode, the auto-corrections of
 | 
			
		||||
     *    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
 | 
			
		||||
     *    convenient for debugging.
 | 
			
		||||
     */
 | 
			
		||||
#define configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG     0
 | 
			
		||||
 * 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
 | 
			
		||||
 *    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.
 | 
			
		||||
 * 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 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 convenient for debugging.
 | 
			
		||||
 */
 | 
			
		||||
#define configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG 0
 | 
			
		||||
 | 
			
		||||
#endif /* FREERTOS_CONFIG_H */
 | 
			
		||||
 | 
			
		||||
@ -7,33 +7,33 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
Battery *Battery::instance = nullptr;
 | 
			
		||||
Battery* Battery::instance = nullptr;
 | 
			
		||||
 | 
			
		||||
Battery::Battery() {
 | 
			
		||||
  instance = this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Battery::Init() {
 | 
			
		||||
  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(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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Battery::Update() {
 | 
			
		||||
 | 
			
		||||
  isCharging = !nrf_gpio_pin_read(chargingPin);
 | 
			
		||||
  isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
 | 
			
		||||
  
 | 
			
		||||
  if ( isReading ) return;
 | 
			
		||||
 | 
			
		||||
  if (isReading)
 | 
			
		||||
    return;
 | 
			
		||||
  // Non blocking read
 | 
			
		||||
  samples = 0;
 | 
			
		||||
  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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -41,48 +41,44 @@ void Battery::SaadcInit() {
 | 
			
		||||
  nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
 | 
			
		||||
  APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, adcCallbackStatic));
 | 
			
		||||
 | 
			
		||||
  nrf_saadc_channel_config_t adcChannelConfig = {
 | 
			
		||||
          .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
 | 
			
		||||
          .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
 | 
			
		||||
          .gain       = NRF_SAADC_GAIN1_5,
 | 
			
		||||
          .reference  = NRF_SAADC_REFERENCE_INTERNAL,
 | 
			
		||||
          .acq_time   = NRF_SAADC_ACQTIME_3US,
 | 
			
		||||
          .mode       = NRF_SAADC_MODE_SINGLE_ENDED,
 | 
			
		||||
          .burst      = NRF_SAADC_BURST_ENABLED,
 | 
			
		||||
          .pin_p      = batteryVoltageAdcInput,
 | 
			
		||||
          .pin_n      = NRF_SAADC_INPUT_DISABLED
 | 
			
		||||
  };
 | 
			
		||||
  nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
 | 
			
		||||
                                                 .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
 | 
			
		||||
                                                 .gain = NRF_SAADC_GAIN1_5,
 | 
			
		||||
                                                 .reference = NRF_SAADC_REFERENCE_INTERNAL,
 | 
			
		||||
                                                 .acq_time = NRF_SAADC_ACQTIME_3US,
 | 
			
		||||
                                                 .mode = NRF_SAADC_MODE_SINGLE_ENDED,
 | 
			
		||||
                                                 .burst = NRF_SAADC_BURST_ENABLED,
 | 
			
		||||
                                                 .pin_p = batteryVoltageAdcInput,
 | 
			
		||||
                                                 .pin_n = NRF_SAADC_INPUT_DISABLED};
 | 
			
		||||
  APP_ERROR_CHECK(nrfx_saadc_channel_init(0, &adcChannelConfig));
 | 
			
		||||
  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_min = 3.20; // minimum voltage of battery before shutdown ( depends on the battery )
 | 
			
		||||
  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 )
 | 
			
		||||
 | 
			
		||||
    if (p_event->type == NRFX_SAADC_EVT_DONE) {
 | 
			
		||||
      
 | 
			
		||||
      APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
 | 
			
		||||
  if (p_event->type == NRFX_SAADC_EVT_DONE) {
 | 
			
		||||
 | 
			
		||||
      voltage = (static_cast<float>(p_event->data.done.p_buffer[0]) * 2.04f) / (1024 / 3.0f);
 | 
			
		||||
      voltage = roundf(voltage * 100) / 100;
 | 
			
		||||
    APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
 | 
			
		||||
 | 
			
		||||
      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 = std::min(percentRemaining, 100);
 | 
			
		||||
    percentRemaining = static_cast<int>(((voltage - battery_min) / (battery_max - battery_min)) * 100);
 | 
			
		||||
 | 
			
		||||
      percentRemainingBuffer.insert(percentRemaining);
 | 
			
		||||
    percentRemaining = std::max(percentRemaining, 0);
 | 
			
		||||
    percentRemaining = std::min(percentRemaining, 100);
 | 
			
		||||
 | 
			
		||||
      samples++;
 | 
			
		||||
      if ( samples > percentRemainingSamples ) {
 | 
			
		||||
        nrfx_saadc_uninit();
 | 
			
		||||
        isReading = false;
 | 
			
		||||
      } else {
 | 
			
		||||
        nrfx_saadc_sample();
 | 
			
		||||
      }
 | 
			
		||||
    percentRemainingBuffer.insert(percentRemaining);
 | 
			
		||||
 | 
			
		||||
    samples++;
 | 
			
		||||
    if (samples > percentRemainingSamples) {
 | 
			
		||||
      nrfx_saadc_uninit();
 | 
			
		||||
      isReading = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      nrfx_saadc_sample();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,18 +7,18 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
 | 
			
		||||
    /** A simple circular buffer that can be used to average 
 | 
			
		||||
     out the sensor values. The total capacity of the CircBuffer  
 | 
			
		||||
    /** A simple circular buffer that can be used to average
 | 
			
		||||
     out the sensor values. The total capacity of the CircBuffer
 | 
			
		||||
    is given as the template parameter N.
 | 
			
		||||
    */ 
 | 
			
		||||
    template <int N> 
 | 
			
		||||
    class CircBuffer {
 | 
			
		||||
    */
 | 
			
		||||
    template <int N> class CircBuffer {
 | 
			
		||||
    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.
 | 
			
		||||
    */ 
 | 
			
		||||
    */
 | 
			
		||||
      void insert(const int num) {
 | 
			
		||||
        head %= cap;
 | 
			
		||||
        arr[head++] = num;
 | 
			
		||||
@ -34,49 +34,56 @@ namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      std::array<int, N> arr; /**< internal array used to store the values*/
 | 
			
		||||
      uint8_t sz; /**< The current size of the array.*/
 | 
			
		||||
      uint8_t cap; /**< Total capacity of the CircBuffer.*/
 | 
			
		||||
      uint8_t head; /**< The current head of the CircBuffer*/
 | 
			
		||||
      uint8_t sz;             /**< The current size of the array.*/
 | 
			
		||||
      uint8_t cap;            /**< Total capacity of the CircBuffer.*/
 | 
			
		||||
      uint8_t head;           /**< The current head of the CircBuffer*/
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class Battery {
 | 
			
		||||
      public:
 | 
			
		||||
    public:
 | 
			
		||||
      Battery();
 | 
			
		||||
 | 
			
		||||
        Battery();
 | 
			
		||||
      void Init();
 | 
			
		||||
      void Update();
 | 
			
		||||
 | 
			
		||||
        void Init();
 | 
			
		||||
        void Update();
 | 
			
		||||
        
 | 
			
		||||
        int PercentRemaining() const { 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 IsPowerPresent() const { return isPowerPresent; }        
 | 
			
		||||
      bool IsCharging() const {
 | 
			
		||||
        return isCharging;
 | 
			
		||||
      }
 | 
			
		||||
      bool IsPowerPresent() const {
 | 
			
		||||
        return isPowerPresent;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        static Battery *instance;
 | 
			
		||||
        nrf_saadc_value_t  saadc_value;
 | 
			
		||||
        
 | 
			
		||||
        static constexpr uint8_t percentRemainingSamples = 5;
 | 
			
		||||
        CircBuffer<percentRemainingSamples> percentRemainingBuffer {};
 | 
			
		||||
    private:
 | 
			
		||||
      static Battery* instance;
 | 
			
		||||
      nrf_saadc_value_t saadc_value;
 | 
			
		||||
 | 
			
		||||
        static constexpr uint32_t chargingPin = 12;
 | 
			
		||||
        static constexpr uint32_t powerPresentPin = 19;
 | 
			
		||||
        static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
 | 
			
		||||
        float voltage = 0.0f;
 | 
			
		||||
        int percentRemaining = -1;
 | 
			
		||||
      static constexpr uint8_t percentRemainingSamples = 5;
 | 
			
		||||
      CircBuffer<percentRemainingSamples> percentRemainingBuffer {};
 | 
			
		||||
 | 
			
		||||
        bool isCharging = false;
 | 
			
		||||
        bool isPowerPresent = false;
 | 
			
		||||
        
 | 
			
		||||
        void SaadcInit();
 | 
			
		||||
      static constexpr uint32_t chargingPin = 12;
 | 
			
		||||
      static constexpr uint32_t powerPresentPin = 19;
 | 
			
		||||
      static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
 | 
			
		||||
      float voltage = 0.0f;
 | 
			
		||||
      int percentRemaining = -1;
 | 
			
		||||
 | 
			
		||||
        void SaadcEventHandler(nrfx_saadc_evt_t const * p_event);
 | 
			
		||||
        static void adcCallbackStatic(nrfx_saadc_evt_t const *event);
 | 
			
		||||
      bool isCharging = false;
 | 
			
		||||
      bool isPowerPresent = false;
 | 
			
		||||
 | 
			
		||||
        bool isReading = false;
 | 
			
		||||
        uint8_t samples = 0;
 | 
			
		||||
      void SaadcInit();
 | 
			
		||||
 | 
			
		||||
      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;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -12,50 +12,42 @@ constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid;
 | 
			
		||||
constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  int
 | 
			
		||||
  OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service,
 | 
			
		||||
                           void *arg) {
 | 
			
		||||
    auto client = static_cast<AlertNotificationClient *>(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<AlertNotificationClient*>(arg);
 | 
			
		||||
    return client->OnDiscoveryEvent(conn_handle, error, service);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
 | 
			
		||||
                                                          const struct ble_gatt_chr *chr, void *arg) {
 | 
			
		||||
    auto client = static_cast<AlertNotificationClient *>(arg);
 | 
			
		||||
  int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle,
 | 
			
		||||
                                                          const struct ble_gatt_error* error,
 | 
			
		||||
                                                          const struct ble_gatt_chr* chr,
 | 
			
		||||
                                                          void* arg) {
 | 
			
		||||
    auto client = static_cast<AlertNotificationClient*>(arg);
 | 
			
		||||
    return client->OnCharacteristicsDiscoveryEvent(conn_handle, error, chr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int OnAlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle,
 | 
			
		||||
                                                          const struct ble_gatt_error *error,
 | 
			
		||||
                                                          uint16_t chr_val_handle,
 | 
			
		||||
                                                          const struct ble_gatt_dsc *dsc,
 | 
			
		||||
                                                          void *arg) {
 | 
			
		||||
    auto client = static_cast<AlertNotificationClient *>(arg);
 | 
			
		||||
  int OnAlertNotificationDescriptorDiscoveryEventCallback(
 | 
			
		||||
    uint16_t conn_handle, const struct ble_gatt_error* error, uint16_t chr_val_handle, const struct ble_gatt_dsc* dsc, void* arg) {
 | 
			
		||||
    auto client = static_cast<AlertNotificationClient*>(arg);
 | 
			
		||||
    return client->OnDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int NewAlertSubcribeCallback(uint16_t conn_handle,
 | 
			
		||||
                               const struct ble_gatt_error *error,
 | 
			
		||||
                               struct ble_gatt_attr *attr,
 | 
			
		||||
                               void *arg) {
 | 
			
		||||
    auto client = static_cast<AlertNotificationClient *>(arg);
 | 
			
		||||
  int NewAlertSubcribeCallback(uint16_t conn_handle, const struct ble_gatt_error* error, struct ble_gatt_attr* attr, void* arg) {
 | 
			
		||||
    auto client = static_cast<AlertNotificationClient*>(arg);
 | 
			
		||||
    return client->OnNewAlertSubcribe(conn_handle, error, attr);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
 | 
			
		||||
                                                 Pinetime::Controllers::NotificationManager ¬ificationManager) :
 | 
			
		||||
        systemTask{systemTask}, notificationManager{notificationManager} {
 | 
			
		||||
AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
                                                 Pinetime::Controllers::NotificationManager& notificationManager)
 | 
			
		||||
  : systemTask {systemTask}, notificationManager {notificationManager} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                               const ble_gatt_svc *service) {
 | 
			
		||||
bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
 | 
			
		||||
  if (service == nullptr && error->status == BLE_HS_EDONE) {
 | 
			
		||||
    if (isDiscovered) {
 | 
			
		||||
      NRF_LOG_INFO("ANS Discovery found, starting characteristics discovery");
 | 
			
		||||
 | 
			
		||||
      ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle,
 | 
			
		||||
                              OnAlertNotificationCharacteristicDiscoveredCallback, this);
 | 
			
		||||
      ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle, OnAlertNotificationCharacteristicDiscoveredCallback, this);
 | 
			
		||||
    } else {
 | 
			
		||||
      NRF_LOG_INFO("ANS not found");
 | 
			
		||||
      onServiceDiscovered(connectionHandle);
 | 
			
		||||
@ -63,7 +55,7 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ansServiceUuid), &service->uuid.u) == 0) {
 | 
			
		||||
  if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ansServiceUuid), &service->uuid.u) == 0) {
 | 
			
		||||
    NRF_LOG_INFO("ANS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
 | 
			
		||||
    ansStartHandle = service->start_handle;
 | 
			
		||||
    ansEndHandle = service->end_handle;
 | 
			
		||||
@ -72,8 +64,9 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                                             const ble_gatt_chr *characteristic) {
 | 
			
		||||
int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle,
 | 
			
		||||
                                                             const ble_gatt_error* error,
 | 
			
		||||
                                                             const ble_gatt_chr* characteristic) {
 | 
			
		||||
  if (error->status != 0 && error->status != BLE_HS_EDONE) {
 | 
			
		||||
    NRF_LOG_INFO("ANS Characteristic discovery ERROR");
 | 
			
		||||
    onServiceDiscovered(connectionHandle);
 | 
			
		||||
@ -83,41 +76,34 @@ int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connection
 | 
			
		||||
  if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
 | 
			
		||||
    NRF_LOG_INFO("ANS Characteristic discovery complete");
 | 
			
		||||
    if (isCharacteristicDiscovered) {
 | 
			
		||||
      ble_gattc_disc_all_dscs(connectionHandle,
 | 
			
		||||
                              newAlertHandle, ansEndHandle,
 | 
			
		||||
                              OnAlertNotificationDescriptorDiscoveryEventCallback, this);
 | 
			
		||||
      ble_gattc_disc_all_dscs(connectionHandle, newAlertHandle, ansEndHandle, OnAlertNotificationDescriptorDiscoveryEventCallback, this);
 | 
			
		||||
    } else
 | 
			
		||||
      onServiceDiscovered(connectionHandle);
 | 
			
		||||
  } else {
 | 
			
		||||
    if (characteristic != nullptr &&
 | 
			
		||||
        ble_uuid_cmp(((ble_uuid_t *) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
    if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
      NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid");
 | 
			
		||||
      supportedNewAlertCategoryHandle = characteristic->val_handle;
 | 
			
		||||
    } else if (characteristic != nullptr &&
 | 
			
		||||
               ble_uuid_cmp(((ble_uuid_t *) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
    } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
      NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid");
 | 
			
		||||
      supportedUnreadAlertCategoryHandle = characteristic->val_handle;
 | 
			
		||||
    } else if (characteristic != nullptr &&
 | 
			
		||||
               ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
    } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
      NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid");
 | 
			
		||||
      newAlertHandle = characteristic->val_handle;
 | 
			
		||||
      newAlertDefHandle = characteristic->def_handle;
 | 
			
		||||
      isCharacteristicDiscovered = true;
 | 
			
		||||
    } else if (characteristic != nullptr &&
 | 
			
		||||
               ble_uuid_cmp(((ble_uuid_t *) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
    } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
      NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid");
 | 
			
		||||
      unreadAlertStatusHandle = characteristic->val_handle;
 | 
			
		||||
    } else if (characteristic != nullptr &&
 | 
			
		||||
               ble_uuid_cmp(((ble_uuid_t *) &controlPointUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
    } else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &controlPointUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
      NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid");
 | 
			
		||||
      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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                                ble_gatt_attr *attribute) {
 | 
			
		||||
int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute) {
 | 
			
		||||
  if (error->status == 0) {
 | 
			
		||||
    NRF_LOG_INFO("ANS New alert subscribe OK");
 | 
			
		||||
  } else {
 | 
			
		||||
@ -128,12 +114,12 @@ int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const
 | 
			
		||||
  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,
 | 
			
		||||
                                                                const ble_gatt_dsc *descriptor) {
 | 
			
		||||
                                                                const ble_gatt_dsc* descriptor) {
 | 
			
		||||
  if (error->status == 0) {
 | 
			
		||||
    if (characteristicValueHandle == newAlertHandle &&
 | 
			
		||||
        ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &descriptor->uuid.u)) {
 | 
			
		||||
    if (characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &descriptor->uuid.u)) {
 | 
			
		||||
      if (newAlertDescriptorHandle == 0) {
 | 
			
		||||
        NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle);
 | 
			
		||||
        newAlertDescriptorHandle = descriptor->handle;
 | 
			
		||||
@ -151,16 +137,17 @@ int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connect
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlertNotificationClient::OnNotification(ble_gap_event *event) {
 | 
			
		||||
void AlertNotificationClient::OnNotification(ble_gap_event* event) {
 | 
			
		||||
  if (event->notify_rx.attr_handle == newAlertHandle) {
 | 
			
		||||
    constexpr size_t stringTerminatorSize = 1; // end of string '\0'
 | 
			
		||||
    constexpr size_t headerSize = 3;
 | 
			
		||||
    const auto maxMessageSize{NotificationManager::MaximumMessageSize()};
 | 
			
		||||
    const auto maxBufferSize{maxMessageSize + headerSize};
 | 
			
		||||
    const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
 | 
			
		||||
    const auto maxBufferSize {maxMessageSize + headerSize};
 | 
			
		||||
 | 
			
		||||
    // Ignore notifications with empty message
 | 
			
		||||
    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);
 | 
			
		||||
    auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));
 | 
			
		||||
 | 
			
		||||
@ -19,68 +19,52 @@ namespace Pinetime {
 | 
			
		||||
    class NotificationManager;
 | 
			
		||||
 | 
			
		||||
    class AlertNotificationClient : public BleClient {
 | 
			
		||||
      public:
 | 
			
		||||
        explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
 | 
			
		||||
                                         Pinetime::Controllers::NotificationManager ¬ificationManager);
 | 
			
		||||
    public:
 | 
			
		||||
      explicit AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
                                       Pinetime::Controllers::NotificationManager& notificationManager);
 | 
			
		||||
 | 
			
		||||
        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,
 | 
			
		||||
                                            const ble_gatt_chr *characteristic);
 | 
			
		||||
        int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
 | 
			
		||||
        int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                               uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
 | 
			
		||||
        void OnNotification(ble_gap_event *event);
 | 
			
		||||
        void Reset();
 | 
			
		||||
        void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
 | 
			
		||||
      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, const ble_gatt_chr* characteristic);
 | 
			
		||||
      int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
 | 
			
		||||
      int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
 | 
			
		||||
                                             const ble_gatt_error* error,
 | 
			
		||||
                                             uint16_t characteristicValueHandle,
 | 
			
		||||
                                             const ble_gatt_dsc* descriptor);
 | 
			
		||||
      void OnNotification(ble_gap_event* event);
 | 
			
		||||
      void Reset();
 | 
			
		||||
      void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        static constexpr uint16_t ansServiceId{0x1811};
 | 
			
		||||
        static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
 | 
			
		||||
        static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48;
 | 
			
		||||
        static constexpr uint16_t newAlertId = 0x2a46;
 | 
			
		||||
        static constexpr uint16_t unreadAlertStatusId = 0x2a45;
 | 
			
		||||
        static constexpr uint16_t controlPointId = 0x2a44;
 | 
			
		||||
    private:
 | 
			
		||||
      static constexpr uint16_t ansServiceId {0x1811};
 | 
			
		||||
      static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
 | 
			
		||||
      static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48;
 | 
			
		||||
      static constexpr uint16_t newAlertId = 0x2a46;
 | 
			
		||||
      static constexpr uint16_t unreadAlertStatusId = 0x2a45;
 | 
			
		||||
      static constexpr uint16_t controlPointId = 0x2a44;
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t ansServiceUuid{
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = ansServiceId
 | 
			
		||||
        };
 | 
			
		||||
        static constexpr ble_uuid16_t supportedNewAlertCategoryUuid{
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = supportedNewAlertCategoryId
 | 
			
		||||
        };
 | 
			
		||||
        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
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t ansServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansServiceId};
 | 
			
		||||
      static constexpr ble_uuid16_t supportedNewAlertCategoryUuid {.u {.type = BLE_UUID_TYPE_16}, .value = supportedNewAlertCategoryId};
 | 
			
		||||
      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 ansEndHandle = 0;
 | 
			
		||||
        uint16_t supportedNewAlertCategoryHandle = 0;
 | 
			
		||||
        uint16_t supportedUnreadAlertCategoryHandle = 0;
 | 
			
		||||
        uint16_t newAlertHandle = 0;
 | 
			
		||||
        uint16_t newAlertDescriptorHandle = 0;
 | 
			
		||||
        uint16_t newAlertDefHandle = 0;
 | 
			
		||||
        uint16_t unreadAlertStatusHandle = 0;
 | 
			
		||||
        uint16_t controlPointHandle = 0;
 | 
			
		||||
        bool isDiscovered = false;
 | 
			
		||||
        Pinetime::System::SystemTask &systemTask;
 | 
			
		||||
        Pinetime::Controllers::NotificationManager ¬ificationManager;
 | 
			
		||||
        std::function<void(uint16_t)> onServiceDiscovered;
 | 
			
		||||
        bool isCharacteristicDiscovered = false;
 | 
			
		||||
        bool isDescriptorFound = false;
 | 
			
		||||
      uint16_t ansStartHandle = 0;
 | 
			
		||||
      uint16_t ansEndHandle = 0;
 | 
			
		||||
      uint16_t supportedNewAlertCategoryHandle = 0;
 | 
			
		||||
      uint16_t supportedUnreadAlertCategoryHandle = 0;
 | 
			
		||||
      uint16_t newAlertHandle = 0;
 | 
			
		||||
      uint16_t newAlertDescriptorHandle = 0;
 | 
			
		||||
      uint16_t newAlertDefHandle = 0;
 | 
			
		||||
      uint16_t unreadAlertStatusHandle = 0;
 | 
			
		||||
      uint16_t controlPointHandle = 0;
 | 
			
		||||
      bool isDiscovered = false;
 | 
			
		||||
      Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
      Pinetime::Controllers::NotificationManager& notificationManager;
 | 
			
		||||
      std::function<void(uint16_t)> onServiceDiscovered;
 | 
			
		||||
      bool isCharacteristicDiscovered = false;
 | 
			
		||||
      bool isDescriptorFound = false;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,7 @@ constexpr ble_uuid16_t AlertNotificationService::ansUuid;
 | 
			
		||||
constexpr ble_uuid16_t AlertNotificationService::ansCharUuid;
 | 
			
		||||
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);
 | 
			
		||||
  return anService->OnAlert(conn_handle, attr_handle, ctxt);
 | 
			
		||||
}
 | 
			
		||||
@ -26,62 +25,52 @@ void AlertNotificationService::Init() {
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AlertNotificationService::AlertNotificationService ( System::SystemTask& systemTask, NotificationManager& notificationManager )
 | 
			
		||||
  : characteristicDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &ansCharUuid,
 | 
			
		||||
                        .access_cb = AlertNotificationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_WRITE
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) ¬ificationEventUuid,
 | 
			
		||||
                        .access_cb = AlertNotificationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                        .val_handle = &eventHandle
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  0
 | 
			
		||||
                }
 | 
			
		||||
        },
 | 
			
		||||
    serviceDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        /* Device Information Service */
 | 
			
		||||
                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &ansUuid,
 | 
			
		||||
                        .characteristics = characteristicDefinition
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                },
 | 
			
		||||
        }, systemTask{systemTask}, notificationManager{notificationManager} {
 | 
			
		||||
AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager)
 | 
			
		||||
  : characteristicDefinition {{.uuid = (ble_uuid_t*) &ansCharUuid,
 | 
			
		||||
                               .access_cb = AlertNotificationCallback,
 | 
			
		||||
                               .arg = this,
 | 
			
		||||
                               .flags = BLE_GATT_CHR_F_WRITE},
 | 
			
		||||
                              {.uuid = (ble_uuid_t*) ¬ificationEventUuid,
 | 
			
		||||
                               .access_cb = AlertNotificationCallback,
 | 
			
		||||
                               .arg = this,
 | 
			
		||||
                               .flags = BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                               .val_handle = &eventHandle},
 | 
			
		||||
                              {0}},
 | 
			
		||||
    serviceDefinition {
 | 
			
		||||
      {/* 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,
 | 
			
		||||
                                                    struct ble_gatt_access_ctxt *ctxt) {
 | 
			
		||||
int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
 | 
			
		||||
  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
 | 
			
		||||
    constexpr size_t stringTerminatorSize = 1; // end of string '\0'
 | 
			
		||||
    constexpr size_t headerSize = 3;
 | 
			
		||||
    const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
 | 
			
		||||
    const auto maxBufferSize{maxMessageSize + headerSize};
 | 
			
		||||
    const auto maxBufferSize {maxMessageSize + headerSize};
 | 
			
		||||
 | 
			
		||||
    // Ignore notifications with empty message
 | 
			
		||||
    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);
 | 
			
		||||
    auto messageSize = std::min(maxMessageSize, (bufferSize-headerSize));
 | 
			
		||||
    auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));
 | 
			
		||||
    Categories category;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    notif.message[messageSize-1] = '\0';
 | 
			
		||||
    notif.message[messageSize - 1] = '\0';
 | 
			
		||||
    notif.size = messageSize;
 | 
			
		||||
 | 
			
		||||
    // TODO convert all ANS categories to NotificationController categories
 | 
			
		||||
    switch(category) {
 | 
			
		||||
    switch (category) {
 | 
			
		||||
      case Categories::Call:
 | 
			
		||||
        notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall;
 | 
			
		||||
        break;
 | 
			
		||||
@ -99,7 +88,7 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle
 | 
			
		||||
 | 
			
		||||
void AlertNotificationService::AcceptIncomingCall() {
 | 
			
		||||
  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();
 | 
			
		||||
 | 
			
		||||
@ -112,7 +101,7 @@ void AlertNotificationService::AcceptIncomingCall() {
 | 
			
		||||
 | 
			
		||||
void AlertNotificationService::RejectIncomingCall() {
 | 
			
		||||
  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();
 | 
			
		||||
 | 
			
		||||
@ -125,7 +114,7 @@ void AlertNotificationService::RejectIncomingCall() {
 | 
			
		||||
 | 
			
		||||
void AlertNotificationService::MuteIncomingCall() {
 | 
			
		||||
  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();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,9 @@
 | 
			
		||||
#undef max
 | 
			
		||||
#undef min
 | 
			
		||||
 | 
			
		||||
//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}
 | 
			
		||||
// 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 }
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
@ -19,64 +20,49 @@ namespace Pinetime {
 | 
			
		||||
    class NotificationManager;
 | 
			
		||||
 | 
			
		||||
    class AlertNotificationService {
 | 
			
		||||
      public:
 | 
			
		||||
        AlertNotificationService(Pinetime::System::SystemTask &systemTask,
 | 
			
		||||
                                         Pinetime::Controllers::NotificationManager ¬ificationManager);
 | 
			
		||||
        void Init();
 | 
			
		||||
    public:
 | 
			
		||||
      AlertNotificationService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager);
 | 
			
		||||
      void Init();
 | 
			
		||||
 | 
			
		||||
        int OnAlert(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                                    struct ble_gatt_access_ctxt *ctxt);
 | 
			
		||||
      int OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
 | 
			
		||||
 | 
			
		||||
        void AcceptIncomingCall();
 | 
			
		||||
        void RejectIncomingCall();
 | 
			
		||||
        void MuteIncomingCall();
 | 
			
		||||
      void AcceptIncomingCall();
 | 
			
		||||
      void RejectIncomingCall();
 | 
			
		||||
      void MuteIncomingCall();
 | 
			
		||||
 | 
			
		||||
        enum class IncomingCallResponses : uint8_t {
 | 
			
		||||
          Reject = 0x00,
 | 
			
		||||
          Answer = 0x01,
 | 
			
		||||
          Mute = 0x02
 | 
			
		||||
        };
 | 
			
		||||
      enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 };
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        enum class Categories : uint8_t {
 | 
			
		||||
          SimpleAlert = 0x00,
 | 
			
		||||
          Email = 0x01,
 | 
			
		||||
          News = 0x02,
 | 
			
		||||
          Call = 0x03,
 | 
			
		||||
          MissedCall = 0x04,
 | 
			
		||||
          MmsSms = 0x05,
 | 
			
		||||
          VoiceMail = 0x06,
 | 
			
		||||
          Schedule = 0x07,
 | 
			
		||||
          HighPrioritizedAlert = 0x08,
 | 
			
		||||
          InstantMessage = 0x09,
 | 
			
		||||
          All = 0xff
 | 
			
		||||
        };
 | 
			
		||||
    private:
 | 
			
		||||
      enum class Categories : uint8_t {
 | 
			
		||||
        SimpleAlert = 0x00,
 | 
			
		||||
        Email = 0x01,
 | 
			
		||||
        News = 0x02,
 | 
			
		||||
        Call = 0x03,
 | 
			
		||||
        MissedCall = 0x04,
 | 
			
		||||
        MmsSms = 0x05,
 | 
			
		||||
        VoiceMail = 0x06,
 | 
			
		||||
        Schedule = 0x07,
 | 
			
		||||
        HighPrioritizedAlert = 0x08,
 | 
			
		||||
        InstantMessage = 0x09,
 | 
			
		||||
        All = 0xff
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        static constexpr uint16_t ansId {0x1811};
 | 
			
		||||
        static constexpr uint16_t ansCharId {0x2a46};
 | 
			
		||||
      static constexpr uint16_t ansId {0x1811};
 | 
			
		||||
      static constexpr uint16_t ansCharId {0x2a46};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t ansUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = ansId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t ansUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t ansCharUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = ansCharId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t ansCharUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansCharId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid128_t notificationEventUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_128 },
 | 
			
		||||
                .value = NOTIFICATION_EVENT_SERVICE_UUID_BASE
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid128_t notificationEventUuid {.u {.type = BLE_UUID_TYPE_128}, .value = NOTIFICATION_EVENT_SERVICE_UUID_BASE};
 | 
			
		||||
 | 
			
		||||
        struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
        struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
      struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
      struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
 | 
			
		||||
        Pinetime::System::SystemTask &systemTask;
 | 
			
		||||
        NotificationManager ¬ificationManager;
 | 
			
		||||
      Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
      NotificationManager& notificationManager;
 | 
			
		||||
 | 
			
		||||
        uint16_t eventHandle;
 | 
			
		||||
      uint16_t eventHandle;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,39 +7,26 @@ using namespace Pinetime::Controllers;
 | 
			
		||||
constexpr ble_uuid16_t BatteryInformationService::batteryInformationServiceUuid;
 | 
			
		||||
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);
 | 
			
		||||
  return batteryInformationService->OnBatteryServiceRequested(conn_handle, attr_handle, ctxt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController) :
 | 
			
		||||
        batteryController{batteryController},
 | 
			
		||||
        characteristicDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &batteryLevelUuid,
 | 
			
		||||
                        .access_cb = BatteryInformationServiceCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                        .val_handle = &batteryLevelHandle
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                }
 | 
			
		||||
        },
 | 
			
		||||
        serviceDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        /* Device Information Service */
 | 
			
		||||
                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &batteryInformationServiceUuid,
 | 
			
		||||
                        .characteristics = characteristicDefinition
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                },
 | 
			
		||||
        }{
 | 
			
		||||
 | 
			
		||||
BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController)
 | 
			
		||||
  : batteryController {batteryController},
 | 
			
		||||
    characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid,
 | 
			
		||||
                               .access_cb = BatteryInformationServiceCallback,
 | 
			
		||||
                               .arg = this,
 | 
			
		||||
                               .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                               .val_handle = &batteryLevelHandle},
 | 
			
		||||
                              {0}},
 | 
			
		||||
    serviceDefinition {
 | 
			
		||||
      {/* Device Information Service */
 | 
			
		||||
       .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
       .uuid = (ble_uuid_t*) &batteryInformationServiceUuid,
 | 
			
		||||
       .characteristics = characteristicDefinition},
 | 
			
		||||
      {0},
 | 
			
		||||
    } {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BatteryInformationService::Init() {
 | 
			
		||||
@ -51,9 +38,10 @@ void BatteryInformationService::Init() {
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle,
 | 
			
		||||
                                                         ble_gatt_access_ctxt *context) {
 | 
			
		||||
  if(attributeHandle == batteryLevelHandle) {
 | 
			
		||||
int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle,
 | 
			
		||||
                                                         uint16_t attributeHandle,
 | 
			
		||||
                                                         ble_gatt_access_ctxt* context) {
 | 
			
		||||
  if (attributeHandle == batteryLevelHandle) {
 | 
			
		||||
    NRF_LOG_INFO("BATTERY : handle = %d", batteryLevelHandle);
 | 
			
		||||
    static uint8_t batteryValue = batteryController.PercentRemaining();
 | 
			
		||||
    int res = os_mbuf_append(context->om, &batteryValue, 1);
 | 
			
		||||
 | 
			
		||||
@ -12,33 +12,25 @@ namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class Battery;
 | 
			
		||||
    class BatteryInformationService {
 | 
			
		||||
      public:
 | 
			
		||||
        BatteryInformationService(Controllers::Battery& batteryController);
 | 
			
		||||
        void Init();
 | 
			
		||||
    public:
 | 
			
		||||
      BatteryInformationService(Controllers::Battery& batteryController);
 | 
			
		||||
      void Init();
 | 
			
		||||
 | 
			
		||||
        int
 | 
			
		||||
        OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
 | 
			
		||||
      int OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        Controllers::Battery& batteryController;
 | 
			
		||||
        static constexpr uint16_t batteryInformationServiceId {0x180F};
 | 
			
		||||
        static constexpr uint16_t batteryLevelId {0x2A19};
 | 
			
		||||
    private:
 | 
			
		||||
      Controllers::Battery& batteryController;
 | 
			
		||||
      static constexpr uint16_t batteryInformationServiceId {0x180F};
 | 
			
		||||
      static constexpr uint16_t batteryLevelId {0x2A19};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t batteryInformationServiceUuid {
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = batteryInformationServiceId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t batteryInformationServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = batteryInformationServiceId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t batteryLevelUuid {
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = batteryLevelId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t batteryLevelUuid {.u {.type = BLE_UUID_TYPE_16}, .value = batteryLevelId};
 | 
			
		||||
 | 
			
		||||
        struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
        struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
 | 
			
		||||
        uint16_t batteryLevelHandle;
 | 
			
		||||
      struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
      struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
 | 
			
		||||
      uint16_t batteryLevelHandle;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,10 @@
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers{
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class BleClient {
 | 
			
		||||
      public:
 | 
			
		||||
        virtual void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) = 0;
 | 
			
		||||
    public:
 | 
			
		||||
      virtual void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) = 0;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -25,5 +25,3 @@ void Ble::FirmwareUpdateTotalBytes(uint32_t totalBytes) {
 | 
			
		||||
void Ble::FirmwareUpdateCurrentBytes(uint32_t currentBytes) {
 | 
			
		||||
  firmwareUpdateCurrentBytes = currentBytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,39 +6,57 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class Ble {
 | 
			
		||||
      public:
 | 
			
		||||
        using BleAddress = std::array<uint8_t, 6>;
 | 
			
		||||
        enum class FirmwareUpdateStates {Idle, Running, Validated, Error};
 | 
			
		||||
        enum class AddressTypes { Public, Random };
 | 
			
		||||
    public:
 | 
			
		||||
      using BleAddress = std::array<uint8_t, 6>;
 | 
			
		||||
      enum class FirmwareUpdateStates { Idle, Running, Validated, Error };
 | 
			
		||||
      enum class AddressTypes { Public, Random };
 | 
			
		||||
 | 
			
		||||
        Ble() = default;
 | 
			
		||||
        bool IsConnected() const {return isConnected;}
 | 
			
		||||
        void Connect();
 | 
			
		||||
        void Disconnect();
 | 
			
		||||
      Ble() = default;
 | 
			
		||||
      bool IsConnected() const {
 | 
			
		||||
        return isConnected;
 | 
			
		||||
      }
 | 
			
		||||
      void Connect();
 | 
			
		||||
      void Disconnect();
 | 
			
		||||
 | 
			
		||||
        void StartFirmwareUpdate();
 | 
			
		||||
        void StopFirmwareUpdate();
 | 
			
		||||
        void FirmwareUpdateTotalBytes(uint32_t totalBytes);
 | 
			
		||||
        void FirmwareUpdateCurrentBytes(uint32_t currentBytes);
 | 
			
		||||
        void State(FirmwareUpdateStates state) { firmwareUpdateState = state; }
 | 
			
		||||
      void StartFirmwareUpdate();
 | 
			
		||||
      void StopFirmwareUpdate();
 | 
			
		||||
      void FirmwareUpdateTotalBytes(uint32_t totalBytes);
 | 
			
		||||
      void FirmwareUpdateCurrentBytes(uint32_t currentBytes);
 | 
			
		||||
      void State(FirmwareUpdateStates state) {
 | 
			
		||||
        firmwareUpdateState = state;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        bool IsFirmwareUpdating() const { return isFirmwareUpdating; }
 | 
			
		||||
        uint32_t FirmwareUpdateTotalBytes() const { return firmwareUpdateTotalBytes; }
 | 
			
		||||
        uint32_t FirmwareUpdateCurrentBytes() const { return firmwareUpdateCurrentBytes; }
 | 
			
		||||
        FirmwareUpdateStates State() const { return firmwareUpdateState; }
 | 
			
		||||
      bool IsFirmwareUpdating() const {
 | 
			
		||||
        return isFirmwareUpdating;
 | 
			
		||||
      }
 | 
			
		||||
      uint32_t FirmwareUpdateTotalBytes() const {
 | 
			
		||||
        return firmwareUpdateTotalBytes;
 | 
			
		||||
      }
 | 
			
		||||
      uint32_t FirmwareUpdateCurrentBytes() const {
 | 
			
		||||
        return firmwareUpdateCurrentBytes;
 | 
			
		||||
      }
 | 
			
		||||
      FirmwareUpdateStates State() const {
 | 
			
		||||
        return firmwareUpdateState;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        void Address(BleAddress&& addr) { address = addr; }
 | 
			
		||||
        const BleAddress& Address() const { return address; }
 | 
			
		||||
        void AddressType(AddressTypes t) { addressType = t;}
 | 
			
		||||
      private:
 | 
			
		||||
        bool isConnected = false;
 | 
			
		||||
        bool isFirmwareUpdating = false;
 | 
			
		||||
        uint32_t firmwareUpdateTotalBytes = 0;
 | 
			
		||||
        uint32_t firmwareUpdateCurrentBytes = 0;
 | 
			
		||||
        FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle;
 | 
			
		||||
        BleAddress address;
 | 
			
		||||
        AddressTypes addressType;
 | 
			
		||||
      void Address(BleAddress&& addr) {
 | 
			
		||||
        address = addr;
 | 
			
		||||
      }
 | 
			
		||||
      const BleAddress& Address() const {
 | 
			
		||||
        return address;
 | 
			
		||||
      }
 | 
			
		||||
      void AddressType(AddressTypes t) {
 | 
			
		||||
        addressType = t;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      bool isConnected = false;
 | 
			
		||||
      bool isFirmwareUpdating = false;
 | 
			
		||||
      uint32_t firmwareUpdateTotalBytes = 0;
 | 
			
		||||
      uint32_t firmwareUpdateCurrentBytes = 0;
 | 
			
		||||
      FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle;
 | 
			
		||||
      BleAddress address;
 | 
			
		||||
      AddressTypes addressType;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -9,39 +9,37 @@ constexpr ble_uuid16_t CurrentTimeClient::ctsServiceUuid;
 | 
			
		||||
constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  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);
 | 
			
		||||
  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);
 | 
			
		||||
    return client->OnDiscoveryEvent(conn_handle, error, service);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int OnCurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
 | 
			
		||||
                                                    const struct ble_gatt_chr *chr, void *arg) {
 | 
			
		||||
    auto client = static_cast<CurrentTimeClient *>(arg);
 | 
			
		||||
  int OnCurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle,
 | 
			
		||||
                                                    const struct ble_gatt_error* error,
 | 
			
		||||
                                                    const struct ble_gatt_chr* chr,
 | 
			
		||||
                                                    void* arg) {
 | 
			
		||||
    auto client = static_cast<CurrentTimeClient*>(arg);
 | 
			
		||||
    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) {
 | 
			
		||||
    auto client = static_cast<CurrentTimeClient *>(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);
 | 
			
		||||
    return client->OnCurrentTimeReadResult(conn_handle, error, attr);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CurrentTimeClient::CurrentTimeClient(DateTime &dateTimeController) : dateTimeController{dateTimeController} {
 | 
			
		||||
 | 
			
		||||
CurrentTimeClient::CurrentTimeClient(DateTime& dateTimeController) : dateTimeController {dateTimeController} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CurrentTimeClient::Init() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                         const ble_gatt_svc *service) {
 | 
			
		||||
bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
 | 
			
		||||
  if (service == nullptr && error->status == BLE_HS_EDONE) {
 | 
			
		||||
    if (isDiscovered) {
 | 
			
		||||
      NRF_LOG_INFO("CTS found, starting characteristics discovery");
 | 
			
		||||
 | 
			
		||||
      ble_gattc_disc_all_chrs(connectionHandle, ctsStartHandle, ctsEndHandle,
 | 
			
		||||
                              OnCurrentTimeCharacteristicDiscoveredCallback, this);
 | 
			
		||||
      ble_gattc_disc_all_chrs(connectionHandle, ctsStartHandle, ctsEndHandle, OnCurrentTimeCharacteristicDiscoveredCallback, this);
 | 
			
		||||
    } else {
 | 
			
		||||
      NRF_LOG_INFO("CTS not found");
 | 
			
		||||
      onServiceDiscovered(connectionHandle);
 | 
			
		||||
@ -49,7 +47,7 @@ bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_ga
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ctsServiceUuid), &service->uuid.u) == 0) {
 | 
			
		||||
  if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ctsServiceUuid), &service->uuid.u) == 0) {
 | 
			
		||||
    NRF_LOG_INFO("CTS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
 | 
			
		||||
    isDiscovered = true;
 | 
			
		||||
    ctsStartHandle = service->start_handle;
 | 
			
		||||
@ -59,8 +57,9 @@ bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_ga
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
 | 
			
		||||
                                                      const ble_gatt_chr *characteristic) {
 | 
			
		||||
int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle,
 | 
			
		||||
                                                      const ble_gatt_error* error,
 | 
			
		||||
                                                      const ble_gatt_chr* characteristic) {
 | 
			
		||||
  if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
 | 
			
		||||
    if (isCharacteristicDiscovered) {
 | 
			
		||||
      NRF_LOG_INFO("CTS Characteristic discovery complete, fetching time");
 | 
			
		||||
@ -73,8 +72,7 @@ int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, cons
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (characteristic != nullptr &&
 | 
			
		||||
      ble_uuid_cmp(((ble_uuid_t *) ¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
  if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) ¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
 | 
			
		||||
    NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle);
 | 
			
		||||
    isCharacteristicDiscovered = true;
 | 
			
		||||
    currentTimeHandle = characteristic->val_handle;
 | 
			
		||||
@ -82,17 +80,15 @@ int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, cons
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error,
 | 
			
		||||
                                               const ble_gatt_attr *attribute) {
 | 
			
		||||
int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_attr* attribute) {
 | 
			
		||||
  if (error->status == 0) {
 | 
			
		||||
    // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
 | 
			
		||||
    CtsData result;
 | 
			
		||||
    os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
 | 
			
		||||
    NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
 | 
			
		||||
                 result.month, result.dayofmonth,
 | 
			
		||||
                 result.hour, result.minute, result.second);
 | 
			
		||||
    dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
 | 
			
		||||
                               0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
 | 
			
		||||
    NRF_LOG_INFO(
 | 
			
		||||
      "Received data: %d-%d-%d %d:%d:%d", result.year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
 | 
			
		||||
    dateTimeController.SetTime(
 | 
			
		||||
      result.year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
 | 
			
		||||
  } else {
 | 
			
		||||
    NRF_LOG_INFO("Error retrieving current time: %d", error->status);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -8,54 +8,51 @@
 | 
			
		||||
#include "BleClient.h"
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
    namespace Controllers {
 | 
			
		||||
        class DateTime;
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class DateTime;
 | 
			
		||||
 | 
			
		||||
        class CurrentTimeClient : public BleClient {
 | 
			
		||||
        public:
 | 
			
		||||
            explicit CurrentTimeClient(DateTime& dateTimeController);
 | 
			
		||||
            void Init();
 | 
			
		||||
            void Reset();
 | 
			
		||||
            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,
 | 
			
		||||
                                               const ble_gatt_chr *characteristic);
 | 
			
		||||
            int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute);
 | 
			
		||||
            static constexpr const ble_uuid16_t* Uuid() { 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;
 | 
			
		||||
    class CurrentTimeClient : public BleClient {
 | 
			
		||||
    public:
 | 
			
		||||
      explicit CurrentTimeClient(DateTime& dateTimeController);
 | 
			
		||||
      void Init();
 | 
			
		||||
      void Reset();
 | 
			
		||||
      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, const ble_gatt_chr* characteristic);
 | 
			
		||||
      int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_attr* attribute);
 | 
			
		||||
      static constexpr const ble_uuid16_t* Uuid() {
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
          private:
 | 
			
		||||
            typedef struct __attribute__((packed)) {
 | 
			
		||||
                uint16_t year;
 | 
			
		||||
                uint8_t month;
 | 
			
		||||
                uint8_t dayofmonth;
 | 
			
		||||
                uint8_t hour;
 | 
			
		||||
                uint8_t minute;
 | 
			
		||||
                uint8_t second;
 | 
			
		||||
                uint8_t millis;
 | 
			
		||||
                uint8_t reason;
 | 
			
		||||
            } CtsData;
 | 
			
		||||
    private:
 | 
			
		||||
      typedef struct __attribute__((packed)) {
 | 
			
		||||
        uint16_t year;
 | 
			
		||||
        uint8_t month;
 | 
			
		||||
        uint8_t dayofmonth;
 | 
			
		||||
        uint8_t hour;
 | 
			
		||||
        uint8_t minute;
 | 
			
		||||
        uint8_t second;
 | 
			
		||||
        uint8_t millis;
 | 
			
		||||
        uint8_t reason;
 | 
			
		||||
      } CtsData;
 | 
			
		||||
 | 
			
		||||
            static constexpr uint16_t ctsServiceId {0x1805};
 | 
			
		||||
            static constexpr uint16_t currentTimeCharacteristicId {0x2a2b};
 | 
			
		||||
      static constexpr uint16_t ctsServiceId {0x1805};
 | 
			
		||||
      static constexpr uint16_t currentTimeCharacteristicId {0x2a2b};
 | 
			
		||||
 | 
			
		||||
            static constexpr ble_uuid16_t ctsServiceUuid {
 | 
			
		||||
                    .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                    .value = ctsServiceId
 | 
			
		||||
            };
 | 
			
		||||
            static constexpr ble_uuid16_t currentTimeCharacteristicUuid {
 | 
			
		||||
                    .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                    .value = currentTimeCharacteristicId
 | 
			
		||||
            };
 | 
			
		||||
      static constexpr ble_uuid16_t ctsServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsServiceId};
 | 
			
		||||
      static constexpr ble_uuid16_t currentTimeCharacteristicUuid {.u {.type = BLE_UUID_TYPE_16}, .value = currentTimeCharacteristicId};
 | 
			
		||||
 | 
			
		||||
            DateTime& dateTimeController;
 | 
			
		||||
            bool isDiscovered = false;
 | 
			
		||||
            uint16_t ctsStartHandle;
 | 
			
		||||
            uint16_t ctsEndHandle;
 | 
			
		||||
      DateTime& dateTimeController;
 | 
			
		||||
      bool isDiscovered = false;
 | 
			
		||||
      uint16_t ctsStartHandle;
 | 
			
		||||
      uint16_t ctsEndHandle;
 | 
			
		||||
 | 
			
		||||
            bool isCharacteristicDiscovered = false;
 | 
			
		||||
            uint16_t currentTimeHandle;
 | 
			
		||||
            std::function<void(uint16_t)> onServiceDiscovered;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
      bool isCharacteristicDiscovered = false;
 | 
			
		||||
      uint16_t currentTimeHandle;
 | 
			
		||||
      std::function<void(uint16_t)> onServiceDiscovered;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -7,8 +7,7 @@ using namespace Pinetime::Controllers;
 | 
			
		||||
constexpr ble_uuid16_t CurrentTimeService::ctsUuid;
 | 
			
		||||
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);
 | 
			
		||||
  return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt);
 | 
			
		||||
}
 | 
			
		||||
@ -22,22 +21,19 @@ void CurrentTimeService::Init() {
 | 
			
		||||
  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,
 | 
			
		||||
                                                    struct ble_gatt_access_ctxt *ctxt) {
 | 
			
		||||
 | 
			
		||||
    NRF_LOG_INFO("Setting time...");
 | 
			
		||||
  NRF_LOG_INFO("Setting time...");
 | 
			
		||||
 | 
			
		||||
  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
 | 
			
		||||
    CtsData result;
 | 
			
		||||
    os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result);
 | 
			
		||||
 | 
			
		||||
    NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
 | 
			
		||||
            result.month, result.dayofmonth,
 | 
			
		||||
            result.hour, result.minute, result.second);
 | 
			
		||||
    NRF_LOG_INFO(
 | 
			
		||||
      "Received data: %d-%d-%d %d:%d:%d", result.year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
 | 
			
		||||
 | 
			
		||||
    m_dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
 | 
			
		||||
                        0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
 | 
			
		||||
    m_dateTimeController.SetTime(
 | 
			
		||||
      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) {
 | 
			
		||||
    CtsData currentDateTime;
 | 
			
		||||
@ -49,39 +45,26 @@ int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handl
 | 
			
		||||
    currentDateTime.second = m_dateTimeController.Seconds();
 | 
			
		||||
    currentDateTime.millis = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    int res = os_mbuf_append(ctxt->om, ¤tDateTime, sizeof(CtsData));
 | 
			
		||||
    return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) :
 | 
			
		||||
        characteristicDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        .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} {
 | 
			
		||||
CurrentTimeService::CurrentTimeService(DateTime& dateTimeController)
 | 
			
		||||
  : characteristicDefinition {{.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} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,42 +12,35 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class CurrentTimeService {
 | 
			
		||||
      public:
 | 
			
		||||
        CurrentTimeService(DateTime &dateTimeController);
 | 
			
		||||
        void Init();
 | 
			
		||||
    public:
 | 
			
		||||
      CurrentTimeService(DateTime& dateTimeController);
 | 
			
		||||
      void Init();
 | 
			
		||||
 | 
			
		||||
        int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                                    struct ble_gatt_access_ctxt *ctxt);
 | 
			
		||||
      int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        static constexpr uint16_t ctsId {0x1805};
 | 
			
		||||
        static constexpr uint16_t ctsCharId {0x2a2b};
 | 
			
		||||
    private:
 | 
			
		||||
      static constexpr uint16_t ctsId {0x1805};
 | 
			
		||||
      static constexpr uint16_t ctsCharId {0x2a2b};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t ctsUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = ctsId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t ctsUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t ctChrUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = ctsCharId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t ctChrUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsCharId};
 | 
			
		||||
 | 
			
		||||
        struct ble_gatt_chr_def characteristicDefinition[2];
 | 
			
		||||
        struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
      struct ble_gatt_chr_def characteristicDefinition[2];
 | 
			
		||||
      struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
 | 
			
		||||
        typedef struct __attribute__((packed)) {
 | 
			
		||||
          uint16_t year;
 | 
			
		||||
          uint8_t month;
 | 
			
		||||
          uint8_t dayofmonth;
 | 
			
		||||
          uint8_t hour;
 | 
			
		||||
          uint8_t minute;
 | 
			
		||||
          uint8_t second;
 | 
			
		||||
          uint8_t millis;
 | 
			
		||||
          uint8_t reason;
 | 
			
		||||
        } CtsData;
 | 
			
		||||
      typedef struct __attribute__((packed)) {
 | 
			
		||||
        uint16_t year;
 | 
			
		||||
        uint8_t month;
 | 
			
		||||
        uint8_t dayofmonth;
 | 
			
		||||
        uint8_t hour;
 | 
			
		||||
        uint8_t minute;
 | 
			
		||||
        uint8_t second;
 | 
			
		||||
        uint8_t millis;
 | 
			
		||||
        uint8_t reason;
 | 
			
		||||
      } CtsData;
 | 
			
		||||
 | 
			
		||||
        DateTime &m_dateTimeController;
 | 
			
		||||
      DateTime& m_dateTimeController;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,7 @@ constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid;
 | 
			
		||||
constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid;
 | 
			
		||||
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);
 | 
			
		||||
  return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt);
 | 
			
		||||
}
 | 
			
		||||
@ -25,10 +24,8 @@ void DeviceInformationService::Init() {
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                                                    struct ble_gatt_access_ctxt *ctxt) {
 | 
			
		||||
  const char *str;
 | 
			
		||||
int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
 | 
			
		||||
  const char* str;
 | 
			
		||||
 | 
			
		||||
  switch (ble_uuid_u16(ctxt->chr->uuid)) {
 | 
			
		||||
    case manufacturerNameId:
 | 
			
		||||
@ -57,60 +54,49 @@ int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16
 | 
			
		||||
  return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DeviceInformationService::DeviceInformationService() :
 | 
			
		||||
        characteristicDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &manufacturerNameUuid,
 | 
			
		||||
                        .access_cb = DeviceInformationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &modelNumberUuid,
 | 
			
		||||
                        .access_cb = DeviceInformationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &serialNumberUuid,
 | 
			
		||||
                        .access_cb = DeviceInformationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &fwRevisionUuid,
 | 
			
		||||
                        .access_cb = DeviceInformationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &hwRevisionUuid,
 | 
			
		||||
                        .access_cb = DeviceInformationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &swRevisionUuid,
 | 
			
		||||
                        .access_cb = DeviceInformationCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  0
 | 
			
		||||
                }
 | 
			
		||||
        },
 | 
			
		||||
        serviceDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        /* Device Information Service */
 | 
			
		||||
                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &deviceInfoUuid,
 | 
			
		||||
                        .characteristics = characteristicDefinition
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                },
 | 
			
		||||
        }
 | 
			
		||||
         {
 | 
			
		||||
 | 
			
		||||
DeviceInformationService::DeviceInformationService()
 | 
			
		||||
  : characteristicDefinition {{
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &manufacturerNameUuid,
 | 
			
		||||
                                .access_cb = DeviceInformationCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &modelNumberUuid,
 | 
			
		||||
                                .access_cb = DeviceInformationCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &serialNumberUuid,
 | 
			
		||||
                                .access_cb = DeviceInformationCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &fwRevisionUuid,
 | 
			
		||||
                                .access_cb = DeviceInformationCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &hwRevisionUuid,
 | 
			
		||||
                                .access_cb = DeviceInformationCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &swRevisionUuid,
 | 
			
		||||
                                .access_cb = DeviceInformationCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                              },
 | 
			
		||||
                              {0}},
 | 
			
		||||
    serviceDefinition {
 | 
			
		||||
      {/* Device Information Service */
 | 
			
		||||
       .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
       .uuid = (ble_uuid_t*) &deviceInfoUuid,
 | 
			
		||||
       .characteristics = characteristicDefinition},
 | 
			
		||||
      {0},
 | 
			
		||||
    } {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,69 +9,44 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class DeviceInformationService {
 | 
			
		||||
      public:
 | 
			
		||||
        DeviceInformationService();
 | 
			
		||||
        void Init();
 | 
			
		||||
    public:
 | 
			
		||||
      DeviceInformationService();
 | 
			
		||||
      void Init();
 | 
			
		||||
 | 
			
		||||
        int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                                    struct ble_gatt_access_ctxt *ctxt);
 | 
			
		||||
      int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        static constexpr uint16_t deviceInfoId {0x180a};
 | 
			
		||||
        static constexpr uint16_t manufacturerNameId {0x2a29};
 | 
			
		||||
        static constexpr uint16_t modelNumberId {0x2a24};
 | 
			
		||||
        static constexpr uint16_t serialNumberId {0x2a25};
 | 
			
		||||
        static constexpr uint16_t fwRevisionId {0x2a26};
 | 
			
		||||
        static constexpr uint16_t hwRevisionId {0x2a27};
 | 
			
		||||
        static constexpr uint16_t swRevisionId {0x2a28};
 | 
			
		||||
    private:
 | 
			
		||||
      static constexpr uint16_t deviceInfoId {0x180a};
 | 
			
		||||
      static constexpr uint16_t manufacturerNameId {0x2a29};
 | 
			
		||||
      static constexpr uint16_t modelNumberId {0x2a24};
 | 
			
		||||
      static constexpr uint16_t serialNumberId {0x2a25};
 | 
			
		||||
      static constexpr uint16_t fwRevisionId {0x2a26};
 | 
			
		||||
      static constexpr uint16_t hwRevisionId {0x2a27};
 | 
			
		||||
      static constexpr uint16_t swRevisionId {0x2a28};
 | 
			
		||||
 | 
			
		||||
        static constexpr const char* manufacturerName = "PINE64";
 | 
			
		||||
        static constexpr const char* modelNumber = "PineTime";
 | 
			
		||||
        static constexpr const char* hwRevision = "1.0.0";
 | 
			
		||||
        static constexpr const char* serialNumber = "0";
 | 
			
		||||
        static constexpr const char* fwRevision =  Version::VersionString();
 | 
			
		||||
        static constexpr const char* swRevision = "InfiniTime";
 | 
			
		||||
      static constexpr const char* manufacturerName = "PINE64";
 | 
			
		||||
      static constexpr const char* modelNumber = "PineTime";
 | 
			
		||||
      static constexpr const char* hwRevision = "1.0.0";
 | 
			
		||||
      static constexpr const char* serialNumber = "0";
 | 
			
		||||
      static constexpr const char* fwRevision = Version::VersionString();
 | 
			
		||||
      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 {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = deviceInfoId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t manufacturerNameUuid {.u {.type = BLE_UUID_TYPE_16}, .value = manufacturerNameId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t manufacturerNameUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = manufacturerNameId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t modelNumberUuid {.u {.type = BLE_UUID_TYPE_16}, .value = modelNumberId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t modelNumberUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = modelNumberId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t serialNumberUuid {.u {.type = BLE_UUID_TYPE_16}, .value = serialNumberId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t serialNumberUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_16 },
 | 
			
		||||
                .value = serialNumberId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t fwRevisionUuid {.u {.type = BLE_UUID_TYPE_16}, .value = fwRevisionId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t fwRevisionUuid {
 | 
			
		||||
                .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 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];
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -11,67 +11,60 @@ constexpr ble_uuid128_t DfuService::controlPointCharacteristicUuid;
 | 
			
		||||
constexpr ble_uuid128_t DfuService::revisionCharacteristicUuid;
 | 
			
		||||
constexpr ble_uuid128_t DfuService::packetCharacteristicUuid;
 | 
			
		||||
 | 
			
		||||
int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                       struct ble_gatt_access_ctxt *ctxt, void *arg) {
 | 
			
		||||
  auto dfuService = static_cast<DfuService *>(arg);
 | 
			
		||||
int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
 | 
			
		||||
  auto dfuService = static_cast<DfuService*>(arg);
 | 
			
		||||
  return dfuService->OnServiceData(conn_handle, attr_handle, ctxt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NotificationTimerCallback(TimerHandle_t xTimer) {
 | 
			
		||||
  auto notificationManager = static_cast<DfuService::NotificationManager *>(pvTimerGetTimerID(xTimer));
 | 
			
		||||
  auto notificationManager = static_cast<DfuService::NotificationManager*>(pvTimerGetTimerID(xTimer));
 | 
			
		||||
  notificationManager->OnNotificationTimer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TimeoutTimerCallback(TimerHandle_t xTimer) {
 | 
			
		||||
  auto dfuService = static_cast<DfuService *>(pvTimerGetTimerID(xTimer));
 | 
			
		||||
  auto dfuService = static_cast<DfuService*>(pvTimerGetTimerID(xTimer));
 | 
			
		||||
  dfuService->OnTimeout();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DfuService::DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController,
 | 
			
		||||
                       Pinetime::Drivers::SpiNorFlash &spiNorFlash) :
 | 
			
		||||
        systemTask{systemTask},
 | 
			
		||||
        bleController{bleController},
 | 
			
		||||
        dfuImage{spiNorFlash},
 | 
			
		||||
        characteristicDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &packetCharacteristicUuid,
 | 
			
		||||
                        .access_cb = DfuServiceCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
 | 
			
		||||
                        .val_handle = nullptr,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &controlPointCharacteristicUuid,
 | 
			
		||||
                        .access_cb = DfuServiceCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                        .val_handle = nullptr,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &revisionCharacteristicUuid,
 | 
			
		||||
                        .access_cb = DfuServiceCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                        .val_handle = &revision,
 | 
			
		||||
DfuService::DfuService(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
                       Pinetime::Controllers::Ble& bleController,
 | 
			
		||||
                       Pinetime::Drivers::SpiNorFlash& spiNorFlash)
 | 
			
		||||
  : systemTask {systemTask},
 | 
			
		||||
    bleController {bleController},
 | 
			
		||||
    dfuImage {spiNorFlash},
 | 
			
		||||
    characteristicDefinition {{
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &packetCharacteristicUuid,
 | 
			
		||||
                                .access_cb = DfuServiceCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
 | 
			
		||||
                                .val_handle = nullptr,
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &controlPointCharacteristicUuid,
 | 
			
		||||
                                .access_cb = DfuServiceCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                                .val_handle = nullptr,
 | 
			
		||||
                              },
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = (ble_uuid_t*) &revisionCharacteristicUuid,
 | 
			
		||||
                                .access_cb = DfuServiceCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                                .val_handle = &revision,
 | 
			
		||||
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                }
 | 
			
		||||
                              },
 | 
			
		||||
                              {0}
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
        serviceDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        /* Device Information Service */
 | 
			
		||||
                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &serviceUuid,
 | 
			
		||||
                        .characteristics = characteristicDefinition
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                },
 | 
			
		||||
        } {
 | 
			
		||||
  timeoutTimer = xTimerCreate ("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
 | 
			
		||||
    },
 | 
			
		||||
    serviceDefinition {
 | 
			
		||||
      {/* Device Information Service */
 | 
			
		||||
       .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
       .uuid = (ble_uuid_t*) &serviceUuid,
 | 
			
		||||
       .characteristics = characteristicDefinition},
 | 
			
		||||
      {0},
 | 
			
		||||
    } {
 | 
			
		||||
  timeoutTimer = xTimerCreate("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DfuService::Init() {
 | 
			
		||||
@ -83,55 +76,54 @@ void DfuService::Init() {
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) {
 | 
			
		||||
  if(bleController.IsFirmwareUpdating()){
 | 
			
		||||
int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
 | 
			
		||||
  if (bleController.IsFirmwareUpdating()) {
 | 
			
		||||
    xTimerStart(timeoutTimer, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &packetCharacteristicUuid, nullptr,
 | 
			
		||||
                     &packetCharacteristicHandle);
 | 
			
		||||
  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &controlPointCharacteristicUuid, nullptr,
 | 
			
		||||
                     &controlPointCharacteristicHandle);
 | 
			
		||||
  ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &revisionCharacteristicUuid, nullptr,
 | 
			
		||||
                     &revisionCharacteristicHandle);
 | 
			
		||||
  ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &packetCharacteristicUuid, nullptr, &packetCharacteristicHandle);
 | 
			
		||||
  ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &controlPointCharacteristicUuid, nullptr, &controlPointCharacteristicHandle);
 | 
			
		||||
  ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &revisionCharacteristicUuid, nullptr, &revisionCharacteristicHandle);
 | 
			
		||||
 | 
			
		||||
  if (attributeHandle == packetCharacteristicHandle) {
 | 
			
		||||
    if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
 | 
			
		||||
      return WritePacketHandler(connectionHandle, context->om);
 | 
			
		||||
    else return 0;
 | 
			
		||||
    else
 | 
			
		||||
      return 0;
 | 
			
		||||
  } else if (attributeHandle == controlPointCharacteristicHandle) {
 | 
			
		||||
    if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
 | 
			
		||||
      return ControlPointHandler(connectionHandle, context->om);
 | 
			
		||||
    else return 0;
 | 
			
		||||
    else
 | 
			
		||||
      return 0;
 | 
			
		||||
  } else if (attributeHandle == revisionCharacteristicHandle) {
 | 
			
		||||
    if (context->op == BLE_GATT_ACCESS_OP_READ_CHR)
 | 
			
		||||
      return SendDfuRevision(context->om);
 | 
			
		||||
    else return 0;
 | 
			
		||||
    else
 | 
			
		||||
      return 0;
 | 
			
		||||
  } else {
 | 
			
		||||
    NRF_LOG_INFO("[DFU] Unknown Characteristic : %d", attributeHandle);
 | 
			
		||||
    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));
 | 
			
		||||
  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) {
 | 
			
		||||
    case States::Start: {
 | 
			
		||||
      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);
 | 
			
		||||
      applicationSize = om->om_data[8] + (om->om_data[9] << 8) + (om->om_data[10] << 16) + (om->om_data[11] << 24);
 | 
			
		||||
      bleController.FirmwareUpdateTotalBytes(applicationSize);
 | 
			
		||||
      NRF_LOG_INFO("[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize,
 | 
			
		||||
                   bootloaderSize, applicationSize);
 | 
			
		||||
      NRF_LOG_INFO(
 | 
			
		||||
        "[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, bootloaderSize, applicationSize);
 | 
			
		||||
 | 
			
		||||
      dfuImage.Erase();
 | 
			
		||||
 | 
			
		||||
      uint8_t data[]{16, 1, 1};
 | 
			
		||||
      uint8_t data[] {16, 1, 1};
 | 
			
		||||
      notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
 | 
			
		||||
      state = States::Init;
 | 
			
		||||
    }
 | 
			
		||||
@ -139,19 +131,22 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
 | 
			
		||||
    case States::Init: {
 | 
			
		||||
      uint16_t deviceType = om->om_data[0] + (om->om_data[1] << 8);
 | 
			
		||||
      uint16_t deviceRevision = om->om_data[2] + (om->om_data[3] << 8);
 | 
			
		||||
      uint32_t applicationVersion =
 | 
			
		||||
              om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
 | 
			
		||||
      uint32_t applicationVersion = 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 sd[softdeviceArrayLength];
 | 
			
		||||
      for (int i = 0; i < softdeviceArrayLength; i++) {
 | 
			
		||||
        sd[i] = om->om_data[10 + (i * 2)] + (om->om_data[10 + (i * 2) + 1] << 8);
 | 
			
		||||
      }
 | 
			
		||||
      expectedCrc =
 | 
			
		||||
              om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
 | 
			
		||||
      expectedCrc = om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
 | 
			
		||||
 | 
			
		||||
      NRF_LOG_INFO(
 | 
			
		||||
              "[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);
 | 
			
		||||
        "[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);
 | 
			
		||||
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
@ -163,16 +158,18 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
 | 
			
		||||
      bleController.FirmwareUpdateCurrentBytes(bytesReceived);
 | 
			
		||||
 | 
			
		||||
      if ((nbPacketReceived % nbPacketsToNotify) == 0 && bytesReceived != applicationSize) {
 | 
			
		||||
        uint8_t data[5]{static_cast<uint8_t>(Opcodes::PacketReceiptNotification),
 | 
			
		||||
                        (uint8_t) (bytesReceived & 0x000000FFu), (uint8_t) (bytesReceived >> 8u),
 | 
			
		||||
                        (uint8_t) (bytesReceived >> 16u), (uint8_t) (bytesReceived >> 24u)};
 | 
			
		||||
        uint8_t data[5] {static_cast<uint8_t>(Opcodes::PacketReceiptNotification),
 | 
			
		||||
                         (uint8_t) (bytesReceived & 0x000000FFu),
 | 
			
		||||
                         (uint8_t) (bytesReceived >> 8u),
 | 
			
		||||
                         (uint8_t) (bytesReceived >> 16u),
 | 
			
		||||
                         (uint8_t) (bytesReceived >> 24u)};
 | 
			
		||||
        NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received", bytesReceived);
 | 
			
		||||
        notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 5);
 | 
			
		||||
      }
 | 
			
		||||
      if (dfuImage.IsComplete()) {
 | 
			
		||||
        uint8_t data[3]{static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                        static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage),
 | 
			
		||||
                        static_cast<uint8_t>(ErrorCodes::NoError)};
 | 
			
		||||
        uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                         static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage),
 | 
			
		||||
                         static_cast<uint8_t>(ErrorCodes::NoError)};
 | 
			
		||||
        NRF_LOG_INFO("[DFU] -> Send packet notification : all bytes received!");
 | 
			
		||||
        notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
 | 
			
		||||
        state = States::Validate;
 | 
			
		||||
@ -186,7 +183,7 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
 | 
			
		||||
  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]);
 | 
			
		||||
  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);
 | 
			
		||||
        return 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
      break;
 | 
			
		||||
    } break;
 | 
			
		||||
    case Opcodes::InitDFUParameters: {
 | 
			
		||||
      if (state != States::Init) {
 | 
			
		||||
        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");
 | 
			
		||||
 | 
			
		||||
      if (isInitComplete) {
 | 
			
		||||
        uint8_t data[3] {
 | 
			
		||||
                static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                static_cast<uint8_t>(Opcodes::InitDFUParameters),
 | 
			
		||||
                (isInitComplete ? uint8_t{1} : uint8_t{0})
 | 
			
		||||
        };
 | 
			
		||||
        uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                         static_cast<uint8_t>(Opcodes::InitDFUParameters),
 | 
			
		||||
                         (isInitComplete ? uint8_t {1} : uint8_t {0})};
 | 
			
		||||
        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
      if(dfuImage.Validate()){
 | 
			
		||||
      if (dfuImage.Validate()) {
 | 
			
		||||
        state = States::Validated;
 | 
			
		||||
        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
 | 
			
		||||
        NRF_LOG_INFO("Image OK");
 | 
			
		||||
 | 
			
		||||
        uint8_t data[3] {
 | 
			
		||||
                static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                static_cast<uint8_t>(Opcodes::ValidateFirmware),
 | 
			
		||||
                static_cast<uint8_t>(ErrorCodes::NoError)
 | 
			
		||||
        };
 | 
			
		||||
        uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                         static_cast<uint8_t>(Opcodes::ValidateFirmware),
 | 
			
		||||
                         static_cast<uint8_t>(ErrorCodes::NoError)};
 | 
			
		||||
        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
 | 
			
		||||
      } else {
 | 
			
		||||
        bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
 | 
			
		||||
        NRF_LOG_INFO("Image Error : bad CRC");
 | 
			
		||||
 | 
			
		||||
        uint8_t data[3] {
 | 
			
		||||
                static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                static_cast<uint8_t>(Opcodes::ValidateFirmware),
 | 
			
		||||
                static_cast<uint8_t>(ErrorCodes::CrcError)
 | 
			
		||||
        };
 | 
			
		||||
        uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
 | 
			
		||||
                         static_cast<uint8_t>(Opcodes::ValidateFirmware),
 | 
			
		||||
                         static_cast<uint8_t>(ErrorCodes::CrcError)};
 | 
			
		||||
        notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -318,11 +308,11 @@ void DfuService::Reset() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
  if(size != 0 || s > 10)
 | 
			
		||||
bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t* data, size_t s) {
 | 
			
		||||
  if (size != 0 || s > 10)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  connectionHandle = connection;
 | 
			
		||||
@ -334,14 +324,14 @@ bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DfuService::NotificationManager::OnNotificationTimer() {
 | 
			
		||||
  if(size > 0) {
 | 
			
		||||
  if (size > 0) {
 | 
			
		||||
    Send(connectionHandle, characteristicHandle, buffer, size);
 | 
			
		||||
    size = 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
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 ret = ble_gattc_notify_custom(connection, charactHandle, om);
 | 
			
		||||
  ASSERT(ret == 0);
 | 
			
		||||
}
 | 
			
		||||
@ -354,27 +344,29 @@ void DfuService::NotificationManager::Reset() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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->totalSize = totalSize;
 | 
			
		||||
  this->expectedCrc = expectedCrc;
 | 
			
		||||
  this->ready = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DfuService::DfuImage::Append(uint8_t *data, size_t size) {
 | 
			
		||||
  if(!ready) return;
 | 
			
		||||
void DfuService::DfuImage::Append(uint8_t* data, size_t size) {
 | 
			
		||||
  if (!ready)
 | 
			
		||||
    return;
 | 
			
		||||
  ASSERT(size <= 20);
 | 
			
		||||
 | 
			
		||||
  std::memcpy(tempBuffer + bufferWriteIndex, data, size);
 | 
			
		||||
  bufferWriteIndex += size;
 | 
			
		||||
 | 
			
		||||
  if(bufferWriteIndex == bufferSize) {
 | 
			
		||||
  if (bufferWriteIndex == bufferSize) {
 | 
			
		||||
    spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
 | 
			
		||||
    totalWriteIndex += bufferWriteIndex;
 | 
			
		||||
    bufferWriteIndex = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) {
 | 
			
		||||
  if (bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) {
 | 
			
		||||
    spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
 | 
			
		||||
    totalWriteIndex += bufferWriteIndex;
 | 
			
		||||
    if (totalSize < maxSize)
 | 
			
		||||
@ -383,15 +375,16 @@ void DfuService::DfuImage::Append(uint8_t *data, size_t size) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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?
 | 
			
		||||
          0xf395c277,
 | 
			
		||||
          0x7fefd260,
 | 
			
		||||
          0x0f505235,
 | 
			
		||||
          0x8079b62c,
 | 
			
		||||
  uint32_t magic[4] = {
 | 
			
		||||
    // TODO When this variable is a static constexpr, the values written to the memory are not correct. Why?
 | 
			
		||||
    0xf395c277,
 | 
			
		||||
    0x7fefd260,
 | 
			
		||||
    0x0f505235,
 | 
			
		||||
    0x8079b62c,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  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() {
 | 
			
		||||
@ -421,7 +414,7 @@ bool DfuService::DfuImage::Validate() {
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  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() {
 | 
			
		||||
  if(!ready) return false;
 | 
			
		||||
  if (!ready)
 | 
			
		||||
    return false;
 | 
			
		||||
  return totalWriteIndex == totalSize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,146 +20,139 @@ namespace Pinetime {
 | 
			
		||||
    class Ble;
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
        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:
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
        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:
 | 
			
		||||
        Pinetime::System::SystemTask &systemTask;
 | 
			
		||||
        Pinetime::Controllers::Ble &bleController;
 | 
			
		||||
        DfuImage dfuImage;
 | 
			
		||||
        NotificationManager notificationManager;
 | 
			
		||||
        TimerHandle_t timer;
 | 
			
		||||
        uint16_t connectionHandle = 0;
 | 
			
		||||
        uint16_t characteristicHandle = 0;
 | 
			
		||||
        size_t size = 0;
 | 
			
		||||
        uint8_t buffer[10];
 | 
			
		||||
 | 
			
		||||
        static constexpr uint16_t dfuServiceId{0x1530};
 | 
			
		||||
        static constexpr uint16_t packetCharacteristicId{0x1532};
 | 
			
		||||
        static constexpr uint16_t controlPointCharacteristicId{0x1531};
 | 
			
		||||
        static constexpr uint16_t revisionCharacteristicId{0x1534};
 | 
			
		||||
      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();
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
      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;
 | 
			
		||||
 | 
			
		||||
        int SendDfuRevision(os_mbuf *om) const;
 | 
			
		||||
        int WritePacketHandler(uint16_t connectionHandle, os_mbuf *om);
 | 
			
		||||
        int ControlPointHandler(uint16_t connectionHandle, os_mbuf *om);
 | 
			
		||||
        void WriteMagicNumber();
 | 
			
		||||
        uint16_t ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t const* p_crc);
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -8,39 +8,29 @@ constexpr ble_uuid16_t HeartRateService::heartRateServiceUuid;
 | 
			
		||||
constexpr ble_uuid16_t HeartRateService::heartRateMeasurementUuid;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  int HeartRateServiceServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
 | 
			
		||||
  int HeartRateServiceServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
 | 
			
		||||
    auto* heartRateService = static_cast<HeartRateService*>(arg);
 | 
			
		||||
    return heartRateService->OnHeartRateRequested(conn_handle, attr_handle, ctxt);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO Refactoring - remove dependency to SystemTask
 | 
			
		||||
HeartRateService::HeartRateService(Pinetime::System::SystemTask &system, Controllers::HeartRateController& heartRateController) :
 | 
			
		||||
        system{system},
 | 
			
		||||
        heartRateController{heartRateController},
 | 
			
		||||
        characteristicDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &heartRateMeasurementUuid,
 | 
			
		||||
                        .access_cb = HeartRateServiceServiceCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                        .val_handle = &heartRateMeasurementHandle
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                }
 | 
			
		||||
        },
 | 
			
		||||
        serviceDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        /* Device Information Service */
 | 
			
		||||
                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &heartRateServiceUuid,
 | 
			
		||||
                        .characteristics = characteristicDefinition
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                },
 | 
			
		||||
        }{
 | 
			
		||||
HeartRateService::HeartRateService(Pinetime::System::SystemTask& system, Controllers::HeartRateController& heartRateController)
 | 
			
		||||
  : system {system},
 | 
			
		||||
    heartRateController {heartRateController},
 | 
			
		||||
    characteristicDefinition {{.uuid = (ble_uuid_t*) &heartRateMeasurementUuid,
 | 
			
		||||
                               .access_cb = HeartRateServiceServiceCallback,
 | 
			
		||||
                               .arg = this,
 | 
			
		||||
                               .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                               .val_handle = &heartRateMeasurementHandle},
 | 
			
		||||
                              {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)
 | 
			
		||||
  heartRateController.SetService(this);
 | 
			
		||||
}
 | 
			
		||||
@ -54,9 +44,8 @@ void HeartRateService::Init() {
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int HeartRateService::OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle,
 | 
			
		||||
                                           ble_gatt_access_ctxt *context) {
 | 
			
		||||
  if(attributeHandle == heartRateMeasurementHandle) {
 | 
			
		||||
int HeartRateService::OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
 | 
			
		||||
  if (attributeHandle == heartRateMeasurementHandle) {
 | 
			
		||||
    NRF_LOG_INFO("HEARTRATE : handle = %d", heartRateMeasurementHandle);
 | 
			
		||||
    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) {
 | 
			
		||||
  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();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,33 +12,26 @@ namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class HeartRateController;
 | 
			
		||||
    class HeartRateService {
 | 
			
		||||
      public:
 | 
			
		||||
        HeartRateService(Pinetime::System::SystemTask &system, Controllers::HeartRateController& heartRateController);
 | 
			
		||||
        void Init();
 | 
			
		||||
        int OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
 | 
			
		||||
        void OnNewHeartRateValue(uint8_t hearRateValue);
 | 
			
		||||
    public:
 | 
			
		||||
      HeartRateService(Pinetime::System::SystemTask& system, Controllers::HeartRateController& heartRateController);
 | 
			
		||||
      void Init();
 | 
			
		||||
      int OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
 | 
			
		||||
      void OnNewHeartRateValue(uint8_t hearRateValue);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        Pinetime::System::SystemTask &system;
 | 
			
		||||
        Controllers::HeartRateController& heartRateController;
 | 
			
		||||
        static constexpr uint16_t heartRateServiceId {0x180D};
 | 
			
		||||
        static constexpr uint16_t heartRateMeasurementId {0x2A37};
 | 
			
		||||
      Pinetime::System::SystemTask& system;
 | 
			
		||||
      Controllers::HeartRateController& heartRateController;
 | 
			
		||||
      static constexpr uint16_t heartRateServiceId {0x180D};
 | 
			
		||||
      static constexpr uint16_t heartRateMeasurementId {0x2A37};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t heartRateServiceUuid {
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = heartRateServiceId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t heartRateServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateServiceId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t heartRateMeasurementUuid {
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = heartRateMeasurementId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t heartRateMeasurementUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateMeasurementId};
 | 
			
		||||
 | 
			
		||||
        struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
        struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
 | 
			
		||||
        uint16_t heartRateMeasurementHandle;
 | 
			
		||||
      struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
      struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
 | 
			
		||||
      uint16_t heartRateMeasurementHandle;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,49 +9,42 @@ constexpr ble_uuid16_t ImmediateAlertService::immediateAlertServiceUuid;
 | 
			
		||||
constexpr ble_uuid16_t ImmediateAlertService::alertLevelUuid;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  int AlertLevelCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
 | 
			
		||||
    auto *immediateAlertService = static_cast<ImmediateAlertService *>(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);
 | 
			
		||||
    return immediateAlertService->OnAlertLevelChanged(conn_handle, attr_handle, ctxt);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const char* ToString(ImmediateAlertService::Levels level) {
 | 
			
		||||
    switch (level) {
 | 
			
		||||
      case ImmediateAlertService::Levels::NoAlert: return "Alert : None";
 | 
			
		||||
      case ImmediateAlertService::Levels::HighAlert: return "Alert : High";
 | 
			
		||||
      case ImmediateAlertService::Levels::MildAlert: return "Alert : Mild";
 | 
			
		||||
      default: return "";
 | 
			
		||||
      case ImmediateAlertService::Levels::NoAlert:
 | 
			
		||||
        return "Alert : None";
 | 
			
		||||
      case ImmediateAlertService::Levels::HighAlert:
 | 
			
		||||
        return "Alert : High";
 | 
			
		||||
      case ImmediateAlertService::Levels::MildAlert:
 | 
			
		||||
        return "Alert : Mild";
 | 
			
		||||
      default:
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask &systemTask,
 | 
			
		||||
                                             Pinetime::Controllers::NotificationManager ¬ificationManager) :
 | 
			
		||||
        systemTask{systemTask},
 | 
			
		||||
        notificationManager{notificationManager},
 | 
			
		||||
        characteristicDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &alertLevelUuid,
 | 
			
		||||
                        .access_cb = AlertLevelCallback,
 | 
			
		||||
                        .arg = this,
 | 
			
		||||
                        .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
 | 
			
		||||
                        .val_handle = &alertLevelHandle
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                }
 | 
			
		||||
        },
 | 
			
		||||
        serviceDefinition{
 | 
			
		||||
                {
 | 
			
		||||
                        /* Device Information Service */
 | 
			
		||||
                        .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
                        .uuid = (ble_uuid_t *) &immediateAlertServiceUuid,
 | 
			
		||||
                        .characteristics = characteristicDefinition
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                        0
 | 
			
		||||
                },
 | 
			
		||||
        }{
 | 
			
		||||
 | 
			
		||||
ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
                                             Pinetime::Controllers::NotificationManager& notificationManager)
 | 
			
		||||
  : systemTask {systemTask},
 | 
			
		||||
    notificationManager {notificationManager},
 | 
			
		||||
    characteristicDefinition {{.uuid = (ble_uuid_t*) &alertLevelUuid,
 | 
			
		||||
                               .access_cb = AlertLevelCallback,
 | 
			
		||||
                               .arg = this,
 | 
			
		||||
                               .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
 | 
			
		||||
                               .val_handle = &alertLevelHandle},
 | 
			
		||||
                              {0}},
 | 
			
		||||
    serviceDefinition {
 | 
			
		||||
      {/* Device Information Service */
 | 
			
		||||
       .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
       .uuid = (ble_uuid_t*) &immediateAlertServiceUuid,
 | 
			
		||||
       .characteristics = characteristicDefinition},
 | 
			
		||||
      {0},
 | 
			
		||||
    } {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ImmediateAlertService::Init() {
 | 
			
		||||
@ -63,9 +56,9 @@ void ImmediateAlertService::Init() {
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) {
 | 
			
		||||
  if(attributeHandle == alertLevelHandle) {
 | 
			
		||||
    if(context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
 | 
			
		||||
int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
 | 
			
		||||
  if (attributeHandle == alertLevelHandle) {
 | 
			
		||||
    if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
 | 
			
		||||
      auto alertLevel = static_cast<Levels>(context->om->om_data[0]);
 | 
			
		||||
      auto* alertString = ToString(alertLevel);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,39 +12,28 @@ namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class NotificationManager;
 | 
			
		||||
    class ImmediateAlertService {
 | 
			
		||||
      public:
 | 
			
		||||
        enum class Levels : uint8_t {
 | 
			
		||||
            NoAlert = 0,
 | 
			
		||||
            MildAlert = 1,
 | 
			
		||||
            HighAlert = 2
 | 
			
		||||
        };
 | 
			
		||||
    public:
 | 
			
		||||
      enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 };
 | 
			
		||||
 | 
			
		||||
        ImmediateAlertService(Pinetime::System::SystemTask &systemTask,
 | 
			
		||||
                              Pinetime::Controllers::NotificationManager ¬ificationManager);
 | 
			
		||||
        void Init();
 | 
			
		||||
        int OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
 | 
			
		||||
      ImmediateAlertService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager);
 | 
			
		||||
      void Init();
 | 
			
		||||
      int OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
        NotificationManager& notificationManager;
 | 
			
		||||
    private:
 | 
			
		||||
      Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
      NotificationManager& notificationManager;
 | 
			
		||||
 | 
			
		||||
        static constexpr uint16_t immediateAlertServiceId {0x1802};
 | 
			
		||||
        static constexpr uint16_t alertLevelId {0x2A06};
 | 
			
		||||
      static constexpr uint16_t immediateAlertServiceId {0x1802};
 | 
			
		||||
      static constexpr uint16_t alertLevelId {0x2A06};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t immediateAlertServiceUuid {
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = immediateAlertServiceId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t immediateAlertServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = immediateAlertServiceId};
 | 
			
		||||
 | 
			
		||||
        static constexpr ble_uuid16_t alertLevelUuid {
 | 
			
		||||
                .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
                .value = alertLevelId
 | 
			
		||||
        };
 | 
			
		||||
      static constexpr ble_uuid16_t alertLevelUuid {.u {.type = BLE_UUID_TYPE_16}, .value = alertLevelId};
 | 
			
		||||
 | 
			
		||||
        struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
        struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
      struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
      struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
 | 
			
		||||
        uint16_t alertLevelHandle;
 | 
			
		||||
      uint16_t alertLevelHandle;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,12 +18,12 @@
 | 
			
		||||
#include "MusicService.h"
 | 
			
		||||
#include "systemtask/SystemTask.h"
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
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);
 | 
			
		||||
  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[15] = msId[1];
 | 
			
		||||
 | 
			
		||||
@ -86,82 +86,51 @@ Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask &
 | 
			
		||||
  msShuffleCharUuid.value[13] = msShuffleCharId[1];
 | 
			
		||||
  msShuffleCharUuid.value[14] = msId[0];
 | 
			
		||||
  msShuffleCharUuid.value[15] = msId[1];
 | 
			
		||||
  
 | 
			
		||||
  characteristicDefinition[0] = {.uuid = (ble_uuid_t *) (&msEventCharUuid),
 | 
			
		||||
      .access_cb = MSCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags =  BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
      .val_handle = &eventHandle
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[1] = {.uuid = (ble_uuid_t *) (&msStatusCharUuid),
 | 
			
		||||
      .access_cb = MSCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[2] = {.uuid = (ble_uuid_t *) (&msTrackCharUuid),
 | 
			
		||||
      .access_cb = MSCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .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[4] = {.uuid = (ble_uuid_t *) (&msAlbumCharUuid),
 | 
			
		||||
      .access_cb = MSCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[5] = {.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),
 | 
			
		||||
      .access_cb = MSCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[7] = {.uuid = (ble_uuid_t *) (&msTotalLengthCharUuid),
 | 
			
		||||
      .access_cb = MSCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .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[0] = {.uuid = (ble_uuid_t*) (&msEventCharUuid),
 | 
			
		||||
                                 .access_cb = MSCallback,
 | 
			
		||||
                                 .arg = this,
 | 
			
		||||
                                 .flags = BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                                 .val_handle = &eventHandle};
 | 
			
		||||
  characteristicDefinition[1] = {
 | 
			
		||||
    .uuid = (ble_uuid_t*) (&msStatusCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
 | 
			
		||||
  characteristicDefinition[2] = {
 | 
			
		||||
    .uuid = (ble_uuid_t*) (&msTrackCharUuid), .access_cb = MSCallback, .arg = this, .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[4] = {
 | 
			
		||||
    .uuid = (ble_uuid_t*) (&msAlbumCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
 | 
			
		||||
  characteristicDefinition[5] = {
 | 
			
		||||
    .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),
 | 
			
		||||
                                 .access_cb = MSCallback,
 | 
			
		||||
                                 .arg = this,
 | 
			
		||||
                                 .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
 | 
			
		||||
  characteristicDefinition[7] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid),
 | 
			
		||||
                                 .access_cb = MSCallback,
 | 
			
		||||
                                 .arg = this,
 | 
			
		||||
                                 .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};
 | 
			
		||||
  
 | 
			
		||||
  serviceDefinition[0] = {
 | 
			
		||||
      .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
      .uuid = (ble_uuid_t *) &msUuid,
 | 
			
		||||
      .characteristics = characteristicDefinition
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &msUuid, .characteristics = characteristicDefinition};
 | 
			
		||||
  serviceDefinition[1] = {0};
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  artistName = "Waiting for";
 | 
			
		||||
  albumName = "";
 | 
			
		||||
  trackName = "track information..";
 | 
			
		||||
@ -177,41 +146,40 @@ void Pinetime::Controllers::MusicService::Init() {
 | 
			
		||||
  int res = 0;
 | 
			
		||||
  res = ble_gatts_count_cfg(serviceDefinition);
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  res = ble_gatts_add_svcs(serviceDefinition);
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                                                   struct ble_gatt_access_ctxt *ctxt) {
 | 
			
		||||
  
 | 
			
		||||
int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
 | 
			
		||||
 | 
			
		||||
  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
 | 
			
		||||
    size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
 | 
			
		||||
    uint8_t data[notifSize + 1];
 | 
			
		||||
    data[notifSize] = '\0';
 | 
			
		||||
    os_mbuf_copydata(ctxt->om, 0, notifSize, data);
 | 
			
		||||
    char *s = (char *) &data[0];
 | 
			
		||||
    if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &msArtistCharUuid) == 0) {
 | 
			
		||||
    char* s = (char*) &data[0];
 | 
			
		||||
    if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msArtistCharUuid) == 0) {
 | 
			
		||||
      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;
 | 
			
		||||
    } 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;
 | 
			
		||||
    } 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];
 | 
			
		||||
    } 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];
 | 
			
		||||
    } 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];
 | 
			
		||||
    } 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];
 | 
			
		||||
    } 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];
 | 
			
		||||
    } 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];
 | 
			
		||||
    } 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];
 | 
			
		||||
    } 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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -239,14 +207,14 @@ float Pinetime::Controllers::MusicService::getPlaybackSpeed() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  ble_gattc_notify_custom(connectionHandle, eventHandle, om);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -257,4 +225,3 @@ int Pinetime::Controllers::MusicService::getProgress() {
 | 
			
		||||
int Pinetime::Controllers::MusicService::getTrackLength() {
 | 
			
		||||
  return trackLength;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,40 +26,40 @@
 | 
			
		||||
#undef max
 | 
			
		||||
#undef min
 | 
			
		||||
 | 
			
		||||
//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}
 | 
			
		||||
// 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 }
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace System {
 | 
			
		||||
    class SystemTask;
 | 
			
		||||
  }
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    class MusicService {
 | 
			
		||||
    public:
 | 
			
		||||
      explicit MusicService(Pinetime::System::SystemTask &system);
 | 
			
		||||
      
 | 
			
		||||
      explicit MusicService(Pinetime::System::SystemTask& system);
 | 
			
		||||
 | 
			
		||||
      void Init();
 | 
			
		||||
      
 | 
			
		||||
      int OnCommand(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                    struct ble_gatt_access_ctxt *ctxt);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
 | 
			
		||||
 | 
			
		||||
      void event(char event);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      std::string getArtist();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      std::string getTrack();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      std::string getAlbum();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      int getProgress();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      int getTrackLength();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      float getPlaybackSpeed();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      bool isPlaying();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      static const char EVENT_MUSIC_OPEN = 0xe0;
 | 
			
		||||
      static const char EVENT_MUSIC_PLAY = 0x00;
 | 
			
		||||
      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_VOLUP = 0x05;
 | 
			
		||||
      static const char EVENT_MUSIC_VOLDOWN = 0x06;
 | 
			
		||||
      
 | 
			
		||||
      enum MusicStatus {
 | 
			
		||||
        NotPlaying = 0x00,
 | 
			
		||||
        Playing = 0x01
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      static constexpr uint8_t msId[2] = {0x00, 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 msRepeatCharId[2] = {0x0b, 0x00};
 | 
			
		||||
      static constexpr uint8_t msShuffleCharId[2] = {0x0c, 0x00};
 | 
			
		||||
      
 | 
			
		||||
      ble_uuid128_t msUuid{
 | 
			
		||||
          .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 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
 | 
			
		||||
      };
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      ble_uuid128_t msUuid {.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 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_svc_def serviceDefinition[2];
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      uint16_t eventHandle;
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      std::string artistName;
 | 
			
		||||
      std::string albumName;
 | 
			
		||||
      std::string trackName;
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      bool playing;
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      int trackProgress;
 | 
			
		||||
      int trackLength;
 | 
			
		||||
      int trackNumber;
 | 
			
		||||
      int tracksTotal;
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      float playbackSpeed;
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      bool repeat;
 | 
			
		||||
      bool shuffle;
 | 
			
		||||
      
 | 
			
		||||
      Pinetime::System::SystemTask &m_system;
 | 
			
		||||
 | 
			
		||||
      Pinetime::System::SystemTask& m_system;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -20,12 +20,12 @@
 | 
			
		||||
 | 
			
		||||
#include "systemtask/SystemTask.h"
 | 
			
		||||
 | 
			
		||||
int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
 | 
			
		||||
  auto navService = static_cast<Pinetime::Controllers::NavigationService *>(arg);
 | 
			
		||||
int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
 | 
			
		||||
  auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg);
 | 
			
		||||
  return navService->OnCommand(conn_handle, attr_handle, ctxt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask &system) : m_system(system) {
 | 
			
		||||
Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) {
 | 
			
		||||
  navUuid.value[14] = navId[0];
 | 
			
		||||
  navUuid.value[15] = navId[1];
 | 
			
		||||
 | 
			
		||||
@ -49,35 +49,25 @@ Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::Sy
 | 
			
		||||
  navProgressCharUuid.value[14] = navId[0];
 | 
			
		||||
  navProgressCharUuid.value[15] = navId[1];
 | 
			
		||||
 | 
			
		||||
  characteristicDefinition[0] = {.uuid = (ble_uuid_t *) (&navFlagCharUuid),
 | 
			
		||||
      .access_cb = NAVCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags =  BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[0] = {
 | 
			
		||||
    .uuid = (ble_uuid_t*) (&navFlagCharUuid), .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
 | 
			
		||||
 | 
			
		||||
  characteristicDefinition[1] = {.uuid = (ble_uuid_t *) (&navNarrativeCharUuid),
 | 
			
		||||
      .access_cb = NAVCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[2] = {.uuid = (ble_uuid_t *) (&navManDistCharUuid),
 | 
			
		||||
      .access_cb = NAVCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[3] = {.uuid = (ble_uuid_t *) (&navProgressCharUuid),
 | 
			
		||||
      .access_cb = NAVCallback,
 | 
			
		||||
      .arg = this,
 | 
			
		||||
      .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
 | 
			
		||||
  };
 | 
			
		||||
  characteristicDefinition[1] = {.uuid = (ble_uuid_t*) (&navNarrativeCharUuid),
 | 
			
		||||
                                 .access_cb = NAVCallback,
 | 
			
		||||
                                 .arg = this,
 | 
			
		||||
                                 .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
 | 
			
		||||
  characteristicDefinition[2] = {.uuid = (ble_uuid_t*) (&navManDistCharUuid),
 | 
			
		||||
                                 .access_cb = NAVCallback,
 | 
			
		||||
                                 .arg = this,
 | 
			
		||||
                                 .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
 | 
			
		||||
  characteristicDefinition[3] = {.uuid = (ble_uuid_t*) (&navProgressCharUuid),
 | 
			
		||||
                                 .access_cb = NAVCallback,
 | 
			
		||||
                                 .arg = this,
 | 
			
		||||
                                 .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
 | 
			
		||||
 | 
			
		||||
  characteristicDefinition[4] = {0};
 | 
			
		||||
 | 
			
		||||
  serviceDefinition[0] = {
 | 
			
		||||
      .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
      .uuid = (ble_uuid_t *) &navUuid,
 | 
			
		||||
      .characteristics = characteristicDefinition
 | 
			
		||||
  };
 | 
			
		||||
  serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &navUuid, .characteristics = characteristicDefinition};
 | 
			
		||||
  serviceDefinition[1] = {0};
 | 
			
		||||
 | 
			
		||||
  m_progress = 0;
 | 
			
		||||
@ -92,45 +82,39 @@ void Pinetime::Controllers::NavigationService::Init() {
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                                                   struct ble_gatt_access_ctxt *ctxt) {
 | 
			
		||||
int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
 | 
			
		||||
 | 
			
		||||
  if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
 | 
			
		||||
    size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
 | 
			
		||||
    uint8_t data[notifSize + 1];
 | 
			
		||||
    data[notifSize] = '\0';
 | 
			
		||||
    os_mbuf_copydata(ctxt->om, 0, notifSize, data);
 | 
			
		||||
    char *s = (char *) &data[0];
 | 
			
		||||
    if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navFlagCharUuid) == 0) {
 | 
			
		||||
    char* s = (char*) &data[0];
 | 
			
		||||
    if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navFlagCharUuid) == 0) {
 | 
			
		||||
      m_flag = s;
 | 
			
		||||
    } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navNarrativeCharUuid) == 0) {
 | 
			
		||||
    } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navNarrativeCharUuid) == 0) {
 | 
			
		||||
      m_narrative = s;
 | 
			
		||||
    } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navManDistCharUuid) == 0) {
 | 
			
		||||
    } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navManDistCharUuid) == 0) {
 | 
			
		||||
      m_manDist = s;
 | 
			
		||||
    } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t *) &navProgressCharUuid) == 0) {
 | 
			
		||||
    } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &navProgressCharUuid) == 0) {
 | 
			
		||||
      m_progress = data[0];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Pinetime::Controllers::NavigationService::getFlag()
 | 
			
		||||
{
 | 
			
		||||
    return m_flag;
 | 
			
		||||
std::string Pinetime::Controllers::NavigationService::getFlag() {
 | 
			
		||||
  return m_flag;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Pinetime::Controllers::NavigationService::getNarrative()
 | 
			
		||||
{
 | 
			
		||||
    return m_narrative;
 | 
			
		||||
std::string Pinetime::Controllers::NavigationService::getNarrative() {
 | 
			
		||||
  return m_narrative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Pinetime::Controllers::NavigationService::getManDist()
 | 
			
		||||
{
 | 
			
		||||
    return m_manDist;
 | 
			
		||||
std::string Pinetime::Controllers::NavigationService::getManDist() {
 | 
			
		||||
  return m_manDist;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Pinetime::Controllers::NavigationService::getProgress()
 | 
			
		||||
{
 | 
			
		||||
    return m_progress;
 | 
			
		||||
int Pinetime::Controllers::NavigationService::getProgress() {
 | 
			
		||||
  return m_progress;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,9 @@
 | 
			
		||||
#undef max
 | 
			
		||||
#undef min
 | 
			
		||||
 | 
			
		||||
//c7e60000-78fc-48fe-8e23-433b3a1942d0
 | 
			
		||||
#define NAVIGATION_SERVICE_UUID_BASE {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00}
 | 
			
		||||
// c7e60000-78fc-48fe-8e23-433b3a1942d0
 | 
			
		||||
#define NAVIGATION_SERVICE_UUID_BASE                                                                                                       \
 | 
			
		||||
  { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 }
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace System {
 | 
			
		||||
@ -37,12 +38,11 @@ namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
    class NavigationService {
 | 
			
		||||
    public:
 | 
			
		||||
      explicit NavigationService(Pinetime::System::SystemTask &system);
 | 
			
		||||
      explicit NavigationService(Pinetime::System::SystemTask& system);
 | 
			
		||||
 | 
			
		||||
      void Init();
 | 
			
		||||
 | 
			
		||||
      int OnCommand(uint16_t conn_handle, uint16_t attr_handle,
 | 
			
		||||
                    struct ble_gatt_access_ctxt *ctxt);
 | 
			
		||||
      int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
 | 
			
		||||
 | 
			
		||||
      std::string getFlag();
 | 
			
		||||
 | 
			
		||||
@ -59,27 +59,12 @@ namespace Pinetime {
 | 
			
		||||
      static constexpr uint8_t navManDistCharId[2] = {0x03, 0x00};
 | 
			
		||||
      static constexpr uint8_t navProgressCharId[2] = {0x04, 0x00};
 | 
			
		||||
 | 
			
		||||
      ble_uuid128_t navUuid{
 | 
			
		||||
          .u = {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
          .value = NAVIGATION_SERVICE_UUID_BASE
 | 
			
		||||
      };
 | 
			
		||||
      ble_uuid128_t navUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
 | 
			
		||||
 | 
			
		||||
      ble_uuid128_t navFlagCharUuid{
 | 
			
		||||
          .u = {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
          .value = NAVIGATION_SERVICE_UUID_BASE
 | 
			
		||||
      };
 | 
			
		||||
      ble_uuid128_t navNarrativeCharUuid{
 | 
			
		||||
          .u = {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
          .value = NAVIGATION_SERVICE_UUID_BASE
 | 
			
		||||
      };
 | 
			
		||||
      ble_uuid128_t navManDistCharUuid{
 | 
			
		||||
          .u = {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
          .value = NAVIGATION_SERVICE_UUID_BASE
 | 
			
		||||
      };
 | 
			
		||||
      ble_uuid128_t navProgressCharUuid{
 | 
			
		||||
          .u = {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
          .value = NAVIGATION_SERVICE_UUID_BASE
 | 
			
		||||
      };
 | 
			
		||||
      ble_uuid128_t navFlagCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
 | 
			
		||||
      ble_uuid128_t navNarrativeCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
 | 
			
		||||
      ble_uuid128_t navManDistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
 | 
			
		||||
      ble_uuid128_t navProgressCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = NAVIGATION_SERVICE_UUID_BASE};
 | 
			
		||||
 | 
			
		||||
      struct ble_gatt_chr_def characteristicDefinition[5];
 | 
			
		||||
      struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
@ -89,8 +74,7 @@ namespace Pinetime {
 | 
			
		||||
      std::string m_manDist;
 | 
			
		||||
      int m_progress;
 | 
			
		||||
 | 
			
		||||
      Pinetime::System::SystemTask &m_system;
 | 
			
		||||
      Pinetime::System::SystemTask& m_system;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,36 +19,37 @@ using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
                                   Pinetime::Controllers::Ble& bleController,
 | 
			
		||||
        DateTime& dateTimeController,
 | 
			
		||||
        Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
        Controllers::Battery& batteryController,
 | 
			
		||||
        Pinetime::Drivers::SpiNorFlash& spiNorFlash,
 | 
			
		||||
        Controllers::HeartRateController& heartRateController) :
 | 
			
		||||
        systemTask{systemTask},
 | 
			
		||||
        bleController{bleController},
 | 
			
		||||
        dateTimeController{dateTimeController},
 | 
			
		||||
        notificationManager{notificationManager},
 | 
			
		||||
        spiNorFlash{spiNorFlash},
 | 
			
		||||
        dfuService{systemTask, bleController, spiNorFlash},
 | 
			
		||||
        currentTimeClient{dateTimeController},
 | 
			
		||||
        anService{systemTask, notificationManager},
 | 
			
		||||
        alertNotificationClient{systemTask, notificationManager},
 | 
			
		||||
        currentTimeService{dateTimeController},
 | 
			
		||||
        musicService{systemTask},
 | 
			
		||||
        navService{systemTask},
 | 
			
		||||
        batteryInformationService{batteryController},
 | 
			
		||||
        immediateAlertService{systemTask, notificationManager},
 | 
			
		||||
        heartRateService{systemTask, heartRateController},
 | 
			
		||||
        serviceDiscovery({¤tTimeClient, &alertNotificationClient}) {
 | 
			
		||||
                                   DateTime& dateTimeController,
 | 
			
		||||
                                   Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                                   Controllers::Battery& batteryController,
 | 
			
		||||
                                   Pinetime::Drivers::SpiNorFlash& spiNorFlash,
 | 
			
		||||
                                   Controllers::HeartRateController& heartRateController)
 | 
			
		||||
  : systemTask {systemTask},
 | 
			
		||||
    bleController {bleController},
 | 
			
		||||
    dateTimeController {dateTimeController},
 | 
			
		||||
    notificationManager {notificationManager},
 | 
			
		||||
    spiNorFlash {spiNorFlash},
 | 
			
		||||
    dfuService {systemTask, bleController, spiNorFlash},
 | 
			
		||||
    currentTimeClient {dateTimeController},
 | 
			
		||||
    anService {systemTask, notificationManager},
 | 
			
		||||
    alertNotificationClient {systemTask, notificationManager},
 | 
			
		||||
    currentTimeService {dateTimeController},
 | 
			
		||||
    musicService {systemTask},
 | 
			
		||||
    navService {systemTask},
 | 
			
		||||
    batteryInformationService {batteryController},
 | 
			
		||||
    immediateAlertService {systemTask, notificationManager},
 | 
			
		||||
    heartRateService {systemTask, heartRateController},
 | 
			
		||||
    serviceDiscovery({¤tTimeClient, &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);
 | 
			
		||||
  return nimbleController->OnGAPEvent(event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NimbleController::Init() {
 | 
			
		||||
  while (!ble_hs_synced()) {}
 | 
			
		||||
  while (!ble_hs_synced()) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ble_svc_gap_init();
 | 
			
		||||
  ble_svc_gatt_init();
 | 
			
		||||
@ -81,7 +82,8 @@ void NimbleController::Init() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
@ -101,29 +103,27 @@ void NimbleController::StartAdvertising() {
 | 
			
		||||
  adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
 | 
			
		||||
  adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
 | 
			
		||||
 | 
			
		||||
  fields.flags = BLE_HS_ADV_F_DISC_GEN |
 | 
			
		||||
                 BLE_HS_ADV_F_BREDR_UNSUP;
 | 
			
		||||
//  fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE(
 | 
			
		||||
//          0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 | 
			
		||||
//          0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
 | 
			
		||||
  fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
 | 
			
		||||
  //  fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE(
 | 
			
		||||
  //          0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 | 
			
		||||
  //          0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
 | 
			
		||||
  fields.uuids128 = &dfuServiceUuid;
 | 
			
		||||
  fields.num_uuids128 = 1;
 | 
			
		||||
  fields.uuids128_is_complete = 1;
 | 
			
		||||
  fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
 | 
			
		||||
 | 
			
		||||
  rsp_fields.name = (uint8_t *)deviceName;
 | 
			
		||||
  rsp_fields.name = (uint8_t*) deviceName;
 | 
			
		||||
  rsp_fields.name_len = strlen(deviceName);
 | 
			
		||||
  rsp_fields.name_is_complete = 1;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
//  ASSERT(res == 0);
 | 
			
		||||
  //  ASSERT(res == 0);
 | 
			
		||||
 | 
			
		||||
  ble_gap_adv_start(addrType, NULL, 180000,
 | 
			
		||||
                          &adv_params, GAPEventCallback, this);
 | 
			
		||||
//  ASSERT(res == 0);// TODO I've disabled these ASSERT as they sometime asserts and reset the mcu.
 | 
			
		||||
  ble_gap_adv_start(addrType, NULL, 180000, &adv_params, GAPEventCallback, this);
 | 
			
		||||
  //  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
 | 
			
		||||
  // that prevent the advertising from restarting reliably.
 | 
			
		||||
  // 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.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int NimbleController::OnGAPEvent(ble_gap_event *event) {
 | 
			
		||||
int NimbleController::OnGAPEvent(ble_gap_event* event) {
 | 
			
		||||
  switch (event->type) {
 | 
			
		||||
    case BLE_GAP_EVENT_ADV_COMPLETE:
 | 
			
		||||
      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
 | 
			
		||||
@ -141,8 +141,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
 | 
			
		||||
      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONNECT");
 | 
			
		||||
 | 
			
		||||
      /* A new connection was established or a connection attempt failed. */
 | 
			
		||||
      NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed",
 | 
			
		||||
                   event->connect.status);
 | 
			
		||||
      NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed", event->connect.status);
 | 
			
		||||
 | 
			
		||||
      if (event->connect.status != 0) {
 | 
			
		||||
        /* Connection failed; resume advertising. */
 | 
			
		||||
@ -154,8 +153,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
 | 
			
		||||
        connectionHandle = event->connect.conn_handle;
 | 
			
		||||
        // Service discovery is deffered via systemtask
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
      break;
 | 
			
		||||
    } break;
 | 
			
		||||
    case BLE_GAP_EVENT_DISCONNECT:
 | 
			
		||||
      NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_DISCONNECT");
 | 
			
		||||
      NRF_LOG_INFO("disconnect; reason=%d", event->disconnect.reason);
 | 
			
		||||
@ -178,19 +176,16 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
 | 
			
		||||
      return 0;
 | 
			
		||||
    case BLE_GAP_EVENT_SUBSCRIBE:
 | 
			
		||||
      NRF_LOG_INFO("subscribe event; conn_handle=%d attr_handle=%d "
 | 
			
		||||
                        "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
 | 
			
		||||
                  event->subscribe.conn_handle,
 | 
			
		||||
                  event->subscribe.attr_handle,
 | 
			
		||||
                  event->subscribe.reason,
 | 
			
		||||
                  event->subscribe.prev_notify,
 | 
			
		||||
                  event->subscribe.cur_notify,
 | 
			
		||||
                  event->subscribe.prev_indicate);
 | 
			
		||||
                   "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
 | 
			
		||||
                   event->subscribe.conn_handle,
 | 
			
		||||
                   event->subscribe.attr_handle,
 | 
			
		||||
                   event->subscribe.reason,
 | 
			
		||||
                   event->subscribe.prev_notify,
 | 
			
		||||
                   event->subscribe.cur_notify,
 | 
			
		||||
                   event->subscribe.prev_indicate);
 | 
			
		||||
      return 0;
 | 
			
		||||
    case BLE_GAP_EVENT_MTU:
 | 
			
		||||
      NRF_LOG_INFO("mtu update event; conn_handle=%d cid=%d mtu=%d\n",
 | 
			
		||||
                  event->mtu.conn_handle,
 | 
			
		||||
                  event->mtu.channel_id,
 | 
			
		||||
                  event->mtu.value);
 | 
			
		||||
      NRF_LOG_INFO("mtu update event; conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
    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 "
 | 
			
		||||
                   "attr_len=%d",
 | 
			
		||||
                   event->notify_rx.indication ?
 | 
			
		||||
                   "indication" :
 | 
			
		||||
                   "notification",
 | 
			
		||||
                   event->notify_rx.indication ? "indication" : "notification",
 | 
			
		||||
                   event->notify_rx.conn_handle,
 | 
			
		||||
                   event->notify_rx.attr_handle,
 | 
			
		||||
                   notifSize);
 | 
			
		||||
@ -229,7 +222,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) {
 | 
			
		||||
      /* Attribute data is contained in event->notify_rx.attr_data. */
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
//      NRF_LOG_INFO("Advertising event : %d", event->type);
 | 
			
		||||
      //      NRF_LOG_INFO("Advertising event : %d", event->type);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
@ -239,8 +232,6 @@ void NimbleController::StartDiscovery() {
 | 
			
		||||
  serviceDiscovery.StartDiscovery(connectionHandle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint16_t NimbleController::connHandle() {
 | 
			
		||||
    return connectionHandle;
 | 
			
		||||
  return connectionHandle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,62 +36,69 @@ namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
    class NimbleController {
 | 
			
		||||
 | 
			
		||||
      public:
 | 
			
		||||
        NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController,
 | 
			
		||||
                DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash,
 | 
			
		||||
                Controllers::HeartRateController& heartRateController);
 | 
			
		||||
        void Init();
 | 
			
		||||
        void StartAdvertising();
 | 
			
		||||
        int OnGAPEvent(ble_gap_event *event);
 | 
			
		||||
    public:
 | 
			
		||||
      NimbleController(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
                       Pinetime::Controllers::Ble& bleController,
 | 
			
		||||
                       DateTime& dateTimeController,
 | 
			
		||||
                       Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                       Controllers::Battery& batteryController,
 | 
			
		||||
                       Pinetime::Drivers::SpiNorFlash& spiNorFlash,
 | 
			
		||||
                       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 OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                              const ble_gatt_chr *characteristic);
 | 
			
		||||
        int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                              const ble_gatt_chr *characteristic);
 | 
			
		||||
        int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
 | 
			
		||||
        int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
 | 
			
		||||
                                                  uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
 | 
			
		||||
      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, const ble_gatt_chr* characteristic);
 | 
			
		||||
      int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
 | 
			
		||||
      int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
 | 
			
		||||
      int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
 | 
			
		||||
                                                const ble_gatt_error* error,
 | 
			
		||||
                                                uint16_t characteristicValueHandle,
 | 
			
		||||
                                                const ble_gatt_dsc* descriptor);
 | 
			
		||||
 | 
			
		||||
        void StartDiscovery();
 | 
			
		||||
      void StartDiscovery();
 | 
			
		||||
 | 
			
		||||
        Pinetime::Controllers::MusicService& music() {return musicService;};
 | 
			
		||||
        Pinetime::Controllers::NavigationService& navigation() {return navService;};
 | 
			
		||||
        Pinetime::Controllers::AlertNotificationService& alertService() {return anService;};
 | 
			
		||||
      Pinetime::Controllers::MusicService& music() {
 | 
			
		||||
        return musicService;
 | 
			
		||||
      };
 | 
			
		||||
      Pinetime::Controllers::NavigationService& navigation() {
 | 
			
		||||
        return navService;
 | 
			
		||||
      };
 | 
			
		||||
      Pinetime::Controllers::AlertNotificationService& alertService() {
 | 
			
		||||
        return anService;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        uint16_t connHandle();
 | 
			
		||||
      uint16_t connHandle();
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        static constexpr const char* deviceName = "InfiniTime";
 | 
			
		||||
        Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
        Pinetime::Controllers::Ble& bleController;
 | 
			
		||||
        DateTime& dateTimeController;
 | 
			
		||||
        Pinetime::Controllers::NotificationManager& notificationManager;
 | 
			
		||||
        Pinetime::Drivers::SpiNorFlash& spiNorFlash;
 | 
			
		||||
        Pinetime::Controllers::DfuService dfuService;
 | 
			
		||||
    private:
 | 
			
		||||
      static constexpr const char* deviceName = "InfiniTime";
 | 
			
		||||
      Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
      Pinetime::Controllers::Ble& bleController;
 | 
			
		||||
      DateTime& dateTimeController;
 | 
			
		||||
      Pinetime::Controllers::NotificationManager& notificationManager;
 | 
			
		||||
      Pinetime::Drivers::SpiNorFlash& spiNorFlash;
 | 
			
		||||
      Pinetime::Controllers::DfuService dfuService;
 | 
			
		||||
 | 
			
		||||
        DeviceInformationService deviceInformationService;
 | 
			
		||||
        CurrentTimeClient currentTimeClient;
 | 
			
		||||
        AlertNotificationService anService;
 | 
			
		||||
        AlertNotificationClient alertNotificationClient;
 | 
			
		||||
        CurrentTimeService currentTimeService;
 | 
			
		||||
        MusicService musicService;
 | 
			
		||||
        NavigationService navService;
 | 
			
		||||
        BatteryInformationService batteryInformationService;
 | 
			
		||||
        ImmediateAlertService immediateAlertService;
 | 
			
		||||
        HeartRateService heartRateService;
 | 
			
		||||
      DeviceInformationService deviceInformationService;
 | 
			
		||||
      CurrentTimeClient currentTimeClient;
 | 
			
		||||
      AlertNotificationService anService;
 | 
			
		||||
      AlertNotificationClient alertNotificationClient;
 | 
			
		||||
      CurrentTimeService currentTimeService;
 | 
			
		||||
      MusicService musicService;
 | 
			
		||||
      NavigationService navService;
 | 
			
		||||
      BatteryInformationService batteryInformationService;
 | 
			
		||||
      ImmediateAlertService immediateAlertService;
 | 
			
		||||
      HeartRateService heartRateService;
 | 
			
		||||
 | 
			
		||||
        uint8_t addrType; // 1 = Random, 0 = PUBLIC
 | 
			
		||||
        uint16_t connectionHandle = 0;
 | 
			
		||||
      uint8_t addrType; // 1 = Random, 0 = PUBLIC
 | 
			
		||||
      uint16_t connectionHandle = 0;
 | 
			
		||||
 | 
			
		||||
        ble_uuid128_t dfuServiceUuid {
 | 
			
		||||
                .u { .type = BLE_UUID_TYPE_128},
 | 
			
		||||
                .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
 | 
			
		||||
                          0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}
 | 
			
		||||
        };
 | 
			
		||||
      ble_uuid128_t dfuServiceUuid {
 | 
			
		||||
        .u {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
        .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
 | 
			
		||||
 | 
			
		||||
        ServiceDiscovery serviceDiscovery;
 | 
			
		||||
      ServiceDiscovery serviceDiscovery;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,15 +6,15 @@ using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
constexpr uint8_t NotificationManager::MessageSize;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NotificationManager::Push(NotificationManager::Notification &¬if) {
 | 
			
		||||
void NotificationManager::Push(NotificationManager::Notification&& notif) {
 | 
			
		||||
  notif.id = GetNextId();
 | 
			
		||||
  notif.valid = true;
 | 
			
		||||
  notifications[writeIndex] = std::move(notif);
 | 
			
		||||
  writeIndex = (writeIndex + 1 < TotalNbNotifications) ? writeIndex + 1 : 0;
 | 
			
		||||
  if(!empty)
 | 
			
		||||
  if (!empty)
 | 
			
		||||
    readIndex = (readIndex + 1 < TotalNbNotifications) ? readIndex + 1 : 0;
 | 
			
		||||
  else empty = false;
 | 
			
		||||
  else
 | 
			
		||||
    empty = false;
 | 
			
		||||
 | 
			
		||||
  newNotification = true;
 | 
			
		||||
}
 | 
			
		||||
@ -30,40 +30,48 @@ NotificationManager::Notification::Id NotificationManager::GetNextId() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;});
 | 
			
		||||
  if(currentIterator == notifications.end() || currentIterator->id != id) return Notification{};
 | 
			
		||||
  auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n) {
 | 
			
		||||
    return n.valid && n.id == id;
 | 
			
		||||
  });
 | 
			
		||||
  if (currentIterator == notifications.end() || currentIterator->id != id)
 | 
			
		||||
    return Notification {};
 | 
			
		||||
 | 
			
		||||
  auto& lastNotification = notifications[readIndex];
 | 
			
		||||
 | 
			
		||||
  NotificationManager::Notification result;
 | 
			
		||||
 | 
			
		||||
  if(currentIterator == (notifications.end()-1))
 | 
			
		||||
  if (currentIterator == (notifications.end() - 1))
 | 
			
		||||
    result = *(notifications.begin());
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;});
 | 
			
		||||
  if(currentIterator == notifications.end() || currentIterator->id != id) return Notification{};
 | 
			
		||||
  auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n) {
 | 
			
		||||
    return n.valid && n.id == id;
 | 
			
		||||
  });
 | 
			
		||||
  if (currentIterator == notifications.end() || currentIterator->id != id)
 | 
			
		||||
    return Notification {};
 | 
			
		||||
 | 
			
		||||
  auto& lastNotification = notifications[readIndex];
 | 
			
		||||
 | 
			
		||||
  NotificationManager::Notification result;
 | 
			
		||||
 | 
			
		||||
  if(currentIterator == notifications.begin())
 | 
			
		||||
    result = *(notifications.end()-1);
 | 
			
		||||
  if (currentIterator == notifications.begin())
 | 
			
		||||
    result = *(notifications.end() - 1);
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -76,7 +84,7 @@ bool NotificationManager::IsVibrationEnabled() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NotificationManager::ToggleVibrations() {
 | 
			
		||||
  vibrationEnabled = !vibrationEnabled;  
 | 
			
		||||
  vibrationEnabled = !vibrationEnabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NotificationManager::ClearNewNotificationFlag() {
 | 
			
		||||
@ -84,21 +92,23 @@ bool NotificationManager::ClearNewNotificationFlag() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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* itField = std::find(message.begin(), message.begin()+size-1, '\0');
 | 
			
		||||
  if(itField != message.begin()+size-1) {
 | 
			
		||||
    const char* ptr = (itField)+1;
 | 
			
		||||
  const char* itField = std::find(message.begin(), message.begin() + size - 1, '\0');
 | 
			
		||||
  if (itField != message.begin() + size - 1) {
 | 
			
		||||
    const char* ptr = (itField) + 1;
 | 
			
		||||
    return ptr;
 | 
			
		||||
  }
 | 
			
		||||
  return const_cast<char*>(message.data());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* NotificationManager::Notification::Title() const {
 | 
			
		||||
  const char * itField = std::find(message.begin(), message.begin()+size-1, '\0');
 | 
			
		||||
  if(itField != message.begin()+size-1) {
 | 
			
		||||
  const char* itField = std::find(message.begin(), message.begin() + size - 1, '\0');
 | 
			
		||||
  if (itField != message.begin() + size - 1) {
 | 
			
		||||
    return message.data();
 | 
			
		||||
  }
 | 
			
		||||
  return {};
 | 
			
		||||
 | 
			
		||||
@ -8,23 +8,35 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class NotificationManager {
 | 
			
		||||
      public:
 | 
			
		||||
        enum class Categories {Unknown, SimpleAlert, Email, News, IncomingCall, MissedCall, Sms, VoiceMail, Schedule, HighProriotyAlert, InstantMessage };
 | 
			
		||||
        static constexpr uint8_t MessageSize{100};
 | 
			
		||||
    public:
 | 
			
		||||
      enum class Categories {
 | 
			
		||||
        Unknown,
 | 
			
		||||
        SimpleAlert,
 | 
			
		||||
        Email,
 | 
			
		||||
        News,
 | 
			
		||||
        IncomingCall,
 | 
			
		||||
        MissedCall,
 | 
			
		||||
        Sms,
 | 
			
		||||
        VoiceMail,
 | 
			
		||||
        Schedule,
 | 
			
		||||
        HighProriotyAlert,
 | 
			
		||||
        InstantMessage
 | 
			
		||||
      };
 | 
			
		||||
      static constexpr uint8_t MessageSize {100};
 | 
			
		||||
 | 
			
		||||
        struct Notification {
 | 
			
		||||
          using Id = uint8_t;
 | 
			
		||||
          Id id;
 | 
			
		||||
          bool valid = false;
 | 
			
		||||
          uint8_t index;
 | 
			
		||||
          uint8_t size;
 | 
			
		||||
          std::array<char, MessageSize+1> message;
 | 
			
		||||
          Categories category = Categories::Unknown;
 | 
			
		||||
      struct Notification {
 | 
			
		||||
        using Id = uint8_t;
 | 
			
		||||
        Id id;
 | 
			
		||||
        bool valid = false;
 | 
			
		||||
        uint8_t index;
 | 
			
		||||
        uint8_t size;
 | 
			
		||||
        std::array<char, MessageSize + 1> message;
 | 
			
		||||
        Categories category = Categories::Unknown;
 | 
			
		||||
 | 
			
		||||
          const char* Message() const;
 | 
			
		||||
          const char* Title() const;
 | 
			
		||||
        };
 | 
			
		||||
        Notification::Id nextId {0};
 | 
			
		||||
        const char* Message() const;
 | 
			
		||||
        const char* Title() const;
 | 
			
		||||
      };
 | 
			
		||||
      Notification::Id nextId {0};
 | 
			
		||||
 | 
			
		||||
      void Push(Notification&& notif);
 | 
			
		||||
      Notification GetLastNotification();
 | 
			
		||||
@ -35,18 +47,20 @@ namespace Pinetime {
 | 
			
		||||
      bool IsVibrationEnabled();
 | 
			
		||||
      void ToggleVibrations();
 | 
			
		||||
 | 
			
		||||
      static constexpr size_t MaximumMessageSize() { return MessageSize; };
 | 
			
		||||
      static constexpr size_t MaximumMessageSize() {
 | 
			
		||||
        return MessageSize;
 | 
			
		||||
      };
 | 
			
		||||
      size_t NbNotifications() const;
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        Notification::Id GetNextId();
 | 
			
		||||
        static constexpr uint8_t TotalNbNotifications = 5;
 | 
			
		||||
        std::array<Notification, TotalNbNotifications> notifications;
 | 
			
		||||
        uint8_t readIndex = 0;
 | 
			
		||||
        uint8_t writeIndex = 0;
 | 
			
		||||
        bool empty = true;
 | 
			
		||||
        std::atomic<bool> newNotification{false};
 | 
			
		||||
        bool vibrationEnabled = true;
 | 
			
		||||
    private:
 | 
			
		||||
      Notification::Id GetNextId();
 | 
			
		||||
      static constexpr uint8_t TotalNbNotifications = 5;
 | 
			
		||||
      std::array<Notification, TotalNbNotifications> notifications;
 | 
			
		||||
      uint8_t readIndex = 0;
 | 
			
		||||
      uint8_t writeIndex = 0;
 | 
			
		||||
      bool empty = true;
 | 
			
		||||
      std::atomic<bool> newNotification {false};
 | 
			
		||||
      bool vibrationEnabled = true;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -4,8 +4,7 @@
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
@ -16,7 +15,7 @@ void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) {
 | 
			
		||||
 | 
			
		||||
void ServiceDiscovery::OnServiceDiscovered(uint16_t connectionHandle) {
 | 
			
		||||
  clientIterator++;
 | 
			
		||||
  if(clientIterator != clients.end()) {
 | 
			
		||||
  if (clientIterator != clients.end()) {
 | 
			
		||||
    DiscoverNextService(connectionHandle);
 | 
			
		||||
  } else {
 | 
			
		||||
    NRF_LOG_INFO("End of service discovery");
 | 
			
		||||
@ -26,7 +25,7 @@ void ServiceDiscovery::OnServiceDiscovered(uint16_t connectionHandle) {
 | 
			
		||||
void ServiceDiscovery::DiscoverNextService(uint16_t connectionHandle) {
 | 
			
		||||
  NRF_LOG_INFO("[Discovery] Discover next service");
 | 
			
		||||
 | 
			
		||||
  auto discoverNextService = [this](uint16_t connectionHandle){
 | 
			
		||||
  auto discoverNextService = [this](uint16_t connectionHandle) {
 | 
			
		||||
    this->OnServiceDiscovered(connectionHandle);
 | 
			
		||||
  };
 | 
			
		||||
  (*clientIterator)->Discover(connectionHandle, discoverNextService);
 | 
			
		||||
 | 
			
		||||
@ -8,17 +8,16 @@ namespace Pinetime {
 | 
			
		||||
    class BleClient;
 | 
			
		||||
 | 
			
		||||
    class ServiceDiscovery {
 | 
			
		||||
      public:
 | 
			
		||||
        ServiceDiscovery(std::array<BleClient*, 2>&& bleClients);
 | 
			
		||||
    public:
 | 
			
		||||
      ServiceDiscovery(std::array<BleClient*, 2>&& bleClients);
 | 
			
		||||
 | 
			
		||||
        void StartDiscovery(uint16_t connectionHandle);
 | 
			
		||||
      void StartDiscovery(uint16_t connectionHandle);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        BleClient** clientIterator;
 | 
			
		||||
        std::array<BleClient*, 2> clients;
 | 
			
		||||
        void OnServiceDiscovered(uint16_t connectionHandle);
 | 
			
		||||
        void DiscoverNextService(uint16_t connectionHandle);
 | 
			
		||||
    private:
 | 
			
		||||
      BleClient** clientIterator;
 | 
			
		||||
      std::array<BleClient*, 2> clients;
 | 
			
		||||
      void OnServiceDiscovered(uint16_t connectionHandle);
 | 
			
		||||
      void DiscoverNextService(uint16_t connectionHandle);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void BrightnessController::Init() {
 | 
			
		||||
  nrf_gpio_cfg_output(pinLcdBacklight1);
 | 
			
		||||
  nrf_gpio_cfg_output(pinLcdBacklight2);
 | 
			
		||||
@ -14,7 +13,7 @@ void BrightnessController::Init() {
 | 
			
		||||
 | 
			
		||||
void BrightnessController::Set(BrightnessController::Levels level) {
 | 
			
		||||
  this->level = level;
 | 
			
		||||
  switch(level) {
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    default:
 | 
			
		||||
    case Levels::High:
 | 
			
		||||
      nrf_gpio_pin_clear(pinLcdBacklight1);
 | 
			
		||||
@ -40,20 +39,34 @@ void BrightnessController::Set(BrightnessController::Levels level) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BrightnessController::Lower() {
 | 
			
		||||
  switch(level) {
 | 
			
		||||
    case Levels::High: Set(Levels::Medium); break;
 | 
			
		||||
    case Levels::Medium: Set(Levels::Low); break;
 | 
			
		||||
    case Levels::Low: Set(Levels::Off); break;
 | 
			
		||||
    default: break;
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    case Levels::High:
 | 
			
		||||
      Set(Levels::Medium);
 | 
			
		||||
      break;
 | 
			
		||||
    case Levels::Medium:
 | 
			
		||||
      Set(Levels::Low);
 | 
			
		||||
      break;
 | 
			
		||||
    case Levels::Low:
 | 
			
		||||
      Set(Levels::Off);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BrightnessController::Higher() {
 | 
			
		||||
  switch(level) {
 | 
			
		||||
    case Levels::Off: Set(Levels::Low); break;
 | 
			
		||||
    case Levels::Low: Set(Levels::Medium); break;
 | 
			
		||||
    case Levels::Medium: Set(Levels::High); break;
 | 
			
		||||
    default: break;
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    case Levels::Off:
 | 
			
		||||
      Set(Levels::Low);
 | 
			
		||||
      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() {
 | 
			
		||||
  switch(level) {
 | 
			
		||||
    case Levels::Low: Set(Levels::Medium); break;
 | 
			
		||||
    case Levels::Medium: Set(Levels::High); break;
 | 
			
		||||
    case Levels::High: Set(Levels::Low); break;
 | 
			
		||||
    default: break;
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    case Levels::Low:
 | 
			
		||||
      Set(Levels::Medium);
 | 
			
		||||
      break;
 | 
			
		||||
    case Levels::Medium:
 | 
			
		||||
      Set(Levels::High);
 | 
			
		||||
      break;
 | 
			
		||||
    case Levels::High:
 | 
			
		||||
      Set(Levels::Low);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* BrightnessController::GetIcon() {
 | 
			
		||||
  switch(level) {    
 | 
			
		||||
    case Levels::Medium: return Applications::Screens::Symbols::brightnessMedium;
 | 
			
		||||
    case Levels::High: return Applications::Screens::Symbols::brightnessHigh;
 | 
			
		||||
    default: break;
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    case Levels::Medium:
 | 
			
		||||
      return Applications::Screens::Symbols::brightnessMedium;
 | 
			
		||||
    case Levels::High:
 | 
			
		||||
      return Applications::Screens::Symbols::brightnessHigh;
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  return Applications::Screens::Symbols::brightnessLow;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* BrightnessController::ToString() {
 | 
			
		||||
  switch(level) {
 | 
			
		||||
    case Levels::Off: return "Off";
 | 
			
		||||
    case Levels::Low: return "Low";    
 | 
			
		||||
    case Levels::Medium: return "Medium";    
 | 
			
		||||
    case Levels::High: return "High";
 | 
			
		||||
    default : return "???";
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    case Levels::Off:
 | 
			
		||||
      return "Off";
 | 
			
		||||
    case Levels::Low:
 | 
			
		||||
      return "Low";
 | 
			
		||||
    case Levels::Medium:
 | 
			
		||||
      return "Medium";
 | 
			
		||||
    case Levels::High:
 | 
			
		||||
      return "High";
 | 
			
		||||
    default:
 | 
			
		||||
      return "???";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -6,7 +6,7 @@ namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class BrightnessController {
 | 
			
		||||
    public:
 | 
			
		||||
      enum class Levels {Off, Low, Medium, High};
 | 
			
		||||
      enum class Levels { Off, Low, Medium, High };
 | 
			
		||||
      void Init();
 | 
			
		||||
 | 
			
		||||
      void Set(Levels level);
 | 
			
		||||
 | 
			
		||||
@ -5,22 +5,21 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
DateTime::DateTime(System::SystemTask& systemTask) : systemTask{systemTask} {
 | 
			
		||||
 | 
			
		||||
DateTime::DateTime(System::SystemTask& systemTask) : systemTask {systemTask} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void DateTime::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) {
 | 
			
		||||
  std::tm tm = { /* .tm_sec  = */ second,
 | 
			
		||||
          /* .tm_min  = */ minute,
 | 
			
		||||
          /* .tm_hour = */ hour,
 | 
			
		||||
          /* .tm_mday = */ day,
 | 
			
		||||
          /* .tm_mon  = */ month - 1,
 | 
			
		||||
          /* .tm_year = */ year - 1900,
 | 
			
		||||
void DateTime::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) {
 | 
			
		||||
  std::tm tm = {
 | 
			
		||||
    /* .tm_sec  = */ second,
 | 
			
		||||
    /* .tm_min  = */ minute,
 | 
			
		||||
    /* .tm_hour = */ hour,
 | 
			
		||||
    /* .tm_mday = */ day,
 | 
			
		||||
    /* .tm_mon  = */ month - 1,
 | 
			
		||||
    /* .tm_year = */ year - 1900,
 | 
			
		||||
  };
 | 
			
		||||
  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 ", 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) {
 | 
			
		||||
  // Handle systick counter overflow
 | 
			
		||||
  uint32_t systickDelta = 0;
 | 
			
		||||
  if(systickCounter < previousSystickCounter) {
 | 
			
		||||
  if (systickCounter < previousSystickCounter) {
 | 
			
		||||
    systickDelta = 0xffffff - previousSystickCounter;
 | 
			
		||||
    systickDelta += systickCounter + 1;
 | 
			
		||||
  } else {
 | 
			
		||||
@ -42,11 +41,11 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
 * 1000 ms = 1024 ticks
 | 
			
		||||
 */
 | 
			
		||||
   * 1000 ms = 1024 ticks
 | 
			
		||||
   */
 | 
			
		||||
  auto correctedDelta = systickDelta / 1024;
 | 
			
		||||
  auto rest = (systickDelta - (correctedDelta*1024));
 | 
			
		||||
  if(systickCounter >= rest) {
 | 
			
		||||
  auto rest = (systickDelta - (correctedDelta * 1024));
 | 
			
		||||
  if (systickCounter >= rest) {
 | 
			
		||||
    previousSystickCounter = systickCounter - rest;
 | 
			
		||||
  } else {
 | 
			
		||||
    previousSystickCounter = 0xffffff - (rest - systickCounter);
 | 
			
		||||
@ -56,12 +55,12 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
 | 
			
		||||
  uptime += std::chrono::seconds(correctedDelta);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  year = (int)yearMonthDay.year();
 | 
			
		||||
  month = static_cast<Months>((unsigned)yearMonthDay.month());
 | 
			
		||||
  day = (unsigned)yearMonthDay.day();
 | 
			
		||||
  year = (int) yearMonthDay.year();
 | 
			
		||||
  month = static_cast<Months>((unsigned) yearMonthDay.month());
 | 
			
		||||
  day = (unsigned) yearMonthDay.day();
 | 
			
		||||
  dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding());
 | 
			
		||||
 | 
			
		||||
  hour = time.hours().count();
 | 
			
		||||
@ -69,7 +68,7 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
 | 
			
		||||
  second = time.seconds().count();
 | 
			
		||||
 | 
			
		||||
  // Notify new day to SystemTask
 | 
			
		||||
  if(hour == 0 and not isMidnightAlreadyNotified) {
 | 
			
		||||
  if (hour == 0 and not isMidnightAlreadyNotified) {
 | 
			
		||||
    isMidnightAlreadyNotified = true;
 | 
			
		||||
    systemTask.PushMessage(System::SystemTask::Messages::OnNewDay);
 | 
			
		||||
  } else if (hour != 0) {
 | 
			
		||||
@ -77,123 +76,45 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *DateTime::MonthShortToString() {
 | 
			
		||||
  return DateTime::MonthsString[(uint8_t)month];
 | 
			
		||||
const char* DateTime::MonthShortToString() {
 | 
			
		||||
  return DateTime::MonthsString[(uint8_t) month];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *DateTime::MonthShortToStringLow() {
 | 
			
		||||
  return DateTime::MonthsStringLow[(uint8_t)month];
 | 
			
		||||
const char* DateTime::MonthShortToStringLow() {
 | 
			
		||||
  return DateTime::MonthsStringLow[(uint8_t) month];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *DateTime::MonthsToStringLow() {
 | 
			
		||||
  return DateTime::MonthsLow[(uint8_t)month];
 | 
			
		||||
const char* DateTime::MonthsToStringLow() {
 | 
			
		||||
  return DateTime::MonthsLow[(uint8_t) month];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *DateTime::DayOfWeekToString() {
 | 
			
		||||
  return DateTime::DaysString[(uint8_t)dayOfWeek];
 | 
			
		||||
const char* DateTime::DayOfWeekToString() {
 | 
			
		||||
  return DateTime::DaysString[(uint8_t) dayOfWeek];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *DateTime::DayOfWeekShortToString() {
 | 
			
		||||
  return DateTime::DaysStringShort[(uint8_t)dayOfWeek];
 | 
			
		||||
const char* DateTime::DayOfWeekShortToString() {
 | 
			
		||||
  return DateTime::DaysStringShort[(uint8_t) dayOfWeek];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *DateTime::DayOfWeekToStringLow() {
 | 
			
		||||
  return DateTime::DaysStringLow[(uint8_t)dayOfWeek];
 | 
			
		||||
const char* DateTime::DayOfWeekToStringLow() {
 | 
			
		||||
  return DateTime::DaysStringLow[(uint8_t) dayOfWeek];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *DateTime::DayOfWeekShortToStringLow() {
 | 
			
		||||
  return DateTime::DaysStringShortLow[(uint8_t)dayOfWeek];
 | 
			
		||||
const char* DateTime::DayOfWeekShortToStringLow() {
 | 
			
		||||
  return DateTime::DaysStringShortLow[(uint8_t) dayOfWeek];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char const* DateTime::DaysStringLow[] = {"--", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
 | 
			
		||||
 | 
			
		||||
char const *DateTime::DaysStringLow[] = {
 | 
			
		||||
        "--",
 | 
			
		||||
        "Monday",
 | 
			
		||||
        "Tuesday",
 | 
			
		||||
        "Wednesday",
 | 
			
		||||
        "Thursday",
 | 
			
		||||
        "Friday",
 | 
			
		||||
        "Saturday",
 | 
			
		||||
        "Sunday"
 | 
			
		||||
};
 | 
			
		||||
char const* DateTime::DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
 | 
			
		||||
 | 
			
		||||
char const *DateTime::DaysStringShortLow[] = {
 | 
			
		||||
        "--",
 | 
			
		||||
        "Mon",
 | 
			
		||||
        "Tue",
 | 
			
		||||
        "Wed",
 | 
			
		||||
        "Thu",
 | 
			
		||||
        "Fri",
 | 
			
		||||
        "Sat",
 | 
			
		||||
        "Sun"
 | 
			
		||||
};
 | 
			
		||||
char const* DateTime::DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
 | 
			
		||||
 | 
			
		||||
char const *DateTime::DaysStringShort[] = {
 | 
			
		||||
        "--",
 | 
			
		||||
        "MON",
 | 
			
		||||
        "TUE",
 | 
			
		||||
        "WED",
 | 
			
		||||
        "THU",
 | 
			
		||||
        "FRI",
 | 
			
		||||
        "SAT",
 | 
			
		||||
        "SUN"
 | 
			
		||||
};
 | 
			
		||||
char const* DateTime::DaysString[] = {"--", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"};
 | 
			
		||||
 | 
			
		||||
char const *DateTime::DaysString[] = {
 | 
			
		||||
        "--",
 | 
			
		||||
        "MONDAY",
 | 
			
		||||
        "TUESDAY",
 | 
			
		||||
        "WEDNESDAY",
 | 
			
		||||
        "THURSDAY",
 | 
			
		||||
        "FRIDAY",
 | 
			
		||||
        "SATURDAY",
 | 
			
		||||
        "SUNDAY"
 | 
			
		||||
};
 | 
			
		||||
char const* DateTime::MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
 | 
			
		||||
 | 
			
		||||
char const *DateTime::MonthsString[] = {
 | 
			
		||||
        "--",
 | 
			
		||||
        "JAN",
 | 
			
		||||
        "FEB",
 | 
			
		||||
        "MAR",
 | 
			
		||||
        "APR",
 | 
			
		||||
        "MAY",
 | 
			
		||||
        "JUN",
 | 
			
		||||
        "JUL",
 | 
			
		||||
        "AUG",
 | 
			
		||||
        "SEP",
 | 
			
		||||
        "OCT",
 | 
			
		||||
        "NOV",
 | 
			
		||||
        "DEC"
 | 
			
		||||
};
 | 
			
		||||
char const* DateTime::MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 | 
			
		||||
 | 
			
		||||
char const *DateTime::MonthsStringLow[] = {
 | 
			
		||||
        "--",
 | 
			
		||||
        "Jan",
 | 
			
		||||
        "Feb",
 | 
			
		||||
        "Mar",
 | 
			
		||||
        "Apr",
 | 
			
		||||
        "May",
 | 
			
		||||
        "Jun",
 | 
			
		||||
        "Jul",
 | 
			
		||||
        "Aug",
 | 
			
		||||
        "Sep",
 | 
			
		||||
        "Oct",
 | 
			
		||||
        "Nov",
 | 
			
		||||
        "Dec"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
char const *DateTime::MonthsLow[] = {
 | 
			
		||||
        "--",
 | 
			
		||||
        "January",
 | 
			
		||||
        "February",
 | 
			
		||||
        "March",
 | 
			
		||||
        "April",
 | 
			
		||||
        "May",
 | 
			
		||||
        "June",
 | 
			
		||||
        "July",
 | 
			
		||||
        "August",
 | 
			
		||||
        "September",
 | 
			
		||||
        "October",
 | 
			
		||||
        "November",
 | 
			
		||||
        "December"
 | 
			
		||||
};
 | 
			
		||||
char const* DateTime::MonthsLow[] = {
 | 
			
		||||
  "--", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
 | 
			
		||||
@ -9,56 +9,95 @@ namespace Pinetime {
 | 
			
		||||
  }
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class DateTime {
 | 
			
		||||
      public:
 | 
			
		||||
        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};
 | 
			
		||||
    public:
 | 
			
		||||
      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
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        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 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; }
 | 
			
		||||
      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 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 *MonthShortToStringLow();
 | 
			
		||||
        const char *MonthsToStringLow();
 | 
			
		||||
        const char *DayOfWeekToString();
 | 
			
		||||
        const char *DayOfWeekShortToString();
 | 
			
		||||
        const char *DayOfWeekToStringLow();
 | 
			
		||||
        const char *DayOfWeekShortToStringLow();
 | 
			
		||||
      const char* MonthShortToString();
 | 
			
		||||
      const char* MonthShortToStringLow();
 | 
			
		||||
      const char* MonthsToStringLow();
 | 
			
		||||
      const char* DayOfWeekToString();
 | 
			
		||||
      const char* DayOfWeekShortToString();
 | 
			
		||||
      const char* DayOfWeekToStringLow();
 | 
			
		||||
      const char* DayOfWeekShortToStringLow();
 | 
			
		||||
 | 
			
		||||
        std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const { return currentDateTime; }
 | 
			
		||||
        std::chrono::seconds Uptime() const { return uptime; }
 | 
			
		||||
      private:
 | 
			
		||||
        System::SystemTask& systemTask;
 | 
			
		||||
        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;
 | 
			
		||||
      std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
 | 
			
		||||
        return currentDateTime;
 | 
			
		||||
      }
 | 
			
		||||
      std::chrono::seconds Uptime() const {
 | 
			
		||||
        return uptime;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        uint32_t previousSystickCounter = 0;
 | 
			
		||||
        std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
 | 
			
		||||
        std::chrono::seconds uptime {0};
 | 
			
		||||
    private:
 | 
			
		||||
      System::SystemTask& systemTask;
 | 
			
		||||
      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[];
 | 
			
		||||
        static char const *DaysStringShort[];
 | 
			
		||||
        static char const *DaysStringLow[];
 | 
			
		||||
        static char const *DaysStringShortLow[];
 | 
			
		||||
        static char const *MonthsString[];
 | 
			
		||||
        static char const *MonthsStringLow[];
 | 
			
		||||
        static char const *MonthsLow[];
 | 
			
		||||
      bool isMidnightAlreadyNotified = false;
 | 
			
		||||
 | 
			
		||||
      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[];
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -6,12 +6,12 @@
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
bool FirmwareValidator::IsValidated() const {
 | 
			
		||||
  auto* imageOkPtr = reinterpret_cast<uint32_t *>(validBitAdress);
 | 
			
		||||
  auto* imageOkPtr = reinterpret_cast<uint32_t*>(validBitAdress);
 | 
			
		||||
  return (*imageOkPtr) == validBitValue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FirmwareValidator::Validate() {
 | 
			
		||||
  if(!IsValidated())
 | 
			
		||||
  if (!IsValidated())
 | 
			
		||||
    Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,14 +5,15 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class FirmwareValidator {
 | 
			
		||||
      public:
 | 
			
		||||
        void Validate();
 | 
			
		||||
        bool IsValidated() const;
 | 
			
		||||
    public:
 | 
			
		||||
      void Validate();
 | 
			
		||||
      bool IsValidated() const;
 | 
			
		||||
 | 
			
		||||
        void Reset();
 | 
			
		||||
      private:
 | 
			
		||||
        static constexpr uint32_t validBitAdress {0x7BFE8};
 | 
			
		||||
        static constexpr uint32_t validBitValue {1};
 | 
			
		||||
      void Reset();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      static constexpr uint32_t validBitAdress {0x7BFE8};
 | 
			
		||||
      static constexpr uint32_t validBitValue {1};
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,10 @@
 | 
			
		||||
#include "drivers/St7789.h"
 | 
			
		||||
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::ClearScreen() {
 | 
			
		||||
@ -17,10 +16,9 @@ void Gfx::ClearScreen() {
 | 
			
		||||
  state.busy = true;
 | 
			
		||||
  state.action = Action::FillRectangle;
 | 
			
		||||
  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) {
 | 
			
		||||
@ -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.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();
 | 
			
		||||
}
 | 
			
		||||
@ -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.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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)) {
 | 
			
		||||
    // Not enough space to write even single char.
 | 
			
		||||
    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;
 | 
			
		||||
  uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8);
 | 
			
		||||
  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 (uint8_t k = 0; k < 8; k++) {
 | 
			
		||||
      if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) {
 | 
			
		||||
        buffer[(j*8)+k] = color;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        buffer[(j*8)+k] = bg;
 | 
			
		||||
        buffer[(j * 8) + k] = color;
 | 
			
		||||
      } else {
 | 
			
		||||
        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.busy = true;
 | 
			
		||||
  state.action = Action::DrawChar;
 | 
			
		||||
  state.font = const_cast<FONT_INFO *>(font);
 | 
			
		||||
  state.font = const_cast<FONT_INFO*>(font);
 | 
			
		||||
  state.character = c;
 | 
			
		||||
  state.color = color;
 | 
			
		||||
  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();
 | 
			
		||||
 | 
			
		||||
  *x += font->charInfo[char_idx].widthBits + font->spacePixels;
 | 
			
		||||
@ -136,13 +133,14 @@ void Gfx::Wakeup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Gfx::SetBackgroundColor(uint16_t color) {
 | 
			
		||||
  for(int i = 0; i < width; i++) {
 | 
			
		||||
  for (int i = 0; i < width; i++) {
 | 
			
		||||
    buffer[i] = color;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
 | 
			
		||||
  if(!state.busy) return false;
 | 
			
		||||
bool Gfx::GetNextBuffer(uint8_t** data, size_t& size) {
 | 
			
		||||
  if (!state.busy)
 | 
			
		||||
    return false;
 | 
			
		||||
  state.remainingIterations--;
 | 
			
		||||
  if (state.remainingIterations == 0) {
 | 
			
		||||
    state.busy = false;
 | 
			
		||||
@ -150,27 +148,26 @@ bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(state.action == Action::FillRectangle) {
 | 
			
		||||
    *data = reinterpret_cast<uint8_t *>(buffer);
 | 
			
		||||
  if (state.action == Action::FillRectangle) {
 | 
			
		||||
    *data = reinterpret_cast<uint8_t*>(buffer);
 | 
			
		||||
    size = width * 2;
 | 
			
		||||
  } else if(state.action == Action::DrawChar) {
 | 
			
		||||
  } else if (state.action == Action::DrawChar) {
 | 
			
		||||
    uint16_t bg = 0x0000;
 | 
			
		||||
    uint8_t char_idx = state.character - state.font->startChar;
 | 
			
		||||
    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 (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]) {
 | 
			
		||||
          buffer[(j*8)+k] = state.color;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          buffer[(j*8)+k] = bg;
 | 
			
		||||
        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;
 | 
			
		||||
        } else {
 | 
			
		||||
          buffer[(j * 8) + k] = bg;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *data = reinterpret_cast<uint8_t *>(buffer);
 | 
			
		||||
    size = bytes_in_line*8*2;
 | 
			
		||||
    *data = reinterpret_cast<uint8_t*>(buffer);
 | 
			
		||||
    size = bytes_in_line * 8 * 2;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state.currentIteration++;
 | 
			
		||||
@ -179,7 +176,7 @@ bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Gfx::NotifyEndOfTransfer(TaskHandle_t task) {
 | 
			
		||||
  if(task != nullptr) {
 | 
			
		||||
  if (task != nullptr) {
 | 
			
		||||
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
 | 
			
		||||
    vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken);
 | 
			
		||||
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
 | 
			
		||||
 | 
			
		||||
@ -12,49 +12,48 @@ namespace Pinetime {
 | 
			
		||||
  }
 | 
			
		||||
  namespace Components {
 | 
			
		||||
    class Gfx : public Pinetime::Drivers::BufferProvider {
 | 
			
		||||
      public:
 | 
			
		||||
        explicit Gfx(Drivers::St7789& lcd);
 | 
			
		||||
        void Init();
 | 
			
		||||
        void ClearScreen();
 | 
			
		||||
        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 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 SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
 | 
			
		||||
        void SetScrollStartLine(uint16_t line);
 | 
			
		||||
    public:
 | 
			
		||||
      explicit Gfx(Drivers::St7789& lcd);
 | 
			
		||||
      void Init();
 | 
			
		||||
      void ClearScreen();
 | 
			
		||||
      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 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 SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
 | 
			
		||||
      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();
 | 
			
		||||
        void Wakeup();
 | 
			
		||||
        bool GetNextBuffer(uint8_t **buffer, size_t &size) override;
 | 
			
		||||
        void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
 | 
			
		||||
    private:
 | 
			
		||||
      static constexpr uint8_t width = 240;
 | 
			
		||||
      static constexpr uint8_t height = 240;
 | 
			
		||||
 | 
			
		||||
      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:
 | 
			
		||||
        static constexpr uint8_t width = 240;
 | 
			
		||||
        static constexpr uint8_t height = 240;
 | 
			
		||||
      volatile State state;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        };
 | 
			
		||||
      uint16_t buffer[width]; // 1 line buffer
 | 
			
		||||
      Drivers::St7789& lcd;
 | 
			
		||||
 | 
			
		||||
        volatile State state;
 | 
			
		||||
 | 
			
		||||
        uint16_t buffer[width]; // 1 line buffer
 | 
			
		||||
        Drivers::St7789& lcd;
 | 
			
		||||
 | 
			
		||||
        void SetBackgroundColor(uint16_t color);
 | 
			
		||||
        void WaitTransferFinished() const;
 | 
			
		||||
        void NotifyEndOfTransfer(TaskHandle_t task);
 | 
			
		||||
      void SetBackgroundColor(uint16_t color);
 | 
			
		||||
      void WaitTransferFinished() const;
 | 
			
		||||
      void NotifyEndOfTransfer(TaskHandle_t task);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,7 @@
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
/** 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) {
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ namespace Pinetime {
 | 
			
		||||
    /// Direct Form II Biquad Filter
 | 
			
		||||
    class Biquad {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
@ -4,38 +4,35 @@
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
  this->state = newState;
 | 
			
		||||
  if(this->heartRate != heartRate) {
 | 
			
		||||
  if (this->heartRate != heartRate) {
 | 
			
		||||
    this->heartRate = heartRate;
 | 
			
		||||
    service->OnNewHeartRateValue(heartRate);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HeartRateController::Start() {
 | 
			
		||||
  if(task != nullptr) {
 | 
			
		||||
  if (task != nullptr) {
 | 
			
		||||
    state = States::NotEnoughData;
 | 
			
		||||
    task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StartMeasurement);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HeartRateController::Stop() {
 | 
			
		||||
  if(task != nullptr) {
 | 
			
		||||
  if (task != nullptr) {
 | 
			
		||||
    state = States::Stopped;
 | 
			
		||||
    task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StopMeasurement);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HeartRateController::SetHeartRateTask(Pinetime::Applications::HeartRateTask *task) {
 | 
			
		||||
void HeartRateController::SetHeartRateTask(Pinetime::Applications::HeartRateTask* task) {
 | 
			
		||||
  this->task = task;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HeartRateController::SetService(Pinetime::Controllers::HeartRateService *service) {
 | 
			
		||||
void HeartRateController::SetService(Pinetime::Controllers::HeartRateService* service) {
 | 
			
		||||
  this->service = service;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class HeartRateController {
 | 
			
		||||
    public:
 | 
			
		||||
      enum class States { Stopped, NotEnoughData, NoTouch, Running};
 | 
			
		||||
      enum class States { Stopped, NotEnoughData, NoTouch, Running };
 | 
			
		||||
 | 
			
		||||
      explicit HeartRateController(System::SystemTask& systemTask);
 | 
			
		||||
 | 
			
		||||
@ -22,10 +22,14 @@ namespace Pinetime {
 | 
			
		||||
      void Update(States newState, uint8_t heartRate);
 | 
			
		||||
 | 
			
		||||
      void SetHeartRateTask(Applications::HeartRateTask* task);
 | 
			
		||||
      States State() const { return state; }
 | 
			
		||||
      uint8_t HeartRate() const { return heartRate; }
 | 
			
		||||
      States State() const {
 | 
			
		||||
        return state;
 | 
			
		||||
      }
 | 
			
		||||
      uint8_t HeartRate() const {
 | 
			
		||||
        return heartRate;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      void SetService(Pinetime::Controllers::HeartRateService *service);
 | 
			
		||||
      void SetService(Pinetime::Controllers::HeartRateService* service);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      System::SystemTask& systemTask;
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ using namespace Pinetime::Controllers;
 | 
			
		||||
namespace {
 | 
			
		||||
  int Compare(int* d1, int* d2, size_t count) {
 | 
			
		||||
    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];
 | 
			
		||||
      e += d * d;
 | 
			
		||||
    }
 | 
			
		||||
@ -21,15 +21,15 @@ namespace {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
    auto z2 = CompareShift(d, mn-2, size);
 | 
			
		||||
    auto z1 = CompareShift(d, mn-1, size);
 | 
			
		||||
    for(int i = mn; i < mx + 1; i++) {
 | 
			
		||||
    auto z2 = CompareShift(d, mn - 2, size);
 | 
			
		||||
    auto z1 = CompareShift(d, mn - 1, size);
 | 
			
		||||
    for (int i = mn; i < mx + 1; i++) {
 | 
			
		||||
      auto z = CompareShift(d, i, size);
 | 
			
		||||
      if(z2 > z1 && z1 < z)
 | 
			
		||||
      if (z2 > z1 && z1 < z)
 | 
			
		||||
        return i;
 | 
			
		||||
      z2 = z1;
 | 
			
		||||
      z1 = z;
 | 
			
		||||
@ -38,11 +38,11 @@ namespace {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ppg::Ppg(float spl) : offset{spl},
 | 
			
		||||
                      hpf{0.87033078, -1.74066156, 0.87033078,-1.72377617, 0.75754694},
 | 
			
		||||
                      agc{20, 0.971, 2},
 | 
			
		||||
                      lpf{0.11595249, 0.23190498, 0.11595249,-0.72168143, 0.18549138} {
 | 
			
		||||
 | 
			
		||||
Ppg::Ppg(float spl)
 | 
			
		||||
  : offset {spl},
 | 
			
		||||
    hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
 | 
			
		||||
    agc {20, 0.971, 2},
 | 
			
		||||
    lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Ppg::Preprocess(float spl) {
 | 
			
		||||
@ -53,13 +53,13 @@ int Ppg::Preprocess(float spl) {
 | 
			
		||||
 | 
			
		||||
  auto spl_int = static_cast<int>(spl);
 | 
			
		||||
 | 
			
		||||
  if(dataIndex < 200)
 | 
			
		||||
  if (dataIndex < 200)
 | 
			
		||||
    data[dataIndex++] = spl_int;
 | 
			
		||||
  return spl_int;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float Ppg::HeartRate() {
 | 
			
		||||
  if(dataIndex < 200)
 | 
			
		||||
  if (dataIndex < 200)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  NRF_LOG_INFO("PREPROCESS, offset = %d", offset);
 | 
			
		||||
@ -71,26 +71,26 @@ float Ppg::HeartRate() {
 | 
			
		||||
int cccount = 0;
 | 
			
		||||
float Ppg::ProcessHeartRate() {
 | 
			
		||||
 | 
			
		||||
  if(cccount > 2)
 | 
			
		||||
  if (cccount > 2)
 | 
			
		||||
    asm("nop");
 | 
			
		||||
  cccount ++;
 | 
			
		||||
  cccount++;
 | 
			
		||||
  auto t0 = Trough(data.data(), dataIndex, 7, 48);
 | 
			
		||||
  if(t0 < 0)
 | 
			
		||||
  if (t0 < 0)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  float t1 = t0 * 2;
 | 
			
		||||
  t1 = Trough(data.data(), dataIndex, t1-5, t1+5);
 | 
			
		||||
  if(t1 < 0)
 | 
			
		||||
  t1 = Trough(data.data(), dataIndex, t1 - 5, t1 + 5);
 | 
			
		||||
  if (t1 < 0)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  float t2 = static_cast<int>(t1 * 3) / 2;
 | 
			
		||||
  t2 = Trough(data.data(), dataIndex, t2 - 5, t2 + 5);
 | 
			
		||||
  if(t2 < 0)
 | 
			
		||||
  if (t2 < 0)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  float t3 = static_cast<int>(t2 * 4) / 3;
 | 
			
		||||
  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 * 4) / static_cast<int>(t3);
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ namespace Pinetime {
 | 
			
		||||
      Ptagc agc;
 | 
			
		||||
      Biquad lpf;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      float ProcessHeartRate();
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -10,17 +10,16 @@
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
/** 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) {
 | 
			
		||||
  if(std::abs(spl) > peak)
 | 
			
		||||
  if (std::abs(spl) > peak)
 | 
			
		||||
    peak *= boost;
 | 
			
		||||
  else
 | 
			
		||||
    peak *= decay;
 | 
			
		||||
 | 
			
		||||
  if((spl > (peak * threshold)) || (spl < (peak * -threshold)))
 | 
			
		||||
  if ((spl > (peak * threshold)) || (spl < (peak * -threshold)))
 | 
			
		||||
    return 0.0f;
 | 
			
		||||
 | 
			
		||||
  spl = 100.0f * spl / (2.0f * peak);
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,6 @@ namespace Pinetime {
 | 
			
		||||
      float decay;
 | 
			
		||||
      float boost;
 | 
			
		||||
      float threshold;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,14 +8,24 @@ namespace Pinetime {
 | 
			
		||||
    public:
 | 
			
		||||
      void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
 | 
			
		||||
 | 
			
		||||
      int16_t X() const { return x; }
 | 
			
		||||
      int16_t Y() const { return y; }
 | 
			
		||||
      int16_t Z() const { return z; }
 | 
			
		||||
      uint32_t NbSteps() const { return nbSteps; }
 | 
			
		||||
      int16_t X() const {
 | 
			
		||||
        return x;
 | 
			
		||||
      }
 | 
			
		||||
      int16_t Y() const {
 | 
			
		||||
        return y;
 | 
			
		||||
      }
 | 
			
		||||
      int16_t Z() const {
 | 
			
		||||
        return z;
 | 
			
		||||
      }
 | 
			
		||||
      uint32_t NbSteps() const {
 | 
			
		||||
        return nbSteps;
 | 
			
		||||
      }
 | 
			
		||||
      bool ShouldWakeUp(bool isSleeping);
 | 
			
		||||
 | 
			
		||||
      void IsSensorOk(bool isOk);
 | 
			
		||||
      bool IsSensorOk() const { return isSensorOk; }
 | 
			
		||||
      bool IsSensorOk() const {
 | 
			
		||||
        return isSensorOk;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      uint32_t nbSteps;
 | 
			
		||||
 | 
			
		||||
@ -7,24 +7,26 @@ APP_TIMER_DEF(vibTimer);
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
MotorController::MotorController( Controllers::Settings &settingsController ) : settingsController{settingsController} {}
 | 
			
		||||
MotorController::MotorController(Controllers::Settings& settingsController) : settingsController {settingsController} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MotorController::Init() {
 | 
			
		||||
    nrf_gpio_cfg_output(pinMotor);
 | 
			
		||||
    nrf_gpio_pin_set(pinMotor);
 | 
			
		||||
    app_timer_init();
 | 
			
		||||
    app_timer_create(&vibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate);
 | 
			
		||||
  nrf_gpio_cfg_output(pinMotor);
 | 
			
		||||
  nrf_gpio_pin_set(pinMotor);
 | 
			
		||||
  app_timer_init();
 | 
			
		||||
  app_timer_create(&vibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MotorController::SetDuration(uint8_t motorDuration) {
 | 
			
		||||
 | 
			
		||||
    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*/
 | 
			
		||||
    app_timer_start(vibTimer, APP_TIMER_TICKS(motorDuration), NULL);
 | 
			
		||||
  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*/
 | 
			
		||||
  app_timer_start(vibTimer, APP_TIMER_TICKS(motorDuration), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MotorController::vibrate(void * p_context) {
 | 
			
		||||
    nrf_gpio_pin_set(pinMotor);
 | 
			
		||||
void MotorController::vibrate(void* p_context) {
 | 
			
		||||
  nrf_gpio_pin_set(pinMotor);
 | 
			
		||||
}
 | 
			
		||||
@ -9,14 +9,14 @@ namespace Pinetime {
 | 
			
		||||
    static constexpr uint8_t pinMotor = 16;
 | 
			
		||||
 | 
			
		||||
    class MotorController {
 | 
			
		||||
      public:
 | 
			
		||||
        MotorController( Controllers::Settings &settingsController );
 | 
			
		||||
        void Init();
 | 
			
		||||
        void SetDuration(uint8_t motorDuration);
 | 
			
		||||
    public:
 | 
			
		||||
      MotorController(Controllers::Settings& settingsController);
 | 
			
		||||
      void Init();
 | 
			
		||||
      void SetDuration(uint8_t motorDuration);
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        Controllers::Settings& settingsController;
 | 
			
		||||
        static void vibrate(void * p_context);
 | 
			
		||||
    private:
 | 
			
		||||
      Controllers::Settings& settingsController;
 | 
			
		||||
      static void vibrate(void* p_context);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,18 +2,16 @@
 | 
			
		||||
 | 
			
		||||
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->backgroundColor = backgroundColor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void RleDecoder::DecodeNext(uint8_t *output, size_t maxBytes) {
 | 
			
		||||
  for (;encodedBufferIndex<size; encodedBufferIndex++) {
 | 
			
		||||
void RleDecoder::DecodeNext(uint8_t* output, size_t maxBytes) {
 | 
			
		||||
  for (; encodedBufferIndex < size; encodedBufferIndex++) {
 | 
			
		||||
    uint8_t rl = buffer[encodedBufferIndex] - processedCount;
 | 
			
		||||
    while (rl) {
 | 
			
		||||
      output[bp] = color >> 8;
 | 
			
		||||
@ -36,4 +34,3 @@ void RleDecoder::DecodeNext(uint8_t *output, size_t maxBytes) {
 | 
			
		||||
      color = backgroundColor;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,110 +5,107 @@
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
struct SettingsHeader {
 | 
			
		||||
    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
 | 
			
		||||
  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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define HEADER_SIZE sizeof(SettingsHeader)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Settings::Settings( Pinetime::Drivers::SpiNorFlash &spiNorFlash ) : spiNorFlash{spiNorFlash} {}
 | 
			
		||||
Settings::Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash {spiNorFlash} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Settings::Init() {
 | 
			
		||||
 | 
			
		||||
    // Load default settings from Flash
 | 
			
		||||
    LoadSettingsFromFlash();
 | 
			
		||||
 | 
			
		||||
  // Load default settings from Flash
 | 
			
		||||
  LoadSettingsFromFlash();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Settings::SaveSettings() {
 | 
			
		||||
 | 
			
		||||
    // verify if is necessary to save
 | 
			
		||||
    if ( settingsChanged ) {
 | 
			
		||||
        SaveSettingsToFlash();
 | 
			
		||||
    }
 | 
			
		||||
    settingsChanged = false;
 | 
			
		||||
  // verify if is necessary to save
 | 
			
		||||
  if (settingsChanged) {
 | 
			
		||||
    SaveSettingsToFlash();
 | 
			
		||||
  }
 | 
			
		||||
  settingsChanged = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool Settings::FindHeader() {
 | 
			
		||||
    SettingsHeader settingsHeader;
 | 
			
		||||
    uint8_t bufferHead[sizeof(settingsHeader)];
 | 
			
		||||
  SettingsHeader settingsHeader;
 | 
			
		||||
  uint8_t bufferHead[sizeof(settingsHeader)];
 | 
			
		||||
 | 
			
		||||
    for (uint8_t block = 0; block < 10; block++) {
 | 
			
		||||
    
 | 
			
		||||
        spiNorFlash.Read( settingsBaseAddr + (block * 0x1000), bufferHead, sizeof(settingsHeader) );
 | 
			
		||||
        std::memcpy(&settingsHeader, bufferHead, sizeof(settingsHeader));
 | 
			
		||||
        if ( settingsHeader.isActive == 0xF1 && settingsHeader.version == settingsVersion ) {
 | 
			
		||||
            settingsFlashBlock = block;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
  for (uint8_t block = 0; block < 10; block++) {
 | 
			
		||||
 | 
			
		||||
    spiNorFlash.Read(settingsBaseAddr + (block * 0x1000), bufferHead, sizeof(settingsHeader));
 | 
			
		||||
    std::memcpy(&settingsHeader, bufferHead, sizeof(settingsHeader));
 | 
			
		||||
    if (settingsHeader.isActive == 0xF1 && settingsHeader.version == settingsVersion) {
 | 
			
		||||
      settingsFlashBlock = block;
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Settings::ReadSettingsData() {
 | 
			
		||||
    uint8_t bufferSettings[sizeof(settings)];
 | 
			
		||||
    spiNorFlash.Read( settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings) );
 | 
			
		||||
    std::memcpy(&settings, bufferSettings, sizeof(settings));
 | 
			
		||||
  uint8_t bufferSettings[sizeof(settings)];
 | 
			
		||||
  spiNorFlash.Read(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
 | 
			
		||||
  std::memcpy(&settings, bufferSettings, sizeof(settings));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Settings::EraseBlock() {
 | 
			
		||||
 | 
			
		||||
    spiNorFlash.SectorErase(settingsBaseAddr + (settingsFlashBlock * 0x1000));
 | 
			
		||||
  spiNorFlash.SectorErase(settingsBaseAddr + (settingsFlashBlock * 0x1000));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Settings::SetHeader( bool state ) {
 | 
			
		||||
    SettingsHeader settingsHeader;
 | 
			
		||||
    uint8_t bufferHead[sizeof(settingsHeader)];
 | 
			
		||||
    settingsHeader.isActive = state ? 0xF1 : 0xF0;
 | 
			
		||||
    settingsHeader.version = settingsVersion;
 | 
			
		||||
 | 
			
		||||
    std::memcpy(bufferHead, &settingsHeader, sizeof(settingsHeader));
 | 
			
		||||
    spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000), bufferHead, sizeof(settingsHeader));
 | 
			
		||||
void Settings::SetHeader(bool state) {
 | 
			
		||||
  SettingsHeader settingsHeader;
 | 
			
		||||
  uint8_t bufferHead[sizeof(settingsHeader)];
 | 
			
		||||
  settingsHeader.isActive = state ? 0xF1 : 0xF0;
 | 
			
		||||
  settingsHeader.version = settingsVersion;
 | 
			
		||||
 | 
			
		||||
  std::memcpy(bufferHead, &settingsHeader, sizeof(settingsHeader));
 | 
			
		||||
  spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000), bufferHead, sizeof(settingsHeader));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Settings::SaveSettingsData() {
 | 
			
		||||
    uint8_t bufferSettings[sizeof(settings)];
 | 
			
		||||
    std::memcpy(bufferSettings, &settings, sizeof(settings));
 | 
			
		||||
    spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
 | 
			
		||||
  uint8_t bufferSettings[sizeof(settings)];
 | 
			
		||||
  std::memcpy(bufferSettings, &settings, sizeof(settings));
 | 
			
		||||
  spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Settings::LoadSettingsFromFlash() {
 | 
			
		||||
 | 
			
		||||
    if ( settingsFlashBlock == 99 ) {
 | 
			
		||||
        // Find current Block, if can't find use default settings and set block to 0 ans save !
 | 
			
		||||
        if ( FindHeader() ) {
 | 
			
		||||
            ReadSettingsData();
 | 
			
		||||
        } else {
 | 
			
		||||
            SaveSettingsToFlash();
 | 
			
		||||
        }
 | 
			
		||||
  if (settingsFlashBlock == 99) {
 | 
			
		||||
    // Find current Block, if can't find use default settings and set block to 0 ans save !
 | 
			
		||||
    if (FindHeader()) {
 | 
			
		||||
      ReadSettingsData();
 | 
			
		||||
    } else {
 | 
			
		||||
        // Read Settings from flash...
 | 
			
		||||
        // never used :)
 | 
			
		||||
        ReadSettingsData();
 | 
			
		||||
      SaveSettingsToFlash();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
  } else {
 | 
			
		||||
    // Read Settings from flash...
 | 
			
		||||
    // never used :)
 | 
			
		||||
    ReadSettingsData();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 ) {
 | 
			
		||||
        SetHeader( false );
 | 
			
		||||
    }
 | 
			
		||||
  // if first time hever, only saves to block 0 and set settingsFlashBlock
 | 
			
		||||
 | 
			
		||||
    settingsFlashBlock++;
 | 
			
		||||
    if ( settingsFlashBlock > 9 ) settingsFlashBlock = 0;
 | 
			
		||||
  if (settingsFlashBlock != 99) {
 | 
			
		||||
    SetHeader(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    EraseBlock();
 | 
			
		||||
    SetHeader( true );
 | 
			
		||||
    SaveSettingsData();
 | 
			
		||||
  settingsFlashBlock++;
 | 
			
		||||
  if (settingsFlashBlock > 9)
 | 
			
		||||
    settingsFlashBlock = 0;
 | 
			
		||||
 | 
			
		||||
  EraseBlock();
 | 
			
		||||
  SetHeader(true);
 | 
			
		||||
  SaveSettingsData();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,104 +8,129 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class Settings {
 | 
			
		||||
      public:
 | 
			
		||||
        enum class ClockType {H24, H12};
 | 
			
		||||
        enum class Vibration {ON, OFF};
 | 
			
		||||
        enum class WakeUpMode {None, SingleTap, DoubleTap, RaiseWrist};
 | 
			
		||||
    public:
 | 
			
		||||
      enum class ClockType { H24, H12 };
 | 
			
		||||
      enum class Vibration { ON, OFF };
 | 
			
		||||
      enum class WakeUpMode { None, SingleTap, DoubleTap, RaiseWrist };
 | 
			
		||||
 | 
			
		||||
        Settings( Pinetime::Drivers::SpiNorFlash &spiNorFlash );
 | 
			
		||||
      Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash);
 | 
			
		||||
 | 
			
		||||
        void Init();
 | 
			
		||||
        void SaveSettings();
 | 
			
		||||
      void Init();
 | 
			
		||||
      void SaveSettings();
 | 
			
		||||
 | 
			
		||||
        void SetClockFace( uint8_t face ) {
 | 
			
		||||
          if ( face != settings.clockFace ) settingsChanged = true;
 | 
			
		||||
          settings.clockFace = face; 
 | 
			
		||||
        };
 | 
			
		||||
        uint8_t GetClockFace() const { return settings.clockFace; };
 | 
			
		||||
      void SetClockFace(uint8_t face) {
 | 
			
		||||
        if (face != settings.clockFace)
 | 
			
		||||
          settingsChanged = true;
 | 
			
		||||
        settings.clockFace = face;
 | 
			
		||||
      };
 | 
			
		||||
      uint8_t GetClockFace() const {
 | 
			
		||||
        return settings.clockFace;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void SetAppMenu( uint8_t menu ) { appMenu = menu; };
 | 
			
		||||
        uint8_t GetAppMenu() { return appMenu; };
 | 
			
		||||
      void SetAppMenu(uint8_t menu) {
 | 
			
		||||
        appMenu = menu;
 | 
			
		||||
      };
 | 
			
		||||
      uint8_t GetAppMenu() {
 | 
			
		||||
        return appMenu;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void SetSettingsMenu( uint8_t menu ) { settingsMenu = menu; };
 | 
			
		||||
        uint8_t GetSettingsMenu() const { return settingsMenu; };
 | 
			
		||||
      void SetSettingsMenu(uint8_t menu) {
 | 
			
		||||
        settingsMenu = menu;
 | 
			
		||||
      };
 | 
			
		||||
      uint8_t GetSettingsMenu() const {
 | 
			
		||||
        return settingsMenu;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void SetClockType( ClockType clocktype ) { 
 | 
			
		||||
          if ( clocktype != settings.clockType ) settingsChanged = true;
 | 
			
		||||
          settings.clockType = clocktype; 
 | 
			
		||||
        };
 | 
			
		||||
        ClockType GetClockType() const { return settings.clockType; };
 | 
			
		||||
      void SetClockType(ClockType clocktype) {
 | 
			
		||||
        if (clocktype != settings.clockType)
 | 
			
		||||
          settingsChanged = true;
 | 
			
		||||
        settings.clockType = clocktype;
 | 
			
		||||
      };
 | 
			
		||||
      ClockType GetClockType() const {
 | 
			
		||||
        return settings.clockType;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void SetVibrationStatus( Vibration status ) { 
 | 
			
		||||
          if ( status != settings.vibrationStatus ) settingsChanged = true;
 | 
			
		||||
          settings.vibrationStatus = status; 
 | 
			
		||||
        };
 | 
			
		||||
        Vibration GetVibrationStatus() const { return settings.vibrationStatus; };
 | 
			
		||||
      void SetVibrationStatus(Vibration status) {
 | 
			
		||||
        if (status != settings.vibrationStatus)
 | 
			
		||||
          settingsChanged = true;
 | 
			
		||||
        settings.vibrationStatus = status;
 | 
			
		||||
      };
 | 
			
		||||
      Vibration GetVibrationStatus() const {
 | 
			
		||||
        return settings.vibrationStatus;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void SetScreenTimeOut( uint32_t timeout ) { 
 | 
			
		||||
          if ( timeout != settings.screenTimeOut ) settingsChanged = true;
 | 
			
		||||
          settings.screenTimeOut = timeout; 
 | 
			
		||||
        };
 | 
			
		||||
        uint32_t GetScreenTimeOut() const { return settings.screenTimeOut; };        
 | 
			
		||||
      void SetScreenTimeOut(uint32_t timeout) {
 | 
			
		||||
        if (timeout != settings.screenTimeOut)
 | 
			
		||||
          settingsChanged = true;
 | 
			
		||||
        settings.screenTimeOut = timeout;
 | 
			
		||||
      };
 | 
			
		||||
      uint32_t GetScreenTimeOut() const {
 | 
			
		||||
        return settings.screenTimeOut;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void setWakeUpMode( WakeUpMode wakeUp ) { 
 | 
			
		||||
          if ( wakeUp != settings.wakeUpMode ) settingsChanged = true;
 | 
			
		||||
          settings.wakeUpMode = wakeUp; 
 | 
			
		||||
        };
 | 
			
		||||
        WakeUpMode getWakeUpMode() const { return settings.wakeUpMode; };
 | 
			
		||||
      void setWakeUpMode(WakeUpMode wakeUp) {
 | 
			
		||||
        if (wakeUp != settings.wakeUpMode)
 | 
			
		||||
          settingsChanged = true;
 | 
			
		||||
        settings.wakeUpMode = wakeUp;
 | 
			
		||||
      };
 | 
			
		||||
      WakeUpMode getWakeUpMode() const {
 | 
			
		||||
        return settings.wakeUpMode;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void SetBrightness( Controllers::BrightnessController::Levels level ) { 
 | 
			
		||||
          if ( level != settings.brightLevel ) settingsChanged = true;
 | 
			
		||||
          settings.brightLevel = level; 
 | 
			
		||||
        };
 | 
			
		||||
        Controllers::BrightnessController::Levels GetBrightness() const { return settings.brightLevel; };
 | 
			
		||||
      void SetBrightness(Controllers::BrightnessController::Levels level) {
 | 
			
		||||
        if (level != settings.brightLevel)
 | 
			
		||||
          settingsChanged = true;
 | 
			
		||||
        settings.brightLevel = level;
 | 
			
		||||
      };
 | 
			
		||||
      Controllers::BrightnessController::Levels GetBrightness() const {
 | 
			
		||||
        return settings.brightLevel;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
        void SetStepsGoal( uint32_t goal ) { 
 | 
			
		||||
          if ( goal != settings.stepsGoal ) settingsChanged = true;
 | 
			
		||||
          settings.stepsGoal = goal; 
 | 
			
		||||
        };
 | 
			
		||||
        uint32_t GetStepsGoal() const { return settings.stepsGoal; };
 | 
			
		||||
      void SetStepsGoal( uint32_t goal ) { 
 | 
			
		||||
        if ( goal != settings.stepsGoal ) 
 | 
			
		||||
          settingsChanged = true;
 | 
			
		||||
        settings.stepsGoal = goal; 
 | 
			
		||||
      };
 | 
			
		||||
      
 | 
			
		||||
      uint32_t GetStepsGoal() const { return settings.stepsGoal; };
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
    private:
 | 
			
		||||
      Pinetime::Drivers::SpiNorFlash& spiNorFlash;
 | 
			
		||||
      struct SettingsData {
 | 
			
		||||
 | 
			
		||||
        Pinetime::Drivers::SpiNorFlash& spiNorFlash;        
 | 
			
		||||
        struct SettingsData {
 | 
			
		||||
        ClockType clockType = ClockType::H24;
 | 
			
		||||
        Vibration vibrationStatus = Vibration::ON;
 | 
			
		||||
 | 
			
		||||
          ClockType clockType = ClockType::H24;
 | 
			
		||||
          Vibration vibrationStatus = Vibration::ON;
 | 
			
		||||
        uint8_t clockFace = 0;
 | 
			
		||||
 | 
			
		||||
          uint8_t clockFace = 0;
 | 
			
		||||
        uint32_t stepsGoal = 10000;
 | 
			
		||||
        uint32_t screenTimeOut = 15000;
 | 
			
		||||
 | 
			
		||||
          uint32_t stepsGoal = 10000;
 | 
			
		||||
          uint32_t screenTimeOut = 15000;
 | 
			
		||||
        WakeUpMode wakeUpMode = WakeUpMode::None;
 | 
			
		||||
 | 
			
		||||
          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;
 | 
			
		||||
        bool settingsChanged = false;
 | 
			
		||||
      // 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
 | 
			
		||||
 | 
			
		||||
        uint8_t appMenu = 0;
 | 
			
		||||
        uint8_t settingsMenu = 0;
 | 
			
		||||
 | 
			
		||||
        // 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();
 | 
			
		||||
      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();
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -3,9 +3,32 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    enum class Apps {
 | 
			
		||||
      None, Launcher, Clock, SysInfo, FirmwareUpdate, FirmwareValidation, NotificationsPreview, Notifications, FlashLight, BatteryInfo,
 | 
			
		||||
      Music, Paint, Paddle, Twos, HeartRate, Navigation, StopWatch, Motion, Steps,
 | 
			
		||||
      QuickSettings, Settings, SettingWatchFace, SettingTimeFormat, SettingDisplay, SettingWakeUp, SettingSteps
 | 
			
		||||
      None,
 | 
			
		||||
      Launcher,
 | 
			
		||||
      Clock,
 | 
			
		||||
      SysInfo,
 | 
			
		||||
      FirmwareUpdate,
 | 
			
		||||
      FirmwareValidation,
 | 
			
		||||
      NotificationsPreview,
 | 
			
		||||
      Notifications,
 | 
			
		||||
      FlashLight,
 | 
			
		||||
      BatteryInfo,
 | 
			
		||||
      Music,
 | 
			
		||||
      Paint,
 | 
			
		||||
      Paddle,
 | 
			
		||||
      Twos,
 | 
			
		||||
      HeartRate,
 | 
			
		||||
      Navigation,
 | 
			
		||||
      StopWatch,
 | 
			
		||||
      Motion,
 | 
			
		||||
      Steps,
 | 
			
		||||
      QuickSettings,
 | 
			
		||||
      Settings,
 | 
			
		||||
      SettingWatchFace,
 | 
			
		||||
      SettingTimeFormat,
 | 
			
		||||
      SettingDisplay,
 | 
			
		||||
      SettingWakeUp,
 | 
			
		||||
      SettingSteps
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,29 +42,33 @@
 | 
			
		||||
using namespace Pinetime::Applications;
 | 
			
		||||
using namespace Pinetime::Applications::Display;
 | 
			
		||||
 | 
			
		||||
DisplayApp::DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &touchPanel,
 | 
			
		||||
                       Controllers::Battery &batteryController, Controllers::Ble &bleController,
 | 
			
		||||
                       Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog,
 | 
			
		||||
                       System::SystemTask &systemTask,
 | 
			
		||||
DisplayApp::DisplayApp(Drivers::St7789& lcd,
 | 
			
		||||
                       Components::LittleVgl& lvgl,
 | 
			
		||||
                       Drivers::Cst816S& touchPanel,
 | 
			
		||||
                       Controllers::Battery& batteryController,
 | 
			
		||||
                       Controllers::Ble& bleController,
 | 
			
		||||
                       Controllers::DateTime& dateTimeController,
 | 
			
		||||
                       Drivers::WatchdogView& watchdog,
 | 
			
		||||
                       System::SystemTask& systemTask,
 | 
			
		||||
                       Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                       Pinetime::Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                       Controllers::Settings &settingsController,
 | 
			
		||||
                       Pinetime::Controllers::MotionController& motionController) :
 | 
			
		||||
        lcd{lcd},
 | 
			
		||||
        lvgl{lvgl},
 | 
			
		||||
        touchPanel{touchPanel},
 | 
			
		||||
        batteryController{batteryController},
 | 
			
		||||
        bleController{bleController},
 | 
			
		||||
        dateTimeController{dateTimeController},
 | 
			
		||||
        watchdog{watchdog},
 | 
			
		||||
        systemTask{systemTask},
 | 
			
		||||
        notificationManager{notificationManager},
 | 
			
		||||
        heartRateController{heartRateController},
 | 
			
		||||
        settingsController{settingsController},
 | 
			
		||||
        motionController{motionController} {
 | 
			
		||||
                       Controllers::Settings& settingsController,
 | 
			
		||||
                       Pinetime::Controllers::MotionController& motionController)
 | 
			
		||||
  : lcd {lcd},
 | 
			
		||||
    lvgl {lvgl},
 | 
			
		||||
    touchPanel {touchPanel},
 | 
			
		||||
    batteryController {batteryController},
 | 
			
		||||
    bleController {bleController},
 | 
			
		||||
    dateTimeController {dateTimeController},
 | 
			
		||||
    watchdog {watchdog},
 | 
			
		||||
    systemTask {systemTask},
 | 
			
		||||
    notificationManager {notificationManager},
 | 
			
		||||
    heartRateController {heartRateController},
 | 
			
		||||
    settingsController {settingsController},
 | 
			
		||||
    motionController {motionController} {
 | 
			
		||||
  msgQueue = xQueueCreate(queueSize, itemSize);
 | 
			
		||||
  // Start clock when smartwatch boots
 | 
			
		||||
  LoadApp( Apps::Clock, DisplayApp::FullRefreshDirections::None );
 | 
			
		||||
  LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::Start() {
 | 
			
		||||
@ -72,8 +76,8 @@ void DisplayApp::Start() {
 | 
			
		||||
    APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::Process(void *instance) {
 | 
			
		||||
  auto *app = static_cast<DisplayApp *>(instance);
 | 
			
		||||
void DisplayApp::Process(void* instance) {
 | 
			
		||||
  auto* app = static_cast<DisplayApp*>(instance);
 | 
			
		||||
  NRF_LOG_INFO("displayapp task started!");
 | 
			
		||||
  app->InitHw();
 | 
			
		||||
 | 
			
		||||
@ -114,7 +118,7 @@ void DisplayApp::Refresh() {
 | 
			
		||||
    switch (msg) {
 | 
			
		||||
      case Messages::GoToSleep:
 | 
			
		||||
        brightnessController.Backup();
 | 
			
		||||
        while(brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
 | 
			
		||||
        while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
 | 
			
		||||
          brightnessController.Lower();
 | 
			
		||||
          vTaskDelay(100);
 | 
			
		||||
        }
 | 
			
		||||
@ -129,30 +133,32 @@ void DisplayApp::Refresh() {
 | 
			
		||||
        break;
 | 
			
		||||
      case Messages::UpdateTimeOut:
 | 
			
		||||
        systemTask.PushMessage(System::SystemTask::Messages::UpdateTimeOut);
 | 
			
		||||
      break;
 | 
			
		||||
        break;
 | 
			
		||||
      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;
 | 
			
		||||
      case Messages::UpdateBatteryLevel:
 | 
			
		||||
        batteryController.Update();
 | 
			
		||||
        break;
 | 
			
		||||
      case Messages::NewNotification:
 | 
			
		||||
        LoadApp( Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down );
 | 
			
		||||
        LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down);
 | 
			
		||||
        break;
 | 
			
		||||
      case Messages::TouchEvent: {
 | 
			
		||||
        if (state != States::Running) break;
 | 
			
		||||
        if (state != States::Running)
 | 
			
		||||
          break;
 | 
			
		||||
        auto gesture = OnTouchEvent();
 | 
			
		||||
        if(!currentScreen->OnTouchEvent(gesture)) {
 | 
			
		||||
          if ( currentApp == Apps::Clock ) {
 | 
			
		||||
        if (!currentScreen->OnTouchEvent(gesture)) {
 | 
			
		||||
          if (currentApp == Apps::Clock) {
 | 
			
		||||
            switch (gesture) {
 | 
			
		||||
              case TouchEvents::SwipeUp:
 | 
			
		||||
                LoadApp( Apps::Launcher, DisplayApp::FullRefreshDirections::Up );
 | 
			
		||||
                LoadApp(Apps::Launcher, DisplayApp::FullRefreshDirections::Up);
 | 
			
		||||
                break;
 | 
			
		||||
              case TouchEvents::SwipeDown:
 | 
			
		||||
                LoadApp( Apps::Notifications, DisplayApp::FullRefreshDirections::Down );
 | 
			
		||||
                LoadApp(Apps::Notifications, DisplayApp::FullRefreshDirections::Down);
 | 
			
		||||
                break;
 | 
			
		||||
              case TouchEvents::SwipeRight:
 | 
			
		||||
                LoadApp( Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim );
 | 
			
		||||
                LoadApp(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim);
 | 
			
		||||
                break;
 | 
			
		||||
              case TouchEvents::DoubleTap:
 | 
			
		||||
                systemTask.PushMessage(System::SystemTask::Messages::GoToSleep);
 | 
			
		||||
@ -160,36 +166,35 @@ void DisplayApp::Refresh() {
 | 
			
		||||
              default:
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
          } else if ( returnTouchEvent == gesture ) {
 | 
			
		||||
            LoadApp( returnToApp, returnDirection );
 | 
			
		||||
          } else if (returnTouchEvent == gesture) {
 | 
			
		||||
            LoadApp(returnToApp, returnDirection);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
        break;
 | 
			
		||||
      } break;
 | 
			
		||||
      case Messages::ButtonPushed:
 | 
			
		||||
        if( currentApp == Apps::Clock ) {
 | 
			
		||||
        if (currentApp == Apps::Clock) {
 | 
			
		||||
          systemTask.PushMessage(System::SystemTask::Messages::GoToSleep);
 | 
			
		||||
        } else {
 | 
			
		||||
          if ( !currentScreen->OnButtonPushed() ) {
 | 
			
		||||
            LoadApp( returnToApp, returnDirection );
 | 
			
		||||
          if (!currentScreen->OnButtonPushed()) {
 | 
			
		||||
            LoadApp(returnToApp, returnDirection);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      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;
 | 
			
		||||
       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();
 | 
			
		||||
    if(info.action == 2) {// 2 = contact
 | 
			
		||||
      if(!currentScreen->OnTouchEvent(info.x, info.y)) {
 | 
			
		||||
    if (info.action == 2) { // 2 = contact
 | 
			
		||||
      if (!currentScreen->OnTouchEvent(info.x, info.y)) {
 | 
			
		||||
        lvgl.SetNewTapEvent(info.x, info.y);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -197,14 +202,14 @@ void DisplayApp::Refresh() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::RunningState() {
 | 
			
		||||
  if(!currentScreen->Refresh()) {
 | 
			
		||||
    LoadApp( returnToApp, returnDirection );
 | 
			
		||||
  if (!currentScreen->Refresh()) {
 | 
			
		||||
    LoadApp(returnToApp, returnDirection);
 | 
			
		||||
  }
 | 
			
		||||
  lv_task_handler();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) {
 | 
			
		||||
  LoadApp( app, direction );
 | 
			
		||||
  LoadApp(app, direction);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::returnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent) {
 | 
			
		||||
@ -215,19 +220,26 @@ void DisplayApp::returnApp(Apps app, DisplayApp::FullRefreshDirections direction
 | 
			
		||||
 | 
			
		||||
void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) {
 | 
			
		||||
  currentScreen.reset(nullptr);
 | 
			
		||||
  SetFullRefresh( direction );
 | 
			
		||||
  SetFullRefresh(direction);
 | 
			
		||||
 | 
			
		||||
  // default return to launcher
 | 
			
		||||
  returnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::SwipeDown);
 | 
			
		||||
 | 
			
		||||
  switch(app) {
 | 
			
		||||
  switch (app) {
 | 
			
		||||
    case Apps::Launcher:
 | 
			
		||||
      currentScreen = std::make_unique<Screens::ApplicationList>(this, settingsController, batteryController, dateTimeController);
 | 
			
		||||
      returnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::SwipeDown);
 | 
			
		||||
      break;
 | 
			
		||||
    case Apps::None:
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    case Apps::FirmwareValidation:
 | 
			
		||||
@ -239,17 +251,20 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
      break;
 | 
			
		||||
    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);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    // Settings
 | 
			
		||||
    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);
 | 
			
		||||
      break;
 | 
			
		||||
    case Apps::Settings:
 | 
			
		||||
@ -281,10 +296,11 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
 | 
			
		||||
      returnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
 | 
			
		||||
      break;
 | 
			
		||||
    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);
 | 
			
		||||
      break;
 | 
			
		||||
    //
 | 
			
		||||
      //
 | 
			
		||||
 | 
			
		||||
    case Apps::FlashLight:
 | 
			
		||||
      currentScreen = std::make_unique<Screens::FlashLight>(this, systemTask, brightnessController);
 | 
			
		||||
@ -322,7 +338,6 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::IdleState() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::PushMessage(Messages msg) {
 | 
			
		||||
@ -337,10 +352,10 @@ void DisplayApp::PushMessage(Messages msg) {
 | 
			
		||||
 | 
			
		||||
TouchEvents DisplayApp::OnTouchEvent() {
 | 
			
		||||
  auto info = touchPanel.GetTouchInfo();
 | 
			
		||||
  if(info.isTouch) {
 | 
			
		||||
    switch(info.gesture) {
 | 
			
		||||
  if (info.isTouch) {
 | 
			
		||||
    switch (info.gesture) {
 | 
			
		||||
      case Pinetime::Drivers::Cst816S::Gestures::SingleTap:
 | 
			
		||||
        if(touchMode == TouchModes::Gestures)
 | 
			
		||||
        if (touchMode == TouchModes::Gestures)
 | 
			
		||||
          lvgl.SetNewTapEvent(info.x, info.y);
 | 
			
		||||
        return TouchEvents::Tap;
 | 
			
		||||
      case Pinetime::Drivers::Cst816S::Gestures::LongPress:
 | 
			
		||||
@ -364,7 +379,7 @@ TouchEvents DisplayApp::OnTouchEvent() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
 | 
			
		||||
  switch(direction){
 | 
			
		||||
  switch (direction) {
 | 
			
		||||
    case DisplayApp::FullRefreshDirections::Down:
 | 
			
		||||
      lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Down);
 | 
			
		||||
      break;
 | 
			
		||||
@ -383,11 +398,11 @@ void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
 | 
			
		||||
    case DisplayApp::FullRefreshDirections::RightAnim:
 | 
			
		||||
      lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::RightAnim);
 | 
			
		||||
      break;
 | 
			
		||||
    default: break;
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::SetTouchMode(DisplayApp::TouchModes mode) {
 | 
			
		||||
  touchMode = mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -35,74 +35,73 @@ namespace Pinetime {
 | 
			
		||||
  };
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    class DisplayApp {
 | 
			
		||||
      public:
 | 
			
		||||
        enum class States {Idle, Running};
 | 
			
		||||
        enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
 | 
			
		||||
        enum class TouchModes { Gestures, Polling };
 | 
			
		||||
    public:
 | 
			
		||||
      enum class States { Idle, Running };
 | 
			
		||||
      enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
 | 
			
		||||
      enum class TouchModes { Gestures, Polling };
 | 
			
		||||
 | 
			
		||||
        DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &,
 | 
			
		||||
                   Controllers::Battery &batteryController, Controllers::Ble &bleController,
 | 
			
		||||
                   Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog,
 | 
			
		||||
                   System::SystemTask &systemTask,
 | 
			
		||||
                   Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                   Pinetime::Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                   Controllers::Settings &settingsController,
 | 
			
		||||
                   Pinetime::Controllers::MotionController& motionController
 | 
			
		||||
                   );
 | 
			
		||||
        void Start();
 | 
			
		||||
        void PushMessage(Display::Messages msg);
 | 
			
		||||
      DisplayApp(Drivers::St7789& lcd,
 | 
			
		||||
                 Components::LittleVgl& lvgl,
 | 
			
		||||
                 Drivers::Cst816S&,
 | 
			
		||||
                 Controllers::Battery& batteryController,
 | 
			
		||||
                 Controllers::Ble& bleController,
 | 
			
		||||
                 Controllers::DateTime& dateTimeController,
 | 
			
		||||
                 Drivers::WatchdogView& watchdog,
 | 
			
		||||
                 System::SystemTask& systemTask,
 | 
			
		||||
                 Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                 Pinetime::Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                 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 SetTouchMode(TouchModes mode);
 | 
			
		||||
      void SetFullRefresh(FullRefreshDirections direction);
 | 
			
		||||
      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::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::Controllers::FirmwareValidator validator;
 | 
			
		||||
      Controllers::BrightnessController brightnessController;
 | 
			
		||||
 | 
			
		||||
        Pinetime::Controllers::FirmwareValidator validator;
 | 
			
		||||
        Controllers::BrightnessController brightnessController;
 | 
			
		||||
      TaskHandle_t taskHandle;
 | 
			
		||||
 | 
			
		||||
        TaskHandle_t taskHandle;
 | 
			
		||||
      States state = States::Running;
 | 
			
		||||
      QueueHandle_t msgQueue;
 | 
			
		||||
 | 
			
		||||
        States state = States::Running;
 | 
			
		||||
        QueueHandle_t msgQueue;
 | 
			
		||||
      static constexpr uint8_t queueSize = 10;
 | 
			
		||||
      static constexpr uint8_t itemSize = 1;
 | 
			
		||||
 | 
			
		||||
        static constexpr uint8_t queueSize = 10;
 | 
			
		||||
        static constexpr uint8_t itemSize = 1;
 | 
			
		||||
      std::unique_ptr<Screens::Screen> currentScreen;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        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);
 | 
			
		||||
      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);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,17 +7,20 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Applications;
 | 
			
		||||
 | 
			
		||||
DisplayApp::DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &touchPanel,
 | 
			
		||||
                       Controllers::Battery &batteryController, Controllers::Ble &bleController,
 | 
			
		||||
                       Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog,
 | 
			
		||||
                       System::SystemTask &systemTask,
 | 
			
		||||
DisplayApp::DisplayApp(Drivers::St7789& lcd,
 | 
			
		||||
                       Components::LittleVgl& lvgl,
 | 
			
		||||
                       Drivers::Cst816S& touchPanel,
 | 
			
		||||
                       Controllers::Battery& batteryController,
 | 
			
		||||
                       Controllers::Ble& bleController,
 | 
			
		||||
                       Controllers::DateTime& dateTimeController,
 | 
			
		||||
                       Drivers::WatchdogView& watchdog,
 | 
			
		||||
                       System::SystemTask& systemTask,
 | 
			
		||||
                       Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                       Pinetime::Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                       Pinetime::Controllers::Settings& settingsController,
 | 
			
		||||
                       Pinetime::Controllers::MotionController& motionController):
 | 
			
		||||
    lcd{lcd}, bleController{bleController} {
 | 
			
		||||
                       Pinetime::Controllers::MotionController& motionController)
 | 
			
		||||
  : lcd {lcd}, bleController {bleController} {
 | 
			
		||||
  msgQueue = xQueueCreate(queueSize, itemSize);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::Start() {
 | 
			
		||||
@ -25,8 +28,8 @@ void DisplayApp::Start() {
 | 
			
		||||
    APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::Process(void *instance) {
 | 
			
		||||
  auto *app = static_cast<DisplayApp *>(instance);
 | 
			
		||||
void DisplayApp::Process(void* instance) {
 | 
			
		||||
  auto* app = static_cast<DisplayApp*>(instance);
 | 
			
		||||
  NRF_LOG_INFO("displayapp task started!");
 | 
			
		||||
 | 
			
		||||
  // Send a dummy notification to unlock the lvgl display driver for the first iteration
 | 
			
		||||
@ -61,8 +64,9 @@ void DisplayApp::Refresh() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (bleController.IsFirmwareUpdating()) {
 | 
			
		||||
    uint8_t percent = (static_cast<float>(bleController.FirmwareUpdateCurrentBytes()) /
 | 
			
		||||
                       static_cast<float>(bleController.FirmwareUpdateTotalBytes())) * 100.0f;
 | 
			
		||||
    uint8_t percent =
 | 
			
		||||
      (static_cast<float>(bleController.FirmwareUpdateCurrentBytes()) / static_cast<float>(bleController.FirmwareUpdateTotalBytes())) *
 | 
			
		||||
      100.0f;
 | 
			
		||||
    switch (bleController.State()) {
 | 
			
		||||
      case Controllers::Ble::FirmwareUpdateStates::Running:
 | 
			
		||||
        DisplayOtaProgress(percent, colorWhite);
 | 
			
		||||
@ -81,20 +85,20 @@ void DisplayApp::Refresh() {
 | 
			
		||||
 | 
			
		||||
void DisplayApp::DisplayLogo(uint16_t color) {
 | 
			
		||||
  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);
 | 
			
		||||
    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) {
 | 
			
		||||
  const uint8_t barHeight = 20;
 | 
			
		||||
  std::fill(displayBuffer, displayBuffer+(displayWidth * bytesPerPixel), color);
 | 
			
		||||
  for(int i = 0; i < barHeight; i++) {
 | 
			
		||||
  std::fill(displayBuffer, displayBuffer + (displayWidth * bytesPerPixel), color);
 | 
			
		||||
  for (int i = 0; i < barHeight; i++) {
 | 
			
		||||
    ulTaskNotifyTake(pdTRUE, 500);
 | 
			
		||||
    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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,10 +30,14 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    class DisplayApp {
 | 
			
		||||
    public:
 | 
			
		||||
      DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Drivers::Cst816S &,
 | 
			
		||||
                 Controllers::Battery &batteryController, Controllers::Ble &bleController,
 | 
			
		||||
                 Controllers::DateTime &dateTimeController, Drivers::WatchdogView &watchdog,
 | 
			
		||||
                 System::SystemTask &systemTask,
 | 
			
		||||
      DisplayApp(Drivers::St7789& lcd,
 | 
			
		||||
                 Components::LittleVgl& lvgl,
 | 
			
		||||
                 Drivers::Cst816S&,
 | 
			
		||||
                 Controllers::Battery& batteryController,
 | 
			
		||||
                 Controllers::Ble& bleController,
 | 
			
		||||
                 Controllers::DateTime& dateTimeController,
 | 
			
		||||
                 Drivers::WatchdogView& watchdog,
 | 
			
		||||
                 System::SystemTask& systemTask,
 | 
			
		||||
                 Pinetime::Controllers::NotificationManager& notificationManager,
 | 
			
		||||
                 Pinetime::Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                 Pinetime::Controllers::Settings& settingsController,
 | 
			
		||||
@ -49,7 +53,7 @@ namespace Pinetime {
 | 
			
		||||
      void InitHw();
 | 
			
		||||
      void Refresh();
 | 
			
		||||
      Pinetime::Drivers::St7789& lcd;
 | 
			
		||||
      Controllers::Ble &bleController;
 | 
			
		||||
      Controllers::Ble& bleController;
 | 
			
		||||
 | 
			
		||||
      static constexpr uint8_t queueSize = 10;
 | 
			
		||||
      static constexpr uint8_t itemSize = 1;
 | 
			
		||||
@ -66,10 +70,6 @@ namespace Pinetime {
 | 
			
		||||
      static constexpr uint16_t colorRedSwapped = 0x00ff;
 | 
			
		||||
      static constexpr uint16_t colorBlack = 0x0000;
 | 
			
		||||
      uint8_t displayBuffer[displayWidth * bytesPerPixel];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,22 +9,25 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Components {
 | 
			
		||||
    class LittleVgl {
 | 
			
		||||
      public:
 | 
			
		||||
        enum class FullRefreshDirections { None, Up, Down };
 | 
			
		||||
        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) {}
 | 
			
		||||
    public:
 | 
			
		||||
      enum class FullRefreshDirections { None, Up, Down };
 | 
			
		||||
      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) {
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,17 +11,18 @@ using namespace Pinetime::Components;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
  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);
 | 
			
		||||
  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();
 | 
			
		||||
  InitTheme();
 | 
			
		||||
  InitDisplay();
 | 
			
		||||
@ -29,8 +30,8 @@ LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S&
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_drv_init(&disp_drv);                    /*Basic initialization*/
 | 
			
		||||
  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*/
 | 
			
		||||
 | 
			
		||||
  /*Set up the functions to access to your display*/
 | 
			
		||||
 | 
			
		||||
@ -59,7 +60,7 @@ void LittleVgl::InitTouchpad() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LittleVgl::SetFullRefresh(FullRefreshDirections direction) {
 | 
			
		||||
  if(scrollDirection == FullRefreshDirections::None) {
 | 
			
		||||
  if (scrollDirection == FullRefreshDirections::None) {
 | 
			
		||||
    scrollDirection = direction;
 | 
			
		||||
    if (scrollDirection == FullRefreshDirections::Down) {
 | 
			
		||||
      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;
 | 
			
		||||
 | 
			
		||||
  ulTaskNotifyTake(pdTRUE, 200);
 | 
			
		||||
  // 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.
 | 
			
		||||
  
 | 
			
		||||
  if( (scrollDirection == LittleVgl::FullRefreshDirections::Down) && (area->y2 == visibleNbLines - 1)) {
 | 
			
		||||
 | 
			
		||||
  if ((scrollDirection == LittleVgl::FullRefreshDirections::Down) && (area->y2 == visibleNbLines - 1)) {
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -94,11 +95,11 @@ void LittleVgl::FlushDisplay(const lv_area_t *area, lv_color_t *color_p) {
 | 
			
		||||
  width = (area->x2 - area->x1) + 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;
 | 
			
		||||
        if(area->y1 == 0) {
 | 
			
		||||
      if (area->y1 == 0) {
 | 
			
		||||
        toScroll = height * 2;
 | 
			
		||||
        scrollDirection = FullRefreshDirections::None;
 | 
			
		||||
        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;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if(scrollOffset >= toScroll)
 | 
			
		||||
      if (scrollOffset >= toScroll)
 | 
			
		||||
        scrollOffset -= toScroll;
 | 
			
		||||
      else {
 | 
			
		||||
        toScroll -= scrollOffset;
 | 
			
		||||
        scrollOffset = (totalNbLines) - toScroll;
 | 
			
		||||
        scrollOffset = (totalNbLines) -toScroll;
 | 
			
		||||
      }
 | 
			
		||||
      lcd.VerticalScrollStartAddress(scrollOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  } else if(scrollDirection == FullRefreshDirections::Up) {
 | 
			
		||||
  } else if (scrollDirection == FullRefreshDirections::Up) {
 | 
			
		||||
 | 
			
		||||
    if(area->y1 > 0) {
 | 
			
		||||
      if(area->y2 == visibleNbLines - 1) {
 | 
			
		||||
    if (area->y1 > 0) {
 | 
			
		||||
      if (area->y2 == visibleNbLines - 1) {
 | 
			
		||||
        scrollOffset += (height * 2);
 | 
			
		||||
        scrollDirection = FullRefreshDirections::None;
 | 
			
		||||
        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;
 | 
			
		||||
      lcd.VerticalScrollStartAddress(scrollOffset);
 | 
			
		||||
    }
 | 
			
		||||
  } else if(scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) {
 | 
			
		||||
    if(area->x2 == visibleNbLines - 1) {
 | 
			
		||||
  } else if (scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) {
 | 
			
		||||
    if (area->x2 == visibleNbLines - 1) {
 | 
			
		||||
      scrollDirection = FullRefreshDirections::None;
 | 
			
		||||
      lv_disp_set_direction(lv_disp_get_default(), 0);
 | 
			
		||||
    }
 | 
			
		||||
  } else if(scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) {
 | 
			
		||||
    if(area->x1 == 0) {
 | 
			
		||||
  } else if (scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) {
 | 
			
		||||
    if (area->x1 == 0) {
 | 
			
		||||
      scrollDirection = FullRefreshDirections::None;
 | 
			
		||||
      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) {
 | 
			
		||||
    height = totalNbLines - y1;
 | 
			
		||||
 | 
			
		||||
    if ( height > 0 ) {
 | 
			
		||||
      lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t *>(color_p), width * height * 2);
 | 
			
		||||
    if (height > 0) {
 | 
			
		||||
      lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
 | 
			
		||||
      ulTaskNotifyTake(pdTRUE, 100);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    uint16_t pixOffset = width * height;
 | 
			
		||||
    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 {
 | 
			
		||||
    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!!!
 | 
			
		||||
@ -167,8 +168,8 @@ void LittleVgl::SetNewTapEvent(uint16_t x, uint16_t y) {
 | 
			
		||||
  tapped = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t *ptr) {
 | 
			
		||||
  if(tapped) {
 | 
			
		||||
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
 | 
			
		||||
  if (tapped) {
 | 
			
		||||
    ptr->point.x = tap_x;
 | 
			
		||||
    ptr->point.y = tap_y;
 | 
			
		||||
    ptr->state = LV_INDEV_STATE_PR;
 | 
			
		||||
@ -202,14 +203,8 @@ bool LittleVgl::GetTouchPadInfo(lv_indev_data_t *ptr) {
 | 
			
		||||
 | 
			
		||||
void LittleVgl::InitTheme() {
 | 
			
		||||
 | 
			
		||||
  lv_theme_t * th = lv_pinetime_theme_init(
 | 
			
		||||
    LV_COLOR_WHITE, LV_COLOR_SILVER,
 | 
			
		||||
    0, 
 | 
			
		||||
    &jetbrains_mono_bold_20, 
 | 
			
		||||
    &jetbrains_mono_bold_20,
 | 
			
		||||
    &jetbrains_mono_bold_20, 
 | 
			
		||||
    &jetbrains_mono_bold_20);
 | 
			
		||||
  lv_theme_t* th = lv_pinetime_theme_init(
 | 
			
		||||
    LV_COLOR_WHITE, LV_COLOR_SILVER, 0, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20);
 | 
			
		||||
 | 
			
		||||
  lv_theme_set_act(th);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,50 +10,49 @@ namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
  namespace Components {
 | 
			
		||||
    class LittleVgl {
 | 
			
		||||
      public:
 | 
			
		||||
        enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
 | 
			
		||||
        LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel);
 | 
			
		||||
    public:
 | 
			
		||||
      enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
 | 
			
		||||
      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;
 | 
			
		||||
      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);
 | 
			
		||||
        void SetFullRefresh(FullRefreshDirections direction);
 | 
			
		||||
        void SetNewTapEvent(uint16_t x, uint16_t y);
 | 
			
		||||
      void FlushDisplay(const lv_area_t* area, lv_color_t* color_p);
 | 
			
		||||
      bool GetTouchPadInfo(lv_indev_data_t* ptr);
 | 
			
		||||
      void SetFullRefresh(FullRefreshDirections direction);
 | 
			
		||||
      void SetNewTapEvent(uint16_t x, uint16_t y);
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        void InitDisplay();
 | 
			
		||||
        void InitTouchpad();
 | 
			
		||||
        void InitTheme();
 | 
			
		||||
    private:
 | 
			
		||||
      void InitDisplay();
 | 
			
		||||
      void InitTouchpad();
 | 
			
		||||
      void InitTheme();
 | 
			
		||||
 | 
			
		||||
      Pinetime::Drivers::St7789& lcd;
 | 
			
		||||
      Pinetime::Drivers::Cst816S& touchPanel;
 | 
			
		||||
 | 
			
		||||
        Pinetime::Drivers::St7789& lcd;
 | 
			
		||||
        Pinetime::Drivers::Cst816S& touchPanel;
 | 
			
		||||
      lv_disp_buf_t disp_buf_2;
 | 
			
		||||
      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;
 | 
			
		||||
        lv_color_t buf2_1[LV_HOR_RES_MAX * 4];
 | 
			
		||||
        lv_color_t buf2_2[LV_HOR_RES_MAX * 4];
 | 
			
		||||
      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;
 | 
			
		||||
 | 
			
		||||
        lv_disp_drv_t disp_drv;
 | 
			
		||||
        lv_point_t previousClick;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
      uint16_t tap_x = 0;
 | 
			
		||||
      uint16_t tap_y = 0;
 | 
			
		||||
      bool tapped = false;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,16 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Display {
 | 
			
		||||
      enum class Messages : uint8_t {
 | 
			
		||||
        GoToSleep, GoToRunning, UpdateDateTime, UpdateBleConnection, UpdateBatteryLevel, TouchEvent, ButtonPushed,
 | 
			
		||||
        NewNotification, BleFirmwareUpdateStarted, UpdateTimeOut
 | 
			
		||||
        GoToSleep,
 | 
			
		||||
        GoToRunning,
 | 
			
		||||
        UpdateDateTime,
 | 
			
		||||
        UpdateBleConnection,
 | 
			
		||||
        UpdateBatteryLevel,
 | 
			
		||||
        TouchEvent,
 | 
			
		||||
        ButtonPushed,
 | 
			
		||||
        NewNotification,
 | 
			
		||||
        BleFirmwareUpdateStarted,
 | 
			
		||||
        UpdateTimeOut
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,6 @@
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
 | 
			
		||||
    enum class TouchEvents { None, Tap, SwipeLeft, SwipeRight, SwipeUp, SwipeDown, LongTap, DoubleTap};
 | 
			
		||||
    enum class TouchEvents { None, Tap, SwipeLeft, SwipeRight, SwipeUp, SwipeDown, LongTap, DoubleTap };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								src/displayapp/fonts/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/displayapp/fonts/README.md
									
									
									
									
									
										Normal 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
 | 
			
		||||
@ -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
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
/**********************
 | 
			
		||||
 *  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
 | 
			
		||||
@ -67,244 +67,240 @@ static bool inited;
 | 
			
		||||
 *   STATIC FUNCTIONS
 | 
			
		||||
 **********************/
 | 
			
		||||
 | 
			
		||||
static void style_init_reset(lv_style_t * style)
 | 
			
		||||
{
 | 
			
		||||
    if(inited) lv_style_reset(style);
 | 
			
		||||
    else lv_style_init(style);
 | 
			
		||||
static void style_init_reset(lv_style_t* style) {
 | 
			
		||||
  if (inited)
 | 
			
		||||
    lv_style_reset(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);
 | 
			
		||||
    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_circle);
 | 
			
		||||
  lv_style_set_radius(&style_circle, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_circle);
 | 
			
		||||
    lv_style_set_radius(&style_circle, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
  style_init_reset(&style_bg);
 | 
			
		||||
  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);
 | 
			
		||||
    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_box);
 | 
			
		||||
  lv_style_set_bg_opa(&style_box, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
  lv_style_set_radius(&style_box, LV_STATE_DEFAULT, 10);
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_bg_opa(&style_box, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
    lv_style_set_radius(&style_box, LV_STATE_DEFAULT, 10);
 | 
			
		||||
    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_border);
 | 
			
		||||
  lv_style_set_bg_opa(&style_box_border, LV_STATE_DEFAULT, LV_OPA_TRANSP);
 | 
			
		||||
  lv_style_set_border_width(&style_box_border, LV_STATE_DEFAULT, 2);
 | 
			
		||||
  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_box_border);
 | 
			
		||||
    lv_style_set_bg_opa(&style_box_border, LV_STATE_DEFAULT, LV_OPA_TRANSP);
 | 
			
		||||
    lv_style_set_border_width(&style_box_border, LV_STATE_DEFAULT, 2);
 | 
			
		||||
    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_title);
 | 
			
		||||
  lv_style_set_text_color(&style_title, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
  lv_style_set_text_font(&style_title, LV_STATE_DEFAULT, theme.font_subtitle);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_text_color(&style_title, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
    lv_style_set_text_font(&style_title, LV_STATE_DEFAULT, theme.font_subtitle);
 | 
			
		||||
  style_init_reset(&style_btn);
 | 
			
		||||
  lv_style_set_radius(&style_btn, LV_STATE_DEFAULT, 10);
 | 
			
		||||
  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_label_white, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
  lv_style_set_text_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
 | 
			
		||||
  lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
 | 
			
		||||
  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_radius(&style_btn, LV_STATE_DEFAULT, 10);
 | 
			
		||||
    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);
 | 
			
		||||
  lv_style_set_value_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
 | 
			
		||||
  lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
 | 
			
		||||
  lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
 | 
			
		||||
  lv_style_set_value_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888));
 | 
			
		||||
 | 
			
		||||
    lv_style_set_text_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
 | 
			
		||||
    lv_style_set_text_color(&style_btn, LV_STATE_CHECKED,  lv_color_hex(0xffffff));
 | 
			
		||||
    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));
 | 
			
		||||
  lv_style_set_pad_left(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
  lv_style_set_pad_right(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
    lv_style_set_value_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
 | 
			
		||||
    lv_style_set_value_color(&style_btn, LV_STATE_CHECKED,  lv_color_hex(0xffffff));
 | 
			
		||||
    lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
 | 
			
		||||
    lv_style_set_value_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888));
 | 
			
		||||
  style_init_reset(&style_btn_border);
 | 
			
		||||
  lv_style_set_radius(&style_btn_border, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
  lv_style_set_border_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
  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));
 | 
			
		||||
    lv_style_set_pad_right(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
    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_icon);
 | 
			
		||||
  lv_style_set_text_color(&style_icon, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_btn_border);
 | 
			
		||||
    lv_style_set_radius(&style_btn_border, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
    lv_style_set_border_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
    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);
 | 
			
		||||
  style_init_reset(&style_back);
 | 
			
		||||
  lv_style_set_value_color(&style_back, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
  lv_style_set_value_str(&style_back, LV_STATE_DEFAULT, LV_SYMBOL_LEFT);
 | 
			
		||||
  lv_style_set_value_font(&style_back, LV_STATE_DEFAULT, theme.font_subtitle);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_icon);
 | 
			
		||||
    lv_style_set_text_color(&style_icon, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
  style_init_reset(&style_bar_indic);
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_value_color(&style_back, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
    lv_style_set_value_str(&style_back, LV_STATE_DEFAULT, LV_SYMBOL_LEFT);
 | 
			
		||||
    lv_style_set_value_font(&style_back, LV_STATE_DEFAULT, theme.font_subtitle);
 | 
			
		||||
  style_init_reset(&style_scrollbar);
 | 
			
		||||
  lv_style_set_bg_opa(&style_scrollbar, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
  lv_style_set_radius(&style_scrollbar, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
  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);
 | 
			
		||||
    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_list_btn);
 | 
			
		||||
  lv_style_set_bg_opa(&style_list_btn, 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_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);
 | 
			
		||||
    lv_style_set_bg_opa(&style_scrollbar, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
    lv_style_set_radius(&style_scrollbar, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
    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_ddlist_list);
 | 
			
		||||
  lv_style_set_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25);
 | 
			
		||||
  lv_style_set_shadow_width(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 20);
 | 
			
		||||
  lv_style_set_shadow_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_list_btn);
 | 
			
		||||
    lv_style_set_bg_opa(&style_list_btn, 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_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_selected);
 | 
			
		||||
  lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
  lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_ddlist_list);
 | 
			
		||||
    lv_style_set_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25);
 | 
			
		||||
    lv_style_set_shadow_width(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 20);
 | 
			
		||||
    lv_style_set_shadow_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
  style_init_reset(&style_sw_bg);
 | 
			
		||||
  lv_style_set_bg_opa(&style_sw_bg, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
  lv_style_set_bg_color(&style_sw_bg, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_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);
 | 
			
		||||
    lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
    lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
 | 
			
		||||
  style_init_reset(&style_sw_indic);
 | 
			
		||||
  lv_style_set_bg_opa(&style_sw_indic, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
  lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, LV_PINETIME_GREEN);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_sw_bg);
 | 
			
		||||
    lv_style_set_bg_opa(&style_sw_bg, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
    lv_style_set_bg_color(&style_sw_bg, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_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_sw_knob);
 | 
			
		||||
  lv_style_set_bg_opa(&style_sw_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
  lv_style_set_bg_color(&style_sw_knob, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
  lv_style_set_radius(&style_sw_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_bg_opa(&style_sw_indic, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
    lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, LV_PINETIME_GREEN);
 | 
			
		||||
  style_init_reset(&style_slider_knob);
 | 
			
		||||
  lv_style_set_bg_opa(&style_slider_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_bg_opa(&style_sw_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
    lv_style_set_bg_color(&style_sw_knob, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
    lv_style_set_radius(&style_sw_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
    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_arc_indic);
 | 
			
		||||
  lv_style_set_line_color(&style_arc_indic, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
 | 
			
		||||
  lv_style_set_line_width(&style_arc_indic, LV_STATE_DEFAULT, LV_DPX(25));
 | 
			
		||||
  lv_style_set_line_rounded(&style_arc_indic, LV_STATE_DEFAULT, true);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_slider_knob);
 | 
			
		||||
    lv_style_set_bg_opa(&style_slider_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
 | 
			
		||||
    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_arc_bg);
 | 
			
		||||
  lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
  lv_style_set_line_width(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(25));
 | 
			
		||||
  lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_arc_indic);
 | 
			
		||||
    lv_style_set_line_color(&style_arc_indic, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
 | 
			
		||||
    lv_style_set_line_width(&style_arc_indic, LV_STATE_DEFAULT, LV_DPX(25));
 | 
			
		||||
    lv_style_set_line_rounded(&style_arc_indic, LV_STATE_DEFAULT, true);
 | 
			
		||||
  style_init_reset(&style_table_cell);
 | 
			
		||||
  lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
  lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1);
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
    lv_style_set_line_width(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(25));
 | 
			
		||||
    lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true);
 | 
			
		||||
  style_init_reset(&style_pad_small);
 | 
			
		||||
  lv_style_int_t pad_small_value = 10;
 | 
			
		||||
  lv_style_set_pad_left(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
 | 
			
		||||
    lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1);
 | 
			
		||||
    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_bg_grad);
 | 
			
		||||
  lv_style_set_bg_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 40));
 | 
			
		||||
  lv_style_set_bg_grad_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 20));
 | 
			
		||||
  lv_style_set_bg_grad_dir(&style_bg_grad, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_pad_small);
 | 
			
		||||
    lv_style_int_t pad_small_value = 10;
 | 
			
		||||
    lv_style_set_pad_left(&style_pad_small, LV_STATE_DEFAULT,  pad_small_value);
 | 
			
		||||
    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_lmeter);
 | 
			
		||||
  lv_style_set_radius(&style_lmeter, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
  lv_style_set_pad_left(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
  lv_style_set_pad_right(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
  lv_style_set_pad_top(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
  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));
 | 
			
		||||
 | 
			
		||||
    style_init_reset(&style_bg_grad);
 | 
			
		||||
    lv_style_set_bg_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 40));
 | 
			
		||||
    lv_style_set_bg_grad_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 20));
 | 
			
		||||
    lv_style_set_bg_grad_dir(&style_bg_grad, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
 | 
			
		||||
  lv_style_set_line_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary);
 | 
			
		||||
  lv_style_set_scale_grad_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary);
 | 
			
		||||
  lv_style_set_scale_end_color(&style_lmeter, LV_STATE_DEFAULT, lv_color_hex3(0x888));
 | 
			
		||||
  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);
 | 
			
		||||
    lv_style_set_radius(&style_lmeter, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
 | 
			
		||||
    lv_style_set_pad_left(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
    lv_style_set_pad_right(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
    lv_style_set_pad_top(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
 | 
			
		||||
    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));
 | 
			
		||||
  style_init_reset(&style_chart_serie);
 | 
			
		||||
  lv_style_set_line_color(&style_chart_serie, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
  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_set_line_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary);
 | 
			
		||||
    lv_style_set_scale_grad_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary);
 | 
			
		||||
    lv_style_set_scale_end_color(&style_lmeter, LV_STATE_DEFAULT, lv_color_hex3(0x888));
 | 
			
		||||
    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_chart_serie);
 | 
			
		||||
    lv_style_set_line_color(&style_chart_serie, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
 | 
			
		||||
    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_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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**********************
 | 
			
		||||
 *   GLOBAL FUNCTIONS
 | 
			
		||||
 **********************/
 | 
			
		||||
@ -320,221 +316,216 @@ static void basic_init(void)
 | 
			
		||||
 * @param font_title pointer to a extra large font
 | 
			
		||||
 * @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,
 | 
			
		||||
                                    const lv_font_t * font_small, const lv_font_t * font_normal, const lv_font_t * font_subtitle,
 | 
			
		||||
                                    const lv_font_t * font_title)
 | 
			
		||||
{
 | 
			
		||||
    theme.color_primary = color_primary;
 | 
			
		||||
    theme.color_secondary = color_secondary;
 | 
			
		||||
    theme.font_small = font_small;
 | 
			
		||||
    theme.font_normal = font_normal;
 | 
			
		||||
    theme.font_subtitle = font_subtitle;
 | 
			
		||||
    theme.font_title = font_title;
 | 
			
		||||
    theme.flags = flags;
 | 
			
		||||
lv_theme_t* lv_pinetime_theme_init(lv_color_t color_primary,
 | 
			
		||||
                                   lv_color_t color_secondary,
 | 
			
		||||
                                   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) {
 | 
			
		||||
  theme.color_primary = color_primary;
 | 
			
		||||
  theme.color_secondary = color_secondary;
 | 
			
		||||
  theme.font_small = font_small;
 | 
			
		||||
  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)
 | 
			
		||||
{
 | 
			
		||||
    lv_style_list_t * list;
 | 
			
		||||
  /*To avoid warnings*/
 | 
			
		||||
  uint32_t name_int = (uint32_t) name;
 | 
			
		||||
  switch (name_int) {
 | 
			
		||||
    case LV_THEME_NONE:
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    /*To avoid warnings*/
 | 
			
		||||
    uint32_t name_int = (uint32_t) name;
 | 
			
		||||
    switch(name_int) {
 | 
			
		||||
        case LV_THEME_NONE:
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_SCR:
 | 
			
		||||
      lv_obj_clean_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_label_white);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_SCR:
 | 
			
		||||
            lv_obj_clean_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_label_white);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_OBJ:
 | 
			
		||||
      lv_obj_clean_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_box);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_OBJ:
 | 
			
		||||
            lv_obj_clean_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_box);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_CONT:
 | 
			
		||||
      lv_obj_clean_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);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_CONT:
 | 
			
		||||
            lv_obj_clean_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);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_BTN:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_btn);
 | 
			
		||||
      //_lv_style_list_add_style(list, &style_bg_grad);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_BTN:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_btn);
 | 
			
		||||
            //_lv_style_list_add_style(list, &style_bg_grad);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_BTNMATRIX:
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BG);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_bg);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_pad_small);
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_BTNMATRIX:
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BG);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_bg);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_pad_small);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BTN);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_btn);
 | 
			
		||||
      //_lv_style_list_add_style(list, &style_bg_grad);
 | 
			
		||||
      //_lv_style_list_add_style(list, &style_bg_click);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BTN);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_btn);
 | 
			
		||||
            //_lv_style_list_add_style(list, &style_bg_grad);
 | 
			
		||||
            //_lv_style_list_add_style(list, &style_bg_click);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_BAR:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_BAR_PART_BG);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_BAR_PART_BG);
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_BAR:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_BAR_PART_BG);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_BAR_PART_BG);
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_BAR_PART_INDIC);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_BAR_PART_INDIC);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_bar_indic);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_BAR_PART_INDIC);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_BAR_PART_INDIC);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_bar_indic);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_IMAGE:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_IMG_PART_MAIN);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_IMG_PART_MAIN);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_icon);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_IMAGE:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_IMG_PART_MAIN);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_IMG_PART_MAIN);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_icon);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_LABEL:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_LABEL_PART_MAIN);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_LABEL_PART_MAIN);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_label_white);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_LABEL:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_LABEL_PART_MAIN);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_LABEL_PART_MAIN);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_label_white);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_SLIDER:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_SLIDER_PART_BG);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_SLIDER_PART_BG);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_sw_bg);
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_SLIDER:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_SLIDER_PART_BG);
 | 
			
		||||
            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);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_SLIDER_PART_INDIC);
 | 
			
		||||
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_SLIDER_PART_INDIC);
 | 
			
		||||
            list = lv_obj_get_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_KNOB);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_slider_knob);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_SLIDER_PART_KNOB);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_SLIDER_PART_KNOB);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_slider_knob);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_LIST:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_LIST_PART_BG);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_LIST_PART_BG);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_box);
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_LIST:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_LIST_PART_BG);
 | 
			
		||||
            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);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_LIST_PART_SCROLLABLE);
 | 
			
		||||
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_LIST_PART_SCROLLABLE);
 | 
			
		||||
            list = lv_obj_get_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_SCROLLBAR);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_scrollbar);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_LIST_PART_SCROLLBAR);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_LIST_PART_SCROLLBAR);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_scrollbar);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_LIST_BTN:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_list_btn);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_LIST_BTN:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_list_btn);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_ARC:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_ARC_PART_BG);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_ARC_PART_BG);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_arc_bg);
 | 
			
		||||
 | 
			
		||||
      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:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_ARC_PART_BG);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_ARC_PART_BG);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_arc_bg);
 | 
			
		||||
    case LV_THEME_SWITCH:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_SWITCH_PART_BG);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_SWITCH_PART_BG);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_sw_bg);
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_SWITCH_PART_INDIC);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_SWITCH_PART_INDIC);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_sw_indic);
 | 
			
		||||
 | 
			
		||||
      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:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_SWITCH_PART_BG);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_SWITCH_PART_BG);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_sw_bg);
 | 
			
		||||
    case LV_THEME_DROPDOWN:
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_MAIN);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_MAIN);
 | 
			
		||||
      _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);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_SWITCH_PART_INDIC);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_sw_indic);
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_LIST);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_LIST);
 | 
			
		||||
      _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);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_SWITCH_PART_KNOB);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_sw_knob);
 | 
			
		||||
            break;
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_SELECTED);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SELECTED);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_ddlist_selected);
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_DROPDOWN:
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_MAIN);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_MAIN);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_btn);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_pad);
 | 
			
		||||
      lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_SCROLLBAR);
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SCROLLBAR);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_scrollbar);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_LIST);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_LIST);
 | 
			
		||||
            _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);
 | 
			
		||||
    case LV_THEME_TABLE:
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_TABLE_PART_BG);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_bg);
 | 
			
		||||
 | 
			
		||||
            lv_obj_clean_style_list(obj, LV_DROPDOWN_PART_SELECTED);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SELECTED);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_ddlist_selected);
 | 
			
		||||
      int idx = 1; /* start value should be 1, not zero, since cell styles
 | 
			
		||||
                  start at 1 due to presence of LV_TABLE_PART_BG=0
 | 
			
		||||
                  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);
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_DROPDOWN_PART_SCROLLBAR);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_scrollbar);
 | 
			
		||||
            break;
 | 
			
		||||
    case LV_THEME_LINEMETER:
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_LINEMETER_PART_MAIN);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_bg);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_lmeter);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
         case LV_THEME_TABLE: 
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_TABLE_PART_BG);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_bg);
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
            int idx = 1; /* start value should be 1, not zero, since cell styles
 | 
			
		||||
                        start at 1 due to presence of LV_TABLE_PART_BG=0
 | 
			
		||||
                        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;
 | 
			
		||||
    case LV_THEME_CHECKBOX:
 | 
			
		||||
      list = lv_obj_get_style_list(obj, LV_CHECKBOX_PART_BG);
 | 
			
		||||
      _lv_style_list_add_style(list, &style_cb_bg);
 | 
			
		||||
 | 
			
		||||
        case LV_THEME_LINEMETER:
 | 
			
		||||
            list = lv_obj_get_style_list(obj, LV_LINEMETER_PART_MAIN);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_bg);
 | 
			
		||||
            _lv_style_list_add_style(list, &style_lmeter);
 | 
			
		||||
            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);
 | 
			
		||||
      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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**********************
 | 
			
		||||
 | 
			
		||||
@ -19,24 +19,22 @@ extern "C" {
 | 
			
		||||
 *      DEFINES
 | 
			
		||||
 *********************/
 | 
			
		||||
/*Colors*/
 | 
			
		||||
#define LV_PINETIME_WHITE           lv_color_hex(0xffffff)
 | 
			
		||||
#define LV_PINETIME_LIGHT           lv_color_hex(0xf3f8fe)
 | 
			
		||||
#define LV_PINETIME_GRAY            lv_color_hex(0x8a8a8a)
 | 
			
		||||
#define LV_PINETIME_LIGHT_GRAY      lv_color_hex(0xc4c4c4)
 | 
			
		||||
#define LV_PINETIME_BLUE            lv_color_hex(0x2f3243) //006fb6
 | 
			
		||||
#define LV_PINETIME_GREEN           lv_color_hex(0x4cb242)
 | 
			
		||||
#define LV_PINETIME_RED             lv_color_hex(0xd51732)
 | 
			
		||||
#define LV_PINETIME_WHITE      lv_color_hex(0xffffff)
 | 
			
		||||
#define LV_PINETIME_LIGHT      lv_color_hex(0xf3f8fe)
 | 
			
		||||
#define LV_PINETIME_GRAY       lv_color_hex(0x8a8a8a)
 | 
			
		||||
#define LV_PINETIME_LIGHT_GRAY lv_color_hex(0xc4c4c4)
 | 
			
		||||
#define LV_PINETIME_BLUE       lv_color_hex(0x2f3243) // 006fb6
 | 
			
		||||
#define LV_PINETIME_GREEN      lv_color_hex(0x4cb242)
 | 
			
		||||
#define LV_PINETIME_RED        lv_color_hex(0xd51732)
 | 
			
		||||
 | 
			
		||||
/**********************
 | 
			
		||||
 *      TYPEDEFS
 | 
			
		||||
 **********************/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**********************
 | 
			
		||||
 * GLOBAL PROTOTYPES
 | 
			
		||||
 **********************/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Initialize the default
 | 
			
		||||
 * @param color_primary the primary color of the theme
 | 
			
		||||
@ -48,9 +46,13 @@ extern "C" {
 | 
			
		||||
 * @param font_title pointer to a extra large font
 | 
			
		||||
 * @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,
 | 
			
		||||
                                    const lv_font_t * font_small, const lv_font_t * font_normal, const lv_font_t * font_subtitle,
 | 
			
		||||
                                    const lv_font_t * font_title);
 | 
			
		||||
lv_theme_t* lv_pinetime_theme_init(lv_color_t color_primary,
 | 
			
		||||
                                   lv_color_t color_secondary,
 | 
			
		||||
                                   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
 | 
			
		||||
 **********************/
 | 
			
		||||
 | 
			
		||||
@ -8,31 +8,34 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp *app,
 | 
			
		||||
          Pinetime::Controllers::Settings &settingsController,
 | 
			
		||||
          Pinetime::Controllers::Battery& batteryController,
 | 
			
		||||
          Controllers::DateTime& dateTimeController) :
 | 
			
		||||
        Screen(app),
 | 
			
		||||
        settingsController{settingsController},
 | 
			
		||||
        batteryController{batteryController},
 | 
			
		||||
        dateTimeController{dateTimeController},
 | 
			
		||||
        screens{app,
 | 
			
		||||
          settingsController.GetAppMenu(),
 | 
			
		||||
          {
 | 
			
		||||
                [this]() -> std::unique_ptr<Screen> { return CreateScreen1(); },
 | 
			
		||||
                [this]() -> std::unique_ptr<Screen> { return CreateScreen2(); },
 | 
			
		||||
                //[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
 | 
			
		||||
          },
 | 
			
		||||
          Screens::ScreenListModes::UpDown
 | 
			
		||||
        } {}
 | 
			
		||||
 | 
			
		||||
ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app,
 | 
			
		||||
                                 Pinetime::Controllers::Settings& settingsController,
 | 
			
		||||
                                 Pinetime::Controllers::Battery& batteryController,
 | 
			
		||||
                                 Controllers::DateTime& dateTimeController)
 | 
			
		||||
  : Screen(app),
 | 
			
		||||
    settingsController {settingsController},
 | 
			
		||||
    batteryController {batteryController},
 | 
			
		||||
    dateTimeController {dateTimeController},
 | 
			
		||||
    screens {app,
 | 
			
		||||
             settingsController.GetAppMenu(),
 | 
			
		||||
             {
 | 
			
		||||
               [this]() -> std::unique_ptr<Screen> {
 | 
			
		||||
                 return CreateScreen1();
 | 
			
		||||
               },
 | 
			
		||||
               [this]() -> std::unique_ptr<Screen> {
 | 
			
		||||
                 return CreateScreen2();
 | 
			
		||||
               },
 | 
			
		||||
               //[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
 | 
			
		||||
             },
 | 
			
		||||
             Screens::ScreenListModes::UpDown} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationList::~ApplicationList() {
 | 
			
		||||
  lv_obj_clean(lv_scr_act());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApplicationList::Refresh() {
 | 
			
		||||
  if(running)
 | 
			
		||||
  if (running)
 | 
			
		||||
    running = screens.Refresh();
 | 
			
		||||
  return running;
 | 
			
		||||
}
 | 
			
		||||
@ -42,31 +45,27 @@ bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
 | 
			
		||||
  std::array<Screens::Tile::Applications, 6> applications {
 | 
			
		||||
          {
 | 
			
		||||
            {Symbols::stopWatch,    Apps::StopWatch},
 | 
			
		||||
            {Symbols::music,        Apps::Music},
 | 
			
		||||
            {Symbols::map,          Apps::Navigation},
 | 
			
		||||
            {Symbols::shoe,         Apps::Steps},
 | 
			
		||||
            {Symbols::heartBeat,    Apps::HeartRate},
 | 
			
		||||
            {"",                    Apps::None},
 | 
			
		||||
          }
 | 
			
		||||
  };
 | 
			
		||||
  std::array<Screens::Tile::Applications, 6> applications {{
 | 
			
		||||
    {Symbols::stopWatch, Apps::StopWatch},
 | 
			
		||||
    {Symbols::music, Apps::Music},
 | 
			
		||||
    {Symbols::map, Apps::Navigation},
 | 
			
		||||
    {Symbols::shoe, Apps::Steps},
 | 
			
		||||
    {Symbols::heartBeat, Apps::HeartRate},
 | 
			
		||||
    {"", Apps::None},
 | 
			
		||||
  }};
 | 
			
		||||
 | 
			
		||||
  return std::make_unique<Screens::Tile>(0, 2, app, settingsController, batteryController, dateTimeController, applications);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
 | 
			
		||||
  std::array<Screens::Tile::Applications, 6> applications {
 | 
			
		||||
          {
 | 
			
		||||
            {Symbols::paintbrush,   Apps::Paint},
 | 
			
		||||
            {Symbols::paddle,       Apps::Paddle},
 | 
			
		||||
            {"2",                   Apps::Twos},
 | 
			
		||||
            {"M",                   Apps::Motion},
 | 
			
		||||
            {"",                    Apps::None},
 | 
			
		||||
            {"",                    Apps::None},
 | 
			
		||||
          }
 | 
			
		||||
  };
 | 
			
		||||
  std::array<Screens::Tile::Applications, 6> applications {{
 | 
			
		||||
    {Symbols::paintbrush, Apps::Paint},
 | 
			
		||||
    {Symbols::paddle, Apps::Paddle},
 | 
			
		||||
    {"2", Apps::Twos},
 | 
			
		||||
    {"M", Apps::Motion},
 | 
			
		||||
    {"", Apps::None},
 | 
			
		||||
    {"", Apps::None},
 | 
			
		||||
  }};
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,24 +12,24 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
      class ApplicationList : public Screen {
 | 
			
		||||
        public:
 | 
			
		||||
          explicit ApplicationList(DisplayApp* app,
 | 
			
		||||
            Pinetime::Controllers::Settings &settingsController,
 | 
			
		||||
            Pinetime::Controllers::Battery& batteryController,
 | 
			
		||||
            Controllers::DateTime& dateTimeController);
 | 
			
		||||
          ~ApplicationList() override;
 | 
			
		||||
          bool Refresh() override;
 | 
			
		||||
          bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
        private:
 | 
			
		||||
      public:
 | 
			
		||||
        explicit ApplicationList(DisplayApp* app,
 | 
			
		||||
                                 Pinetime::Controllers::Settings& settingsController,
 | 
			
		||||
                                 Pinetime::Controllers::Battery& batteryController,
 | 
			
		||||
                                 Controllers::DateTime& dateTimeController);
 | 
			
		||||
        ~ApplicationList() override;
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
        bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
 | 
			
		||||
          Controllers::Settings& settingsController;
 | 
			
		||||
          Pinetime::Controllers::Battery& batteryController;
 | 
			
		||||
          Controllers::DateTime& dateTimeController;
 | 
			
		||||
      private:
 | 
			
		||||
        Controllers::Settings& settingsController;
 | 
			
		||||
        Pinetime::Controllers::Battery& batteryController;
 | 
			
		||||
        Controllers::DateTime& dateTimeController;
 | 
			
		||||
 | 
			
		||||
          ScreenList<2> screens;
 | 
			
		||||
          std::unique_ptr<Screen> CreateScreen1();
 | 
			
		||||
          std::unique_ptr<Screen> CreateScreen2();
 | 
			
		||||
          //std::unique_ptr<Screen> CreateScreen3();
 | 
			
		||||
        ScreenList<2> screens;
 | 
			
		||||
        std::unique_ptr<Screen> CreateScreen1();
 | 
			
		||||
        std::unique_ptr<Screen> CreateScreen2();
 | 
			
		||||
        // std::unique_ptr<Screen> CreateScreen3();
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,14 @@
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
const char* BatteryIcon::GetBatteryIcon(int batteryPercent) {
 | 
			
		||||
  if(batteryPercent > 90) return Symbols::batteryFull;
 | 
			
		||||
  if(batteryPercent > 75) return Symbols::batteryThreeQuarter;
 | 
			
		||||
  if(batteryPercent > 50) return Symbols::batteryHalf;
 | 
			
		||||
  if(batteryPercent > 25) return Symbols::batteryOneQuarter;
 | 
			
		||||
  if (batteryPercent > 90)
 | 
			
		||||
    return Symbols::batteryFull;
 | 
			
		||||
  if (batteryPercent > 75)
 | 
			
		||||
    return Symbols::batteryThreeQuarter;
 | 
			
		||||
  if (batteryPercent > 50)
 | 
			
		||||
    return Symbols::batteryHalf;
 | 
			
		||||
  if (batteryPercent > 25)
 | 
			
		||||
    return Symbols::batteryOneQuarter;
 | 
			
		||||
  return Symbols::batteryEmpty;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -15,8 +19,9 @@ const char* BatteryIcon::GetUnknownIcon() {
 | 
			
		||||
  return Symbols::batteryEmpty;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *BatteryIcon::GetPlugIcon(bool isCharging) {
 | 
			
		||||
  if(isCharging)
 | 
			
		||||
const char* BatteryIcon::GetPlugIcon(bool isCharging) {
 | 
			
		||||
  if (isCharging)
 | 
			
		||||
    return Symbols::plug;
 | 
			
		||||
  else return "";
 | 
			
		||||
  else
 | 
			
		||||
    return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,8 @@ namespace Pinetime {
 | 
			
		||||
      class BatteryIcon {
 | 
			
		||||
      public:
 | 
			
		||||
        static const char* GetUnknownIcon();
 | 
			
		||||
          static const char* GetBatteryIcon(int batteryPercent);
 | 
			
		||||
          static const char* GetPlugIcon(bool isCharging);
 | 
			
		||||
        static const char* GetBatteryIcon(int batteryPercent);
 | 
			
		||||
        static const char* GetPlugIcon(bool isCharging);
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -4,22 +4,18 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
static void lv_update_task(struct _lv_task_t *task) {  
 | 
			
		||||
  auto user_data = static_cast<BatteryInfo *>(task->user_data);
 | 
			
		||||
static void lv_update_task(struct _lv_task_t* task) {
 | 
			
		||||
  auto user_data = static_cast<BatteryInfo*>(task->user_data);
 | 
			
		||||
  user_data->UpdateScreen();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lv_anim_task(struct _lv_task_t *task) {  
 | 
			
		||||
  auto user_data = static_cast<BatteryInfo *>(task->user_data);
 | 
			
		||||
static void lv_anim_task(struct _lv_task_t* task) {
 | 
			
		||||
  auto user_data = static_cast<BatteryInfo*>(task->user_data);
 | 
			
		||||
  user_data->UpdateAnim();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BatteryInfo::BatteryInfo(
 | 
			
		||||
    Pinetime::Applications::DisplayApp *app, 
 | 
			
		||||
    Pinetime::Controllers::Battery& batteryController) : 
 | 
			
		||||
  Screen(app),
 | 
			
		||||
  batteryController{batteryController}
 | 
			
		||||
{
 | 
			
		||||
BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Battery& batteryController)
 | 
			
		||||
  : Screen(app), batteryController {batteryController} {
 | 
			
		||||
 | 
			
		||||
  batteryPercent = batteryController.PercentRemaining();
 | 
			
		||||
  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_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_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);
 | 
			
		||||
 | 
			
		||||
  status = lv_label_create(lv_scr_act(), nullptr);  
 | 
			
		||||
  lv_label_set_text_static(status,"Reading Battery status");
 | 
			
		||||
  status = lv_label_create(lv_scr_act(), nullptr);
 | 
			
		||||
  lv_label_set_text_static(status, "Reading Battery status");
 | 
			
		||||
  lv_label_set_align(status, LV_LABEL_ALIGN_CENTER);
 | 
			
		||||
  lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  if ( batteryPercent >= 0) {
 | 
			
		||||
    lv_label_set_text_fmt(percent,"%02i%%", batteryPercent);
 | 
			
		||||
  if (batteryPercent >= 0) {
 | 
			
		||||
    lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
 | 
			
		||||
  } else {
 | 
			
		||||
    lv_label_set_text(percent,"--%");
 | 
			
		||||
    lv_label_set_text(percent, "--%");
 | 
			
		||||
  }
 | 
			
		||||
  lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT);
 | 
			
		||||
  lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60);
 | 
			
		||||
 | 
			
		||||
  // hack to not use the flot functions from printf
 | 
			
		||||
  uint8_t batteryVoltageBytes[2];
 | 
			
		||||
  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[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
 | 
			
		||||
  //
 | 
			
		||||
 | 
			
		||||
  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_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_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_obj_set_size(backgroundLabel, 240, 240);
 | 
			
		||||
  lv_obj_set_pos(backgroundLabel, 0, 0);
 | 
			
		||||
@ -73,7 +70,6 @@ BatteryInfo::BatteryInfo(
 | 
			
		||||
  UpdateScreen();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BatteryInfo::~BatteryInfo() {
 | 
			
		||||
  lv_task_del(taskUpdate);
 | 
			
		||||
  lv_task_del(taskAnim);
 | 
			
		||||
@ -83,9 +79,9 @@ BatteryInfo::~BatteryInfo() {
 | 
			
		||||
void BatteryInfo::UpdateAnim() {
 | 
			
		||||
  batteryPercent = batteryController.PercentRemaining();
 | 
			
		||||
 | 
			
		||||
  if ( batteryPercent >= 0 ) {
 | 
			
		||||
    if ( batteryController.IsCharging() and batteryPercent < 100 ) {
 | 
			
		||||
      animation +=1;
 | 
			
		||||
  if (batteryPercent >= 0) {
 | 
			
		||||
    if (batteryController.IsCharging() and batteryPercent < 100) {
 | 
			
		||||
      animation += 1;
 | 
			
		||||
      if (animation >= 100) {
 | 
			
		||||
        animation = 0;
 | 
			
		||||
      }
 | 
			
		||||
@ -110,40 +106,39 @@ void BatteryInfo::UpdateScreen() {
 | 
			
		||||
  batteryPercent = batteryController.PercentRemaining();
 | 
			
		||||
  batteryVoltage = batteryController.Voltage();
 | 
			
		||||
 | 
			
		||||
  if ( batteryPercent >= 0 ) {
 | 
			
		||||
    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_label_set_text_static(status,"Battery charging");
 | 
			
		||||
    } else if ( batteryPercent == 100 ) {
 | 
			
		||||
      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");
 | 
			
		||||
    } else if ( batteryPercent < 10 ) {
 | 
			
		||||
      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");
 | 
			
		||||
  if (batteryPercent >= 0) {
 | 
			
		||||
    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_label_set_text_static(status, "Battery charging");
 | 
			
		||||
    } else if (batteryPercent == 100) {
 | 
			
		||||
      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");
 | 
			
		||||
    } else if (batteryPercent < 10) {
 | 
			
		||||
      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");
 | 
			
		||||
    } else {
 | 
			
		||||
      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_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_fmt(percent,"%02i%%", batteryPercent);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
 | 
			
		||||
 | 
			
		||||
  } else {
 | 
			
		||||
    lv_label_set_text_static(status,"Reading Battery status");
 | 
			
		||||
    lv_label_set_text(percent,"--%");
 | 
			
		||||
    lv_label_set_text_static(status, "Reading Battery status");
 | 
			
		||||
    lv_label_set_text(percent, "--%");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
 | 
			
		||||
  // hack to not use the flot functions from printf
 | 
			
		||||
  uint8_t batteryVoltageBytes[2];
 | 
			
		||||
  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[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
 | 
			
		||||
  //
 | 
			
		||||
  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() {
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  return running;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@
 | 
			
		||||
#include "Screen.h"
 | 
			
		||||
#include <lvgl/lvgl.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class Battery;
 | 
			
		||||
@ -15,34 +14,30 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
 | 
			
		||||
      class BatteryInfo : public Screen{
 | 
			
		||||
        public:
 | 
			
		||||
          BatteryInfo(DisplayApp* app, 
 | 
			
		||||
            Pinetime::Controllers::Battery& batteryController);
 | 
			
		||||
          ~BatteryInfo() override;
 | 
			
		||||
      class BatteryInfo : public Screen {
 | 
			
		||||
      public:
 | 
			
		||||
        BatteryInfo(DisplayApp* app, Pinetime::Controllers::Battery& batteryController);
 | 
			
		||||
        ~BatteryInfo() override;
 | 
			
		||||
 | 
			
		||||
          bool Refresh() override;
 | 
			
		||||
          
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
 | 
			
		||||
          void UpdateScreen();
 | 
			
		||||
          void UpdateAnim();
 | 
			
		||||
        void UpdateScreen();
 | 
			
		||||
        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_obj_t* percent;
 | 
			
		||||
          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;
 | 
			
		||||
        lv_task_t* taskUpdate;
 | 
			
		||||
        lv_task_t* taskAnim;
 | 
			
		||||
 | 
			
		||||
        int8_t animation = 0;
 | 
			
		||||
        int8_t batteryPercent = -1;
 | 
			
		||||
        float batteryVoltage = 0.0f;
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,8 @@
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
const char* BleIcon::GetIcon(bool isConnected) {
 | 
			
		||||
  if(isConnected) return Symbols::bluetooth;
 | 
			
		||||
  else return "";
 | 
			
		||||
  if (isConnected)
 | 
			
		||||
    return Symbols::bluetooth;
 | 
			
		||||
  else
 | 
			
		||||
    return "";
 | 
			
		||||
}
 | 
			
		||||
@ -3,14 +3,15 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
void slider_event_cb(lv_obj_t * slider, lv_event_t event) {
 | 
			
		||||
  if(event == LV_EVENT_VALUE_CHANGED) {
 | 
			
		||||
void slider_event_cb(lv_obj_t* slider, lv_event_t event) {
 | 
			
		||||
  if (event == LV_EVENT_VALUE_CHANGED) {
 | 
			
		||||
    auto* brightnessSlider = static_cast<Brightness*>(slider->user_data);
 | 
			
		||||
    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);
 | 
			
		||||
  lv_obj_set_user_data(slider, this);
 | 
			
		||||
  lv_obj_set_width(slider, LV_DPI * 2);
 | 
			
		||||
@ -33,13 +34,18 @@ bool Brightness::Refresh() {
 | 
			
		||||
  return running;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *Brightness::LevelToString(Pinetime::Controllers::BrightnessController::Levels level) {
 | 
			
		||||
  switch(level) {
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Off: return "Off";
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Low: return "Low";
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Medium: return "Medium";
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::High: return "High";
 | 
			
		||||
    default : return "???";
 | 
			
		||||
const char* Brightness::LevelToString(Pinetime::Controllers::BrightnessController::Levels level) {
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Off:
 | 
			
		||||
      return "Off";
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Low:
 | 
			
		||||
      return "Low";
 | 
			
		||||
    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) {
 | 
			
		||||
  switch(value) {
 | 
			
		||||
    case 0: brightness.Set(Controllers::BrightnessController::Levels::Low); break;
 | 
			
		||||
    case 1: brightness.Set(Controllers::BrightnessController::Levels::Medium); break;
 | 
			
		||||
    case 2: brightness.Set(Controllers::BrightnessController::Levels::High); break;
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      brightness.Set(Controllers::BrightnessController::Levels::Low);
 | 
			
		||||
      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()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t Brightness::LevelToInt(Pinetime::Controllers::BrightnessController::Levels level) {
 | 
			
		||||
  switch(level) {
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Off: return 0;
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Low: return 0;
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Medium: return 1;
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::High: return 2;
 | 
			
		||||
    default : return 0;
 | 
			
		||||
  switch (level) {
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Off:
 | 
			
		||||
      return 0;
 | 
			
		||||
    case Pinetime::Controllers::BrightnessController::Levels::Low:
 | 
			
		||||
      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) {
 | 
			
		||||
  switch(event) {
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    case TouchEvents::SwipeLeft:
 | 
			
		||||
      brightness.Lower();
 | 
			
		||||
      if ( brightness.Level() == Pinetime::Controllers::BrightnessController::Levels::Off) {
 | 
			
		||||
      if (brightness.Level() == Pinetime::Controllers::BrightnessController::Levels::Off) {
 | 
			
		||||
        brightness.Set(Controllers::BrightnessController::Levels::Low);
 | 
			
		||||
      }
 | 
			
		||||
      SetValue();
 | 
			
		||||
 | 
			
		||||
@ -9,25 +9,25 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
      class Brightness : public Screen {
 | 
			
		||||
        public:
 | 
			
		||||
          Brightness(DisplayApp* app, Controllers::BrightnessController& brightness);
 | 
			
		||||
          ~Brightness() override;
 | 
			
		||||
          bool Refresh() override;
 | 
			
		||||
          
 | 
			
		||||
          bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
      public:
 | 
			
		||||
        Brightness(DisplayApp* app, Controllers::BrightnessController& brightness);
 | 
			
		||||
        ~Brightness() override;
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
 | 
			
		||||
          void OnValueChanged();
 | 
			
		||||
        private:
 | 
			
		||||
          
 | 
			
		||||
          Controllers::BrightnessController& brightness;
 | 
			
		||||
        bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
 | 
			
		||||
          lv_obj_t * slider_label;
 | 
			
		||||
          lv_obj_t * slider;
 | 
			
		||||
        void OnValueChanged();
 | 
			
		||||
 | 
			
		||||
          const char* LevelToString(Controllers::BrightnessController::Levels level);
 | 
			
		||||
          uint8_t LevelToInt(Controllers::BrightnessController::Levels level);
 | 
			
		||||
          void SetValue(uint8_t value);
 | 
			
		||||
          void SetValue();
 | 
			
		||||
      private:
 | 
			
		||||
        Controllers::BrightnessController& brightness;
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -15,45 +15,48 @@
 | 
			
		||||
#include "WatchFaceDigital.h"
 | 
			
		||||
#include "WatchFaceAnalog.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
Clock::Clock(DisplayApp* app,
 | 
			
		||||
        Controllers::DateTime& dateTimeController,
 | 
			
		||||
        Controllers::Battery& batteryController,
 | 
			
		||||
        Controllers::Ble& bleController,
 | 
			
		||||
        Controllers::NotificationManager& notificatioManager,
 | 
			
		||||
        Controllers::Settings &settingsController,
 | 
			
		||||
        Controllers::HeartRateController& heartRateController,
 | 
			
		||||
        Controllers::MotionController& motionController) : Screen(app),
 | 
			
		||||
        dateTimeController{dateTimeController}, batteryController{batteryController},
 | 
			
		||||
        bleController{bleController}, notificatioManager{notificatioManager},
 | 
			
		||||
        settingsController{settingsController},
 | 
			
		||||
        heartRateController{heartRateController},
 | 
			
		||||
        motionController{motionController},
 | 
			
		||||
        screens{app,
 | 
			
		||||
          settingsController.GetClockFace(),
 | 
			
		||||
          {
 | 
			
		||||
                [this]() -> std::unique_ptr<Screen> { return WatchFaceDigitalScreen(); },
 | 
			
		||||
                [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          
 | 
			
		||||
        } {
 | 
			
		||||
             Controllers::DateTime& dateTimeController,
 | 
			
		||||
             Controllers::Battery& batteryController,
 | 
			
		||||
             Controllers::Ble& bleController,
 | 
			
		||||
             Controllers::NotificationManager& notificatioManager,
 | 
			
		||||
             Controllers::Settings& settingsController,
 | 
			
		||||
             Controllers::HeartRateController& heartRateController,
 | 
			
		||||
             Controllers::MotionController& motionController)
 | 
			
		||||
  : Screen(app),
 | 
			
		||||
    dateTimeController {dateTimeController},
 | 
			
		||||
    batteryController {batteryController},
 | 
			
		||||
    bleController {bleController},
 | 
			
		||||
    notificatioManager {notificatioManager},
 | 
			
		||||
    settingsController {settingsController},
 | 
			
		||||
    heartRateController {heartRateController},
 | 
			
		||||
    motionController {motionController},
 | 
			
		||||
    screens {app,
 | 
			
		||||
             settingsController.GetClockFace(),
 | 
			
		||||
             {
 | 
			
		||||
               [this]() -> std::unique_ptr<Screen> {
 | 
			
		||||
                 return WatchFaceDigitalScreen();
 | 
			
		||||
               },
 | 
			
		||||
               [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() {
 | 
			
		||||
  lv_obj_clean(lv_scr_act());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool Clock::Refresh() {  
 | 
			
		||||
  screens.Refresh();  
 | 
			
		||||
bool Clock::Refresh() {
 | 
			
		||||
  screens.Refresh();
 | 
			
		||||
  return running;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -61,21 +64,31 @@ bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
 | 
			
		||||
  return screens.OnTouchEvent(event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {  
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceDigital>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController, motionController);
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceDigital>(app,
 | 
			
		||||
                                                     dateTimeController,
 | 
			
		||||
                                                     batteryController,
 | 
			
		||||
                                                     bleController,
 | 
			
		||||
                                                     notificatioManager,
 | 
			
		||||
                                                     settingsController,
 | 
			
		||||
                                                     heartRateController,
 | 
			
		||||
                                                     motionController);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {  
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceAnalog>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceAnalog>(
 | 
			
		||||
    app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
// Examples for more watch faces
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() {  
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceMinimal>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() {
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceMinimal>(app, dateTimeController, batteryController, bleController, notificatioManager,
 | 
			
		||||
settingsController);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceCustomScreen() {  
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceCustom>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
 | 
			
		||||
std::unique_ptr<Screen> Clock::WatchFaceCustomScreen() {
 | 
			
		||||
  return std::make_unique<Screens::WatchFaceCustom>(app, dateTimeController, batteryController, bleController, notificatioManager,
 | 
			
		||||
settingsController);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
@ -23,42 +23,37 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
      class Clock : public Screen {
 | 
			
		||||
        public:
 | 
			
		||||
          Clock(DisplayApp* app,
 | 
			
		||||
                  Controllers::DateTime& dateTimeController,
 | 
			
		||||
                  Controllers::Battery& batteryController,
 | 
			
		||||
                  Controllers::Ble& bleController,
 | 
			
		||||
                  Controllers::NotificationManager& notificatioManager,
 | 
			
		||||
                  Controllers::Settings &settingsController,
 | 
			
		||||
                  Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                  Controllers::MotionController& motionController);
 | 
			
		||||
          ~Clock() override;
 | 
			
		||||
      public:
 | 
			
		||||
        Clock(DisplayApp* app,
 | 
			
		||||
              Controllers::DateTime& dateTimeController,
 | 
			
		||||
              Controllers::Battery& batteryController,
 | 
			
		||||
              Controllers::Ble& bleController,
 | 
			
		||||
              Controllers::NotificationManager& notificatioManager,
 | 
			
		||||
              Controllers::Settings& settingsController,
 | 
			
		||||
              Controllers::HeartRateController& heartRateController,
 | 
			
		||||
              Controllers::MotionController& motionController);
 | 
			
		||||
        ~Clock() override;
 | 
			
		||||
 | 
			
		||||
          bool Refresh() override;
 | 
			
		||||
          
 | 
			
		||||
          bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
        bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
 | 
			
		||||
          Controllers::DateTime& dateTimeController;
 | 
			
		||||
          Controllers::Battery& batteryController;
 | 
			
		||||
          Controllers::Ble& bleController;
 | 
			
		||||
          Controllers::NotificationManager& notificatioManager;
 | 
			
		||||
          Controllers::Settings& settingsController;
 | 
			
		||||
          Controllers::HeartRateController& heartRateController;
 | 
			
		||||
          Controllers::MotionController& motionController;
 | 
			
		||||
      private:
 | 
			
		||||
        Controllers::DateTime& dateTimeController;
 | 
			
		||||
        Controllers::Battery& batteryController;
 | 
			
		||||
        Controllers::Ble& bleController;
 | 
			
		||||
        Controllers::NotificationManager& notificatioManager;
 | 
			
		||||
        Controllers::Settings& settingsController;
 | 
			
		||||
        Controllers::HeartRateController& heartRateController;
 | 
			
		||||
        Controllers::MotionController& motionController;
 | 
			
		||||
 | 
			
		||||
        ScreenList<2> screens;
 | 
			
		||||
        std::unique_ptr<Screen> WatchFaceDigitalScreen();
 | 
			
		||||
        std::unique_ptr<Screen> WatchFaceAnalogScreen();
 | 
			
		||||
 | 
			
		||||
          ScreenList<2> screens;
 | 
			
		||||
          std::unique_ptr<Screen> WatchFaceDigitalScreen();
 | 
			
		||||
          std::unique_ptr<Screen> WatchFaceAnalogScreen();
 | 
			
		||||
 | 
			
		||||
          // Examples for more watch faces
 | 
			
		||||
          //std::unique_ptr<Screen> WatchFaceMinimalScreen();
 | 
			
		||||
          //std::unique_ptr<Screen> WatchFaceCustomScreen();
 | 
			
		||||
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
        // Examples for more watch faces
 | 
			
		||||
        // std::unique_ptr<Screen> WatchFaceMinimalScreen();
 | 
			
		||||
        // std::unique_ptr<Screen> WatchFaceCustomScreen();
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -5,20 +5,21 @@
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  ddlist = lv_ddlist_create(lv_scr_act(), nullptr);
 | 
			
		||||
  lv_ddlist_set_options(ddlist, "Apple\n"
 | 
			
		||||
                                "Banana\n"
 | 
			
		||||
                                "Orange\n"
 | 
			
		||||
                                "Melon\n"
 | 
			
		||||
                                "Grape\n"
 | 
			
		||||
                                "Raspberry\n"
 | 
			
		||||
                                "A\n"
 | 
			
		||||
                                "B\n"
 | 
			
		||||
                                "C\n"
 | 
			
		||||
                                "D\n"
 | 
			
		||||
                                "E");
 | 
			
		||||
  lv_ddlist_set_options(ddlist,
 | 
			
		||||
                        "Apple\n"
 | 
			
		||||
                        "Banana\n"
 | 
			
		||||
                        "Orange\n"
 | 
			
		||||
                        "Melon\n"
 | 
			
		||||
                        "Grape\n"
 | 
			
		||||
                        "Raspberry\n"
 | 
			
		||||
                        "A\n"
 | 
			
		||||
                        "B\n"
 | 
			
		||||
                        "C\n"
 | 
			
		||||
                        "D\n"
 | 
			
		||||
                        "E");
 | 
			
		||||
  lv_ddlist_set_fix_width(ddlist, 150);
 | 
			
		||||
  lv_ddlist_set_draw_arrow(ddlist, true);
 | 
			
		||||
  lv_ddlist_set_fix_height(ddlist, 150);
 | 
			
		||||
@ -32,12 +33,12 @@ DropDownDemo::~DropDownDemo() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  // dropdown while it is opened.
 | 
			
		||||
  // 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);
 | 
			
		||||
  else
 | 
			
		||||
    app->SetTouchMode(DisplayApp::TouchModes::Gestures);
 | 
			
		||||
@ -47,11 +48,10 @@ bool DropDownDemo::Refresh() {
 | 
			
		||||
bool DropDownDemo::OnTouchEvent(Pinetime::Applications::TouchEvents 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).
 | 
			
		||||
  auto* list = static_cast<lv_ddlist_ext_t *>(ddlist->ext_attr);
 | 
			
		||||
  if(list->opened) {
 | 
			
		||||
  auto* list = static_cast<lv_ddlist_ext_t*>(ddlist->ext_attr);
 | 
			
		||||
  if (list->opened) {
 | 
			
		||||
    return true;
 | 
			
		||||
  } else {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,18 +9,18 @@ namespace Pinetime {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
 | 
			
		||||
      class DropDownDemo : public Screen {
 | 
			
		||||
        public:
 | 
			
		||||
          DropDownDemo(DisplayApp* app);
 | 
			
		||||
          ~DropDownDemo() override;
 | 
			
		||||
      public:
 | 
			
		||||
        DropDownDemo(DisplayApp* app);
 | 
			
		||||
        ~DropDownDemo() override;
 | 
			
		||||
 | 
			
		||||
          bool Refresh() override;
 | 
			
		||||
          
 | 
			
		||||
          bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
          lv_obj_t * ddlist;
 | 
			
		||||
          
 | 
			
		||||
          bool isDropDownOpened = false;
 | 
			
		||||
        bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
        lv_obj_t* ddlist;
 | 
			
		||||
 | 
			
		||||
        bool isDropDownOpened = false;
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -5,11 +5,10 @@
 | 
			
		||||
 | 
			
		||||
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) :
 | 
			
		||||
      Screen(app), bleController{bleController} {
 | 
			
		||||
 | 
			
		||||
  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_obj_set_size(backgroundLabel, 240, 240);
 | 
			
		||||
  lv_obj_set_pos(backgroundLabel, 0, 0);
 | 
			
		||||
@ -38,21 +37,21 @@ FirmwareUpdate::~FirmwareUpdate() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FirmwareUpdate::Refresh() {
 | 
			
		||||
  switch(bleController.State()) {
 | 
			
		||||
  switch (bleController.State()) {
 | 
			
		||||
    default:
 | 
			
		||||
    case Pinetime::Controllers::Ble::FirmwareUpdateStates::Idle:
 | 
			
		||||
    case Pinetime::Controllers::Ble::FirmwareUpdateStates::Running:
 | 
			
		||||
      if(state != States::Running)
 | 
			
		||||
      if (state != States::Running)
 | 
			
		||||
        state = States::Running;
 | 
			
		||||
      return DisplayProgression();
 | 
			
		||||
    case Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated:
 | 
			
		||||
      if(state != States::Validated) {
 | 
			
		||||
      if (state != States::Validated) {
 | 
			
		||||
        UpdateValidated();
 | 
			
		||||
        state = States::Validated;
 | 
			
		||||
      }
 | 
			
		||||
      return running;
 | 
			
		||||
    case Pinetime::Controllers::Ble::FirmwareUpdateStates::Error:
 | 
			
		||||
      if(state != States::Error) {
 | 
			
		||||
      if (state != States::Error) {
 | 
			
		||||
        UpdateError();
 | 
			
		||||
        state = States::Error;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -10,29 +10,28 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
 | 
			
		||||
      class FirmwareUpdate : public Screen{
 | 
			
		||||
        public:
 | 
			
		||||
          FirmwareUpdate(DisplayApp* app, Pinetime::Controllers::Ble& bleController);
 | 
			
		||||
          ~FirmwareUpdate() override;
 | 
			
		||||
      class FirmwareUpdate : public Screen {
 | 
			
		||||
      public:
 | 
			
		||||
        FirmwareUpdate(DisplayApp* app, Pinetime::Controllers::Ble& bleController);
 | 
			
		||||
        ~FirmwareUpdate() override;
 | 
			
		||||
 | 
			
		||||
          bool Refresh() override;
 | 
			
		||||
          
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
          enum class States { Idle, Running, Validated, Error };
 | 
			
		||||
          Pinetime::Controllers::Ble& bleController;
 | 
			
		||||
          lv_obj_t* bar1;
 | 
			
		||||
          lv_obj_t* percentLabel;
 | 
			
		||||
          lv_obj_t* titleLabel;
 | 
			
		||||
          mutable char percentStr[10];
 | 
			
		||||
          
 | 
			
		||||
          States state;
 | 
			
		||||
      private:
 | 
			
		||||
        enum class States { Idle, Running, Validated, Error };
 | 
			
		||||
        Pinetime::Controllers::Ble& bleController;
 | 
			
		||||
        lv_obj_t* bar1;
 | 
			
		||||
        lv_obj_t* percentLabel;
 | 
			
		||||
        lv_obj_t* titleLabel;
 | 
			
		||||
        mutable char percentStr[10];
 | 
			
		||||
 | 
			
		||||
          bool DisplayProgression() const;
 | 
			
		||||
        States state;
 | 
			
		||||
 | 
			
		||||
          void UpdateValidated();
 | 
			
		||||
        bool DisplayProgression() const;
 | 
			
		||||
 | 
			
		||||
          void UpdateError();
 | 
			
		||||
        void UpdateValidated();
 | 
			
		||||
 | 
			
		||||
        void UpdateError();
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -7,23 +7,20 @@
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  static void ButtonEventHandler(lv_obj_t * obj, lv_event_t event)
 | 
			
		||||
  {
 | 
			
		||||
    FirmwareValidation* screen = static_cast<FirmwareValidation *>(obj->user_data);
 | 
			
		||||
  static void ButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
 | 
			
		||||
    FirmwareValidation* screen = static_cast<FirmwareValidation*>(obj->user_data);
 | 
			
		||||
    screen->OnButtonEvent(obj, event);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app,
 | 
			
		||||
                                       Pinetime::Controllers::FirmwareValidator &validator)
 | 
			
		||||
                                       : Screen{app}, validator{validator} {
 | 
			
		||||
FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator)
 | 
			
		||||
  : Screen {app}, validator {validator} {
 | 
			
		||||
  labelVersionInfo = lv_label_create(lv_scr_act(), nullptr);
 | 
			
		||||
  lv_obj_align(labelVersionInfo, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
 | 
			
		||||
  lv_label_set_text(labelVersionInfo, "Version : ");
 | 
			
		||||
  lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  labelVersionValue = lv_label_create(lv_scr_act(), nullptr);
 | 
			
		||||
  lv_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
 | 
			
		||||
  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_obj_set_width(labelIsValidated, 240);
 | 
			
		||||
 | 
			
		||||
  if(validator.IsValidated())
 | 
			
		||||
  if (validator.IsValidated())
 | 
			
		||||
    lv_label_set_text(labelIsValidated, "You have already\n#00ff00 validated# this firmware#");
 | 
			
		||||
  else {
 | 
			
		||||
    lv_label_set_text(labelIsValidated,
 | 
			
		||||
                      "Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version.");
 | 
			
		||||
    lv_label_set_text(labelIsValidated, "Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version.");
 | 
			
		||||
 | 
			
		||||
    buttonValidate = lv_btn_create(lv_scr_act(), nullptr);
 | 
			
		||||
    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));
 | 
			
		||||
 | 
			
		||||
    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->user_data = this;
 | 
			
		||||
    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_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() {
 | 
			
		||||
  lv_obj_clean(lv_scr_act());
 | 
			
		||||
@ -71,12 +66,11 @@ bool FirmwareValidation::Refresh() {
 | 
			
		||||
  return running;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FirmwareValidation::OnButtonEvent(lv_obj_t *object, lv_event_t event) {
 | 
			
		||||
  if(object == buttonValidate && event == LV_EVENT_PRESSED) {
 | 
			
		||||
void FirmwareValidation::OnButtonEvent(lv_obj_t* object, lv_event_t event) {
 | 
			
		||||
  if (object == buttonValidate && event == LV_EVENT_PRESSED) {
 | 
			
		||||
    validator.Validate();
 | 
			
		||||
    running  = false;
 | 
			
		||||
  } else if(object == buttonReset && event == LV_EVENT_PRESSED) {
 | 
			
		||||
    running = false;
 | 
			
		||||
  } else if (object == buttonReset && event == LV_EVENT_PRESSED) {
 | 
			
		||||
    validator.Reset();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,28 +11,26 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
 | 
			
		||||
      class FirmwareValidation : public Screen{
 | 
			
		||||
        public:
 | 
			
		||||
          FirmwareValidation(DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator);
 | 
			
		||||
          ~FirmwareValidation() override;
 | 
			
		||||
      class FirmwareValidation : public Screen {
 | 
			
		||||
      public:
 | 
			
		||||
        FirmwareValidation(DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator);
 | 
			
		||||
        ~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:
 | 
			
		||||
          Pinetime::Controllers::FirmwareValidator& validator;
 | 
			
		||||
      private:
 | 
			
		||||
        Pinetime::Controllers::FirmwareValidator& validator;
 | 
			
		||||
 | 
			
		||||
          lv_obj_t* labelVersionInfo;
 | 
			
		||||
          lv_obj_t* labelVersionValue;
 | 
			
		||||
          char version[9];
 | 
			
		||||
          lv_obj_t* labelIsValidated;
 | 
			
		||||
          lv_obj_t* buttonValidate;
 | 
			
		||||
          lv_obj_t* labelButtonValidate;
 | 
			
		||||
          lv_obj_t* buttonReset;
 | 
			
		||||
          lv_obj_t* labelButtonReset;
 | 
			
		||||
          
 | 
			
		||||
        lv_obj_t* labelVersionInfo;
 | 
			
		||||
        lv_obj_t* labelVersionValue;
 | 
			
		||||
        char version[9];
 | 
			
		||||
        lv_obj_t* labelIsValidated;
 | 
			
		||||
        lv_obj_t* buttonValidate;
 | 
			
		||||
        lv_obj_t* labelButtonValidate;
 | 
			
		||||
        lv_obj_t* buttonReset;
 | 
			
		||||
        lv_obj_t* labelButtonReset;
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -5,46 +5,43 @@
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  static void event_handler(lv_obj_t * obj, lv_event_t event) {
 | 
			
		||||
    FlashLight* screen = static_cast<FlashLight *>(obj->user_data);
 | 
			
		||||
  static void event_handler(lv_obj_t* obj, lv_event_t event) {
 | 
			
		||||
    FlashLight* screen = static_cast<FlashLight*>(obj->user_data);
 | 
			
		||||
    screen->OnClickEvent(obj, event);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FlashLight::FlashLight(
 | 
			
		||||
    Pinetime::Applications::DisplayApp *app,
 | 
			
		||||
    System::SystemTask &systemTask,
 | 
			
		||||
    Controllers::BrightnessController& brightness) : 
 | 
			
		||||
  Screen(app),
 | 
			
		||||
  systemTask{systemTask},
 | 
			
		||||
  brightness{brightness}
 | 
			
		||||
  
 | 
			
		||||
FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app,
 | 
			
		||||
                       System::SystemTask& systemTask,
 | 
			
		||||
                       Controllers::BrightnessController& brightness)
 | 
			
		||||
  : Screen(app),
 | 
			
		||||
    systemTask {systemTask},
 | 
			
		||||
    brightness {brightness}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
  brightness.Backup();
 | 
			
		||||
  brightness.Set(Controllers::BrightnessController::Levels::High);
 | 
			
		||||
  // Set the background
 | 
			
		||||
  lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF));
 | 
			
		||||
  
 | 
			
		||||
  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_font(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
 | 
			
		||||
  lv_label_set_text_static(flashLight, Symbols::highlight);
 | 
			
		||||
  lv_obj_align(flashLight, NULL, LV_ALIGN_CENTER, 0, 0);
 | 
			
		||||
 | 
			
		||||
  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_obj_set_size(backgroundAction, 240, 240);
 | 
			
		||||
  lv_obj_set_pos(backgroundAction, 0, 0);
 | 
			
		||||
  lv_label_set_text(backgroundAction, "");
 | 
			
		||||
  lv_obj_set_click(backgroundAction, true);
 | 
			
		||||
  backgroundAction->user_data = this;  
 | 
			
		||||
  backgroundAction->user_data = this;
 | 
			
		||||
  lv_obj_set_event_cb(backgroundAction, event_handler);
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FlashLight::~FlashLight() {
 | 
			
		||||
  lv_obj_clean(lv_scr_act());
 | 
			
		||||
  lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
 | 
			
		||||
@ -52,19 +49,18 @@ FlashLight::~FlashLight() {
 | 
			
		||||
  systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FlashLight::OnClickEvent(lv_obj_t *obj, lv_event_t event) {
 | 
			
		||||
  if(obj == backgroundAction) {
 | 
			
		||||
void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) {
 | 
			
		||||
  if (obj == backgroundAction) {
 | 
			
		||||
    if (event == LV_EVENT_CLICKED) {
 | 
			
		||||
      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_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
 | 
			
		||||
      } else {
 | 
			
		||||
        lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
 | 
			
		||||
        lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -76,4 +72,3 @@ bool FlashLight::Refresh() {
 | 
			
		||||
bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,30 +6,28 @@
 | 
			
		||||
#include "systemtask/SystemTask.h"
 | 
			
		||||
#include "components/brightness/BrightnessController.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
 | 
			
		||||
      class FlashLight : public Screen{
 | 
			
		||||
        public:
 | 
			
		||||
          FlashLight(DisplayApp* app, System::SystemTask &systemTask, Controllers::BrightnessController& brightness);
 | 
			
		||||
          ~FlashLight() override;
 | 
			
		||||
      class FlashLight : public Screen {
 | 
			
		||||
      public:
 | 
			
		||||
        FlashLight(DisplayApp* app, System::SystemTask& systemTask, Controllers::BrightnessController& brightness);
 | 
			
		||||
        ~FlashLight() override;
 | 
			
		||||
 | 
			
		||||
          bool Refresh() override;
 | 
			
		||||
          
 | 
			
		||||
          bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;
 | 
			
		||||
          void OnClickEvent(lv_obj_t *obj, lv_event_t event);
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
          Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
          Controllers::BrightnessController& brightness;
 | 
			
		||||
        bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;
 | 
			
		||||
        void OnClickEvent(lv_obj_t* obj, lv_event_t event);
 | 
			
		||||
 | 
			
		||||
          lv_obj_t* flashLight;
 | 
			
		||||
          lv_obj_t* backgroundAction;
 | 
			
		||||
          bool isOn = true;
 | 
			
		||||
      private:
 | 
			
		||||
        Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
        Controllers::BrightnessController& brightness;
 | 
			
		||||
 | 
			
		||||
        lv_obj_t* flashLight;
 | 
			
		||||
        lv_obj_t* backgroundAction;
 | 
			
		||||
        bool isOn = true;
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  const char *ToString(Pinetime::Controllers::HeartRateController::States s) {
 | 
			
		||||
  const char* ToString(Pinetime::Controllers::HeartRateController::States s) {
 | 
			
		||||
    switch (s) {
 | 
			
		||||
      case Pinetime::Controllers::HeartRateController::States::NotEnoughData:
 | 
			
		||||
        return "Not enough data,\nplease wait...";
 | 
			
		||||
@ -21,35 +21,37 @@ namespace {
 | 
			
		||||
    return "";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static void btnStartStopEventHandler(lv_obj_t *obj, lv_event_t event) {
 | 
			
		||||
    HeartRate *screen = static_cast<HeartRate *>(obj->user_data);
 | 
			
		||||
  static void btnStartStopEventHandler(lv_obj_t* obj, lv_event_t event) {
 | 
			
		||||
    HeartRate* screen = static_cast<HeartRate*>(obj->user_data);
 | 
			
		||||
    screen->OnStartStopEvent(event);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HeartRate::HeartRate(Pinetime::Applications::DisplayApp *app, Controllers::HeartRateController& heartRateController, System::SystemTask &systemTask) : 
 | 
			
		||||
  Screen(app), heartRateController{heartRateController}, systemTask{systemTask} {
 | 
			
		||||
HeartRate::HeartRate(Pinetime::Applications::DisplayApp* app,
 | 
			
		||||
                     Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                     System::SystemTask& systemTask)
 | 
			
		||||
  : Screen(app), heartRateController {heartRateController}, systemTask {systemTask} {
 | 
			
		||||
  bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  if(isHrRunning)
 | 
			
		||||
  if (isHrRunning)
 | 
			
		||||
    lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
 | 
			
		||||
  else
 | 
			
		||||
    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_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);
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  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_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);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  UpdateStartStopButton(isHrRunning);
 | 
			
		||||
  if(isHrRunning)
 | 
			
		||||
  if (isHrRunning)
 | 
			
		||||
    systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -72,10 +74,10 @@ HeartRate::~HeartRate() {
 | 
			
		||||
bool HeartRate::Refresh() {
 | 
			
		||||
 | 
			
		||||
  auto state = heartRateController.State();
 | 
			
		||||
  switch(state) {
 | 
			
		||||
  switch (state) {
 | 
			
		||||
    case Controllers::HeartRateController::States::NoTouch:
 | 
			
		||||
    case Controllers::HeartRateController::States::NotEnoughData:
 | 
			
		||||
    //case Controllers::HeartRateController::States::Stopped:
 | 
			
		||||
      // case Controllers::HeartRateController::States::Stopped:
 | 
			
		||||
      lv_label_set_text(label_hr, "000");
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
@ -90,13 +92,12 @@ bool HeartRate::Refresh() {
 | 
			
		||||
 | 
			
		||||
void HeartRate::OnStartStopEvent(lv_event_t event) {
 | 
			
		||||
  if (event == LV_EVENT_CLICKED) {
 | 
			
		||||
    if(heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
 | 
			
		||||
    if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
 | 
			
		||||
      heartRateController.Start();
 | 
			
		||||
      UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
 | 
			
		||||
      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);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    } else {
 | 
			
		||||
      heartRateController.Stop();
 | 
			
		||||
      UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
 | 
			
		||||
      systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping);
 | 
			
		||||
@ -106,7 +107,7 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HeartRate::UpdateStartStopButton(bool isRunning) {
 | 
			
		||||
  if(isRunning)
 | 
			
		||||
  if (isRunning)
 | 
			
		||||
    lv_label_set_text(label_startStop, "Stop");
 | 
			
		||||
  else
 | 
			
		||||
    lv_label_set_text(label_startStop, "Start");
 | 
			
		||||
 | 
			
		||||
@ -15,13 +15,13 @@ namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
 | 
			
		||||
      class HeartRate : public Screen{
 | 
			
		||||
      class HeartRate : public Screen {
 | 
			
		||||
      public:
 | 
			
		||||
        HeartRate(DisplayApp* app, Controllers::HeartRateController& HeartRateController, System::SystemTask &systemTask);
 | 
			
		||||
        HeartRate(DisplayApp* app, Controllers::HeartRateController& HeartRateController, System::SystemTask& systemTask);
 | 
			
		||||
        ~HeartRate() override;
 | 
			
		||||
 | 
			
		||||
        bool Refresh() override;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        void OnStartStopEvent(lv_event_t event);
 | 
			
		||||
 | 
			
		||||
      private:
 | 
			
		||||
@ -33,9 +33,6 @@ namespace Pinetime {
 | 
			
		||||
        lv_obj_t* label_status;
 | 
			
		||||
        lv_obj_t* btn_startStop;
 | 
			
		||||
        lv_obj_t* label_startStop;
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
  std::fill(b, b + bufferSize, selectColor);
 | 
			
		||||
}
 | 
			
		||||
@ -20,8 +20,8 @@ bool InfiniPaint::Refresh() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
 | 
			
		||||
  switch(event) {
 | 
			
		||||
    case Pinetime::Applications::TouchEvents::LongTap:      
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    case Pinetime::Applications::TouchEvents::LongTap:
 | 
			
		||||
      switch (color) {
 | 
			
		||||
        case 0:
 | 
			
		||||
          selectColor = LV_COLOR_MAGENTA;
 | 
			
		||||
@ -47,13 +47,13 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
 | 
			
		||||
        case 7:
 | 
			
		||||
          selectColor = LV_COLOR_BLACK;
 | 
			
		||||
          break;
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
          color = 0;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      std::fill(b, b + bufferSize, selectColor);  
 | 
			
		||||
      std::fill(b, b + bufferSize, selectColor);
 | 
			
		||||
      color++;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
@ -72,4 +72,3 @@ bool InfiniPaint::OnTouchEvent(uint16_t x, uint16_t y) {
 | 
			
		||||
  lvgl.FlushDisplay(&area, b);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,6 @@ namespace Pinetime {
 | 
			
		||||
        lv_color_t b[bufferSize];
 | 
			
		||||
        lv_color_t selectColor = LV_COLOR_WHITE;
 | 
			
		||||
        uint8_t color = 2;
 | 
			
		||||
        
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -2,24 +2,21 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
Label::Label(uint8_t screenID, uint8_t numScreens,
 | 
			
		||||
  Pinetime::Applications::DisplayApp *app, lv_obj_t* labelText)  : 
 | 
			
		||||
  Screen(app), 
 | 
			
		||||
  labelText{labelText} {
 | 
			
		||||
  
 | 
			
		||||
  if ( numScreens > 1 ) {
 | 
			
		||||
Label::Label(uint8_t screenID, uint8_t numScreens, Pinetime::Applications::DisplayApp* app, lv_obj_t* labelText)
 | 
			
		||||
  : Screen(app), labelText {labelText} {
 | 
			
		||||
 | 
			
		||||
  if (numScreens > 1) {
 | 
			
		||||
    pageIndicatorBasePoints[0].x = 240 - 1;
 | 
			
		||||
    pageIndicatorBasePoints[0].y = 6;
 | 
			
		||||
    pageIndicatorBasePoints[1].x = 240 - 1;
 | 
			
		||||
    pageIndicatorBasePoints[1].y = 240 - 6;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    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_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_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
    uint16_t indicatorSize = 228 / numScreens;
 | 
			
		||||
    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_line_set_points(pageIndicator, pageIndicatorPoints, 2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Label::~Label() {
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user