Merge branch 'develop' into HEAD

This commit is contained in:
Riku Isokoski 2021-08-01 11:47:26 +03:00
commit 5bdef365f2
139 changed files with 5350 additions and 1804 deletions

View File

@ -1,4 +1,5 @@
Checks: '*, Checks: '*,
-altera-unroll-loops,
-llvmlibc-callee-namespace, -llvmlibc-callee-namespace,
-llvm-header-guard, -llvm-header-guard,
-llvm-namespace-comment, -llvm-namespace-comment,
@ -14,17 +15,20 @@ Checks: '*,
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-special-member-functions,
-readability-magic-numbers, -readability-magic-numbers,
-readability-uppercase-literal-suffix, -readability-uppercase-literal-suffix,
-modernize-use-trailing-return-type, -modernize-use-trailing-return-type,
-modernize-avoid-c-arrays, -modernize-avoid-c-arrays,
-hicpp-signed-bitwise,
-hicpp-no-assembler,
-hicpp-avoid-c-arrays, -hicpp-avoid-c-arrays,
-hicpp-uppercase-literal-suffix, -hicpp-uppercase-literal-suffix,
-hicpp-vararg,
-hicpp-no-assembler,
-hicpp-no-array-decay, -hicpp-no-array-decay,
-hicpp-signed-bitwise,
-hicpp-special-member-functions,
-cert-err58-cpp, -cert-err58-cpp,
-cert-err60-cpp' -cert-err60-cpp'
CheckOptions: CheckOptions:
- key: readability-function-cognitive-complexity.Threshold - key: readability-function-cognitive-complexity.Threshold
value: 100 value: 100

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "src/libs/lvgl"] [submodule "src/libs/lvgl"]
path = src/libs/lvgl path = src/libs/lvgl
url = https://github.com/joaquimorg/lvgl.git url = https://github.com/joaquimorg/lvgl.git
[submodule "src/libs/littlefs"]
path = src/libs/littlefs
url = https://github.com/littlefs-project/littlefs.git

View File

@ -12,6 +12,29 @@
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" /> <option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" /> <option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
</Objective-C> </Objective-C>
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
<codeStyleSettings language="ObjectiveC"> <codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="140" /> <option name="RIGHT_MARGIN" value="140" />
<option name="IF_BRACE_FORCE" value="3" /> <option name="IF_BRACE_FORCE" value="3" />

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(pinetime VERSION 1.0.0 LANGUAGES C CXX ASM) project(pinetime VERSION 1.3.0 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
@ -21,10 +21,6 @@ if (NOT NRF5_SDK_PATH)
message(FATAL_ERROR "The path to the NRF52 SDK must be specified on the command line (add -DNRF5_SDK_PATH=<path>") message(FATAL_ERROR "The path to the NRF52 SDK must be specified on the command line (add -DNRF5_SDK_PATH=<path>")
endif () endif ()
if(NOT USE_JLINK AND NOT USE_GDB_CLIENT AND NOT USE_OPENOCD)
set(USE_JLINK true)
endif()
if(USE_JLINK) if(USE_JLINK)
if (NOT NRFJPROG) if (NOT NRFJPROG)
message(FATAL_ERROR "the path to the tool nrfjprog must be specified on the command line (add -DNRFJPROG=<path>") message(FATAL_ERROR "the path to the tool nrfjprog must be specified on the command line (add -DNRFJPROG=<path>")
@ -55,10 +51,22 @@ if(BUILD_DFU)
set(BUILD_DFU true) set(BUILD_DFU true)
endif() endif()
set(PROJECT_GIT_COMMIT_HASH "")
execute_process(COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE PROJECT_GIT_COMMIT_HASH
RESULT_VARIABLE PROJECT_GIT_COMMIT_HASH_SUCCESS)
string(STRIP ${PROJECT_GIT_COMMIT_HASH} PROJECT_GIT_COMMIT_HASH)
message("PROJECT_GIT_COMMIT_HASH_SUCCESS? " ${PROJECT_GIT_COMMIT_HASH_SUCCESS})
message("BUILD CONFIGURATION") message("BUILD CONFIGURATION")
message("-------------------") message("-------------------")
message(" * Version : " ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) message(" * Version : " ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
message(" * Toolchain : " ${ARM_NONE_EABI_TOOLCHAIN_PATH}) message(" * Toolchain : " ${ARM_NONE_EABI_TOOLCHAIN_PATH})
message(" * GitRef(S) : " ${PROJECT_GIT_COMMIT_HASH})
message(" * NRF52 SDK : " ${NRF5_SDK_PATH}) message(" * NRF52 SDK : " ${NRF5_SDK_PATH})
set(PROGRAMMER "???") set(PROGRAMMER "???")
if(USE_JLINK) if(USE_JLINK)

View File

@ -1,4 +1,3 @@
# PineTime # PineTime
[![Build PineTime Firmware](https://github.com/JF002/InfiniTime/workflows/Build%20PineTime%20Firmware/badge.svg?branch=master)](https://github.com/JF002/InfiniTime/actions) [![Build PineTime Firmware](https://github.com/JF002/InfiniTime/workflows/Build%20PineTime%20Firmware/badge.svg?branch=master)](https://github.com/JF002/InfiniTime/actions)
@ -12,9 +11,10 @@
The **Pinetime** smartwatch is built around the NRF52832 MCU (512KB Flash, 64KB RAM), a 240*240 LCD display driven by the ST7789 controller, an accelerometer, a heart rate sensor, and a vibration motor. The **Pinetime** smartwatch is built around the NRF52832 MCU (512KB Flash, 64KB RAM), a 240*240 LCD display driven by the ST7789 controller, an accelerometer, a heart rate sensor, and a vibration motor.
# InfiniTime # InfiniTime
![InfiniTime logo](images/infinitime-logo.jpg "InfiniTime Logo") ![InfiniTime logo](images/infinitime-logo.jpg "InfiniTime Logo")
The goal of this project is to design an open-source firmware for the Pinetime smartwatch : The goal of this project is to design an open-source firmware for the Pinetime smartwatch :
- Code written in **modern C++**; - Code written in **modern C++**;
- Build system based on **CMake**; - Build system based on **CMake**;
@ -36,50 +36,57 @@ As of now, here is the list of achievements of this project:
- Heart rate measurements - Heart rate measurements
- Step counting - Step counting
- Wake-up on wrist rotation - Wake-up on wrist rotation
- Quick actions - Quick actions
* Disable vibration on notification * Disable vibration on notification
* Brightness settings * Brightness settings
* Flashlight * Flashlight
* Settings * Settings
- 2 watch faces: - 3 watch faces:
* Digital * Digital
* Analog * Analog
* [PineTimeStyle](https://wiki.pine64.org/wiki/PineTimeStyle)
- Multiple 'apps' : - Multiple 'apps' :
* Music (control the playback of the music on your phone) * Music (control the playback of music on your phone)
* Heart rate (controls the heart rate sensor and display current heartbeat) * Heart rate (measure your heart rate)
* Navigation (displays navigation instructions coming from the companion app) * Navigation (displays navigation instructions coming from the companion app)
* Notification (displays the last notification received) * Notification (displays the last notification received)
* Paddle (single player pong-like game) * Paddle (single player pong-like game)
* Two (2048 clone game) * Twos (2048 clone game)
* Stopwatch (with all the necessary functions such as play, pause, lap, stop) * Stopwatch
* Motion sensor and step counter (displays the number of steps and the state of the motion sensor in real-time) * Steps (displays the number of steps taken)
* Timer (set a countdown timer that will notify you when it expires)
* Metronome (vibrates to a given bpm with a customizable beats per bar)
- User settings: - User settings:
* Display timeout * Display timeout
* Wake-up condition * Wake-up condition
* Time format (12/24h) * Time format (12/24h)
* Default watch face * Default watch face
* Daily step goal
* Battery status * Battery status
* Firmware validation * Firmware validation
* System information * System information
- Supported by 3 companion apps (development is in progress): - Supported by 3 companion apps (development is in progress):
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android) * [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android via F-Droid)
* [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS and Linux) * [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS and Linux)
* [Siglo](https://github.com/alexr4535/siglo) (on 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]** [WebBLEWatch](https://hubmartin.github.io/WebBLEWatch/) Synchronize time directly from your web browser. [video](https://youtu.be/IakiuhVDdrY)
- OTA (Over-the-air) update via BLE - OTA (Over-the-air) update via BLE
- [Bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader) based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/) - [Bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader) based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/)
## Documentation ## Documentation
### Getting started ### Getting started
- [Getting started with InfiniTime 1.0 (quick user guide, update bootloader and InfiniTime,...)](doc/gettingStarted/gettingStarted-1.0.md) - [Getting started with InfiniTime 1.0 (quick user guide, update bootloader and InfiniTime,...)](doc/gettingStarted/gettingStarted-1.0.md)
- [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/ota-gadgetbridge-nrfconnect.md) - [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/ota-gadgetbridge-nrfconnect.md)
### Develop ### Develop
- [Generate the fonts and symbols](src/displayapp/fonts/README.md) - [Generate the fonts and symbols](src/displayapp/fonts/README.md)
- [Creating a stopwatch in Pinetime(article)](https://pankajraghav.com/2021/04/03/PINETIME-STOPCLOCK.html) - [Creating a stopwatch in Pinetime(article)](https://pankajraghav.com/2021/04/03/PINETIME-STOPCLOCK.html)
### Build, flash and debug ### Build, flash and debug
- [Project branches](doc/branches.md) - [Project branches](doc/branches.md)
- [Versioning](doc/versioning.md) - [Versioning](doc/versioning.md)
- [Files included in the release notes](doc/filesInReleaseNotes.md) - [Files included in the release notes](doc/filesInReleaseNotes.md)
@ -92,20 +99,23 @@ As of now, here is the list of achievements of this project:
- Using files from the releases - Using files from the releases
### Contribute ### Contribute
- [How to contribute ?](doc/contribute.md) - [How to contribute ?](doc/contribute.md)
### API ### API
- [BLE implementation and API](./doc/ble.md) - [BLE implementation and API](./doc/ble.md)
### Architecture and technical topics ### Architecture and technical topics
- [Memory analysis](./doc/MemoryAnalysis.md) - [Memory analysis](./doc/MemoryAnalysis.md)
### Using the firmware ### Using the firmware
- [Integration with Gadgetbridge](doc/companionapps/Gadgetbridge.md) - [Integration with Gadgetbridge](doc/companionapps/Gadgetbridge.md)
- [Integration with AmazFish](doc/companionapps/Amazfish.md) - [Integration with AmazFish](doc/companionapps/Amazfish.md)
- [Firmware update, OTA](doc/companionapps/NrfconnectOTA.md) - [Firmware update, OTA](doc/companionapps/NrfconnectOTA.md)
## TODO - contribute ## TODO - contribute
This project is far from being finished, and there are still a lot of things to do for this project to become a firmware usable by the general public. This project is far from being finished, and there are still a lot of things to do for this project to become a firmware usable by the general public.
@ -119,12 +129,13 @@ Here a quick list out of my head of things to do for this project:
- Measure power consumption and improve battery life - Measure power consumption and improve battery life
- Improve documentation, take better pictures and video than mine - Improve documentation, take better pictures and video than mine
- Improve the UI - Improve the UI
- Create companion app for multiple OSes (Linux, Android, iOS) and platforms (desktop, ARM, mobile). Do not forget the other devices from Pine64 like [the Pinephone](https://www.pine64.org/pinephone/) and the [Pinebook Pro](https://www.pine64.org/pinebook-pro/). - Create companion app for multiple OSes (Linux, Android, iOS) and platforms (desktop, ARM, mobile). Do not forget the other devices from Pine64 like [the Pinephone](https://www.pine64.org/pinephone/) and the [Pinebook Pro](https://www.pine64.org/pinebook-pro/).
- Design a simple CI (preferably self-hosted and easy to reproduce). - Design a simple CI (preferably self-hosted and easy to reproduce).
Do not hesitate to clone/fork the code, hack it and create pull-requests. I'll do my best to review and merge them :) Do not hesitate to clone/fork the code, hack it and create pull-requests. I'll do my best to review and merge them :)
## Licenses ## Licenses
This project is released under the GNU General Public License version 3 or, at your option, any later version. This project is released under the GNU General Public License version 3 or, at your option, any later version.
It integrates the following projects: It integrates the following projects:
@ -132,8 +143,9 @@ It integrates the following projects:
- UI : **[LittleVGL/LVGL](https://lvgl.io/)** under the MIT license - UI : **[LittleVGL/LVGL](https://lvgl.io/)** under the MIT license
- BLE stack : **[NimBLE](https://github.com/apache/mynewt-nimble)** under the Apache 2.0 license - BLE stack : **[NimBLE](https://github.com/apache/mynewt-nimble)** under the Apache 2.0 license
- Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license - Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license
## Credits ## Credits
Im not working alone on this project. First, many people create PR for this projects. Then, there is the whole #pinetime community : a lot of people all around the world who are hacking, searching, experimenting and programming the Pinetime. We exchange our ideas, experiments and code in the chat rooms and forums. Im not working alone on this project. First, many people create PR for this projects. Then, there is the whole #pinetime community : a lot of people all around the world who are hacking, searching, experimenting and programming the Pinetime. We exchange our ideas, experiments and code in the chat rooms and forums.
Here are some people I would like to highlight: Here are some people I would like to highlight:

View File

@ -74,21 +74,6 @@ macro(nRF5x_setup)
) )
endif () endif ()
set(COMMON_FLAGS "-MP -MD -mthumb -mabi=aapcs -Wall -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums ${CPU_FLAGS} -Wreturn-type -Werror=return-type")
# compiler/assambler/linker flags
set(CMAKE_C_FLAGS "${COMMON_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g3")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS "${COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g3")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_ASM_FLAGS "-MP -MD -x assembler-with-cpp")
set(CMAKE_EXE_LINKER_FLAGS "-mthumb -mabi=aapcs -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} ${CPU_FLAGS} -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm")
# note: we must override the default cmake linker flags so that CMAKE_C_FLAGS are not added implicitly
set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_COMPILER} <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_C_COMPILER} <LINK_FLAGS> <OBJECTS> -lstdc++ -o <TARGET> <LINK_LIBRARIES>")
# basic board definitions and drivers # basic board definitions and drivers
include_directories( include_directories(
"${NRF5_SDK_PATH}/components" "${NRF5_SDK_PATH}/components"

View File

@ -1,7 +1,274 @@
# Memory analysis # Memory analysis
The PineTime is equipped with the following memories:
- The internal RAM : **64KB**
- The internal Flash : **512KB**
- The external (SPI) Flash : **4MB**
Note that the NRF52832 cannot execute code stored in the external flash : we need to store the whole firmware in the internal flash memory, and use the external one to store graphicals assets, fonts...
This document describes how the RAM and Flash memories are used in InfiniTime and how to analyze and monitor their usage. It was written in the context of [this memory analysis effort](https://github.com/JF002/InfiniTime/issues/313).
## Code sections
A binary is composed of multiple sections. Most of the time, these sections are : .text, .rodata, .data and .bss but more sections can be defined in the linker script.
Here is a small description of these sections and where they end up in memory:
- **TEXT** = code (FLASH)
- **RODATA** = constants (FLASH)
- **DATA** = initialized variables (FLASH + RAM)
- **BSS** = uninitialized variables (RAM)
## Internal FLASH
The internal flash memory stores the whole firmware: code, variable that are not default-initialized, constants...
The content of the flash memory can be easily analyzed thanks to the MAP file generated by the compiler. This file lists all the symbols from the program along with their size and location (section and addresses) in RAM and FLASH.
![Map file](./memoryAnalysis/mapfile.png)
As you can see on the picture above, this file contains a lot of information and is not easily readable by a human being. Fortunately, you can easily find tools that parse and display the content of the MAP file in a more understandable way.
In this analysis, I used [Linkermapviz](https://github.com/PromyLOPh/linkermapviz).
### Linkermapviz
[Linkermapviz](https://github.com/PromyLOPh/linkermapviz) parses the MAP file and displays its content in a graphical way into an HTML page:
![linkermapviz](./memoryAnalysis/linkermapviz.png)
Using this tool, you can easily see the size of each symbol relative to the other one, and check what is using most of the space,...
Also, as Linkermapviz is written in Python, you can easily modify it to adapt it to your firmware, export data in another format,... For example, [I modified it to parse the contents of the MAP file and export it in a CSV file](https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620). I could later on open this file in LibreOffice Calc and use sort/filter functionality to search for specific symbols in specific files...
### Puncover
[Puncover](https://github.com/HBehrens/puncover) is another useful tools that analyses the binary file generated by the compiler (the .out file that contains all debug information). It provides valuable information about the symbols (data and code): name, position, size, max stack of each functions, callers, callees...
![Puncover](./memoryAnalysis/puncover.png)
Puncover is really easy to install:
- clone the repo and cd into the cloned directory
- setup a venv
- `python -m virtualenv venv`
- `source venv/bin/activate`
- Install : `pip install .`
- Run : `puncover --gcc_tools_base=/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi- --elf_file /path/to/build/directory/src/pinetime-app-1.1.0.out --src_root /path/to/sources --build_dir /path/to/build/directory`
- Replace
* `/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin` with the path to your gcc-arm-none-eabi toolchain
* `/path/to/build/directory/src/pinetime-app-1.1.0.out` with the path to the binary generated by GCC (.out file)
* `/path/to/sources` with the path to the root folder of the sources (checkout directory)
* `/path/to/build/directory` with the path to the build directory
- Launch a browser at http://localhost:5000/
### Analysis
Using the MAP file and tools, we can easily see what symbols are using most of the FLASH memory space. In this case, with no surprise, fonts and graphics are the biggest flash space consumer.
![Puncover](./memoryAnalysis/puncover-all-symbols.png)
This way, you can easily check what needs to be optimized : we should find a way to store big static data (like fonts and graphics) in the external flash memory, for example.
It's always a good idea to check the flash memory space when working on the project : this way, you can easily check that your developments are using a reasonable amount of space.
### Links
- Analysis with linkermapviz : https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620
- Analysis with Puncover : https://github.com/JF002/InfiniTime/issues/313#issuecomment-847311392
## RAM
RAM memory contains all the data that can be modified at run-time: variables, stack, heap...
### Data
RAM memory can be *statically* allocated, meaning that the size and position of the data are known at compile-time:
You can easily analyze the memory used by variables declared in the global scope using the MAP. You'll find them in the .BSS or .DATA sections. Linkermapviz and Puncover can be used to analyze their memory usage.
Variables declared in the scope of a function will be allocated on the stack. It means that the stack usage will vary according to the state of the program, and cannot be easily analyzed at compile time.
```
uint8_t buffer[1024]
int main() {
int a;
}
```
#### Analysis
In Infinitime 1.1, the biggest buffers are the buffers allocated for LVGL (14KB) and the one for FreeRTOS (16KB). Nimble also allocated 9KB of RAM.
### Stack
The stack will be used for everything except tasks, which have their own stack allocated by FreeRTOS. The stack is 8192B and is allocated in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L148).
An easy way to monitor its usage is by filling the section with a known pattern at boot time, then use the firmware and dump the memory. You can then check the maximum stack usage by checking the address from the beginning of the stack that were overwritten.
#### Fill the stack section by a known pattern:
Edit <NRFSDK>/modules/nrfx/mdk/gcc_startup_nrf52.S and add the following code after the copy of the data from read only memory to RAM at around line 243:
```
/* Loop to copy data from read only memory to RAM.
* The ranges of copy from/to are specified by following symbols:
* __etext: LMA of start of the section to copy from. Usually end of text
* __data_start__: VMA of start of the section to copy to.
* __bss_start__: VMA of end of the section to copy to. Normally __data_end__ is used, but by using __bss_start__
* the user can add their own initialized data section before BSS section with the INTERT AFTER command.
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__etext
ldr r2, =__data_start__
ldr r3, =__bss_start__
subs r3, r3, r2
ble .L_loop1_done
.L_loop1:
subs r3, r3, #4
ldr r0, [r1,r3]
str r0, [r2,r3]
bgt .L_loop1
.L_loop1_done:
/* Add this code to fill the stack section with 0xFFEEDDBB */
ldr r0, =__StackLimit
ldr r1, =8192
ldr r2, =0xFFEEDDBB
.L_fill:
str r2, [r0]
adds r0, 4
subs r1, 4
bne .L_fill
/* -- */
```
#### Dump RAM memory and check usage
Dumping the content of the ram is easy using JLink debugger and `nrfjprog`:
```
nrfjprog --readram ram.bin
```
You can then display the file using objdump:
```
hexdump ram.bin -v | less
```
The stack is positionned at the end of the RAM -> 0xFFFF. Its size is 8192 bytes, so the end of the stack is at 0xE000.
On the following dump, the maximum stack usage is 520 bytes (0xFFFF - 0xFDF8):
```
000fdb0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fdc0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fdd0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fde0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fdf0 ddbb ffee ddbb ffee ffff ffff c24b 0003
000fe00 ffff ffff ffff ffff ffff ffff 0000 0000
000fe10 0018 0000 0000 0000 0000 0000 fe58 2000
000fe20 0000 0000 0000 00ff ddbb 00ff 0018 0000
000fe30 929c 2000 0000 0000 0018 0000 0000 0000
000fe40 92c4 2000 0458 2000 0000 0000 80e7 0003
000fe50 0000 0000 8cd9 0003 ddbb ffee ddbb ffee
000fe60 00dc 2000 92c4 2000 0005 0000 929c 2000
000fe70 007f 0000 feb0 2000 92c4 2000 feb8 2000
000fe80 ddbb ffee 0005 0000 929c 2000 0000 0000
000fe90 aca0 2000 0000 0000 0028 0000 418b 0005
000fea0 02f4 2000 001f 0000 0000 0000 0013 0000
000feb0 b5a8 2000 2199 0005 b5a8 2000 2201 0005
000fec0 b5a8 2000 001e 0000 0000 0000 0013 0000
000fed0 b5b0 2000 0fe0 0006 b5a8 2000 0000 0000
000fee0 0013 0000 2319 0005 0013 0000 0000 0000
000fef0 0000 0000 3b1c 2000 3b1c 2000 d0e3 0000
000ff00 4b70 2000 54ac 2000 4b70 2000 ffff ffff
000ff10 0000 0000 1379 0003 6578 2000 0d75 0003
000ff20 6578 2000 ffff ffff 0000 0000 1379 0003
000ff30 000c 0000 cfeb 0002 39a1 2000 a824 2000
000ff40 0015 0000 cfeb 0002 39a1 2000 a824 2000
000ff50 39a1 2000 0015 0000 001b 0000 b4b9 0002
000ff60 0000 0000 a9f4 2000 4b70 2000 0d75 0003
000ff70 4b70 2000 ffff ffff 0000 0000 1379 0003
000ff80 ed00 e000 a820 2000 1000 4001 7fc0 2000
000ff90 7f64 2000 75a7 0001 a884 2000 7b04 2000
000ffa0 a8c0 2000 0000 0000 0000 0000 0000 0000
000ffb0 7fc0 2000 7f64 2000 8024 2000 a5a5 a5a5
000ffc0 ed00 e000 3fd5 0001 0000 0000 72c0 2000
000ffd0 0000 0000 72e4 2000 3f65 0001 7f64 2000
000ffe0 0000 2001 0000 0000 ef30 e000 0010 0000
000fff0 7fc0 2000 4217 0001 3f0a 0001 0000 6100
```
#### Analysis
According to my experimentations, we don't use the stack that much, and 8192 bytes is probably way too big for InfiniTime!
#### Links
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-851035070
### Heap
The heap is declared in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L136) and its current size is 8192 bytes. The heap is used for dynamic memory allocation(`malloc()`, `new`...).
Heap monitoring is not easy, but it seems that we can use the following code to know the current usage of the heap:
```
auto m = mallinfo();
NRF_LOG_INFO("heap : %d", m.uordblks);
```
#### Analysis
According to my experimentation, InfiniTime uses ~6000bytes of heap most of the time. Except when the Navigation app is launched, where the heap usage increases to... more than 9500 bytes (meaning that the heap overflows and could potentially corrupt the stack!!!). This is a bug that should be fixed in #362.
To know exactly what's consuming heap memory, you can `wrap` functions like `malloc()` into your own functions. In this wrapper, you can add logging code or put breakpoints:
- Add ` -Wl,-wrap,malloc` to the cmake variable `LINK_FLAGS` of the target you want to debug (pinetime-app, most probably)
- Add the following code in `main.cpp`
```
extern "C" {
void *__real_malloc (size_t);
void* __wrap_malloc(size_t size) {
return __real_malloc(size);
}
}
```
Now, your function `__wrap_malloc()` will be called instead of `malloc()`. You can call the actual malloc from the stdlib by calling `__real_malloc()`.
Using this technique, I was able to trace all malloc calls at boot (boot -> digital watchface):
- system task = 3464 bytes (SystemTask could potentially be declared as a global variable to avoid heap allocation here)
- string music = 31 (maybe we should not use std::string when not needed, as it does heap allocation)
- ble_att_svr_start = 1720
- ble gatts start = 40 + 88
- ble ll task = 24
- display app = 104
- digital clock = 96 + 152
- hr task = 304
#### Links
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-851035625
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-1-calculating-stack-size/
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-2-properly-allocating-stacks/
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-3-avoiding-heap-errors/
## LVGL
I did a deep analysis of the usage of the buffer dedicated for lvgl (managed by lv_mem).
This buffer is used by lvgl to allocated memory for drivers (display/touch), screens, themes, and all widgets created by the apps.
The usage of this buffer can be monitored using this code :
```
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
NRF_LOG_INFO("\t Free %d / %d -- max %d", mon.free_size, mon.total_size, mon.max_used);
```
The most interesting metric is `mon.max_used` which specifies the maximum number of bytes that were used from this buffer since the initialization of lvgl.
According to my measurements, initializing the theme, display/touch driver and screens cost **4752** bytes!
Then, initializing the digital clock face costs **1541 bytes**.
For example a simple lv_label needs **~140 bytes** of memory.
I tried to monitor this max value while going through all the apps of InfiniTime 1.1 : the max value I've seen is **5660 bytes**. It means that we could probably **reduce the size of the buffer from 14KB to 6 - 10 KB** (we have to take the fragmentation of the memory into account).
### Links
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-850890064
## FreeRTOS heap and task stack ## FreeRTOS heap and task stack
FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h* FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h*
FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes,...). FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes...).
The function `xPortGetFreeHeapSize()` returns the amount of memory available in this *ucHeap* buffer. If this value reaches 0, FreeRTOS runs out of memory. The function `xPortGetFreeHeapSize()` returns the amount of memory available in this *ucHeap* buffer. If this value reaches 0, FreeRTOS runs out of memory.
@ -20,59 +287,3 @@ for (int i = 0; i < nb; i++) {
``` ```
## Global heap
Heap is used for **dynamic memory allocation (malloc() / new)**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__HEAP_SIZE=8192` in *src/CMakeLists.txt*:
```
add_definitions(-D__HEAP_SIZE=8192)
```
You can trace the dynamic memory allocation by using the flag `--wrap` of the linker. When this flag is enabled, the linker will replace the calls to a specific function by a call to __wrap_the_function(). For example, if you specify `-Wl,-wrap,malloc` in the linker flags, the linker will replace all calls to `void* malloc(size_t)` by calls to `void* __wrap_malloc(size_t)`. This is a function you'll have to define in your code. In this function, you can call `__real_malloc()` to call the actual `malloc()' function.
This technic allows you to wrap all calls to malloc() with you own code.
In *src/CMakeLists.txt*:
```
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
...
LINK_FLAGS "-Wl,-wrap,malloc ..."
...
)
```
In *main.cpp*:
```
uint32_t totalMalloc = 0;
extern "C" {
extern void* __real_malloc(size_t s);
void *__wrap_malloc(size_t s) {
totalMalloc += s;
return __real_malloc(s);
}
}
```
This function sums all the memory that is allocated during the runtime. You can monitor or log this value. You can also place breakpoints in this function to determine where the dynamic memory allocation occurs in your code.
# Global stack
The stack is used to allocate memory used by functions : **parameters and local variables**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__STACK_SIZE=8192` in *src/CMakeLists.txt*:
```
add_definitions(-D__STACK_SIZE=8192)
```
*NOTE*: FreeRTOS uses its own stack buffer. Thus, the global stack is only used for main() and IRQ handlers. It should be possible to reduce its size to a much lower value.
**NOTE**: [?] How to track the global stack usage?
#LittleVGL buffer
*TODO*
#NimBLE buffers
*TODO*
#Tools
- https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC.

View File

@ -1,7 +1,7 @@
# Build a stub for PineTime using NRF52-DK # Build a stub for PineTime using NRF52-DK
[NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK) is the official developpment kit for NRF52832 SoC from Nordic Semiconductor. [NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK) is the official developpment kit for NRF52832 SoC from Nordic Semiconductor.
It can be very useful for PineTime developpment: It can be very useful for PineTime development:
* You can use it embedded JLink SWD programmer/debugger to program and debug you code on the PineTime * You can use it embedded JLink SWD programmer/debugger to program and debug you code on the PineTime
* As it's based on the same SoC than the PineTime, you can program it to actually run the same code than the PineTime. * As it's based on the same SoC than the PineTime, you can program it to actually run the same code than the PineTime.
@ -47,4 +47,4 @@ You also need to enable the I/O expander to disconnect pins from buttons and led
| --------- | --------- | | --------- | --------- |
| DETECT | GND | | DETECT | GND |
Now, you should be able to program the SoC on the NRF52-DK board, and use it as if it was running on the pintime. Now, you should be able to program the SoC on the NRF52-DK board, and use it as if it was running on the PineTime.

View File

@ -5,9 +5,9 @@ This page describes the BLE implementation and API built in this firmware.
**Note** : I'm a beginner in BLE related technologies and the information of this document reflect my current knowledge and understanding of the BLE stack. These informations might be erroneous or incomplete. Feel free to submit a PR if you think you can improve these. **Note** : I'm a beginner in BLE related technologies and the information of this document reflect my current knowledge and understanding of the BLE stack. These informations might be erroneous or incomplete. Feel free to submit a PR if you think you can improve these.
## BLE Connection ## BLE Connection
When starting the firmware start a BLE advertising : it send small messages that can be received by any *central* device in range. This allows the device to announce its presence to other devices. When starting the firmware start a BLE advertising : it sends small messages that can be received by any *central* device in range. This allows the device to announce its presence to other devices.
A companion application (running on a PC, RasberryPi, smartphone) which received this avertising packet can request a connection to the device. This connection procedure allows the 2 devices to negociate communication parameters, security keys,... A companion application (running on a PC, RaspberryPi, smartphone) which received this avertising packet can request a connection to the device. This connection procedure allows the 2 devices to negotiate communication parameters, security keys,...
When the connection is established, the pinetime will try to discover services running on the companion application. For now **CTS** (**C**urrent **T**ime **S**ervice) and **ANS** (**A**lert **N**otification **S**ervice) are supported. When the connection is established, the pinetime will try to discover services running on the companion application. For now **CTS** (**C**urrent **T**ime **S**ervice) and **ANS** (**A**lert **N**otification **S**ervice) are supported.

View File

@ -2,7 +2,7 @@
The branching model of this project is based on the workflow named [Git flow](https://nvie.com/posts/a-successful-git-branching-model/). The branching model of this project is based on the workflow named [Git flow](https://nvie.com/posts/a-successful-git-branching-model/).
It is based on 2 main branches: It is based on 2 main branches:
- **master** : this branch is always ready to be reployed. It means that at any time, we should be able to build the branch and release a new version of the application. - **master** : this branch is always ready to be deployed. It means that at any time, we should be able to build the branch and release a new version of the application.
- **develop** : this branch contains the latest development that will be integrated in the next release once it's considered as stable. - **develop** : this branch contains the latest development that will be integrated in the next release once it's considered as stable.
New features should be implemented in **feature branches** created from **develop**. When the feature is ready, a pull-request is created and it'll be merge into **develop** when it is succesfully reviewed and accepted. New features should be implemented in **feature branches** created from **develop**. When the feature is ready, a pull-request is created and it'll be merge into **develop** when it is succesfully reviewed and accepted.

View File

@ -27,7 +27,7 @@ CMake configures the project according to variables you specify the command line
**NRFJPROG**|Path to the NRFJProg executable. Used only if `USE_JLINK` is 1.|`-DNRFJPROG=/opt/nrfjprog/nrfjprog` **NRFJPROG**|Path to the NRFJProg executable. Used only if `USE_JLINK` is 1.|`-DNRFJPROG=/opt/nrfjprog/nrfjprog`
**GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb` **GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb`
**GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0` **GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0`
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-BUILD_DFU=1` **BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
####(**) Note about **CMAKE_BUILD_TYPE**: ####(**) Note about **CMAKE_BUILD_TYPE**:
By default, this variable is set to *Release*. It compiles the code with size and speed optimizations. We use this value for all the binaries we publish when we [release](https://github.com/JF002/InfiniTime/releases) new versions of InfiniTime. By default, this variable is set to *Release*. It compiles the code with size and speed optimizations. We use this value for all the binaries we publish when we [release](https://github.com/JF002/InfiniTime/releases) new versions of InfiniTime.
@ -163,7 +163,7 @@ J-Link>g
``` ```
#### JLink RTT #### JLink RTT
RTT is a feature from Segger's JLink devices that allows bidirectionnal communication between the debugger and the target. This feature can be used to get the logs from the embedded software on the development computer. RTT is a feature from Segger's JLink devices that allows bidirectional communication between the debugger and the target. This feature can be used to get the logs from the embedded software on the development computer.
- Program the MCU with the code (see above) - Program the MCU with the code (see above)
- Start JLinkExe - Start JLinkExe

View File

@ -13,7 +13,7 @@ Based on Ubuntu 18.04 with the following build dependencies:
The `infinitime-build` image contains all the dependencies you need. The default `CMD` will compile sources found in `/sources`, so you need only mount your code. The `infinitime-build` image contains all the dependencies you need. The default `CMD` will compile sources found in `/sources`, so you need only mount your code.
This example will build the firmware, generate the MCUBoot image and generate the DFU file. Outputs will be written to **<project_root>/build/output**: This example will build the firmware, generate the MCUBoot image and generate the DFU file. For cloning the repo, see [these instructions](../doc/buildAndProgram.md#clone-the-repo). Outputs will be written to **<project_root>/build/output**:
```bash ```bash
cd <project_root> # e.g. cd ./work/Pinetime cd <project_root> # e.g. cd ./work/Pinetime

View File

@ -8,8 +8,9 @@ The following features are implemented:
- Time synchronization - Time synchronization
- Notifications - Notifications
- Music control - Music control
- Navigation with Puremaps
## Demo ## Demo
[This video](https://seafile.codingfield.com/f/21c5d023452740279e36/) shows how to connect to the Pinetime and control the playback of the music on the phone. [This video](https://seafile.codingfield.com/f/21c5d023452740279e36/) shows how to connect to the Pinetime and control the playback of the music on the phone.
Amazfish and Sailfish OS are running on the [Pinephone](https://www.pine64.org/pinephone/), another awesome device from Pine64. Amazfish and Sailfish OS are running on the [Pinephone](https://www.pine64.org/pinephone/), another awesome device from Pine64.

View File

@ -1,7 +1,7 @@
# Integration with Gadgetbridge # Integration with Gadgetbridge
[Gadgetbridge](https://gadgetbridge.org/) is an Android application that supports many smartwatches and fitness trackers. [Gadgetbridge](https://gadgetbridge.org/) is an Android application that supports many smartwatches and fitness trackers.
The integration of InfiniTime (previously Pinetime-JF) is now merged into the master branch (https://codeberg.org/Freeyourgadget/Gadgetbridge/). The integration of InfiniTime (previously Pinetime-JF) is now merged into the master branch (https://codeberg.org/Freeyourgadget/Gadgetbridge/) and initial support is available [starting with version 0.47](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/branch/master/CHANGELOG.md). Note that the official version is only available on F-Droid (as of May 2021), and the unofficial fork available on the Play Store is outdated and does not support Infinitime.
## Features ## Features
The following features are implemented: The following features are implemented:

View File

@ -48,7 +48,7 @@ When reviewing PR, the author and contributors will first look at the **descript
Then, reviewing **a few files that were modified for a single purpose** is a lot more easier than to review 30 files modified for many reasons (bug fix, UI improvements, typos in doc,...), even if all these changes make sense. Also, it's possible that we agree on some modification but not on some other, and we won't be able to merge the PR because of the changes that are not accepted. Then, reviewing **a few files that were modified for a single purpose** is a lot more easier than to review 30 files modified for many reasons (bug fix, UI improvements, typos in doc,...), even if all these changes make sense. Also, it's possible that we agree on some modification but not on some other, and we won't be able to merge the PR because of the changes that are not accepted.
We do our best to keep the code as consistent as possible, and that mean we pay attention to the **formatting** of the code. If the code formatting is not consistent with our code base, we'll ask you to review it, which will take more time. We do our best to keep the code as consistent as possible, and that means we pay attention to the **formatting** of the code. If the code formatting is not consistent with our code base, we'll ask you to review it, which will take more time.
The last step of the review consists in **testing** the modification. If it doesn't work out of the box, we'll ask your to review your code and to ensure that it works as expected. The last step of the review consists in **testing** the modification. If it doesn't work out of the box, we'll ask your to review your code and to ensure that it works as expected.

View File

@ -1,16 +1,16 @@
# Using the releases # Using the releases
For each new *stable* version of Pinetime, a [release note](https://github.com/JF002/InfiniTime/releases) is created. It contains a description of the main changes in the release and some files you can use to flash the firmware in your Pinetime. For each new *stable* version of IniniTime, a [release note](https://github.com/JF002/InfiniTime/releases) is created. It contains a description of the main changes in the release and some files you can use to flash the firmware to your Pinetime.
This page describes the files from the release notes and how to use them. This page describes the files from the release notes and how to use them.
**NOTE :** the files included in different could be different. This page describes the release note of [version 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1), which is the version that'll probably be pre-programmed at the factory for the next batch of Pinetime devkits. **NOTE :** the files included in different Releases could be different. This page describes the release notes of [version 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1), which is the version that is pre-programmed for the last batches of pinetimes but will be replaced with [1.0.0](https://github.com/jF002/infiniTime/releases/tag/1.0.0) around june 2021.
## Files included in the release note ## Files included in the release notes
### Standalone firmware ### Standalone firmware
This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flash at offset 0, meaning it will erase any bootloader that might be present in memory. This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flashed at offset 0, meaning it will erase any bootloader that might be present in memory.
- **pinetime-app.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB. - **pinetime-app.out** : Output file of GCC containing debug symbols, useful if you want to debug the firmware using GDB.
- **pinetime-app.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it. - **pinetime-app.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it.
- **pintime-app.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed. - **pintime-app.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed.
- **pinetime-app.map** : Map file containing all the symbols, addresses in memory,... - **pinetime-app.map** : Map file containing all the symbols, addresses in memory,...
@ -18,7 +18,7 @@ This firmware is standalone, meaning that it does not need a bootloader to actua
**This firmware must be flashed at address 0x00 in the main flash memory** **This firmware must be flashed at address 0x00 in the main flash memory**
### Bootloader ### Bootloader
The bootloader is maintained by [lupyuen](https://github.com/lupyuen) and is a binary version of [this release](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4). The bootloader is maintained by [lupyuen](https://github.com/lupyuen) and is a binary version of [this release](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4).
- **bootloader.hex** : Firmware in Intel HEX file format. - **bootloader.hex** : Firmware in Intel HEX file format.
@ -38,7 +38,7 @@ This firmware is a small utility firmware that writes the boot graphic in the ex
### Firmware with bootloader ### Firmware with bootloader
This firmware is intended to be used with our [MCUBoot-based bootloader](../bootloader/README.md). This firmware is intended to be used with our [MCUBoot-based bootloader](../bootloader/README.md).
- **pinetime-mcuboot-app-image.hex** : Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed **@ 0x8000** into flash memory. If the [bootloader](../bootloader/README.md) has been successfully programmed, it should run this firmware after the next reset. - **pinetime-mcuboot-app-image.hex**: Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed at **0x8000** into the flash memory. If the [bootloader](../bootloader/README.md) has been successfully programmed, it should run this firmware after the next reset.
The following files are not directly usable by the bootloader: The following files are not directly usable by the bootloader:

View File

@ -1,7 +1,9 @@
# Getting started with InfiniTime 1.0 # Getting started with InfiniTime 1.0
On April 22 2021, InfiniTime and Pine64 [announced the release of InfiniTime 1.0](https://www.pine64.org/2021/04/22/its-time-infinitime-1-0/) and the availability of PineTime smartwatches as *enthusiast grade end-user product*. This page aims to guide you with your first step with your new PineTime. On April 22 2021, InfiniTime and Pine64 [announced the release of InfiniTime 1.0](https://www.pine64.org/2021/04/22/its-time-infinitime-1-0/) and the availability of PineTime smartwatches as *enthusiast grade end-user product*. This page aims to guide you with your first step with your new PineTime.
## Firmware, InfiniTime, Bootloader, Recovery firmware, OTA, DFU... What is it? ## Firmware, InfiniTime, Bootloader, Recovery firmware, OTA, DFU... What is it?
You might have already seen these words by reading the announcement, release notes, or [the wiki guide](https://wiki.pine64.org/wiki/Upgrade_PineTime_to_InfiniTime_1.0.0) and, you may find them misleading if you're not familiar with the project. You might have already seen these words by reading the announcement, release notes, or [the wiki guide](https://wiki.pine64.org/wiki/Upgrade_PineTime_to_InfiniTime_1.0.0) and, you may find them misleading if you're not familiar with the project.
Basically, a **firmware** is just a software running on the embedded hardware of a device, the PineTime in this case. Basically, a **firmware** is just a software running on the embedded hardware of a device, the PineTime in this case.
@ -13,9 +15,10 @@ Basically, a **firmware** is just a software running on the embedded hardware of
**OTA** and **DFU** refer to the update of the firmware over BLE (**B**luetooth **L**ow **E**nergy). **OTA** means **O**ver **T**he **A**ir, this is a functionality that allows the user to update the firmware how their device using a wireless communication like BLE. When we talk about **DFU** (**D**igital **F**irmware **U**pdate), we refer to the file format and protocol used to send the update of the firmware to the watch over-the-air. InfiniTime implement the (legacy) DFU protocol from Nordic Semiconductor (NRF). **OTA** and **DFU** refer to the update of the firmware over BLE (**B**luetooth **L**ow **E**nergy). **OTA** means **O**ver **T**he **A**ir, this is a functionality that allows the user to update the firmware how their device using a wireless communication like BLE. When we talk about **DFU** (**D**igital **F**irmware **U**pdate), we refer to the file format and protocol used to send the update of the firmware to the watch over-the-air. InfiniTime implement the (legacy) DFU protocol from Nordic Semiconductor (NRF).
## How to check the version of InfiniTime and the bootloader? ## How to check the version of InfiniTime and the bootloader?
Since September 2020, all PineTimes (devkits or sealed) are flashed using the **[first iteration of the bootloader](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7)** and **[InfiniTime 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1)**. There was no recovery firmware at that time. Since September 2020, all PineTimes (devkits or sealed) are flashed using the **[first iteration of the bootloader](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7)** and **[InfiniTime 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1)**. There was no recovery firmware at that time.
The bootloader only runs when the watch starts (from an empty battery, for example) or after a reset (after a succesful OTA or a manual reset - long push on the button). The bootloader only runs when the watch starts (from an empty battery, for example) or after a reset (after a successful OTA or a manual reset - long push on the button).
You can recognize this first iteration of the bootloader with it greenish **PINETIME** logo. You can recognize this first iteration of the bootloader with it greenish **PINETIME** logo.
@ -30,14 +33,14 @@ And for version >= 1.0 :
![InfiniTime 1.0 version](version-1.0.jpg) ![InfiniTime 1.0 version](version-1.0.jpg)
PineTime shipped from June 2021 (to be confirmed) will be flashed with the [new version of the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/tag/1.0.0), the [recovery firmware](https://github.com/JF002/InfiniTime/releases/tag/0.14.1) and [InfiniTime 1.0](https://github.com/JF002/InfiniTime/releases/tag/1.0.0).
PineTime shipped from June 2020 (to be confirmed) will be flashed with the [new version of the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/tag/1.0.0), the [recovery firmware](https://github.com/JF002/InfiniTime/releases/tag/0.14.1) and [InfiniTime 1.0](https://github.com/JF002/InfiniTime/releases/tag/1.0.0).
The bootloader is easily recognizable with it white pine cone that is progressively drawn in green. It also displays its own version on the bottom (1.0.0 as of now). The bootloader is easily recognizable with it white pine cone that is progressively drawn in green. It also displays its own version on the bottom (1.0.0 as of now).
![Bootloader 1.0](bootloader-1.0.jpg) ![Bootloader 1.0](bootloader-1.0.jpg)
## How to update your PineTime? ## How to update your PineTime?
To update your PineTime, you can use one of the compatible companion applications. Here are the main ones: To update your PineTime, you can use one of the compatible companion applications. Here are the main ones:
- **[Amazfish](https://github.com/piggz/harbour-amazfish)** (Desktop Linux, mobile Linux, SailfishOS, runs on the PinebookPro and the Pinephone) - **[Amazfish](https://github.com/piggz/harbour-amazfish)** (Desktop Linux, mobile Linux, SailfishOS, runs on the PinebookPro and the Pinephone)
@ -45,36 +48,41 @@ To update your PineTime, you can use one of the compatible companion application
- **[Siglo](https://github.com/alexr4535/siglo)** (Linux, GTK based) - **[Siglo](https://github.com/alexr4535/siglo)** (Linux, GTK based)
- **NRFConnect** (closed source, Android & iOS). - **NRFConnect** (closed source, Android & iOS).
See [this page](ota-gadgetbridge-nrfconnect.md) for more info about the OTA procedure using Gadgetbrige and NRFCOnnect. See [this page](ota-gadgetbridge-nrfconnect.md) for more info about the OTA procedure using Gadgetbridge and NRFConnect.
### From InfiniTime 0.7.1 / old bootloader ### From InfiniTime 0.7.1 / old bootloader
If your PineTime is currently running InfiniTime 0.7.1 and the old bootloader, we strongly recommend you update them to more recent version (Bootloader 1.0.0 and InfiniTime 1.0.0 as of now). We also recommend you install the recovery firmware once the bootloader is up-do-date. If your PineTime is currently running InfiniTime 0.7.1 and the old bootloader, we strongly recommend you update them to more recent version (Bootloader 1.0.0 and InfiniTime 1.0.0 as of now). We also recommend you install the recovery firmware once the bootloader is up-do-date.
Using the companion app of your choice, you'll need to apply the OTA procedure for these 3 firmwares in this sequence (failing to follow this specific order might temporarily or permanently brick your device): Using the companion app of your choice, you'll need to apply the OTA procedure for these 3 firmwares in this sequence (failing to follow this specific order might temporarily or permanently brick your device):
1. Flash the latest version of InfiniTime. The file to upload is named **pinetime-mcuboot-app-dfu-x.y.z.zip**. Here is the link to [InfiniTime 1.0](https://github.com/JF002/InfiniTime/releases/download/1.0.0/pinetime-mcuboot-app-dfu-1.0.0.zip). 1. Flash the latest version of InfiniTime. The file to upload is named **pinetime-mcuboot-app-dfu-x.y.z.zip**. Here is the link to [InfiniTime 1.0](https://github.com/JF002/InfiniTime/releases/download/1.0.0/pinetime-mcuboot-app-dfu-1.0.0.zip).
2. Update the bootloader by applying the OTA procedure with the file named [**reloader-mcuboot.zip** from the repo of the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/download/1.0.0/reloader-mcuboot.zip). 2. Update the bootloader by applying the OTA procedure with the file named [**reloader-mcuboot.zip** from the repo of the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/download/1.0.0/reloader-mcuboot.zip).
3. Install the recovery firmware by applying the OTA procedure with the file named [**pinetime-mcuboot-recovery-loader-dfu-0.14.1.zip** from the version 0.14.1 of InfiniTime](https://github.com/JF002/InfiniTime/releases/download/0.14.1/pinetime-mcuboot-recovery-loader-dfu-0.14.1.zip). 3. Install the recovery firmware by applying the OTA procedure with the file named [**pinetime-mcuboot-recovery-loader-dfu-0.14.1.zip** from the version 0.14.1 of InfiniTime](https://github.com/JF002/InfiniTime/releases/download/0.14.1/pinetime-mcuboot-recovery-loader-dfu-0.14.1.zip).
You'll find more info about this process in [this wiki page](https://wiki.pine64.org/wiki/Upgrade_PineTime_to_InfiniTime_1.0.0). You can also see the procedure in video [here](https://video.codingfield.com/videos/watch/831077c5-16f3-47b4-9b2b-c4bbfecc6529) and [here (from Amazfish)](https://video.codingfield.com/videos/watch/f7bffb3d-a6a1-43c4-8f01-f4aeff4adf9e) You'll find more info about this process in [this wiki page](https://wiki.pine64.org/wiki/Upgrade_PineTime_to_InfiniTime_1.0.0). You can also see the procedure in video [here](https://video.codingfield.com/videos/watch/831077c5-16f3-47b4-9b2b-c4bbfecc6529) and [here (from Amazfish)](https://video.codingfield.com/videos/watch/f7bffb3d-a6a1-43c4-8f01-f4aeff4adf9e)
### From version > 1.0 ### From version > 1.0
If you are already running the new "1.0.0" bootloader, all you have to do is update your version of InfiniTime when it'll be available. We'll write specific instructions when (if) we release a new version of the bootloader. If you are already running the new "1.0.0" bootloader, all you have to do is update your version of InfiniTime when it'll be available. We'll write specific instructions when (if) we release a new version of the bootloader.
### Firmware validation ### Firmware validation
The bootloader requires a (manual) validation of the firmware. If the watch reset with an updated firmware that was not validated, the bootloader will consider it as non-functionning and will revert to the previous version of the firmware. This is a safety feature to prevent bricking your device with a faulty firmware.
The bootloader requires a (manual) validation of the firmware. If the watch reset with an updated firmware that was not validated, the bootloader will consider it as non-functioning and will revert to the previous version of the firmware. This is a safety feature to prevent bricking your device with a faulty firmware.
You can validate your updated firmware on InfiniTime >= 1.0 by following this simple procedure: You can validate your updated firmware on InfiniTime >= 1.0 by following this simple procedure:
- From the watchface, swipe **right** to display the *Quick Actions menu* - From the watchface, swipe **right** to display the *Quick Actions menu*
- Open the **Settings** app by tapping the *gear* icon on the bottom right - Open the **Settings** app by tapping the *gear* icon on the bottom right
- Swipe down and tap on the entry named **Firmware** - Swipe down and tap on the entry named **Firmware**
- This app shows the version that is currently running. If it's not validated yet, it displays 2 buttons: - This app shows the version that is currently running. If it's not validated yet, it displays 2 buttons:
- **Validate** to validate your firmware - **Validate** to validate your firmware
- **Reset** to reset the watch and revert to the previously running version of the firmware - **Reset** to reset the watch and revert to the previously running version of the firmware
## InfiniTime 1.0 quick user guide ## InfiniTime 1.0 quick user guide
### Setting the time ### Setting the time
By default, InfiniTime starts on the digital watchface. It'll probably display the epoch time (1 Jan 1970, 00:00). The time will be automatically synchronized once you connect on of the companion app to your PineTime using BLE connectivity. InfiniTime does not provide any way to manually set the time for now. By default, InfiniTime starts on the digital watchface. It'll probably display the epoch time (1 Jan 1970, 00:00). The time will be automatically synchronized once you connect on of the companion app to your PineTime using BLE connectivity. InfiniTime does not provide any way to manually set the time for now.
### Navigation in the menu ### Navigation in the menu
@ -90,7 +98,7 @@ By default, InfiniTime starts on the digital watchface. It'll probably display t
- Start the **flashlight** app - Start the **flashlight** app
- Enable/disable vibrations on notifications (Do Not Disturb mode) - Enable/disable vibrations on notifications (Do Not Disturb mode)
- Enter the **settings** menu - Enter the **settings** menu
- Settings - Settings
- Display timeout - Display timeout
- Wake up event (Tap, wrist rotation) - Wake up event (Tap, wrist rotation)
- Time format (12/24H) - Time format (12/24H)
@ -104,9 +112,8 @@ By default, InfiniTime starts on the digital watchface. It'll probably display t
Most of the time, the bootloader just runs without your intervention (update and load the firmware). Most of the time, the bootloader just runs without your intervention (update and load the firmware).
However, you can enable 2 functionalities using the push button: However, you can enable 2 functionalities using the push button:
- Push the button until the pine cone is drawn in **blue** to force the rollback of the previous version of the firmware, even if you've already validated the updated one - Push the button until the pine cone is drawn in **blue** to force the rollback of the previous version of the firmware, even if you've already validated the updated one
- Push the button until the pine cone is drawn in **red** to load the recovery firmware. This recovery firmware only provides BLE connectivity and OTA functionality. - Push the button until the pine cone is drawn in **red** to load the recovery firmware. This recovery firmware only provides BLE connectivity and OTA functionality.
More info about the bootloader in [its project page](https://github.com/JF002/pinetime-mcuboot-bootloader/blob/master/README.md). More info about the bootloader in [its project page](https://github.com/JF002/pinetime-mcuboot-bootloader/blob/master/README.md).

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -19,8 +19,11 @@ RUN apt-get update -qq \
libffi-dev \ libffi-dev \
libssl-dev \ libssl-dev \
python3-dev \ python3-dev \
git \
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*; && rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
# Needs to be installed as root # Needs to be installed as root
RUN pip3 install adafruit-nrfutil RUN pip3 install adafruit-nrfutil
RUN pip3 install -Iv cryptography==3.3 RUN pip3 install -Iv cryptography==3.3

View File

@ -19,8 +19,11 @@ RUN apt-get update -qq \
libssl-dev \ libssl-dev \
python3-dev \ python3-dev \
python \ python \
git \
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*; && rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
RUN pip3 install adafruit-nrfutil RUN pip3 install adafruit-nrfutil
RUN pip3 install -Iv cryptography==3.3 RUN pip3 install -Iv cryptography==3.3

5
hooks/README.md Normal file
View File

@ -0,0 +1,5 @@
# Git hooks
This directory contains Git hooks that simplify contributing to this repository.
You can install them by copying the files into `.git/hooks` in the repository folder or by running `git config --local core.hooksPath hooks`

25
hooks/pre-commit Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
if clang-format --version | grep -q 'version 11\.'; then
CLANG_FORMAT_EXECUTABLE="clang-format"
else
CLANG_FORMAT_EXECUTABLE="clang-format-11"
fi
if ! command -v $CLANG_FORMAT_EXECUTABLE &> /dev/null
then
echo $CLANG_FORMAT_EXECUTABLE does not exist, make sure to install it
exit 1
fi
for FILE in $(git diff --cached --name-only)
do
if [[ "$FILE" =~ src/[A-Za-z0-9\ \-]+*\.(c|h|cpp|cc)$ ]]; then
echo Autoformatting $FILE with $CLANG_FORMAT_EXECUTABLE
$CLANG_FORMAT_EXECUTABLE -style=file -i -- $FILE
git add -- $FILE
elif [[ "$FILE" =~ src/(components|displayapp|drivers|heartratetask|logging|systemtask)/.*\.(c|h|cpp|cc)$ ]]; then
echo Autoformatting $FILE with $CLANG_FORMAT_EXECUTABLE
$CLANG_FORMAT_EXECUTABLE -style=file -i -- $FILE
git add -- $FILE
fi
done

View File

@ -1,26 +1,36 @@
#include <cstdint> #include <cstdint>
#include <cstdio>
#include "BootloaderVersion.h" #include "BootloaderVersion.h"
using namespace Pinetime; using namespace Pinetime;
// NOTE : current bootloader does not export its version to the application firmware. // NOTE : version < 1.0.0 of bootloader does not export its version to the application firmware.
uint32_t BootloaderVersion::Major() { uint32_t BootloaderVersion::version = 0;
return 0; char BootloaderVersion::versionString[BootloaderVersion::VERSION_STR_LEN] = "0.0.0";
const uint32_t BootloaderVersion::Major() {
return (BootloaderVersion::version >> 16u) & 0xff;
} }
uint32_t BootloaderVersion::Minor() { const uint32_t BootloaderVersion::Minor() {
return 0; return (BootloaderVersion::version >> 8u) & 0xff;
} }
uint32_t BootloaderVersion::Patch() { const uint32_t BootloaderVersion::Patch() {
return 0; return BootloaderVersion::version & 0xff;
} }
const char* BootloaderVersion::VersionString() { const char* BootloaderVersion::VersionString() {
return "0.0.0"; return BootloaderVersion::versionString;
} }
bool BootloaderVersion::IsValid() { const bool BootloaderVersion::IsValid() {
return false; return BootloaderVersion::version >= 0x00010000;
}
void BootloaderVersion::SetVersion(uint32_t v) {
BootloaderVersion::version = v;
snprintf(BootloaderVersion::versionString, BootloaderVersion::VERSION_STR_LEN, "%ld.%ld.%ld",
BootloaderVersion::Major(), BootloaderVersion::Minor(), BootloaderVersion::Patch());
} }

View File

@ -3,10 +3,15 @@
namespace Pinetime { namespace Pinetime {
class BootloaderVersion { class BootloaderVersion {
public: public:
static uint32_t Major(); static const uint32_t Major();
static uint32_t Minor(); static const uint32_t Minor();
static uint32_t Patch(); static const uint32_t Patch();
static const char* VersionString(); static const char* VersionString();
static bool IsValid(); static const bool IsValid();
static void SetVersion(uint32_t v);
private:
static uint32_t version;
static constexpr size_t VERSION_STR_LEN = 12;
static char versionString[VERSION_STR_LEN];
}; };
} }

View File

@ -166,6 +166,13 @@ set(NIMBLE_SRC
libs/mynewt-nimble/nimble/host/util/src/addr.c libs/mynewt-nimble/nimble/host/util/src/addr.c
) )
set(LITTLEFS_SRC
libs/littlefs/lfs_util.h
libs/littlefs/lfs.h
libs/littlefs/lfs_util.c
libs/littlefs/lfs.c
)
set(LVGL_SRC set(LVGL_SRC
libs/lv_conf.h libs/lv_conf.h
libs/lvgl/lvgl.h libs/lvgl/lvgl.h
@ -235,6 +242,7 @@ set(LVGL_SRC
libs/lvgl/src/lv_widgets/lv_cont.h libs/lvgl/src/lv_widgets/lv_cont.h
libs/lvgl/src/lv_widgets/lv_cpicker.h libs/lvgl/src/lv_widgets/lv_cpicker.h
libs/lvgl/src/lv_widgets/lv_dropdown.h libs/lvgl/src/lv_widgets/lv_dropdown.h
libs/lvgl/src/lv_widgets/lv_gauge.h
libs/lvgl/src/lv_widgets/lv_img.h libs/lvgl/src/lv_widgets/lv_img.h
libs/lvgl/src/lv_widgets/lv_imgbtn.h libs/lvgl/src/lv_widgets/lv_imgbtn.h
libs/lvgl/src/lv_widgets/lv_keyboard.h libs/lvgl/src/lv_widgets/lv_keyboard.h
@ -321,6 +329,7 @@ set(LVGL_SRC
libs/lvgl/src/lv_widgets/lv_cont.c libs/lvgl/src/lv_widgets/lv_cont.c
libs/lvgl/src/lv_widgets/lv_cpicker.c libs/lvgl/src/lv_widgets/lv_cpicker.c
libs/lvgl/src/lv_widgets/lv_dropdown.c libs/lvgl/src/lv_widgets/lv_dropdown.c
libs/lvgl/src/lv_widgets/lv_gauge.c
libs/lvgl/src/lv_widgets/lv_img.c libs/lvgl/src/lv_widgets/lv_img.c
libs/lvgl/src/lv_widgets/lv_imgbtn.c libs/lvgl/src/lv_widgets/lv_imgbtn.c
libs/lvgl/src/lv_widgets/lv_keyboard.c libs/lvgl/src/lv_widgets/lv_keyboard.c
@ -396,6 +405,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/FirmwareUpdate.cpp displayapp/screens/FirmwareUpdate.cpp
displayapp/screens/Music.cpp displayapp/screens/Music.cpp
displayapp/screens/Navigation.cpp displayapp/screens/Navigation.cpp
displayapp/screens/Metronome.cpp
displayapp/screens/Motion.cpp displayapp/screens/Motion.cpp
displayapp/screens/FirmwareValidation.cpp displayapp/screens/FirmwareValidation.cpp
displayapp/screens/ApplicationList.cpp displayapp/screens/ApplicationList.cpp
@ -407,6 +417,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/List.cpp displayapp/screens/List.cpp
displayapp/screens/BatteryInfo.cpp displayapp/screens/BatteryInfo.cpp
displayapp/screens/Steps.cpp displayapp/screens/Steps.cpp
displayapp/screens/Timer.cpp
## Settings ## Settings
displayapp/screens/settings/QuickSettings.cpp displayapp/screens/settings/QuickSettings.cpp
@ -421,6 +432,7 @@ list(APPEND SOURCE_FILES
displayapp/icons/bg_clock.c displayapp/icons/bg_clock.c
displayapp/screens/WatchFaceAnalog.cpp displayapp/screens/WatchFaceAnalog.cpp
displayapp/screens/WatchFaceDigital.cpp displayapp/screens/WatchFaceDigital.cpp
displayapp/screens/PineTimeStyle.cpp
## ##
@ -459,6 +471,8 @@ list(APPEND SOURCE_FILES
components/firmwarevalidator/FirmwareValidator.cpp components/firmwarevalidator/FirmwareValidator.cpp
components/motor/MotorController.cpp components/motor/MotorController.cpp
components/settings/Settings.cpp components/settings/Settings.cpp
components/timer/TimerController.cpp
components/fs/FS.cpp
drivers/Cst816s.cpp drivers/Cst816s.cpp
FreeRTOS/port.c FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c FreeRTOS/port_cmsis_systick.c
@ -470,6 +484,7 @@ list(APPEND SOURCE_FILES
displayapp/fonts/jetbrains_mono_76.c displayapp/fonts/jetbrains_mono_76.c
displayapp/fonts/jetbrains_mono_42.c displayapp/fonts/jetbrains_mono_42.c
displayapp/fonts/lv_font_sys_48.c displayapp/fonts/lv_font_sys_48.c
displayapp/fonts/open_sans_light.c
displayapp/lv_pinetime_theme.c displayapp/lv_pinetime_theme.c
systemtask/SystemTask.cpp systemtask/SystemTask.cpp
@ -520,6 +535,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/ble/HeartRateService.cpp components/ble/HeartRateService.cpp
components/firmwarevalidator/FirmwareValidator.cpp components/firmwarevalidator/FirmwareValidator.cpp
components/settings/Settings.cpp components/settings/Settings.cpp
components/timer/TimerController.cpp
drivers/Cst816s.cpp drivers/Cst816s.cpp
FreeRTOS/port.c FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c FreeRTOS/port_cmsis_systick.c
@ -528,7 +544,6 @@ list(APPEND RECOVERY_SOURCE_FILES
systemtask/SystemTask.cpp systemtask/SystemTask.cpp
drivers/TwiMaster.cpp drivers/TwiMaster.cpp
components/gfx/Gfx.cpp components/gfx/Gfx.cpp
displayapp/icons/infinitime/infinitime-nb.c
components/rle/RleDecoder.cpp components/rle/RleDecoder.cpp
components/heartrate/HeartRateController.cpp components/heartrate/HeartRateController.cpp
heartratetask/HeartRateTask.cpp heartratetask/HeartRateTask.cpp
@ -536,6 +551,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/heartrate/Biquad.cpp components/heartrate/Biquad.cpp
components/heartrate/Ptagc.cpp components/heartrate/Ptagc.cpp
components/motor/MotorController.cpp components/motor/MotorController.cpp
components/fs/FS.cpp
) )
list(APPEND RECOVERYLOADER_SOURCE_FILES list(APPEND RECOVERYLOADER_SOURCE_FILES
@ -555,7 +571,6 @@ list(APPEND RECOVERYLOADER_SOURCE_FILES
drivers/St7789.cpp drivers/St7789.cpp
components/brightness/BrightnessController.cpp components/brightness/BrightnessController.cpp
displayapp/icons/infinitime/infinitime-nb.c
recoveryLoader.cpp recoveryLoader.cpp
) )
@ -589,7 +604,9 @@ set(INCLUDE_FILES
displayapp/Apps.h displayapp/Apps.h
displayapp/screens/Notifications.h displayapp/screens/Notifications.h
displayapp/screens/HeartRate.h displayapp/screens/HeartRate.h
displayapp/screens/Metronome.h
displayapp/screens/Motion.h displayapp/screens/Motion.h
displayapp/screens/Timer.h
drivers/St7789.h drivers/St7789.h
drivers/SpiNorFlash.h drivers/SpiNorFlash.h
drivers/SpiMaster.h drivers/SpiMaster.h
@ -619,6 +636,7 @@ set(INCLUDE_FILES
components/ble/BleClient.h components/ble/BleClient.h
components/ble/HeartRateService.h components/ble/HeartRateService.h
components/settings/Settings.h components/settings/Settings.h
components/timer/TimerController.h
drivers/Cst816s.h drivers/Cst816s.h
FreeRTOS/portmacro.h FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h FreeRTOS/portmacro_cmsis.h
@ -739,16 +757,18 @@ link_directories(
) )
set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -Wno-unknown-pragmas -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wreturn-type -Werror=return-type) set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -Wno-unknown-pragmas -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wreturn-type -Werror=return-type -fstack-usage -fno-exceptions -fno-non-call-exceptions)
add_definitions(-DCONFIG_GPIO_AS_PINRESET) add_definitions(-DCONFIG_GPIO_AS_PINRESET)
add_definitions(-DDEBUG)
add_definitions(-DNIMBLE_CFG_CONTROLLER) add_definitions(-DNIMBLE_CFG_CONTROLLER)
add_definitions(-DOS_CPUTIME_FREQ) add_definitions(-DOS_CPUTIME_FREQ)
add_definitions(-DNRF52 -DNRF52832 -DNRF52832_XXAA -DNRF52_PAN_74 -DNRF52_PAN_64 -DNRF52_PAN_12 -DNRF52_PAN_58 -DNRF52_PAN_54 -DNRF52_PAN_31 -DNRF52_PAN_51 -DNRF52_PAN_36 -DNRF52_PAN_15 -DNRF52_PAN_20 -DNRF52_PAN_55 -DBOARD_PCA10040) add_definitions(-DNRF52 -DNRF52832 -DNRF52832_XXAA -DNRF52_PAN_74 -DNRF52_PAN_64 -DNRF52_PAN_12 -DNRF52_PAN_58 -DNRF52_PAN_54 -DNRF52_PAN_31 -DNRF52_PAN_51 -DNRF52_PAN_36 -DNRF52_PAN_15 -DNRF52_PAN_20 -DNRF52_PAN_55 -DBOARD_PCA10040)
add_definitions(-DFREERTOS) add_definitions(-DFREERTOS)
add_definitions(-DDEBUG_NRF_USER) add_definitions(-D__STACK_SIZE=1024)
add_definitions(-D__STACK_SIZE=8192) add_definitions(-D__HEAP_SIZE=4096)
add_definitions(-D__HEAP_SIZE=8192)
# NOTE : Add the following defines to enable debug mode of the NRF SDK:
#add_definitions(-DDEBUG)
#add_definitions(-DDEBUG_NRF_USER)
if (NOT CMAKE_BUILD_TYPE) if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release") set(CMAKE_BUILD_TYPE "Release")
@ -759,10 +779,10 @@ add_library(nrf-sdk STATIC ${SDK_SOURCE_FILES})
target_include_directories(nrf-sdk SYSTEM PUBLIC . ../) target_include_directories(nrf-sdk SYSTEM PUBLIC . ../)
target_include_directories(nrf-sdk SYSTEM PUBLIC ${INCLUDES_FROM_LIBS}) target_include_directories(nrf-sdk SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(nrf-sdk PRIVATE target_compile_options(nrf-sdk PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
@ -771,10 +791,10 @@ add_library(nimble STATIC ${NIMBLE_SRC} ${TINYCRYPT_SRC})
target_include_directories(nimble SYSTEM PUBLIC . ../) target_include_directories(nimble SYSTEM PUBLIC . ../)
target_include_directories(nimble SYSTEM PUBLIC ${INCLUDES_FROM_LIBS}) target_include_directories(nimble SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(nimble PRIVATE target_compile_options(nimble PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -Wno-unused-but-set-variable -Wno-maybe-uninitialized>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
@ -783,10 +803,22 @@ add_library(lvgl STATIC ${LVGL_SRC})
target_include_directories(lvgl SYSTEM PUBLIC . ../) target_include_directories(lvgl SYSTEM PUBLIC . ../)
target_include_directories(lvgl SYSTEM PUBLIC ${INCLUDES_FROM_LIBS}) target_include_directories(lvgl SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(lvgl PRIVATE target_compile_options(lvgl PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
)
# LITTLEFS_SRC
add_library(littlefs STATIC ${LITTLEFS_SRC})
target_include_directories(littlefs SYSTEM PUBLIC . ../)
target_include_directories(littlefs SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(littlefs PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wno-unused-function -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wno-unused-function -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wno-unused-function -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wno-unused-function -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
@ -796,12 +828,12 @@ set(EXECUTABLE_FILE_NAME ${EXECUTABLE_NAME}-${pinetime_VERSION_MAJOR}.${pinetime
set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld") set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld")
add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES}) add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME}) set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME})
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl) target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs)
target_compile_options(${EXECUTABLE_NAME} PUBLIC target_compile_options(${EXECUTABLE_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
@ -825,13 +857,13 @@ set(IMAGE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERSION_
set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld") set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld")
add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES}) add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES})
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl) target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs)
set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME}) set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME})
target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
@ -861,14 +893,14 @@ endif()
set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery") set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery")
set(EXECUTABLE_RECOVERY_FILE_NAME ${EXECUTABLE_RECOVERY_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}) set(EXECUTABLE_RECOVERY_FILE_NAME ${EXECUTABLE_RECOVERY_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
add_executable(${EXECUTABLE_RECOVERY_NAME} ${RECOVERY_SOURCE_FILES}) add_executable(${EXECUTABLE_RECOVERY_NAME} ${RECOVERY_SOURCE_FILES})
target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk) target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs)
set_target_properties(${EXECUTABLE_RECOVERY_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_FILE_NAME}) set_target_properties(${EXECUTABLE_RECOVERY_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_FILE_NAME})
target_compile_definitions(${EXECUTABLE_RECOVERY_NAME} PUBLIC "PINETIME_IS_RECOVERY") target_compile_definitions(${EXECUTABLE_RECOVERY_NAME} PUBLIC "PINETIME_IS_RECOVERY")
target_compile_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC target_compile_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
@ -891,14 +923,14 @@ set(EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-${
set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.bin) set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.bin)
set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES}) add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES})
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk) target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs)
set_target_properties(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}) set_target_properties(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME})
target_compile_definitions(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC "PINETIME_IS_RECOVERY") target_compile_definitions(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC "PINETIME_IS_RECOVERY")
target_compile_options(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC target_compile_options(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
@ -932,10 +964,10 @@ add_executable(${EXECUTABLE_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE_FILES})
target_link_libraries(${EXECUTABLE_RECOVERYLOADER_NAME} nrf-sdk) target_link_libraries(${EXECUTABLE_RECOVERYLOADER_NAME} nrf-sdk)
set_target_properties(${EXECUTABLE_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERYLOADER_FILE_NAME}) set_target_properties(${EXECUTABLE_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERYLOADER_FILE_NAME})
target_compile_options(${EXECUTABLE_RECOVERYLOADER_NAME} PUBLIC target_compile_options(${EXECUTABLE_RECOVERYLOADER_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
target_include_directories(${EXECUTABLE_RECOVERYLOADER_NAME} PUBLIC target_include_directories(${EXECUTABLE_RECOVERYLOADER_NAME} PUBLIC
@ -965,10 +997,10 @@ add_executable(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE
target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk) target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk)
set_target_properties(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}) set_target_properties(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME})
target_compile_options(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PUBLIC target_compile_options(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3> $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp> $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
) )
target_include_directories(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PUBLIC target_include_directories(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PUBLIC

View File

@ -294,6 +294,25 @@ static void vPortEnableVFP( void )
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
uint32_t ulSetInterruptMaskFromISR( void )
{
__asm volatile (
" mrs r0, PRIMASK \n"
" cpsid i \n"
" bx lr "
::: "memory"
);
}
void vClearInterruptMaskFromISR( __attribute__( ( unused ) ) uint32_t ulMask )
{
__asm volatile (
" msr PRIMASK, r0 \n"
" bx lr "
::: "memory"
);
}
#if ( configASSERT_DEFINED == 1 ) #if ( configASSERT_DEFINED == 1 )
void vPortValidateInterruptPriority( void ) void vPortValidateInterruptPriority( void )
@ -354,24 +373,4 @@ static void vPortEnableVFP( void )
configASSERT( NVIC_GetPriorityGrouping() <= ulMaxPRIGROUPValue ); configASSERT( NVIC_GetPriorityGrouping() <= ulMaxPRIGROUPValue );
} }
uint32_t ulSetInterruptMaskFromISR( void )
{
__asm volatile (
" mrs r0, PRIMASK \n"
" cpsid i \n"
" bx lr "
::: "memory"
);
}
/*-----------------------------------------------------------*/
void vClearInterruptMaskFromISR( __attribute__( ( unused ) ) uint32_t ulMask )
{
__asm volatile (
" msr PRIMASK, r0 \n"
" bx lr "
::: "memory"
);
}
#endif /* configASSERT_DEFINED */ #endif /* configASSERT_DEFINED */

View File

@ -62,7 +62,7 @@
#define configTICK_RATE_HZ 1024 #define configTICK_RATE_HZ 1024
#define configMAX_PRIORITIES (3) #define configMAX_PRIORITIES (3)
#define configMINIMAL_STACK_SIZE (120) #define configMINIMAL_STACK_SIZE (120)
#define configTOTAL_HEAP_SIZE (1024 * 16) #define configTOTAL_HEAP_SIZE (1024 * 17)
#define configMAX_TASK_NAME_LEN (4) #define configMAX_TASK_NAME_LEN (4)
#define configUSE_16_BIT_TICKS 0 #define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1 #define configIDLE_SHOULD_YIELD 1
@ -77,7 +77,7 @@
#define configENABLE_BACKWARD_COMPATIBILITY 1 #define configENABLE_BACKWARD_COMPATIBILITY 1
/* Hook function related definitions. */ /* Hook function related definitions. */
#define configUSE_IDLE_HOOK 1 #define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0 #define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0 #define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_MALLOC_FAILED_HOOK 0

View File

@ -8,11 +8,13 @@ namespace Pinetime {
static constexpr uint32_t Major() {return major;} static constexpr uint32_t Major() {return major;}
static constexpr uint32_t Minor() {return minor;} static constexpr uint32_t Minor() {return minor;}
static constexpr uint32_t Patch() {return patch;} static constexpr uint32_t Patch() {return patch;}
static constexpr const char* GitCommitHash() {return commitHash;}
static constexpr const char* VersionString() {return versionString;} static constexpr const char* VersionString() {return versionString;}
private: private:
static constexpr uint32_t major = @PROJECT_VERSION_MAJOR@; static constexpr uint32_t major = @PROJECT_VERSION_MAJOR@;
static constexpr uint32_t minor = @PROJECT_VERSION_MINOR@; static constexpr uint32_t minor = @PROJECT_VERSION_MINOR@;
static constexpr uint32_t patch = @PROJECT_VERSION_PATCH@; static constexpr uint32_t patch = @PROJECT_VERSION_PATCH@;
static constexpr const char* commitHash = "@PROJECT_GIT_COMMIT_HASH@";
static constexpr const char* versionString = "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@"; static constexpr const char* versionString = "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@";
}; };
} }

View File

@ -1,9 +1,7 @@
#include "BatteryController.h" #include "BatteryController.h"
#include <hal/nrf_gpio.h> #include <hal/nrf_gpio.h>
#include <nrfx_saadc.h> #include <nrfx_saadc.h>
#include <libraries/log/nrf_log.h>
#include <algorithm> #include <algorithm>
#include <math.h>
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
@ -14,17 +12,16 @@ Battery::Battery() {
} }
void Battery::Init() { void Battery::Init() {
nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup); nrf_gpio_cfg_input(chargingPin, static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Pullup);
nrf_gpio_cfg_input(powerPresentPin, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup);
} }
void Battery::Update() { void Battery::Update() {
isCharging = !nrf_gpio_pin_read(chargingPin); isCharging = !nrf_gpio_pin_read(chargingPin);
isPowerPresent = !nrf_gpio_pin_read(powerPresentPin); isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
if (isReading) if (isReading) {
return; return;
}
// Non blocking read // Non blocking read
samples = 0; samples = 0;
isReading = true; isReading = true;
@ -33,13 +30,13 @@ void Battery::Update() {
nrfx_saadc_sample(); nrfx_saadc_sample();
} }
void Battery::adcCallbackStatic(nrfx_saadc_evt_t const* event) { void Battery::AdcCallbackStatic(nrfx_saadc_evt_t const* event) {
instance->SaadcEventHandler(event); instance->SaadcEventHandler(event);
} }
void Battery::SaadcInit() { void Battery::SaadcInit() {
nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG; nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, adcCallbackStatic)); APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, AdcCallbackStatic));
nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED, nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
@ -55,23 +52,23 @@ void Battery::SaadcInit() {
} }
void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) { void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
const uint16_t battery_max = 4180; // maximum voltage of battery ( max charging voltage is 4.21 )
const float battery_max = 4.18; // maximum voltage of battery ( max charging voltage is 4.21 ) const uint16_t battery_min = 3200; // minimum voltage of battery before shutdown ( depends on the battery )
const float battery_min = 3.20; // minimum voltage of battery before shutdown ( depends on the battery )
if (p_event->type == NRFX_SAADC_EVT_DONE) { if (p_event->type == NRFX_SAADC_EVT_DONE) {
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1)); APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
voltage = (static_cast<float>(p_event->data.done.p_buffer[0]) * 2.04f) / (1024 / 3.0f); // A hardware voltage divider divides the battery voltage by 2
voltage = roundf(voltage * 100) / 100; // ADC gain is 1/5
// thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 10
percentRemaining = static_cast<int>(((voltage - battery_min) / (battery_max - battery_min)) * 100); // reference_voltage is 0.6V
// p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
voltage = p_event->data.done.p_buffer[0] * 6000 / 1024;
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
percentRemaining = std::max(percentRemaining, 0); percentRemaining = std::max(percentRemaining, 0);
percentRemaining = std::min(percentRemaining, 100); percentRemaining = std::min(percentRemaining, 100);
percentRemainingBuffer.Insert(percentRemaining);
percentRemainingBuffer.insert(percentRemaining);
samples++; samples++;
if (samples > percentRemainingSamples) { if (samples > percentRemainingSamples) {

View File

@ -19,7 +19,7 @@ namespace Pinetime {
insert member function overwrites the next data to the current insert member function overwrites the next data to the current
HEAD and moves the HEAD to the newly inserted value. HEAD and moves the HEAD to the newly inserted value.
*/ */
void insert(const int num) { void Insert(const uint8_t num) {
head %= cap; head %= cap;
arr[head++] = num; arr[head++] = num;
if (sz != cap) { if (sz != cap) {
@ -27,13 +27,13 @@ namespace Pinetime {
} }
} }
int GetAverage() const { uint8_t GetAverage() const {
int sum = std::accumulate(arr.begin(), arr.end(), 0); int sum = std::accumulate(arr.begin(), arr.end(), 0);
return (sum / sz); return static_cast<uint8_t>(sum / sz);
} }
private: private:
std::array<int, N> arr; /**< internal array used to store the values*/ std::array<uint8_t, N> arr; /**< internal array used to store the values*/
uint8_t sz; /**< The current size of the array.*/ uint8_t sz; /**< The current size of the array.*/
uint8_t cap; /**< Total capacity of the CircBuffer.*/ uint8_t cap; /**< Total capacity of the CircBuffer.*/
uint8_t head; /**< The current head of the CircBuffer*/ uint8_t head; /**< The current head of the CircBuffer*/
@ -46,17 +46,21 @@ namespace Pinetime {
void Init(); void Init();
void Update(); void Update();
int PercentRemaining() const { uint8_t PercentRemaining() const {
return percentRemainingBuffer.GetAverage(); auto avg = percentRemainingBuffer.GetAverage();
avg = std::min(avg, static_cast<uint8_t>(100));
avg = std::max(avg, static_cast<uint8_t>(0));
return avg;
} }
float Voltage() const { uint16_t Voltage() const {
return voltage; return voltage;
} }
bool IsCharging() const { bool IsCharging() const {
return isCharging; return isCharging;
} }
bool IsPowerPresent() const { bool IsPowerPresent() const {
return isPowerPresent; return isPowerPresent;
} }
@ -71,7 +75,7 @@ namespace Pinetime {
static constexpr uint32_t chargingPin = 12; static constexpr uint32_t chargingPin = 12;
static constexpr uint32_t powerPresentPin = 19; static constexpr uint32_t powerPresentPin = 19;
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7; static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
float voltage = 0.0f; uint16_t voltage = 0;
int percentRemaining = -1; int percentRemaining = -1;
bool isCharging = false; bool isCharging = false;
@ -80,10 +84,10 @@ namespace Pinetime {
void SaadcInit(); void SaadcInit();
void SaadcEventHandler(nrfx_saadc_evt_t const* p_event); void SaadcEventHandler(nrfx_saadc_evt_t const* p_event);
static void adcCallbackStatic(nrfx_saadc_evt_t const* event); static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
bool isReading = false; bool isReading = false;
uint8_t samples = 0; uint8_t samples = 0;
}; };
} }
} }

View File

@ -159,7 +159,7 @@ void AlertNotificationClient::OnNotification(ble_gap_event* event) {
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert; notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif)); notificationManager.Push(std::move(notif));
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification); systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
} }
} }

View File

@ -79,7 +79,7 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle
break; break;
} }
auto event = Pinetime::System::SystemTask::Messages::OnNewNotification; auto event = Pinetime::System::Messages::OnNewNotification;
notificationManager.Push(std::move(notif)); notificationManager.Push(std::move(notif));
systemTask.PushMessage(event); systemTask.PushMessage(event);
} }

View File

@ -17,7 +17,7 @@ BatteryInformationService::BatteryInformationService(Controllers::Battery& batte
characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid, characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid,
.access_cb = BatteryInformationServiceCallback, .access_cb = BatteryInformationServiceCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_READ, .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &batteryLevelHandle}, .val_handle = &batteryLevelHandle},
{0}}, {0}},
serviceDefinition { serviceDefinition {
@ -48,4 +48,8 @@ int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHand
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
return 0; return 0;
} }
void BatteryInformationService::NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level) {
auto* om = ble_hs_mbuf_from_flat(&level, 1);
ble_gattc_notify_custom(connectionHandle, batteryLevelHandle, om);
}

View File

@ -17,7 +17,7 @@ namespace Pinetime {
void Init(); 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);
void NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level);
private: private:
Controllers::Battery& batteryController; Controllers::Battery& batteryController;
static constexpr uint16_t batteryInformationServiceId {0x180F}; static constexpr uint16_t batteryInformationServiceId {0x180F};

View File

@ -121,6 +121,11 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
NRF_LOG_INFO( NRF_LOG_INFO(
"[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, bootloaderSize, applicationSize); "[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, bootloaderSize, applicationSize);
// wait until SystemTask has finished waking up all devices
while (systemTask.IsSleeping()) {
vTaskDelay(50); // 50ms
}
dfuImage.Erase(); dfuImage.Erase();
uint8_t data[] {16, 1, 1}; uint8_t data[] {16, 1, 1};
@ -205,7 +210,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Running); bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Running);
bleController.FirmwareUpdateTotalBytes(0xffffffffu); bleController.FirmwareUpdateTotalBytes(0xffffffffu);
bleController.FirmwareUpdateCurrentBytes(0); bleController.FirmwareUpdateCurrentBytes(0);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateStarted); systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateStarted);
return 0; return 0;
} else { } else {
NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType); NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType);
@ -261,13 +266,14 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
static_cast<uint8_t>(ErrorCodes::NoError)}; static_cast<uint8_t>(ErrorCodes::NoError)};
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3); notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
} else { } else {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
NRF_LOG_INFO("Image Error : bad CRC"); NRF_LOG_INFO("Image Error : bad CRC");
uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response), uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
static_cast<uint8_t>(Opcodes::ValidateFirmware), static_cast<uint8_t>(Opcodes::ValidateFirmware),
static_cast<uint8_t>(ErrorCodes::CrcError)}; static_cast<uint8_t>(ErrorCodes::CrcError)};
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3); notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
Reset();
} }
return 0; return 0;
@ -278,10 +284,8 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
return 0; return 0;
} }
NRF_LOG_INFO("[DFU] -> Activate image and reset!"); NRF_LOG_INFO("[DFU] -> Activate image and reset!");
bleController.StopFirmwareUpdate();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
Reset();
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated); bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
Reset();
return 0; return 0;
default: default:
return 0; return 0;
@ -289,6 +293,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
} }
void DfuService::OnTimeout() { void DfuService::OnTimeout() {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
Reset(); Reset();
} }
@ -302,9 +307,8 @@ void DfuService::Reset() {
applicationSize = 0; applicationSize = 0;
expectedCrc = 0; expectedCrc = 0;
notificationManager.Reset(); notificationManager.Reset();
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
bleController.StopFirmwareUpdate(); bleController.StopFirmwareUpdate();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished); systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateFinished);
} }
DfuService::NotificationManager::NotificationManager() { DfuService::NotificationManager::NotificationManager() {

View File

@ -67,7 +67,7 @@ int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert; notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif)); notificationManager.Push(std::move(notif));
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification); systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
} }
} }

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 JF, Adam Pigg, Avamander /* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime. This file is part of InfiniTime.
@ -18,132 +18,103 @@
#include "MusicService.h" #include "MusicService.h"
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { namespace {
auto musicService = static_cast<Pinetime::Controllers::MusicService*>(arg); // 0000yyxx-78fc-48fe-8e23-433b3a1942d0
return musicService->OnCommand(conn_handle, attr_handle, ctxt); constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
return ble_uuid128_t{
.u = {.type = BLE_UUID_TYPE_128},
.value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
};
}
// 00000000-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t BaseUuid() {
return CharUuid(0x00, 0x00);
}
constexpr ble_uuid128_t msUuid {BaseUuid()};
constexpr ble_uuid128_t msEventCharUuid {CharUuid(0x01, 0x00)};
constexpr ble_uuid128_t msStatusCharUuid {CharUuid(0x02, 0x00)};
constexpr ble_uuid128_t msArtistCharUuid {CharUuid(0x03, 0x00)};
constexpr ble_uuid128_t msTrackCharUuid {CharUuid(0x04, 0x00)};
constexpr ble_uuid128_t msAlbumCharUuid {CharUuid(0x05, 0x00)};
constexpr ble_uuid128_t msPositionCharUuid {CharUuid(0x06, 0x00)};
constexpr ble_uuid128_t msTotalLengthCharUuid {CharUuid(0x07, 0x00)};
constexpr ble_uuid128_t msTrackNumberCharUuid {CharUuid(0x08, 0x00)};
constexpr ble_uuid128_t msTrackTotalCharUuid {CharUuid(0x09, 0x00)};
constexpr ble_uuid128_t msPlaybackSpeedCharUuid {CharUuid(0x0a, 0x00)};
constexpr ble_uuid128_t msRepeatCharUuid {CharUuid(0x0b, 0x00)};
constexpr ble_uuid128_t msShuffleCharUuid {CharUuid(0x0c, 0x00)};
int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::MusicService*>(arg)->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]; characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
msUuid.value[15] = msId[1]; .access_cb = MusicCallback,
msEventCharUuid.value[12] = msEventCharId[0];
msEventCharUuid.value[13] = msEventCharId[1];
msEventCharUuid.value[14] = msId[0];
msEventCharUuid.value[15] = msId[1];
msStatusCharUuid.value[12] = msStatusCharId[0];
msStatusCharUuid.value[13] = msStatusCharId[1];
msStatusCharUuid.value[14] = msId[0];
msStatusCharUuid.value[15] = msId[1];
msTrackCharUuid.value[12] = msTrackCharId[0];
msTrackCharUuid.value[13] = msTrackCharId[1];
msTrackCharUuid.value[14] = msId[0];
msTrackCharUuid.value[15] = msId[1];
msArtistCharUuid.value[12] = msArtistCharId[0];
msArtistCharUuid.value[13] = msArtistCharId[1];
msArtistCharUuid.value[14] = msId[0];
msArtistCharUuid.value[15] = msId[1];
msAlbumCharUuid.value[12] = msAlbumCharId[0];
msAlbumCharUuid.value[13] = msAlbumCharId[1];
msAlbumCharUuid.value[14] = msId[0];
msAlbumCharUuid.value[15] = msId[1];
msPositionCharUuid.value[12] = msPositionCharId[0];
msPositionCharUuid.value[13] = msPositionCharId[1];
msPositionCharUuid.value[14] = msId[0];
msPositionCharUuid.value[15] = msId[1];
msTotalLengthCharUuid.value[12] = msTotalLengthCharId[0];
msTotalLengthCharUuid.value[13] = msTotalLengthCharId[1];
msTotalLengthCharUuid.value[14] = msId[0];
msTotalLengthCharUuid.value[15] = msId[1];
msTrackNumberCharUuid.value[12] = msTrackNumberCharId[0];
msTrackNumberCharUuid.value[13] = msTrackNumberCharId[1];
msTrackNumberCharUuid.value[14] = msId[0];
msTrackNumberCharUuid.value[15] = msId[1];
msTrackTotalCharUuid.value[12] = msTrackTotalCharId[0];
msTrackTotalCharUuid.value[13] = msTrackTotalCharId[1];
msTrackTotalCharUuid.value[14] = msId[0];
msTrackTotalCharUuid.value[15] = msId[1];
msPlaybackSpeedCharUuid.value[12] = msPlaybackSpeedCharId[0];
msPlaybackSpeedCharUuid.value[13] = msPlaybackSpeedCharId[1];
msPlaybackSpeedCharUuid.value[14] = msId[0];
msPlaybackSpeedCharUuid.value[15] = msId[1];
msRepeatCharUuid.value[12] = msRepeatCharId[0];
msRepeatCharUuid.value[13] = msRepeatCharId[1];
msRepeatCharUuid.value[14] = msId[0];
msRepeatCharUuid.value[15] = msId[1];
msShuffleCharUuid.value[12] = msShuffleCharId[0];
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, .arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY, .flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &eventHandle}; .val_handle = &eventHandle};
characteristicDefinition[1] = { characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
.uuid = (ble_uuid_t*) (&msStatusCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; .access_cb = MusicCallback,
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, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[7] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid), characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
.access_cb = MSCallback, .access_cb = MusicCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[8] = {.uuid = (ble_uuid_t*) (&msTrackNumberCharUuid), characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
.access_cb = MSCallback, .access_cb = MusicCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[9] = {.uuid = (ble_uuid_t*) (&msTrackTotalCharUuid), characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
.access_cb = MSCallback, .access_cb = MusicCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[10] = {.uuid = (ble_uuid_t*) (&msPlaybackSpeedCharUuid), characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
.access_cb = MSCallback, .access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[12] = {.uuid = &msShuffleCharUuid.u,
.access_cb = MusicCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}; .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[11] = {
.uuid = (ble_uuid_t*) (&msRepeatCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[12] = {
.uuid = (ble_uuid_t*) (&msShuffleCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[13] = {0}; characteristicDefinition[13] = {0};
serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &msUuid, .characteristics = characteristicDefinition}; serviceDefinition[0] = {
.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &msUuid.u, .characteristics = characteristicDefinition};
serviceDefinition[1] = {0}; serviceDefinition[1] = {0};
artistName = "Waiting for";
albumName = "";
trackName = "track information..";
playing = false;
repeat = false;
shuffle = false;
playbackSpeed = 1.0f;
trackProgress = 0;
trackLength = 0;
} }
void Pinetime::Controllers::MusicService::Init() { void Pinetime::Controllers::MusicService::Init() {
int res = 0; uint8_t res = 0;
res = ble_gatts_count_cfg(serviceDefinition); res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0); ASSERT(res == 0);
@ -152,60 +123,67 @@ void Pinetime::Controllers::MusicService::Init() {
} }
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) { if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
size_t notifSize = OS_MBUF_PKTLEN(ctxt->om); size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
uint8_t data[notifSize + 1]; char data[notifSize + 1];
data[notifSize] = '\0'; data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data); os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char* s = (char*) &data[0]; char* s = &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msArtistCharUuid) == 0) { if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
artistName = s; artistName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackCharUuid.u) == 0) {
trackName = s; trackName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msAlbumCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msAlbumCharUuid.u) == 0) {
albumName = s; albumName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msStatusCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msStatusCharUuid.u) == 0) {
playing = s[0]; playing = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msRepeatCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msRepeatCharUuid.u) == 0) {
repeat = s[0]; repeat = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msShuffleCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msShuffleCharUuid.u) == 0) {
shuffle = s[0]; shuffle = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msPositionCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPositionCharUuid.u) == 0) {
trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTotalLengthCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTotalLengthCharUuid.u) == 0) {
trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackNumberCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackNumberCharUuid.u) == 0) {
trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackTotalCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackTotalCharUuid.u) == 0) {
tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msPlaybackSpeedCharUuid) == 0) { } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPlaybackSpeedCharUuid.u) == 0) {
playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f; playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f;
} }
} }
return 0; return 0;
} }
std::string Pinetime::Controllers::MusicService::getAlbum() { std::string Pinetime::Controllers::MusicService::getAlbum() const {
return albumName; return albumName;
} }
std::string Pinetime::Controllers::MusicService::getArtist() { std::string Pinetime::Controllers::MusicService::getArtist() const {
return artistName; return artistName;
} }
std::string Pinetime::Controllers::MusicService::getTrack() { std::string Pinetime::Controllers::MusicService::getTrack() const {
return trackName; return trackName;
} }
bool Pinetime::Controllers::MusicService::isPlaying() { bool Pinetime::Controllers::MusicService::isPlaying() const {
return playing; return playing;
} }
float Pinetime::Controllers::MusicService::getPlaybackSpeed() { float Pinetime::Controllers::MusicService::getPlaybackSpeed() const {
return playbackSpeed; return playbackSpeed;
} }
int Pinetime::Controllers::MusicService::getProgress() const {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() const {
return trackLength;
}
void Pinetime::Controllers::MusicService::event(char event) { void Pinetime::Controllers::MusicService::event(char event) {
auto* om = ble_hs_mbuf_from_flat(&event, 1); auto* om = ble_hs_mbuf_from_flat(&event, 1);
@ -217,11 +195,3 @@ void Pinetime::Controllers::MusicService::event(char event) {
ble_gattc_notify_custom(connectionHandle, eventHandle, om); ble_gattc_notify_custom(connectionHandle, eventHandle, om);
} }
int Pinetime::Controllers::MusicService::getProgress() {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() {
return trackLength;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 JF, Adam Pigg, Avamander /* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime. This file is part of InfiniTime.
@ -26,16 +26,11 @@
#undef max #undef max
#undef min #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 }
namespace Pinetime { namespace Pinetime {
namespace System { namespace System {
class SystemTask; class SystemTask;
} }
namespace Controllers { namespace Controllers {
class MusicService { class MusicService {
public: public:
explicit MusicService(Pinetime::System::SystemTask& system); explicit MusicService(Pinetime::System::SystemTask& system);
@ -46,19 +41,19 @@ namespace Pinetime {
void event(char event); void event(char event);
std::string getArtist(); std::string getArtist() const;
std::string getTrack(); std::string getTrack() const;
std::string getAlbum(); std::string getAlbum() const;
int getProgress(); int getProgress() const;
int getTrackLength(); int getTrackLength() const;
float getPlaybackSpeed(); float getPlaybackSpeed() const;
bool isPlaying(); bool isPlaying() const;
static const char EVENT_MUSIC_OPEN = 0xe0; static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00; static const char EVENT_MUSIC_PLAY = 0x00;
@ -71,55 +66,26 @@ namespace Pinetime {
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 }; enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private: private:
static constexpr uint8_t msId[2] = {0x00, 0x00};
static constexpr uint8_t msEventCharId[2] = {0x01, 0x00};
static constexpr uint8_t msStatusCharId[2] = {0x02, 0x00};
static constexpr uint8_t msArtistCharId[2] = {0x03, 0x00};
static constexpr uint8_t msTrackCharId[2] = {0x04, 0x00};
static constexpr uint8_t msAlbumCharId[2] = {0x05, 0x00};
static constexpr uint8_t msPositionCharId[2] = {0x06, 0x00};
static constexpr uint8_t msTotalLengthCharId[2] = {0x07, 0x00};
static constexpr uint8_t msTrackNumberCharId[2] = {0x08, 0x00};
static constexpr uint8_t msTrackTotalCharId[2] = {0x09, 0x00};
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};
struct ble_gatt_chr_def characteristicDefinition[14]; struct ble_gatt_chr_def characteristicDefinition[14];
struct ble_gatt_svc_def serviceDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2];
uint16_t eventHandle; uint16_t eventHandle {};
std::string artistName; std::string artistName {"Waiting for"};
std::string albumName; std::string albumName {};
std::string trackName; std::string trackName {"track information.."};
bool playing; bool playing {false};
int trackProgress; int trackProgress {0};
int trackLength; int trackLength {0};
int trackNumber; int trackNumber {};
int tracksTotal; int tracksTotal {};
float playbackSpeed; float playbackSpeed {1.0f};
bool repeat; bool repeat {false};
bool shuffle; bool shuffle {false};
Pinetime::System::SystemTask& m_system; Pinetime::System::SystemTask& m_system;
}; };

View File

@ -149,7 +149,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
bleController.Disconnect(); bleController.Disconnect();
} else { } else {
bleController.Connect(); bleController.Connect();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleConnected); systemTask.PushMessage(Pinetime::System::Messages::BleConnected);
connectionHandle = event->connect.conn_handle; connectionHandle = event->connect.conn_handle;
// Service discovery is deffered via systemtask // Service discovery is deffered via systemtask
} }
@ -235,3 +235,9 @@ void NimbleController::StartDiscovery() {
uint16_t NimbleController::connHandle() { uint16_t NimbleController::connHandle() {
return connectionHandle; return connectionHandle;
} }
void NimbleController::NotifyBatteryLevel(uint8_t level) {
if(connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
batteryInformationService.NotifyBatteryLevel(connectionHandle, level);
}
}

View File

@ -70,6 +70,7 @@ namespace Pinetime {
}; };
uint16_t connHandle(); uint16_t connHandle();
void NotifyBatteryLevel(uint8_t level);
private: private:
static constexpr const char* deviceName = "InfiniTime"; static constexpr const char* deviceName = "InfiniTime";
@ -92,7 +93,7 @@ namespace Pinetime {
HeartRateService heartRateService; HeartRateService heartRateService;
uint8_t addrType; // 1 = Random, 0 = PUBLIC uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle = 0; uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
ble_uuid128_t dfuServiceUuid { ble_uuid128_t dfuServiceUuid {
.u {.type = BLE_UUID_TYPE_128}, .u {.type = BLE_UUID_TYPE_128},

View File

@ -5,9 +5,6 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
DateTime::DateTime(System::SystemTask& systemTask) : systemTask {systemTask} {
}
void DateTime::SetTime( void DateTime::SetTime(
uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter) { 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 = { std::tm tm = {
@ -70,7 +67,8 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
// Notify new day to SystemTask // Notify new day to SystemTask
if (hour == 0 and not isMidnightAlreadyNotified) { if (hour == 0 and not isMidnightAlreadyNotified) {
isMidnightAlreadyNotified = true; isMidnightAlreadyNotified = true;
systemTask.PushMessage(System::SystemTask::Messages::OnNewDay); if(systemTask != nullptr)
systemTask->PushMessage(System::Messages::OnNewDay);
} else if (hour != 0) { } else if (hour != 0) {
isMidnightAlreadyNotified = false; isMidnightAlreadyNotified = false;
} }
@ -104,6 +102,10 @@ const char* DateTime::DayOfWeekShortToStringLow() {
return DateTime::DaysStringShortLow[(uint8_t) dayOfWeek]; return DateTime::DaysStringShortLow[(uint8_t) dayOfWeek];
} }
void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}
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"};

View File

@ -27,8 +27,6 @@ namespace Pinetime {
December December
}; };
DateTime(System::SystemTask& systemTask);
void SetTime(uint16_t year, void SetTime(uint16_t year,
uint8_t month, uint8_t month,
uint8_t day, uint8_t day,
@ -75,8 +73,9 @@ namespace Pinetime {
return uptime; return uptime;
} }
void Register(System::SystemTask* systemTask);
private: private:
System::SystemTask& systemTask;
uint16_t year = 0; uint16_t year = 0;
Months month = Months::Unknown; Months month = Months::Unknown;
uint8_t day = 0; uint8_t day = 0;
@ -90,6 +89,7 @@ namespace Pinetime {
std::chrono::seconds uptime {0}; std::chrono::seconds uptime {0};
bool isMidnightAlreadyNotified = false; bool isMidnightAlreadyNotified = false;
System::SystemTask* systemTask = nullptr;
static char const* DaysString[]; static char const* DaysString[];
static char const* DaysStringShort[]; static char const* DaysStringShort[];

197
src/components/fs/FS.cpp Normal file
View File

@ -0,0 +1,197 @@
#include "FS.h"
#include <cstring>
#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using namespace Pinetime::Controllers;
FS::FS(Pinetime::Drivers::SpiNorFlash& driver) :
flashDriver{ driver },
lfsConfig{
.context = this,
.read = SectorRead,
.prog = SectorProg,
.erase = SectorErase,
.sync = SectorSync,
.read_size = 16,
.prog_size = 8,
.block_size = blockSize,
.block_count = size / blockSize,
.block_cycles = 1000u,
.cache_size = 16,
.lookahead_size = 16,
.name_max = 50,
.attr_max = 50,
}
{ }
void FS::Init() {
// try mount
int err = lfs_mount(&lfs, &lfsConfig);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err != LFS_ERR_OK) {
lfs_format(&lfs, &lfsConfig);
err = lfs_mount(&lfs, &lfsConfig);
if (err != LFS_ERR_OK) {
return;
}
}
#ifndef PINETIME_IS_RECOVERY
VerifyResource();
LVGLFileSystemInit();
#endif
}
void FS::VerifyResource() {
// validate the resource metadata
resourcesValid = true;
}
int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
return lfs_file_open(&lfs, file_p, fileName, flags);
}
int FS::FileClose(lfs_file_t* file_p) {
return lfs_file_close(&lfs, file_p);
}
int FS::FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size) {
return lfs_file_read(&lfs, file_p, buff, size);
}
int FS::FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size) {
return lfs_file_write(&lfs, file_p, buff, size);
}
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
return lfs_file_seek(&lfs, file_p, pos, LFS_SEEK_SET);
}
int FS::FileDelete(const char* fileName) {
return lfs_remove(&lfs, fileName);
}
int FS::DirCreate(const char* path) {
return lfs_mkdir(&lfs, path);
}
// Delete directory and all files inside
int FS::DirDelete(const char* path) {
lfs_dir_t lfs_dir;
lfs_info entryInfo;
int err;
err = lfs_dir_open(&lfs, &lfs_dir, path);
if (err) {
return err;
}
while (lfs_dir_read(&lfs, &lfs_dir, &entryInfo)) {
lfs_remove(&lfs, entryInfo.name);
}
lfs_dir_close(&lfs, &lfs_dir);
return LFS_ERR_OK;
}
/*
----------- Interface between littlefs and SpiNorFlash -----------
*/
int FS::SectorSync(const struct lfs_config* c) {
return 0;
}
int FS::SectorErase(const struct lfs_config* c, lfs_block_t block) {
Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
const size_t address = startAddress + (block * blockSize);
lfs.flashDriver.SectorErase(address);
return lfs.flashDriver.EraseFailed() ? -1 : 0;
}
int FS::SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size) {
Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
const size_t address = startAddress + (block * blockSize) + off;
lfs.flashDriver.Write(address, (uint8_t*) buffer, size);
return lfs.flashDriver.ProgramFailed() ? -1 : 0;
}
int FS::SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size) {
Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
const size_t address = startAddress + (block * blockSize) + off;
lfs.flashDriver.Read(address, static_cast<uint8_t*>(buffer), size);
return 0;
}
/*
----------- LVGL filesystem integration -----------
*/
namespace {
lv_fs_res_t lvglOpen(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode) {
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
FS* filesys = static_cast<FS*>(drv->user_data);
filesys->FileOpen(file, path, LFS_O_RDONLY);
if (file->type == 0) {
return LV_FS_RES_FS_ERR;
}
else {
return LV_FS_RES_OK;
}
}
lv_fs_res_t lvglClose(lv_fs_drv_t* drv, void* file_p) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileClose(file);
return LV_FS_RES_OK;
}
lv_fs_res_t lvglRead(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileRead(file, static_cast<uint8_t*>(buf), btr);
*br = btr;
return LV_FS_RES_OK;
}
lv_fs_res_t lvglSeek(lv_fs_drv_t* drv, void* file_p, uint32_t pos) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileSeek(file, pos);
return LV_FS_RES_OK;
}
}
void FS::LVGLFileSystemInit() {
lv_fs_drv_t fs_drv;
lv_fs_drv_init(&fs_drv);
fs_drv.file_size = sizeof(lfs_file_t);
fs_drv.letter = 'F';
fs_drv.open_cb = lvglOpen;
fs_drv.close_cb = lvglClose;
fs_drv.read_cb = lvglRead;
fs_drv.seek_cb = lvglSeek;
fs_drv.user_data = this;
lv_fs_drv_register(&fs_drv);
}

71
src/components/fs/FS.h Normal file
View File

@ -0,0 +1,71 @@
#pragma once
#include <cstdint>
#include "drivers/SpiNorFlash.h"
#include <littlefs/lfs.h>
namespace Pinetime {
namespace Controllers {
class FS {
public:
FS(Pinetime::Drivers::SpiNorFlash&);
void Init();
void LVGLFileSystemInit();
int FileOpen(lfs_file_t* file_p, const char* fileName, const int flags);
int FileClose(lfs_file_t* file_p);
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
int FileSeek(lfs_file_t* file_p, uint32_t pos);
int FileDelete(const char* fileName);
int DirCreate(const char* path);
int DirDelete(const char* path);
void VerifyResource();
private:
Pinetime::Drivers::SpiNorFlash& flashDriver;
/*
* External Flash MAP (4 MBytes)
*
* 0x000000 +---------------------------------------+
* | Bootloader Assets |
* | 256 KBytes |
* | |
* 0x040000 +---------------------------------------+
* | OTA |
* | 464 KBytes |
* | |
* | |
* | |
* 0x0B4000 +---------------------------------------+
* | File System |
* | |
* | |
* | |
* | |
* 0x400000 +---------------------------------------+
*
*/
static constexpr size_t startAddress = 0x0B4000;
static constexpr size_t size = 0x3C0000;
static constexpr size_t blockSize = 4096;
bool resourcesValid = false;
const struct lfs_config lfsConfig;
lfs_t lfs;
static int SectorSync(const struct lfs_config* c);
static int SectorErase(const struct lfs_config* c, lfs_block_t block);
static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
};
}
}

View File

@ -4,9 +4,6 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
HeartRateController::HeartRateController(Pinetime::System::SystemTask& systemTask) : systemTask {systemTask} {
}
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) { void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
this->state = newState; this->state = newState;
if (this->heartRate != heartRate) { if (this->heartRate != heartRate) {

View File

@ -15,8 +15,7 @@ namespace Pinetime {
public: public:
enum class States { Stopped, NotEnoughData, NoTouch, Running }; enum class States { Stopped, NotEnoughData, NoTouch, Running };
explicit HeartRateController(System::SystemTask& systemTask); HeartRateController() = default;
void Start(); void Start();
void Stop(); void Stop();
void Update(States newState, uint8_t heartRate); void Update(States newState, uint8_t heartRate);
@ -32,7 +31,6 @@ namespace Pinetime {
void SetService(Pinetime::Controllers::HeartRateService* service); void SetService(Pinetime::Controllers::HeartRateService* service);
private: private:
System::SystemTask& systemTask;
Applications::HeartRateTask* task = nullptr; Applications::HeartRateTask* task = nullptr;
States state = States::Stopped; States state = States::Stopped;
uint8_t heartRate = 0; uint8_t heartRate = 0;

View File

@ -38,9 +38,8 @@ namespace {
} }
} }
Ppg::Ppg(float spl) Ppg::Ppg()
: offset {spl}, : hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
agc {20, 0.971, 2}, agc {20, 0.971, 2},
lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} { lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} {
} }
@ -67,13 +66,7 @@ float Ppg::HeartRate() {
dataIndex = 0; dataIndex = 0;
return hr; return hr;
} }
int cccount = 0;
float Ppg::ProcessHeartRate() { float Ppg::ProcessHeartRate() {
if (cccount > 2)
asm("nop");
cccount++;
auto t0 = Trough(data.data(), dataIndex, 7, 48); auto t0 = Trough(data.data(), dataIndex, 7, 48);
if (t0 < 0) if (t0 < 0)
return 0; return 0;

View File

@ -8,8 +8,7 @@ namespace Pinetime {
namespace Controllers { namespace Controllers {
class Ppg { class Ppg {
public: public:
explicit Ppg(float spl); Ppg();
int8_t Preprocess(float spl); int8_t Preprocess(float spl);
float HeartRate(); float HeartRate();

View File

@ -34,3 +34,10 @@ bool MotionController::ShouldWakeUp(bool isSleeping) {
void MotionController::IsSensorOk(bool isOk) { void MotionController::IsSensorOk(bool isOk) {
isSensorOk = isOk; isSensorOk = isOk;
} }
void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) {
switch(types){
case Drivers::Bma421::DeviceTypes::BMA421: this->deviceType = DeviceTypes::BMA421; break;
case Drivers::Bma421::DeviceTypes::BMA425: this->deviceType = DeviceTypes::BMA425; break;
default: this->deviceType = DeviceTypes::Unknown; break;
}
}

View File

@ -1,11 +1,18 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <drivers/Bma421.h>
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class MotionController { class MotionController {
public: public:
enum class DeviceTypes{
Unknown,
BMA421,
BMA425,
};
void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps); void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
int16_t X() const { int16_t X() const {
@ -27,6 +34,12 @@ namespace Pinetime {
return isSensorOk; return isSensorOk;
} }
DeviceTypes DeviceType() const {
return deviceType;
}
void Init(Pinetime::Drivers::Bma421::DeviceTypes types);
private: private:
uint32_t nbSteps; uint32_t nbSteps;
int16_t x; int16_t x;
@ -34,6 +47,7 @@ namespace Pinetime {
int16_t z; int16_t z;
int16_t lastYForWakeUp = 0; int16_t lastYForWakeUp = 0;
bool isSensorOk = false; bool isSensorOk = false;
DeviceTypes deviceType = DeviceTypes::Unknown;
}; };
} }
} }

View File

@ -15,7 +15,7 @@ void MotorController::Init() {
nrf_gpio_cfg_output(pinMotor); nrf_gpio_cfg_output(pinMotor);
nrf_gpio_pin_set(pinMotor); nrf_gpio_pin_set(pinMotor);
app_timer_init(); app_timer_init();
app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate); app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate);
app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, vibrate); app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, vibrate);
isBusy = false; isBusy = false;
@ -53,4 +53,4 @@ void MotorController::vibrate(void* p_context) {
} else { } else {
nrf_gpio_pin_clear(pinMotor); nrf_gpio_pin_clear(pinMotor);
} }
} }

View File

@ -21,7 +21,7 @@ namespace Pinetime {
const uint8_t* buffer; const uint8_t* buffer;
size_t size; size_t size;
int encodedBufferIndex = 0; size_t encodedBufferIndex = 0;
int y = 0; int y = 0;
uint16_t bp = 0; uint16_t bp = 0;
uint16_t foregroundColor = 0xffff; uint16_t foregroundColor = 0xffff;

View File

@ -4,108 +4,44 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
struct SettingsHeader { Settings::Settings(Pinetime::Controllers::FS& fs) : fs {fs} {
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} {
} }
void Settings::Init() { void Settings::Init() {
// Load default settings from Flash // Load default settings from Flash
LoadSettingsFromFlash(); LoadSettingsFromFile();
} }
void Settings::SaveSettings() { void Settings::SaveSettings() {
// verify if is necessary to save // verify if is necessary to save
if (settingsChanged) { if (settingsChanged) {
SaveSettingsToFlash(); SaveSettingsToFile();
} }
settingsChanged = false; settingsChanged = false;
} }
bool Settings::FindHeader() { void Settings::LoadSettingsFromFile() {
SettingsHeader settingsHeader; SettingsData bufferSettings;
uint8_t bufferHead[sizeof(settingsHeader)]; lfs_file_t settingsFile;
for (uint8_t block = 0; block < 10; block++) { if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
return;
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; fs.FileRead(&settingsFile, reinterpret_cast<uint8_t*>(&bufferSettings), sizeof(settings));
} fs.FileClose(&settingsFile);
if ( bufferSettings.version == settingsVersion ) {
void Settings::ReadSettingsData() { settings = bufferSettings;
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));
}
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));
}
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();
}
} else {
// Read Settings from flash...
// never used :)
ReadSettingsData();
} }
} }
void Settings::SaveSettingsToFlash() { void Settings::SaveSettingsToFile() {
lfs_file_t settingsFile;
// calculate where to save... if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
// mark current to inactive return;
// erase the new location and save
// set settingsFlashBlock
// if first time hever, only saves to block 0 and set settingsFlashBlock
if (settingsFlashBlock != 99) {
SetHeader(false);
} }
fs.FileWrite(&settingsFile, reinterpret_cast<uint8_t*>(&settings), sizeof(settings));
settingsFlashBlock++; fs.FileClose(&settingsFile);
if (settingsFlashBlock > 9)
settingsFlashBlock = 0;
EraseBlock();
SetHeader(true);
SaveSettingsData();
} }

View File

@ -1,26 +1,32 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <bitset>
#include "components/datetime/DateTimeController.h" #include "components/datetime/DateTimeController.h"
#include "components/brightness/BrightnessController.h" #include "components/brightness/BrightnessController.h"
#include "drivers/SpiNorFlash.h" #include "components/fs/FS.h"
#include "drivers/Cst816s.h" #include "drivers/Cst816s.h"
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
class Settings { class Settings {
public: public:
enum class ClockType { H24, H12 }; enum class ClockType : uint8_t { H24, H12 };
enum class Vibration { ON, OFF }; enum class Vibration : uint8_t { ON, OFF };
enum class WakeUpMode { None, SingleTap, DoubleTap, RaiseWrist }; enum class WakeUpMode : uint8_t {
SingleTap = 0,
DoubleTap = 1,
RaiseWrist = 2,
};
Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash); Settings(Pinetime::Controllers::FS& fs);
void Init(); void Init();
void SaveSettings(); void SaveSettings();
void SetClockFace(uint8_t face) { void SetClockFace(uint8_t face) {
if (face != settings.clockFace) if (face != settings.clockFace) {
settingsChanged = true; settingsChanged = true;
}
settings.clockFace = face; settings.clockFace = face;
}; };
uint8_t GetClockFace() const { uint8_t GetClockFace() const {
@ -42,8 +48,9 @@ namespace Pinetime {
}; };
void SetClockType(ClockType clocktype) { void SetClockType(ClockType clocktype) {
if (clocktype != settings.clockType) if (clocktype != settings.clockType) {
settingsChanged = true; settingsChanged = true;
}
settings.clockType = clocktype; settings.clockType = clocktype;
}; };
ClockType GetClockType() const { ClockType GetClockType() const {
@ -51,8 +58,9 @@ namespace Pinetime {
}; };
void SetVibrationStatus(Vibration status) { void SetVibrationStatus(Vibration status) {
if (status != settings.vibrationStatus) if (status != settings.vibrationStatus) {
settingsChanged = true; settingsChanged = true;
}
settings.vibrationStatus = status; settings.vibrationStatus = status;
}; };
Vibration GetVibrationStatus() const { Vibration GetVibrationStatus() const {
@ -60,26 +68,47 @@ namespace Pinetime {
}; };
void SetScreenTimeOut(uint32_t timeout) { void SetScreenTimeOut(uint32_t timeout) {
if (timeout != settings.screenTimeOut) if (timeout != settings.screenTimeOut) {
settingsChanged = true; settingsChanged = true;
}
settings.screenTimeOut = timeout; settings.screenTimeOut = timeout;
}; };
uint32_t GetScreenTimeOut() const { uint32_t GetScreenTimeOut() const {
return settings.screenTimeOut; return settings.screenTimeOut;
}; };
void setWakeUpMode(WakeUpMode wakeUp) { void setWakeUpMode(WakeUpMode wakeUp, bool enabled) {
if (wakeUp != settings.wakeUpMode) if (!isWakeUpModeOn(wakeUp)) {
settingsChanged = true; settingsChanged = true;
settings.wakeUpMode = wakeUp; }
}; settings.wakeUpMode.set(static_cast<size_t>(wakeUp), enabled);
WakeUpMode getWakeUpMode() const { // Handle special behavior
return settings.wakeUpMode; if (enabled) {
switch (wakeUp) {
case WakeUpMode::SingleTap:
settings.wakeUpMode.set(static_cast<size_t>(WakeUpMode::DoubleTap), false);
break;
case WakeUpMode::DoubleTap:
settings.wakeUpMode.set(static_cast<size_t>(WakeUpMode::SingleTap), false);
break;
case WakeUpMode::RaiseWrist:
break;
}
}
}; };
std::bitset<3> getWakeUpModes() const {
return settings.wakeUpMode;
}
bool isWakeUpModeOn(const WakeUpMode mode) const {
return getWakeUpModes()[static_cast<size_t>(mode)];
}
void SetBrightness(Controllers::BrightnessController::Levels level) { void SetBrightness(Controllers::BrightnessController::Levels level) {
if (level != settings.brightLevel) if (level != settings.brightLevel) {
settingsChanged = true; settingsChanged = true;
}
settings.brightLevel = level; settings.brightLevel = level;
}; };
Controllers::BrightnessController::Levels GetBrightness() const { Controllers::BrightnessController::Levels GetBrightness() const {
@ -87,26 +116,30 @@ namespace Pinetime {
}; };
void SetStepsGoal( uint32_t goal ) { void SetStepsGoal( uint32_t goal ) {
if ( goal != settings.stepsGoal ) if ( goal != settings.stepsGoal ) {
settingsChanged = true; settingsChanged = true;
}
settings.stepsGoal = goal; settings.stepsGoal = goal;
}; };
uint32_t GetStepsGoal() const { return settings.stepsGoal; }; uint32_t GetStepsGoal() const { return settings.stepsGoal; };
private: private:
Pinetime::Drivers::SpiNorFlash& spiNorFlash; Pinetime::Controllers::FS& fs;
static constexpr uint32_t settingsVersion = 0x0001;
struct SettingsData { struct SettingsData {
uint32_t version = settingsVersion;
uint32_t stepsGoal = 10000;
uint32_t screenTimeOut = 15000;
ClockType clockType = ClockType::H24; ClockType clockType = ClockType::H24;
Vibration vibrationStatus = Vibration::ON; Vibration vibrationStatus = Vibration::ON;
uint8_t clockFace = 0; uint8_t clockFace = 0;
uint32_t stepsGoal = 10000; std::bitset<3> wakeUpMode {0};
uint32_t screenTimeOut = 15000;
WakeUpMode wakeUpMode = WakeUpMode::None;
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium; Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
}; };
@ -117,20 +150,8 @@ namespace Pinetime {
uint8_t appMenu = 0; uint8_t appMenu = 0;
uint8_t settingsMenu = 0; uint8_t settingsMenu = 0;
// There are 10 blocks of reserved flash to save settings void LoadSettingsFromFile();
// to minimize wear, the recording is done in a rotating way by the 10 blocks void SaveSettingsToFile();
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();
}; };
} }
} }

View File

@ -0,0 +1,69 @@
//
// Created by florian on 16.05.21.
//
#include "TimerController.h"
#include "systemtask/SystemTask.h"
#include "app_timer.h"
#include "task.h"
using namespace Pinetime::Controllers;
APP_TIMER_DEF(timerAppTimer);
namespace {
void TimerEnd(void* p_context) {
auto* controller = static_cast<Pinetime::Controllers::TimerController*> (p_context);
if(controller != nullptr)
controller->OnTimerEnd();
}
}
void TimerController::Init() {
app_timer_create(&timerAppTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEnd);
}
void TimerController::StartTimer(uint32_t duration) {
app_timer_stop(timerAppTimer);
auto currentTicks = xTaskGetTickCount();
app_timer_start(timerAppTimer, APP_TIMER_TICKS(duration), this);
endTicks = currentTicks + APP_TIMER_TICKS(duration);
timerRunning = true;
}
uint32_t TimerController::GetTimeRemaining() {
if (!timerRunning) {
return 0;
}
auto currentTicks = xTaskGetTickCount();
TickType_t deltaTicks = 0;
if (currentTicks > endTicks) {
deltaTicks = 0xffffffff - currentTicks;
deltaTicks += (endTicks + 1);
} else {
deltaTicks = endTicks - currentTicks;
}
return (static_cast<TickType_t>(deltaTicks) / static_cast<TickType_t>(configTICK_RATE_HZ)) * 1000;
}
void TimerController::StopTimer() {
app_timer_stop(timerAppTimer);
timerRunning = false;
}
bool TimerController::IsRunning() {
return timerRunning;
}
void TimerController::OnTimerEnd() {
timerRunning = false;
if(systemTask != nullptr)
systemTask->PushMessage(System::Messages::OnTimerDone);
}
void TimerController::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <cstdint>
#include "app_timer.h"
#include "portmacro_cmsis.h"
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class TimerController {
public:
TimerController() = default;
void Init();
void StartTimer(uint32_t duration);
void StopTimer();
uint32_t GetTimeRemaining();
bool IsRunning();
void OnTimerEnd();
void Register(System::SystemTask* systemTask);
private:
System::SystemTask* systemTask = nullptr;
TickType_t endTicks;
bool timerRunning = false;
};
}
}

View File

@ -11,6 +11,7 @@ namespace Pinetime {
FirmwareValidation, FirmwareValidation,
NotificationsPreview, NotificationsPreview,
Notifications, Notifications,
Timer,
FlashLight, FlashLight,
BatteryInfo, BatteryInfo,
Music, Music,
@ -20,6 +21,7 @@ namespace Pinetime {
HeartRate, HeartRate,
Navigation, Navigation,
StopWatch, StopWatch,
Metronome,
Motion, Motion,
Steps, Steps,
QuickSettings, QuickSettings,

View File

@ -2,6 +2,7 @@
#include <libraries/log/nrf_log.h> #include <libraries/log/nrf_log.h>
#include <displayapp/screens/HeartRate.h> #include <displayapp/screens/HeartRate.h>
#include <displayapp/screens/Motion.h> #include <displayapp/screens/Motion.h>
#include <displayapp/screens/Timer.h>
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
#include "components/ble/BleController.h" #include "components/ble/BleController.h"
#include "components/datetime/DateTimeController.h" #include "components/datetime/DateTimeController.h"
@ -17,6 +18,7 @@
#include "displayapp/screens/Paddle.h" #include "displayapp/screens/Paddle.h"
#include "displayapp/screens/StopWatch.h" #include "displayapp/screens/StopWatch.h"
#include "displayapp/screens/Meter.h" #include "displayapp/screens/Meter.h"
#include "displayapp/screens/Metronome.h"
#include "displayapp/screens/Music.h" #include "displayapp/screens/Music.h"
#include "displayapp/screens/Navigation.h" #include "displayapp/screens/Navigation.h"
#include "displayapp/screens/Notifications.h" #include "displayapp/screens/Notifications.h"
@ -31,6 +33,7 @@
#include "drivers/St7789.h" #include "drivers/St7789.h"
#include "drivers/Watchdog.h" #include "drivers/Watchdog.h"
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
#include "systemtask/Messages.h"
#include "displayapp/screens/settings/QuickSettings.h" #include "displayapp/screens/settings/QuickSettings.h"
#include "displayapp/screens/settings/Settings.h" #include "displayapp/screens/settings/Settings.h"
@ -40,9 +43,42 @@
#include "displayapp/screens/settings/SettingDisplay.h" #include "displayapp/screens/settings/SettingDisplay.h"
#include "displayapp/screens/settings/SettingSteps.h" #include "displayapp/screens/settings/SettingSteps.h"
#include "libs/lv_conf.h"
using namespace Pinetime::Applications; using namespace Pinetime::Applications;
using namespace Pinetime::Applications::Display; using namespace Pinetime::Applications::Display;
namespace {
static inline bool in_isr(void) {
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}
TouchEvents Convert(Pinetime::Drivers::Cst816S::TouchInfos info) {
if (info.isTouch) {
switch (info.gesture) {
case Pinetime::Drivers::Cst816S::Gestures::SingleTap:
return TouchEvents::Tap;
case Pinetime::Drivers::Cst816S::Gestures::LongPress:
return TouchEvents::LongTap;
case Pinetime::Drivers::Cst816S::Gestures::DoubleTap:
return TouchEvents::DoubleTap;
case Pinetime::Drivers::Cst816S::Gestures::SlideRight:
return TouchEvents::SwipeRight;
case Pinetime::Drivers::Cst816S::Gestures::SlideLeft:
return TouchEvents::SwipeLeft;
case Pinetime::Drivers::Cst816S::Gestures::SlideDown:
return TouchEvents::SwipeDown;
case Pinetime::Drivers::Cst816S::Gestures::SlideUp:
return TouchEvents::SwipeUp;
case Pinetime::Drivers::Cst816S::Gestures::None:
default:
return TouchEvents::None;
}
}
return TouchEvents::None;
}
}
DisplayApp::DisplayApp(Drivers::St7789& lcd, DisplayApp::DisplayApp(Drivers::St7789& lcd,
Components::LittleVgl& lvgl, Components::LittleVgl& lvgl,
Drivers::Cst816S& touchPanel, Drivers::Cst816S& touchPanel,
@ -50,12 +86,12 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController, Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog, Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings& settingsController, Controllers::Settings& settingsController,
Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController) Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController)
: lcd {lcd}, : lcd {lcd},
lvgl {lvgl}, lvgl {lvgl},
touchPanel {touchPanel}, touchPanel {touchPanel},
@ -63,18 +99,20 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
bleController {bleController}, bleController {bleController},
dateTimeController {dateTimeController}, dateTimeController {dateTimeController},
watchdog {watchdog}, watchdog {watchdog},
systemTask {systemTask},
notificationManager {notificationManager}, notificationManager {notificationManager},
heartRateController {heartRateController}, heartRateController {heartRateController},
settingsController {settingsController}, settingsController {settingsController},
motorController {motorController}, motorController {motorController},
motionController {motionController} { motionController {motionController},
msgQueue = xQueueCreate(queueSize, itemSize); timerController {timerController} {
// Start clock when smartwatch boots
LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
} }
void DisplayApp::Start() { void DisplayApp::Start() {
msgQueue = xQueueCreate(queueSize, itemSize);
// Start clock when smartwatch boots
LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 800, this, 0, &taskHandle)) { if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 800, this, 0, &taskHandle)) {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
} }
@ -103,6 +141,7 @@ uint32_t count = 0;
bool toggle = true; bool toggle = true;
void DisplayApp::Refresh() { void DisplayApp::Refresh() {
TickType_t queueTimeout; TickType_t queueTimeout;
TickType_t delta;
switch (state) { switch (state) {
case States::Idle: case States::Idle:
IdleState(); IdleState();
@ -110,7 +149,11 @@ void DisplayApp::Refresh() {
break; break;
case States::Running: case States::Running:
RunningState(); RunningState();
queueTimeout = 20; delta = xTaskGetTickCount() - lastWakeTime;
if (delta > LV_DISP_DEF_REFR_PERIOD) {
delta = LV_DISP_DEF_REFR_PERIOD;
}
queueTimeout = LV_DISP_DEF_REFR_PERIOD - delta;
break; break;
default: default:
queueTimeout = portMAX_DELAY; queueTimeout = portMAX_DELAY;
@ -118,16 +161,25 @@ void DisplayApp::Refresh() {
} }
Messages msg; Messages msg;
if (xQueueReceive(msgQueue, &msg, queueTimeout)) { bool messageReceived = xQueueReceive(msgQueue, &msg, queueTimeout);
lastWakeTime = xTaskGetTickCount();
if (messageReceived) {
switch (msg) { switch (msg) {
case Messages::GoToSleep: case Messages::DimScreen:
// Backup brightness is the brightness to return to after dimming or sleeping
brightnessController.Backup(); brightnessController.Backup();
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
break;
case Messages::RestoreBrightness:
brightnessController.Restore();
break;
case Messages::GoToSleep:
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) { while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
brightnessController.Lower(); brightnessController.Lower();
vTaskDelay(100); vTaskDelay(100);
} }
lcd.DisplayOff(); lcd.DisplayOff();
systemTask.PushMessage(System::SystemTask::Messages::OnDisplayTaskSleeping); PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskSleeping);
state = States::Idle; state = States::Idle;
break; break;
case Messages::GoToRunning: case Messages::GoToRunning:
@ -136,7 +188,7 @@ void DisplayApp::Refresh() {
state = States::Running; state = States::Running;
break; break;
case Messages::UpdateTimeOut: case Messages::UpdateTimeOut:
systemTask.PushMessage(System::SystemTask::Messages::UpdateTimeOut); PushMessageToSystemTask(System::Messages::UpdateTimeOut);
break; break;
case Messages::UpdateBleConnection: case Messages::UpdateBleConnection:
// clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected : // clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected :
@ -148,10 +200,23 @@ void DisplayApp::Refresh() {
case Messages::NewNotification: case Messages::NewNotification:
LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down);
break; break;
case Messages::TimerDone:
if (currentApp == Apps::Timer) {
auto* timer = static_cast<Screens::Timer*>(currentScreen.get());
timer->setDone();
} else {
LoadApp(Apps::Timer, DisplayApp::FullRefreshDirections::Down);
}
break;
case Messages::TouchEvent: { case Messages::TouchEvent: {
if (state != States::Running) if (state != States::Running) {
break; break;
auto gesture = OnTouchEvent(); }
auto info = touchPanel.GetTouchInfo();
auto gesture = Convert(info);
if (gesture == TouchEvents::None) {
break;
}
if (!currentScreen->OnTouchEvent(gesture)) { if (!currentScreen->OnTouchEvent(gesture)) {
if (currentApp == Apps::Clock) { if (currentApp == Apps::Clock) {
switch (gesture) { switch (gesture) {
@ -165,22 +230,30 @@ void DisplayApp::Refresh() {
LoadApp(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim); LoadApp(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim);
break; break;
case TouchEvents::DoubleTap: case TouchEvents::DoubleTap:
systemTask.PushMessage(System::SystemTask::Messages::GoToSleep); PushMessageToSystemTask(System::Messages::GoToSleep);
break; break;
default: default:
break; break;
} }
} else if (returnTouchEvent == gesture) { } else if (returnTouchEvent == gesture) {
LoadApp(returnToApp, returnDirection); LoadApp(returnToApp, returnDirection);
brightnessController.Set(settingsController.GetBrightness());
brightnessController.Backup();
} else if (touchMode == TouchModes::Gestures) {
if (gesture == TouchEvents::Tap) {
lvgl.SetNewTapEvent(info.x, info.y);
}
} }
} }
} break; } break;
case Messages::ButtonPushed: case Messages::ButtonPushed:
if (currentApp == Apps::Clock) { if (currentApp == Apps::Clock) {
systemTask.PushMessage(System::SystemTask::Messages::GoToSleep); PushMessageToSystemTask(System::Messages::GoToSleep);
} else { } else {
if (!currentScreen->OnButtonPushed()) { if (!currentScreen->OnButtonPushed()) {
LoadApp(returnToApp, returnDirection); LoadApp(returnToApp, returnDirection);
brightnessController.Set(settingsController.GetBrightness());
brightnessController.Backup();
} }
} }
break; break;
@ -195,6 +268,11 @@ void DisplayApp::Refresh() {
} }
} }
if (nextApp != Apps::None) {
LoadApp(nextApp, nextDirection);
nextApp = Apps::None;
}
if (state != States::Idle && touchMode == TouchModes::Polling) { if (state != States::Idle && touchMode == TouchModes::Polling) {
auto info = touchPanel.GetTouchInfo(); auto info = touchPanel.GetTouchInfo();
if (info.action == 2) { // 2 = contact if (info.action == 2) { // 2 = contact
@ -213,7 +291,8 @@ void DisplayApp::RunningState() {
} }
void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) { void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) {
LoadApp(app, direction); nextApp = app;
nextDirection = direction;
} }
void DisplayApp::ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent) { void DisplayApp::ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent) {
@ -252,18 +331,22 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
break; break;
case Apps::FirmwareUpdate: case Apps::FirmwareUpdate:
currentScreen = std::make_unique<Screens::FirmwareUpdate>(this, bleController); currentScreen = std::make_unique<Screens::FirmwareUpdate>(this, bleController);
ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None);
break; break;
case Apps::Notifications: case Apps::Notifications:
currentScreen = std::make_unique<Screens::Notifications>( currentScreen = std::make_unique<Screens::Notifications>(
this, notificationManager, systemTask.nimble().alertService(), motorController, Screens::Notifications::Modes::Normal); this, notificationManager, systemTask->nimble().alertService(), motorController, Screens::Notifications::Modes::Normal);
ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
break; break;
case Apps::NotificationsPreview: case Apps::NotificationsPreview:
currentScreen = std::make_unique<Screens::Notifications>( currentScreen = std::make_unique<Screens::Notifications>(
this, notificationManager, systemTask.nimble().alertService(), motorController, Screens::Notifications::Modes::Preview); this, notificationManager, systemTask->nimble().alertService(), motorController, Screens::Notifications::Modes::Preview);
ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
break; break;
case Apps::Timer:
currentScreen = std::make_unique<Screens::Timer>(this, timerController);
break;
// Settings // Settings
case Apps::QuickSettings: case Apps::QuickSettings:
@ -291,7 +374,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
currentScreen = std::make_unique<Screens::SettingDisplay>(this, settingsController); currentScreen = std::make_unique<Screens::SettingDisplay>(this, settingsController);
ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
case Apps::SettingSteps: case Apps::SettingSteps:
currentScreen = std::make_unique<Screens::SettingSteps>(this, settingsController); currentScreen = std::make_unique<Screens::SettingSteps>(this, settingsController);
ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
@ -300,18 +383,16 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
case Apps::SysInfo: case Apps::SysInfo:
currentScreen = currentScreen = std::make_unique<Screens::SystemInfo>(
std::make_unique<Screens::SystemInfo>(this, dateTimeController, batteryController, brightnessController, bleController, watchdog); this, dateTimeController, batteryController, brightnessController, bleController, watchdog, motionController);
ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
//
case Apps::FlashLight: case Apps::FlashLight:
currentScreen = std::make_unique<Screens::FlashLight>(this, systemTask, brightnessController); currentScreen = std::make_unique<Screens::FlashLight>(this, *systemTask, brightnessController);
ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None);
break; break;
case Apps::StopWatch: case Apps::StopWatch:
currentScreen = std::make_unique<Screens::StopWatch>(this); currentScreen = std::make_unique<Screens::StopWatch>(this, *systemTask);
break; break;
case Apps::Twos: case Apps::Twos:
currentScreen = std::make_unique<Screens::Twos>(this); currentScreen = std::make_unique<Screens::Twos>(this);
@ -323,18 +404,21 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
currentScreen = std::make_unique<Screens::Paddle>(this, lvgl); currentScreen = std::make_unique<Screens::Paddle>(this, lvgl);
break; break;
case Apps::Music: case Apps::Music:
currentScreen = std::make_unique<Screens::Music>(this, systemTask.nimble().music()); currentScreen = std::make_unique<Screens::Music>(this, systemTask->nimble().music());
break; break;
case Apps::Navigation: case Apps::Navigation:
currentScreen = std::make_unique<Screens::Navigation>(this, systemTask.nimble().navigation()); currentScreen = std::make_unique<Screens::Navigation>(this, systemTask->nimble().navigation());
break; break;
case Apps::HeartRate: case Apps::HeartRate:
currentScreen = std::make_unique<Screens::HeartRate>(this, heartRateController, systemTask); currentScreen = std::make_unique<Screens::HeartRate>(this, heartRateController, *systemTask);
break;
case Apps::Metronome:
currentScreen = std::make_unique<Screens::Metronome>(this, motorController, *systemTask);
break; break;
case Apps::Motion: case Apps::Motion:
currentScreen = std::make_unique<Screens::Motion>(this, motionController); currentScreen = std::make_unique<Screens::Motion>(this, motionController);
break; break;
case Apps::Steps: case Apps::Steps:
currentScreen = std::make_unique<Screens::Steps>(this, motionController, settingsController); currentScreen = std::make_unique<Screens::Steps>(this, motionController, settingsController);
break; break;
} }
@ -345,42 +429,16 @@ void DisplayApp::IdleState() {
} }
void DisplayApp::PushMessage(Messages msg) { void DisplayApp::PushMessage(Messages msg) {
BaseType_t xHigherPriorityTaskWoken; if (in_isr()) {
xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken;
xQueueSendFromISR(msgQueue, &msg, &xHigherPriorityTaskWoken); xHigherPriorityTaskWoken = pdFALSE;
if (xHigherPriorityTaskWoken) { xQueueSendFromISR(msgQueue, &msg, &xHigherPriorityTaskWoken);
/* Actual macro used here is port specific. */ if (xHigherPriorityTaskWoken) {
// TODO : should I do something here? portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
TouchEvents DisplayApp::OnTouchEvent() {
auto info = touchPanel.GetTouchInfo();
if (info.isTouch) {
switch (info.gesture) {
case Pinetime::Drivers::Cst816S::Gestures::SingleTap:
if (touchMode == TouchModes::Gestures) {
lvgl.SetNewTapEvent(info.x, info.y);
}
return TouchEvents::Tap;
case Pinetime::Drivers::Cst816S::Gestures::LongPress:
return TouchEvents::LongTap;
case Pinetime::Drivers::Cst816S::Gestures::DoubleTap:
return TouchEvents::DoubleTap;
case Pinetime::Drivers::Cst816S::Gestures::SlideRight:
return TouchEvents::SwipeRight;
case Pinetime::Drivers::Cst816S::Gestures::SlideLeft:
return TouchEvents::SwipeLeft;
case Pinetime::Drivers::Cst816S::Gestures::SlideDown:
return TouchEvents::SwipeDown;
case Pinetime::Drivers::Cst816S::Gestures::SlideUp:
return TouchEvents::SwipeUp;
case Pinetime::Drivers::Cst816S::Gestures::None:
default:
return TouchEvents::None;
} }
} else {
xQueueSend(msgQueue, &msg, portMAX_DELAY);
} }
return TouchEvents::None;
} }
void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) { void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
@ -411,3 +469,12 @@ void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
void DisplayApp::SetTouchMode(DisplayApp::TouchModes mode) { void DisplayApp::SetTouchMode(DisplayApp::TouchModes mode) {
touchMode = mode; touchMode = mode;
} }
void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) {
if (systemTask != nullptr)
systemTask->PushMessage(message);
}
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}

View File

@ -4,6 +4,7 @@
#include <queue.h> #include <queue.h>
#include <task.h> #include <task.h>
#include <memory> #include <memory>
#include <systemtask/Messages.h>
#include "Apps.h" #include "Apps.h"
#include "LittleVgl.h" #include "LittleVgl.h"
#include "TouchEvents.h" #include "TouchEvents.h"
@ -12,6 +13,7 @@
#include "components/firmwarevalidator/FirmwareValidator.h" #include "components/firmwarevalidator/FirmwareValidator.h"
#include "components/settings/Settings.h" #include "components/settings/Settings.h"
#include "displayapp/screens/Screen.h" #include "displayapp/screens/Screen.h"
#include "components/timer/TimerController.h"
#include "Messages.h" #include "Messages.h"
namespace Pinetime { namespace Pinetime {
@ -48,12 +50,12 @@ namespace Pinetime {
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController, Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog, Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings& settingsController, Controllers::Settings& settingsController,
Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController); Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController);
void Start(); void Start();
void PushMessage(Display::Messages msg); void PushMessage(Display::Messages msg);
@ -62,6 +64,8 @@ namespace Pinetime {
void SetFullRefresh(FullRefreshDirections direction); void SetFullRefresh(FullRefreshDirections direction);
void SetTouchMode(TouchModes mode); void SetTouchMode(TouchModes mode);
void Register(Pinetime::System::SystemTask* systemTask);
private: private:
Pinetime::Drivers::St7789& lcd; Pinetime::Drivers::St7789& lcd;
Pinetime::Components::LittleVgl& lvgl; Pinetime::Components::LittleVgl& lvgl;
@ -70,12 +74,13 @@ namespace Pinetime {
Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController; Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Drivers::WatchdogView& watchdog; Pinetime::Drivers::WatchdogView& watchdog;
Pinetime::System::SystemTask& systemTask; Pinetime::System::SystemTask* systemTask = nullptr;
Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::HeartRateController& heartRateController; Pinetime::Controllers::HeartRateController& heartRateController;
Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::Settings& settingsController;
Pinetime::Controllers::MotorController& motorController; Pinetime::Controllers::MotorController& motorController;
Pinetime::Controllers::MotionController& motionController; Pinetime::Controllers::MotionController& motionController;
Pinetime::Controllers::TimerController& timerController;
Pinetime::Controllers::FirmwareValidator validator; Pinetime::Controllers::FirmwareValidator validator;
Controllers::BrightnessController brightnessController; Controllers::BrightnessController brightnessController;
@ -97,7 +102,6 @@ namespace Pinetime {
TouchModes touchMode = TouchModes::Gestures; TouchModes touchMode = TouchModes::Gestures;
TouchEvents OnTouchEvent();
void RunningState(); void RunningState();
void IdleState(); void IdleState();
static void Process(void* instance); static void Process(void* instance);
@ -105,6 +109,11 @@ namespace Pinetime {
void Refresh(); void Refresh();
void ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent); void ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent);
void LoadApp(Apps app, DisplayApp::FullRefreshDirections direction); void LoadApp(Apps app, DisplayApp::FullRefreshDirections direction);
void PushMessageToSystemTask(Pinetime::System::Messages message);
Apps nextApp = Apps::None;
DisplayApp::FullRefreshDirections nextDirection;
TickType_t lastWakeTime;
}; };
} }
} }

View File

@ -14,17 +14,18 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController, Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog, Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Controllers::Settings& settingsController, Controllers::Settings& settingsController,
Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController) Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController)
: lcd {lcd}, bleController {bleController} { : lcd {lcd}, bleController {bleController} {
msgQueue = xQueueCreate(queueSize, itemSize);
} }
void DisplayApp::Start() { void DisplayApp::Start() {
msgQueue = xQueueCreate(queueSize, itemSize);
if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 512, this, 0, &taskHandle)) if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 512, this, 0, &taskHandle))
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
} }
@ -112,4 +113,8 @@ void DisplayApp::PushMessage(Display::Messages msg) {
/* Actual macro used here is port specific. */ /* Actual macro used here is port specific. */
// TODO : should I do something here? // TODO : should I do something here?
} }
} }
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
}

View File

@ -23,6 +23,7 @@
#include "Apps.h" #include "Apps.h"
#include "Messages.h" #include "Messages.h"
#include "DummyLittleVgl.h" #include "DummyLittleVgl.h"
#include "components/timer/TimerController.h"
namespace Pinetime { namespace Pinetime {
namespace System { namespace System {
@ -38,14 +39,15 @@ namespace Pinetime {
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController, Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog, Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Controllers::Settings& settingsController, Controllers::Settings& settingsController,
Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController); Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController);
void Start(); void Start();
void PushMessage(Pinetime::Applications::Display::Messages msg); void PushMessage(Pinetime::Applications::Display::Messages msg);
void Register(Pinetime::System::SystemTask* systemTask);
private: private:
TaskHandle_t taskHandle; TaskHandle_t taskHandle;

View File

@ -19,6 +19,10 @@ namespace Pinetime {
LittleVgl(LittleVgl&&) = delete; LittleVgl(LittleVgl&&) = delete;
LittleVgl& operator=(LittleVgl&&) = delete; LittleVgl& operator=(LittleVgl&&) = delete;
void Init() {
}
void FlushDisplay(const lv_area_t* area, lv_color_t* color_p) { void FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
} }
bool GetTouchPadInfo(lv_indev_data_t* ptr) { bool GetTouchPadInfo(lv_indev_data_t* ptr) {

View File

@ -23,6 +23,10 @@ bool touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data) {
LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel) LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel)
: lcd {lcd}, touchPanel {touchPanel}, previousClick {0, 0} { : lcd {lcd}, touchPanel {touchPanel}, previousClick {0, 0} {
}
void LittleVgl::Init() {
lv_init(); lv_init();
InitTheme(); InitTheme();
InitDisplay(); InitDisplay();

View File

@ -19,6 +19,8 @@ namespace Pinetime {
LittleVgl(LittleVgl&&) = delete; LittleVgl(LittleVgl&&) = delete;
LittleVgl& operator=(LittleVgl&&) = delete; LittleVgl& operator=(LittleVgl&&) = delete;
void Init();
void FlushDisplay(const lv_area_t* area, lv_color_t* color_p); void FlushDisplay(const lv_area_t* area, lv_color_t* color_p);
bool GetTouchPadInfo(lv_indev_data_t* ptr); bool GetTouchPadInfo(lv_indev_data_t* ptr);
void SetFullRefresh(FullRefreshDirections direction); void SetFullRefresh(FullRefreshDirections direction);

View File

@ -11,9 +11,12 @@ namespace Pinetime {
TouchEvent, TouchEvent,
ButtonPushed, ButtonPushed,
NewNotification, NewNotification,
TimerDone,
BleFirmwareUpdateStarted, BleFirmwareUpdateStarted,
UpdateTimeOut UpdateTimeOut,
DimScreen,
RestoreBrightness
}; };
} }
} }
} }

Binary file not shown.

View File

@ -2,6 +2,7 @@
* [Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/) * [Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)
* [Awesome font from LVGL](https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff) * [Awesome font from LVGL](https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff)
* [Open Sans Light from Google](https://fonts.google.com/specimen/Open+Sans)
## Generate the fonts: ## Generate the fonts:
@ -10,10 +11,12 @@
* Size : 20 * Size : 20
* Bpp : 1 bit-per-pixel * Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting * Do not enable font compression and horizontal subpixel hinting
* Load the file `JetBrainsMono-Bold.tff` and specify the following range : `0x20-0x7f, 0x410-0x44f` * Load the file `JetBrainsMono-Bold.tff` (use the file in this repo to ensure the version matches) 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 * 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` 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, 0xf252, 0xf569, 0xf201, 0xf06e, 0xf015`
* Click on Convert, and download the file `jetbrains_mono_bold_20.c` and copy it in `src/DisplayApp/Fonts` * Click on Convert, and download the file `jetbrains_mono_bold_20.c` and copy it in `src/DisplayApp/Fonts`
* Add the font .c file path to src/CMakeLists.txt
* Add an LV_FONT_DECLARE line in src/libs/lv_conf.h
Add new symbols: Add new symbols:
@ -28,6 +31,21 @@ Add new symbols:
static constexpr const char* newSymbol = "\xEF\x86\x85"; static constexpr const char* newSymbol = "\xEF\x86\x85";
``` ```
## Simple method to generate a font
If you want to generate a basic font containing only numbers and letters, you can use the above settings but instead of specifying a range, simply list the characters you need in the Symbols field and leave the range blank. This is the approach used for the PineTimeStyle watchface.
This works well for fonts which will only be used to display numbers, but will fail if you try to add a colon or other punctuation.
* Open the [LVGL font converter](https://lvgl.io/tools/fontconverter)
* Name : open_sans_light
* Size : 150
* Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting
* Load the file `open_sans_light.tff` (use the file in this repo to ensure the version matches) and specify the following symbols : `0123456789`
* Click on Convert, and download the file `open_sans_light.c` and copy it in `src/DisplayApp/Fonts`
* Add the font .c file path to src/CMakeLists.txt (search for jetbrains to find the appropriate location/format)
* Add an LV_FONT_DECLARE line in src/libs/lv_conf.h (as above)
#### Navigation font #### Navigation font
To create the navigtion.ttf I use the web app [icomoon](https://icomoon.io/app) To create the navigtion.ttf I use the web app [icomoon](https://icomoon.io/app)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -48,6 +48,7 @@ static lv_style_t style_sw_bg;
static lv_style_t style_sw_indic; static lv_style_t style_sw_indic;
static lv_style_t style_sw_knob; static lv_style_t style_sw_knob;
static lv_style_t style_arc_bg; static lv_style_t style_arc_bg;
static lv_style_t style_arc_knob;
static lv_style_t style_arc_indic; static lv_style_t style_arc_indic;
static lv_style_t style_table_cell; static lv_style_t style_table_cell;
static lv_style_t style_pad_small; static lv_style_t style_pad_small;
@ -191,6 +192,7 @@ static void basic_init(void) {
lv_style_set_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25); 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_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); lv_style_set_shadow_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_bg_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
style_init_reset(&style_ddlist_selected); style_init_reset(&style_ddlist_selected);
lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER);
@ -239,6 +241,13 @@ static void basic_init(void) {
lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, LV_PINETIME_GRAY); 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_width(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true); lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true);
lv_style_set_pad_all(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(5));
lv_style_reset(&style_arc_knob);
lv_style_set_radius(&style_arc_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_bg_opa(&style_arc_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_arc_knob, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_GRAY);
lv_style_set_pad_all(&style_arc_knob, LV_STATE_DEFAULT, LV_DPX(5));
style_init_reset(&style_table_cell); style_init_reset(&style_table_cell);
lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY); lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
@ -447,6 +456,10 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
lv_obj_clean_style_list(obj, LV_ARC_PART_INDIC); lv_obj_clean_style_list(obj, LV_ARC_PART_INDIC);
list = lv_obj_get_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); _lv_style_list_add_style(list, &style_arc_indic);
lv_obj_clean_style_list(obj, LV_ARC_PART_KNOB);
list = lv_obj_get_style_list(obj, LV_ARC_PART_KNOB);
_lv_style_list_add_style(list, &style_arc_knob);
break; break;
case LV_THEME_SWITCH: case LV_THEME_SWITCH:

View File

@ -51,7 +51,7 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
{Symbols::map, Apps::Navigation}, {Symbols::map, Apps::Navigation},
{Symbols::shoe, Apps::Steps}, {Symbols::shoe, Apps::Steps},
{Symbols::heartBeat, Apps::HeartRate}, {Symbols::heartBeat, Apps::HeartRate},
{"", Apps::None}, {Symbols::hourGlass, Apps::Timer},
}}; }};
return std::make_unique<Screens::Tile>(0, 2, app, settingsController, batteryController, dateTimeController, applications); return std::make_unique<Screens::Tile>(0, 2, app, settingsController, batteryController, dateTimeController, applications);
@ -62,8 +62,8 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
{Symbols::paintbrush, Apps::Paint}, {Symbols::paintbrush, Apps::Paint},
{Symbols::paddle, Apps::Paddle}, {Symbols::paddle, Apps::Paddle},
{"2", Apps::Twos}, {"2", Apps::Twos},
{"M", Apps::Motion}, {Symbols::chartLine, Apps::Motion},
{"", Apps::None}, {Symbols::drum, Apps::Metronome},
{"", Apps::None}, {"", Apps::None},
}}; }};

View File

@ -1,9 +1,10 @@
#include <cstdint>
#include "BatteryIcon.h" #include "BatteryIcon.h"
#include "Symbols.h" #include "Symbols.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
const char* BatteryIcon::GetBatteryIcon(int batteryPercent) { const char* BatteryIcon::GetBatteryIcon(uint8_t batteryPercent) {
if (batteryPercent > 90) if (batteryPercent > 90)
return Symbols::batteryFull; return Symbols::batteryFull;
if (batteryPercent > 75) if (batteryPercent > 75)

View File

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

View File

@ -9,11 +9,6 @@ static void lv_update_task(struct _lv_task_t* task) {
user_data->UpdateScreen(); user_data->UpdateScreen();
} }
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) BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Battery& batteryController)
: Screen(app), batteryController {batteryController} { : Screen(app), batteryController {batteryController} {
@ -24,12 +19,12 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
lv_obj_set_size(charging_bar, 200, 15); lv_obj_set_size(charging_bar, 200, 15);
lv_bar_set_range(charging_bar, 0, 100); lv_bar_set_range(charging_bar, 0, 100);
lv_obj_align(charging_bar, nullptr, LV_ALIGN_CENTER, 0, 10); lv_obj_align(charging_bar, nullptr, LV_ALIGN_CENTER, 0, 10);
lv_bar_set_anim_time(charging_bar, 2000); lv_bar_set_anim_time(charging_bar, 1000);
lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, lv_color_hex(0x222222)); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, lv_color_hex(0x222222));
lv_obj_set_style_local_bg_opa(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_100); lv_obj_set_style_local_bg_opa(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_100);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0xFF0000)); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0xFF0000));
lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_OFF); lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON);
status = lv_label_create(lv_scr_act(), nullptr); status = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(status, "Reading Battery status"); lv_label_set_text_static(status, "Reading Battery status");
@ -38,24 +33,13 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
percent = lv_label_create(lv_scr_act(), nullptr); percent = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
if (batteryPercent >= 0) { lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
} else {
lv_label_set_text(percent, "--%");
}
lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT); lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT);
lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60); lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60);
// hack to not use the flot functions from printf
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
//
voltage = lv_label_create(lv_scr_act(), nullptr); voltage = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xC6A600)); lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xC6A600));
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltageBytes[1], batteryVoltageBytes[0]); lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER); lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER);
lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95); lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95);
@ -65,40 +49,15 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
lv_obj_set_pos(backgroundLabel, 0, 0); lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, ""); lv_label_set_text_static(backgroundLabel, "");
taskUpdate = lv_task_create(lv_update_task, 500000, LV_TASK_PRIO_LOW, this); taskUpdate = lv_task_create(lv_update_task, 5000, LV_TASK_PRIO_LOW, this);
taskAnim = lv_task_create(lv_anim_task, 1000, LV_TASK_PRIO_LOW, this);
UpdateScreen(); UpdateScreen();
} }
BatteryInfo::~BatteryInfo() { BatteryInfo::~BatteryInfo() {
lv_task_del(taskUpdate); lv_task_del(taskUpdate);
lv_task_del(taskAnim);
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
void BatteryInfo::UpdateAnim() {
batteryPercent = batteryController.PercentRemaining();
if (batteryPercent >= 0) {
if (batteryController.IsCharging() and batteryPercent < 100) {
animation += 1;
if (animation >= 100) {
animation = 0;
}
} else {
if (animation > batteryPercent) {
animation--;
}
if (animation < batteryPercent) {
animation++;
}
}
lv_bar_set_value(charging_bar, animation, LV_ANIM_OFF);
}
}
void BatteryInfo::UpdateScreen() { void BatteryInfo::UpdateScreen() {
batteryController.Update(); batteryController.Update();
@ -106,39 +65,27 @@ void BatteryInfo::UpdateScreen() {
batteryPercent = batteryController.PercentRemaining(); batteryPercent = batteryController.PercentRemaining();
batteryVoltage = batteryController.Voltage(); batteryVoltage = batteryController.Voltage();
if (batteryPercent >= 0) { if (batteryController.IsCharging() and batteryPercent < 100) {
if (batteryController.IsCharging() and batteryPercent < 100) { lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED); lv_label_set_text_static(status, "Charging");
lv_label_set_text_static(status, "Battery charging"); } else if (batteryPercent == 100) {
} else if (batteryPercent == 100) { lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE); lv_label_set_text_static(status, "Fully charged");
lv_label_set_text_static(status, "Battery is fully charged"); } else if (batteryPercent < 10) {
} else if (batteryPercent < 10) { lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_YELLOW); lv_label_set_text_static(status, "Battery low");
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_label_set_text_fmt(percent, "%02i%%", batteryPercent);
} else { } else {
lv_label_set_text_static(status, "Reading Battery status"); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_label_set_text(percent, "--%"); lv_label_set_text_static(status, "Discharging");
} }
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
// hack to not use the flot functions from printf lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
uint8_t batteryVoltageBytes[2]; lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON);
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]);
} }
bool BatteryInfo::Refresh() { bool BatteryInfo::Refresh() {
return running; return running;
} }

View File

@ -22,7 +22,6 @@ namespace Pinetime {
bool Refresh() override; bool Refresh() override;
void UpdateScreen(); void UpdateScreen();
void UpdateAnim();
private: private:
Pinetime::Controllers::Battery& batteryController; Pinetime::Controllers::Battery& batteryController;
@ -33,11 +32,9 @@ namespace Pinetime {
lv_obj_t* status; lv_obj_t* status;
lv_task_t* taskUpdate; lv_task_t* taskUpdate;
lv_task_t* taskAnim;
int8_t animation = 0; uint8_t batteryPercent = 0;
int8_t batteryPercent = -1; uint16_t batteryVoltage = 0;
float batteryVoltage = 0.0f;
}; };
} }
} }

View File

@ -14,6 +14,7 @@
#include "../DisplayApp.h" #include "../DisplayApp.h"
#include "WatchFaceDigital.h" #include "WatchFaceDigital.h"
#include "WatchFaceAnalog.h" #include "WatchFaceAnalog.h"
#include "PineTimeStyle.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
@ -33,21 +34,20 @@ Clock::Clock(DisplayApp* app,
settingsController {settingsController}, settingsController {settingsController},
heartRateController {heartRateController}, heartRateController {heartRateController},
motionController {motionController}, motionController {motionController},
screens {app, screen {[this, &settingsController]() {
settingsController.GetClockFace(), switch (settingsController.GetClockFace()) {
{ case 0:
[this]() -> std::unique_ptr<Screen> { return WatchFaceDigitalScreen();
return WatchFaceDigitalScreen(); break;
}, case 1:
[this]() -> std::unique_ptr<Screen> { return WatchFaceAnalogScreen();
return WatchFaceAnalogScreen(); break;
}, case 2:
// Examples for more watch faces return PineTimeStyleScreen();
//[this]() -> std::unique_ptr<Screen> { return WatchFaceMinimalScreen(); }, break;
//[this]() -> std::unique_ptr<Screen> { return WatchFaceCustomScreen(); } }
}, return WatchFaceDigitalScreen();
Screens::ScreenListModes::LongPress} { }()} {
settingsController.SetAppMenu(0); settingsController.SetAppMenu(0);
} }
@ -56,12 +56,12 @@ Clock::~Clock() {
} }
bool Clock::Refresh() { bool Clock::Refresh() {
screens.Refresh(); screen->Refresh();
return running; return running;
} }
bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return screens.OnTouchEvent(event); return screen->OnTouchEvent(event);
} }
std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() { std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {
@ -80,6 +80,16 @@ std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {
app, dateTimeController, batteryController, bleController, notificatioManager, settingsController); app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
} }
std::unique_ptr<Screen> Clock::PineTimeStyleScreen() {
return std::make_unique<Screens::PineTimeStyle>(app,
dateTimeController,
batteryController,
bleController,
notificatioManager,
settingsController,
motionController);
}
/* /*
// Examples for more watch faces // Examples for more watch faces
std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() { std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() {

View File

@ -4,8 +4,8 @@
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <components/heartrate/HeartRateController.h>
#include "Screen.h" #include "Screen.h"
#include "ScreenList.h"
#include "components/datetime/DateTimeController.h" #include "components/datetime/DateTimeController.h"
namespace Pinetime { namespace Pinetime {
@ -47,9 +47,10 @@ namespace Pinetime {
Controllers::HeartRateController& heartRateController; Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController; Controllers::MotionController& motionController;
ScreenList<2> screens; std::unique_ptr<Screen> screen;
std::unique_ptr<Screen> WatchFaceDigitalScreen(); std::unique_ptr<Screen> WatchFaceDigitalScreen();
std::unique_ptr<Screen> WatchFaceAnalogScreen(); std::unique_ptr<Screen> WatchFaceAnalogScreen();
std::unique_ptr<Screen> PineTimeStyleScreen();
// Examples for more watch faces // Examples for more watch faces
// std::unique_ptr<Screen> WatchFaceMinimalScreen(); // std::unique_ptr<Screen> WatchFaceMinimalScreen();

View File

@ -27,9 +27,10 @@ FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp* app, Pinetime
lv_bar_set_value(bar1, 0, LV_ANIM_OFF); lv_bar_set_value(bar1, 0, LV_ANIM_OFF);
percentLabel = lv_label_create(lv_scr_act(), nullptr); percentLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text(percentLabel, ""); lv_label_set_text(percentLabel, "Waiting...");
lv_obj_set_auto_realign(percentLabel, true); lv_obj_set_auto_realign(percentLabel, true);
lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60); lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60);
startTime = xTaskGetTickCount();
} }
FirmwareUpdate::~FirmwareUpdate() { FirmwareUpdate::~FirmwareUpdate() {
@ -40,26 +41,42 @@ bool FirmwareUpdate::Refresh() {
switch (bleController.State()) { switch (bleController.State()) {
default: default:
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Idle: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Idle:
// This condition makes sure that the app is exited if somehow it got
// launched without a firmware update. This should never happen.
if (state != States::Error) {
if (xTaskGetTickCount() - startTime > (60 * 1024)) {
UpdateError();
state = States::Error;
}
} else if (xTaskGetTickCount() - startTime > (5 * 1024)) {
running = false;
}
break;
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Running: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Running:
if (state != States::Running) if (state != States::Running)
state = States::Running; state = States::Running;
return DisplayProgression(); DisplayProgression();
break;
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated:
if (state != States::Validated) { if (state != States::Validated) {
UpdateValidated(); UpdateValidated();
state = States::Validated; state = States::Validated;
} }
return running; break;
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Error: case Pinetime::Controllers::Ble::FirmwareUpdateStates::Error:
if (state != States::Error) { if (state != States::Error) {
UpdateError(); UpdateError();
state = States::Error; state = States::Error;
} }
return running; if (xTaskGetTickCount() - startTime > (5 * 1024)) {
running = false;
}
break;
} }
return running;
} }
bool FirmwareUpdate::DisplayProgression() const { void FirmwareUpdate::DisplayProgression() const {
float current = bleController.FirmwareUpdateCurrentBytes() / 1024.0f; float current = bleController.FirmwareUpdateCurrentBytes() / 1024.0f;
float total = bleController.FirmwareUpdateTotalBytes() / 1024.0f; float total = bleController.FirmwareUpdateTotalBytes() / 1024.0f;
int16_t pc = (current / total) * 100.0f; int16_t pc = (current / total) * 100.0f;
@ -67,7 +84,6 @@ bool FirmwareUpdate::DisplayProgression() const {
lv_label_set_text(percentLabel, percentStr); lv_label_set_text(percentLabel, percentStr);
lv_bar_set_value(bar1, pc, LV_ANIM_OFF); lv_bar_set_value(bar1, pc, LV_ANIM_OFF);
return running;
} }
void FirmwareUpdate::UpdateValidated() { void FirmwareUpdate::UpdateValidated() {
@ -78,4 +94,9 @@ void FirmwareUpdate::UpdateValidated() {
void FirmwareUpdate::UpdateError() { void FirmwareUpdate::UpdateError() {
lv_label_set_recolor(percentLabel, true); lv_label_set_recolor(percentLabel, true);
lv_label_set_text(percentLabel, "#ff0000 Error!#"); lv_label_set_text(percentLabel, "#ff0000 Error!#");
startTime = xTaskGetTickCount();
}
bool FirmwareUpdate::OnButtonPushed() {
return true;
} }

View File

@ -2,6 +2,7 @@
#include "Screen.h" #include "Screen.h"
#include <lvgl/src/lv_core/lv_obj.h> #include <lvgl/src/lv_core/lv_obj.h>
#include "FreeRTOS.h"
namespace Pinetime { namespace Pinetime {
namespace Controllers { namespace Controllers {
@ -25,13 +26,17 @@ namespace Pinetime {
lv_obj_t* titleLabel; lv_obj_t* titleLabel;
mutable char percentStr[10]; mutable char percentStr[10];
States state; States state = States::Idle;
bool DisplayProgression() const; void DisplayProgression() const;
bool OnButtonPushed() override;
void UpdateValidated(); void UpdateValidated();
void UpdateError(); void UpdateError();
TickType_t startTime;
}; };
} }
} }

View File

@ -16,19 +16,18 @@ namespace {
FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator) FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator)
: Screen {app}, validator {validator} { : Screen {app}, validator {validator} {
labelVersionInfo = lv_label_create(lv_scr_act(), nullptr); labelVersion = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelVersionInfo, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); lv_label_set_text_fmt(labelVersion,
lv_label_set_text(labelVersionInfo, "Version : "); "Version : %d.%d.%d\n"
lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT); "ShortRef : %s",
Version::Major(),
labelVersionValue = lv_label_create(lv_scr_act(), nullptr); Version::Minor(),
lv_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0); Version::Patch(),
lv_label_set_recolor(labelVersionValue, true); Version::GitCommitHash());
sprintf(version, "%ld.%ld.%ld", Version::Major(), Version::Minor(), Version::Patch()); lv_obj_align(labelVersion, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_label_set_text(labelVersionValue, version);
labelIsValidated = lv_label_create(lv_scr_act(), nullptr); labelIsValidated = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelIsValidated, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 50); lv_obj_align(labelIsValidated, labelVersion, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
lv_label_set_recolor(labelIsValidated, true); lv_label_set_recolor(labelIsValidated, true);
lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK); lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK);
lv_obj_set_width(labelIsValidated, 240); lv_obj_set_width(labelIsValidated, 240);

View File

@ -23,9 +23,7 @@ namespace Pinetime {
private: private:
Pinetime::Controllers::FirmwareValidator& validator; Pinetime::Controllers::FirmwareValidator& validator;
lv_obj_t* labelVersionInfo; lv_obj_t* labelVersion;
lv_obj_t* labelVersionValue;
char version[9];
lv_obj_t* labelIsValidated; lv_obj_t* labelIsValidated;
lv_obj_t* buttonValidate; lv_obj_t* buttonValidate;
lv_obj_t* labelButtonValidate; lv_obj_t* labelButtonValidate;

View File

@ -39,14 +39,14 @@ FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app,
backgroundAction->user_data = this; backgroundAction->user_data = this;
lv_obj_set_event_cb(backgroundAction, event_handler); lv_obj_set_event_cb(backgroundAction, event_handler);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
} }
FlashLight::~FlashLight() { FlashLight::~FlashLight() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
brightness.Restore(); brightness.Restore();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
} }
void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) { void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) {
@ -70,5 +70,5 @@ bool FlashLight::Refresh() {
} }
bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return true; return false;
} }

View File

@ -63,12 +63,12 @@ HeartRate::HeartRate(Pinetime::Applications::DisplayApp* app,
label_startStop = lv_label_create(btn_startStop, nullptr); label_startStop = lv_label_create(btn_startStop, nullptr);
UpdateStartStopButton(isHrRunning); UpdateStartStopButton(isHrRunning);
if (isHrRunning) if (isHrRunning)
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
} }
HeartRate::~HeartRate() { HeartRate::~HeartRate() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
} }
bool HeartRate::Refresh() { bool HeartRate::Refresh() {
@ -95,12 +95,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) { if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
heartRateController.Start(); heartRateController.Start();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
} else { } else {
heartRateController.Stop(); heartRateController.Stop();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
} }
} }

View File

@ -15,8 +15,6 @@ namespace Pinetime {
bool Refresh() override; bool Refresh() override;
private: private:
bool running = true;
lv_obj_t* labelText = nullptr; lv_obj_t* labelText = nullptr;
lv_point_t pageIndicatorBasePoints[2]; lv_point_t pageIndicatorBasePoints[2];
lv_point_t pageIndicatorPoints[2]; lv_point_t pageIndicatorPoints[2];
@ -25,4 +23,4 @@ namespace Pinetime {
}; };
} }
} }
} }

View File

@ -0,0 +1,169 @@
#include "Metronome.h"
#include "Screen.h"
#include "Symbols.h"
#include "lvgl/lvgl.h"
#include "FreeRTOSConfig.h"
#include "task.h"
#include <string>
#include <tuple>
using namespace Pinetime::Applications::Screens;
namespace {
float calculateDelta(const TickType_t startTime, const TickType_t currentTime) {
TickType_t delta = 0;
// Take care of overflow
if (startTime > currentTime) {
delta = 0xffffffff - startTime;
delta += (currentTime + 1);
} else {
delta = currentTime - startTime;
}
return static_cast<float>(delta) / static_cast<float>(configTICK_RATE_HZ);
}
static void eventHandler(lv_obj_t* obj, lv_event_t event) {
Metronome* screen = static_cast<Metronome*>(obj->user_data);
screen->OnEvent(obj, event);
}
lv_obj_t* createLabel(const char* name, lv_obj_t* reference, lv_align_t align, lv_font_t* font, uint8_t x = 0, uint8_t y = 0) {
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_label_set_text(label, name);
lv_obj_align(label, reference, align, x, y);
return label;
}
}
Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask)
: Screen(app), running {true}, currentState {States::Stopped}, startTime {}, motorController {motorController}, systemTask {systemTask} {
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
bpmArc->user_data = this;
lv_obj_set_event_cb(bpmArc, eventHandler);
lv_arc_set_bg_angles(bpmArc, 0, 270);
lv_arc_set_rotation(bpmArc, 135);
lv_arc_set_range(bpmArc, 40, 220);
lv_arc_set_value(bpmArc, bpm);
lv_obj_set_size(bpmArc, 210, 210);
lv_arc_set_adjustable(bpmArc, true);
lv_obj_align(bpmArc, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 7);
bpmValue = createLabel(std::to_string(lv_arc_get_value(bpmArc)).c_str(), bpmArc, LV_ALIGN_IN_TOP_MID, &jetbrains_mono_76, 0, 55);
bpmLegend = createLabel("bpm", bpmValue, LV_ALIGN_OUT_BOTTOM_MID, &jetbrains_mono_bold_20, 0, 0);
bpmTap = lv_btn_create(lv_scr_act(), nullptr);
bpmTap->user_data = this;
lv_obj_set_event_cb(bpmTap, eventHandler);
lv_obj_set_style_local_bg_opa(bpmTap, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_height(bpmTap, 80);
lv_obj_align(bpmTap, bpmValue, LV_ALIGN_IN_TOP_MID, 0, 0);
bpbDropdown = lv_dropdown_create(lv_scr_act(), nullptr);
bpbDropdown->user_data = this;
lv_obj_set_event_cb(bpbDropdown, eventHandler);
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_MAIN, LV_STATE_DEFAULT, 20);
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_LIST, LV_STATE_DEFAULT, 20);
lv_obj_align(bpbDropdown, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 15, -4);
lv_dropdown_set_options(bpbDropdown, "1\n2\n3\n4\n5\n6\n7\n8\n9");
lv_dropdown_set_selected(bpbDropdown, bpb - 1);
bpbLegend = lv_label_create(bpbDropdown, nullptr);
lv_label_set_text(bpbLegend, "bpb");
lv_obj_align(bpbLegend, bpbDropdown, LV_ALIGN_IN_RIGHT_MID, -15, 0);
playPause = lv_btn_create(lv_scr_act(), nullptr);
playPause->user_data = this;
lv_obj_set_event_cb(playPause, eventHandler);
lv_obj_align(playPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -15, -10);
lv_obj_set_height(playPause, 39);
playPauseLabel = lv_label_create(playPause, nullptr);
lv_label_set_text(playPauseLabel, Symbols::play);
app->SetTouchMode(DisplayApp::TouchModes::Polling);
}
Metronome::~Metronome() {
app->SetTouchMode(DisplayApp::TouchModes::Gestures);
systemTask.PushMessage(System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
bool Metronome::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return true;
}
bool Metronome::Refresh() {
switch (currentState) {
case States::Stopped: {
break;
}
case States::Running: {
if (calculateDelta(startTime, xTaskGetTickCount()) >= (60.0 / bpm)) {
counter--;
startTime -= 60.0 / bpm;
startTime = xTaskGetTickCount();
if (counter == 0) {
counter = bpb;
motorController.SetDuration(90);
} else {
motorController.SetDuration(30);
}
}
break;
}
}
return running;
}
void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
switch (event) {
case LV_EVENT_VALUE_CHANGED: {
if (obj == bpmArc) {
bpm = lv_arc_get_value(bpmArc);
lv_label_set_text_fmt(bpmValue, "%03d", bpm);
} else if (obj == bpbDropdown) {
bpb = lv_dropdown_get_selected(obj) + 1;
}
break;
}
case LV_EVENT_PRESSED: {
if (obj == bpmTap) {
float timeDelta = calculateDelta(tappedTime, xTaskGetTickCount());
if (tappedTime == 0 || timeDelta > 3) {
tappedTime = xTaskGetTickCount();
} else {
bpm = ceil(60.0 / timeDelta);
lv_arc_set_value(bpmArc, bpm);
lv_label_set_text_fmt(bpmValue, "%03d", bpm);
tappedTime = xTaskGetTickCount();
}
}
break;
}
case LV_EVENT_CLICKED: {
if (obj == playPause) {
currentState = (currentState == States::Stopped ? States::Running : States::Stopped);
switch (currentState) {
case States::Stopped: {
lv_label_set_text(playPauseLabel, Symbols::play);
systemTask.PushMessage(System::Messages::EnableSleeping);
break;
}
case States::Running: {
lv_label_set_text(playPauseLabel, Symbols::pause);
systemTask.PushMessage(System::Messages::DisableSleeping);
startTime = xTaskGetTickCount();
counter = 1;
break;
}
}
}
break;
}
}
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "systemtask/SystemTask.h"
#include "components/motor/MotorController.h"
#include <array>
namespace Pinetime::Applications::Screens {
class Metronome : public Screen {
public:
Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask);
~Metronome() override;
bool Refresh() override;
bool OnTouchEvent(TouchEvents event) override;
void OnEvent(lv_obj_t* obj, lv_event_t event);
enum class States { Running, Stopped };
private:
bool running;
States currentState;
TickType_t startTime;
TickType_t tappedTime = 0;
Controllers::MotorController& motorController;
System::SystemTask& systemTask;
uint16_t bpm = 120;
uint8_t bpb = 4;
uint8_t counter = 1;
lv_obj_t *bpmArc, *bpmTap, *bpmValue, *bpmLegend;
lv_obj_t *bpbDropdown, *bpbLegend;
lv_obj_t *playPause, *playPauseLabel;
};
}

View File

@ -32,11 +32,10 @@ Motion::Motion(Pinetime::Applications::DisplayApp* app, Controllers::MotionContr
lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label, NULL, LV_ALIGN_IN_TOP_MID, 0, 10); lv_obj_align(label, NULL, LV_ALIGN_IN_TOP_MID, 0, 10);
lv_label_set_recolor(label, true); lv_label_set_recolor(label, true);
labelStep = lv_label_create(lv_scr_act(), NULL); labelStep = lv_label_create(lv_scr_act(), NULL);
lv_obj_align(labelStep, chart, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); lv_obj_align(labelStep, chart, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_label_set_text(labelStep, "Steps ---"); lv_label_set_text(labelStep, "Steps ---");
} }
Motion::~Motion() { Motion::~Motion() {
@ -50,13 +49,12 @@ bool Motion::Refresh() {
lv_label_set_text_fmt(labelStep, "Steps %lu", motionController.NbSteps()); lv_label_set_text_fmt(labelStep, "Steps %lu", motionController.NbSteps());
lv_label_set_text_fmt(label, "X #FF0000 %d# Y #008000 %d# Z #FFFF00 %d#", motionController.X() / 0x10, motionController.Y() / 0x10, motionController.Z() / 0x10); lv_label_set_text_fmt(label,
"X #FF0000 %d# Y #008000 %d# Z #FFFF00 %d#",
motionController.X() / 0x10,
motionController.Y() / 0x10,
motionController.Z() / 0x10);
lv_obj_align(label, NULL, LV_ALIGN_IN_TOP_MID, 0, 10); lv_obj_align(label, NULL, LV_ALIGN_IN_TOP_MID, 0, 10);
return running; return running;
} }
bool Motion::OnButtonPushed() {
running = false;
return true;
}

View File

@ -18,7 +18,6 @@ namespace Pinetime {
~Motion() override; ~Motion() override;
bool Refresh() override; bool Refresh() override;
bool OnButtonPushed() override;
private: private:
Controllers::MotionController& motionController; Controllers::MotionController& motionController;
@ -29,10 +28,7 @@ namespace Pinetime {
lv_obj_t* label; lv_obj_t* label;
lv_obj_t* labelStep; lv_obj_t* labelStep;
static constexpr uint8_t nbStepsBufferSize = 9;
char nbStepsBuffer[nbStepsBufferSize + 1];
bool running = true;
}; };
} }
} }
} }

View File

@ -119,7 +119,6 @@ Music::Music(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Mus
constexpr int8_t MIDDLE_OFFSET = -25; constexpr int8_t MIDDLE_OFFSET = -25;
txtArtist = lv_label_create(lv_scr_act(), nullptr); txtArtist = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(txtArtist, LV_LABEL_LONG_SROLL_CIRC); lv_label_set_long_mode(txtArtist, LV_LABEL_LONG_SROLL_CIRC);
lv_label_set_anim_speed(txtArtist, 1);
lv_obj_align(txtArtist, nullptr, LV_ALIGN_IN_LEFT_MID, 12, MIDDLE_OFFSET + 1 * FONT_HEIGHT); lv_obj_align(txtArtist, nullptr, LV_ALIGN_IN_LEFT_MID, 12, MIDDLE_OFFSET + 1 * FONT_HEIGHT);
lv_label_set_align(txtArtist, LV_ALIGN_IN_LEFT_MID); lv_label_set_align(txtArtist, LV_ALIGN_IN_LEFT_MID);
lv_obj_set_width(txtArtist, LV_HOR_RES - 12); lv_obj_set_width(txtArtist, LV_HOR_RES - 12);
@ -127,7 +126,6 @@ Music::Music(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Mus
txtTrack = lv_label_create(lv_scr_act(), nullptr); txtTrack = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(txtTrack, LV_LABEL_LONG_SROLL_CIRC); lv_label_set_long_mode(txtTrack, LV_LABEL_LONG_SROLL_CIRC);
lv_label_set_anim_speed(txtTrack, 1);
lv_obj_align(txtTrack, nullptr, LV_ALIGN_IN_LEFT_MID, 12, MIDDLE_OFFSET + 2 * FONT_HEIGHT + LINE_PAD); lv_obj_align(txtTrack, nullptr, LV_ALIGN_IN_LEFT_MID, 12, MIDDLE_OFFSET + 2 * FONT_HEIGHT + LINE_PAD);
lv_label_set_align(txtTrack, LV_ALIGN_IN_LEFT_MID); lv_label_set_align(txtTrack, LV_ALIGN_IN_LEFT_MID);
@ -303,4 +301,4 @@ bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return true; return true;
} }
} }
} }

View File

@ -24,6 +24,106 @@ using namespace Pinetime::Applications::Screens;
LV_FONT_DECLARE(lv_font_navi_80) LV_FONT_DECLARE(lv_font_navi_80)
namespace {
constexpr std::array<std::pair<const char*, const char*>, 86> m_iconMap = {{
{"arrive-left", "\xEE\xA4\x81"},
{"arrive-right", "\xEE\xA4\x82"},
{"arrive-straight", "\xEE\xA4\x80"},
{"arrive", "\xEE\xA4\x80"},
{"close", "\xEE\xA4\x83"},
{"continue-left", "\xEE\xA4\x85"},
{"continue-right", "\xEE\xA4\x86"},
{"continue-slight-left", "\xEE\xA4\x87"},
{"continue-slight-right", "\xEE\xA4\x88"},
{"continue-straight", "\xEE\xA4\x84"},
{"continue-uturn", "\xEE\xA4\x89"},
{"continue", "\xEE\xA4\x84"},
{"depart-left", "\xEE\xA4\x8B"},
{"depart-right", "\xEE\xA4\x8C"},
{"depart-straight", "\xEE\xA4\x8A"},
{"end-of-road-left", "\xEE\xA4\x8D"},
{"end-of-road-right", "\xEE\xA4\x8E"},
{"ferry", "\xEE\xA4\x8F"},
{"flag", "\xEE\xA4\x90"},
{"fork-left", "\xEE\xA4\x92"},
{"fork-right", "\xEE\xA4\x93"},
{"fork-slight-left", "\xEE\xA4\x94"},
{"fork-slight-right", "\xEE\xA4\x95"},
{"fork-straight", "\xEE\xA4\x96"},
{"invalid", "\xEE\xA4\x84"},
{"invalid-left", "\xEE\xA4\x85"},
{"invalid-right", "\xEE\xA4\x86"},
{"invalid-slight-left", "\xEE\xA4\x87"},
{"invalid-slight-right", "\xEE\xA4\x88"},
{"invalid-straight", "\xEE\xA4\x84"},
{"invalid-uturn", "\xEE\xA4\x89"},
{"merge-left", "\xEE\xA4\x97"},
{"merge-right", "\xEE\xA4\x98"},
{"merge-slight-left", "\xEE\xA4\x99"},
{"merge-slight-right", "\xEE\xA4\x9A"},
{"merge-straight", "\xEE\xA4\x84"},
{"new-name-left", "\xEE\xA4\x85"},
{"new-name-right", "\xEE\xA4\x86"},
{"new-name-sharp-left", "\xEE\xA4\x9B"},
{"new-name-sharp-right", "\xEE\xA4\x9C"},
{"new-name-slight-left", "\xEE\xA4\x87"},
{"new-name-slight-right", "\xEE\xA4\x88"},
{"new-name-straight", "\xEE\xA4\x84"},
{"notification-left", "\xEE\xA4\x85"},
{"notification-right", "\xEE\xA4\x86"},
{"notification-sharp-left", "\xEE\xA4\x9B"},
{"notification-sharp-right", "\xEE\xA4\xA5"},
{"notification-slight-left", "\xEE\xA4\x87"},
{"notification-slight-right", "\xEE\xA4\x88"},
{"notification-straight", "\xEE\xA4\x84"},
{"off-ramp-left", "\xEE\xA4\x9D"},
{"off-ramp-right", "\xEE\xA4\x9E"},
{"off-ramp-slight-left", "\xEE\xA4\x9F"},
{"off-ramp-slight-right", "\xEE\xA4\xA0"},
{"on-ramp-left", "\xEE\xA4\x85"},
{"on-ramp-right", "\xEE\xA4\x86"},
{"on-ramp-sharp-left", "\xEE\xA4\x9B"},
{"on-ramp-sharp-right", "\xEE\xA4\xA5"},
{"on-ramp-slight-left", "\xEE\xA4\x87"},
{"on-ramp-slight-right", "\xEE\xA4\x88"},
{"on-ramp-straight", "\xEE\xA4\x84"},
{"rotary", "\xEE\xA4\xA1"},
{"rotary-left", "\xEE\xA4\xA2"},
{"rotary-right", "\xEE\xA4\xA3"},
{"rotary-sharp-left", "\xEE\xA4\xA4"},
{"rotary-sharp-right", "\xEE\xA4\xA5"},
{"rotary-slight-left", "\xEE\xA4\xA6"},
{"rotary-slight-right", "\xEE\xA4\xA7"},
{"rotary-straight", "\xEE\xA4\xA8"},
{"roundabout", "\xEE\xA4\xA1"},
{"roundabout-left", "\xEE\xA4\xA2"},
{"roundabout-right", "\xEE\xA4\xA3"},
{"roundabout-sharp-left", "\xEE\xA4\xA4"},
{"roundabout-sharp-right", "\xEE\xA4\xA5"},
{"roundabout-slight-left", "\xEE\xA4\xA6"},
{"roundabout-slight-right", "\xEE\xA4\xA7"},
{"roundabout-straight", "\xEE\xA4\xA8"},
{"turn-left", "\xEE\xA4\x85"},
{"turn-right", "\xEE\xA4\x86"},
{"turn-sharp-left", "\xEE\xA4\x9B"},
{"turn-sharp-right", "\xEE\xA4\xA5"},
{"turn-slight-left", "\xEE\xA4\x87"},
{"turn-slight-right", "\xEE\xA4\x88"},
{"turn-straight", "\xEE\xA4\x84"},
{"updown", "\xEE\xA4\xA9"},
{"uturn", "\xEE\xA4\x89"},
}};
const char* iconForName(const std::string& icon) {
for (auto iter : m_iconMap) {
if (iter.first == icon) {
return iter.second;
}
}
return "\xEE\xA4\x90";
}
}
/** /**
* Navigation watchapp * Navigation watchapp
* *
@ -68,27 +168,25 @@ Navigation::~Navigation() {
} }
bool Navigation::Refresh() { bool Navigation::Refresh() {
if (flag != navService.getFlag()) {
if (m_flag != navService.getFlag()) { flag = navService.getFlag();
m_flag = navService.getFlag(); lv_label_set_text(imgFlag, iconForName(flag));
lv_label_set_text(imgFlag, iconForName(m_flag));
// lv_img_set_src_arr(imgFlag, iconForName(m_flag));
} }
if (m_narrative != navService.getNarrative()) { if (narrative != navService.getNarrative()) {
m_narrative = navService.getNarrative(); narrative = navService.getNarrative();
lv_label_set_text(txtNarrative, m_narrative.data()); lv_label_set_text(txtNarrative, narrative.data());
} }
if (m_manDist != navService.getManDist()) { if (manDist != navService.getManDist()) {
m_manDist = navService.getManDist(); manDist = navService.getManDist();
lv_label_set_text(txtManDist, m_manDist.data()); lv_label_set_text(txtManDist, manDist.data());
} }
if (m_progress != navService.getProgress()) { if (progress != navService.getProgress()) {
m_progress = navService.getProgress(); progress = navService.getProgress();
lv_bar_set_value(barProgress, m_progress, LV_ANIM_OFF); lv_bar_set_value(barProgress, progress, LV_ANIM_OFF);
if (m_progress > 90) { if (progress > 90) {
lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
} else { } else {
lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_ORANGE); lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
@ -98,11 +196,4 @@ bool Navigation::Refresh() {
return running; return running;
} }
const char* Navigation::iconForName(std::string icon) {
for (auto iter : m_iconMap) {
if (iter.first == icon) {
return iter.second;
}
}
return "\xEE\xA4\x90";
}

View File

@ -45,103 +45,10 @@ namespace Pinetime {
Pinetime::Controllers::NavigationService& navService; Pinetime::Controllers::NavigationService& navService;
std::string m_flag; std::string flag;
std::string m_narrative; std::string narrative;
std::string m_manDist; std::string manDist;
int m_progress; int progress;
/** Watchapp */
const char* iconForName(std::string icon);
std::array<std::pair<std::string, const char*>, 89> m_iconMap = {{
{"arrive-left", "\xEE\xA4\x81"},
{"arrive-right", "\xEE\xA4\x82"},
{"arrive-straight", "\xEE\xA4\x80"},
{"arrive", "\xEE\xA4\x80"},
{"close", "\xEE\xA4\x83"},
{"continue-left", "\xEE\xA4\x85"},
{"continue-right", "\xEE\xA4\x86"},
{"continue-slight-left", "\xEE\xA4\x87"},
{"continue-slight-right", "\xEE\xA4\x88"},
{"continue-straight", "\xEE\xA4\x84"},
{"continue-uturn", "\xEE\xA4\x89"},
{"continue", "\xEE\xA4\x84"},
{"depart-left", "\xEE\xA4\x8B"},
{"depart-right", "\xEE\xA4\x8C"},
{"depart-straight", "\xEE\xA4\x8A"},
{"end-of-road-left", "\xEE\xA4\x8D"},
{"end-of-road-right", "\xEE\xA4\x8E"},
{"ferry", "\xEE\xA4\x8F"},
{"flag", "\xEE\xA4\x90"},
{"fork-left", "\xEE\xA4\x92"},
{"fork-right", "\xEE\xA4\x93"},
{"fork-slight-left", "\xEE\xA4\x94"},
{"fork-slight-right", "\xEE\xA4\x95"},
{"fork-straight", "\xEE\xA4\x96"},
{"invalid", "\xEE\xA4\x84"},
{"invalid-left", "\xEE\xA4\x85"},
{"invalid-right", "\xEE\xA4\x86"},
{"invalid-slight-left", "\xEE\xA4\x87"},
{"invalid-slight-right", "\xEE\xA4\x88"},
{"invalid-straight", "\xEE\xA4\x84"},
{"invalid-uturn", "\xEE\xA4\x89"},
{"merge-left", "\xEE\xA4\x97"},
{"merge-right", "\xEE\xA4\x98"},
{"merge-slight-left", "\xEE\xA4\x99"},
{"merge-slight-right", "\xEE\xA4\x9A"},
{"merge-straight", "\xEE\xA4\x84"},
{"new-name-left", "\xEE\xA4\x85"},
{"new-name-right", "\xEE\xA4\x86"},
{"new-name-sharp-left", "\xEE\xA4\x9B"},
{"new-name-sharp-right", "\xEE\xA4\x9C"},
{"new-name-slight-left", "\xEE\xA4\x87"},
{"new-name-slight-right", "\xEE\xA4\x88"},
{"new-name-straight", "\xEE\xA4\x84"},
{"notification-left", "\xEE\xA4\x85"},
{"notification-right", "\xEE\xA4\x86"},
{"notification-sharp-left", "\xEE\xA4\x9B"},
{"notification-sharp-right", "\xEE\xA4\xA5"},
{"notification-slight-left", "\xEE\xA4\x87"},
{"notification-slight-right", "\xEE\xA4\x88"},
{"notification-straight", "\xEE\xA4\x84"},
{"off-ramp-left", "\xEE\xA4\x9D"},
{"off-ramp-right", "\xEE\xA4\x9E"},
{"off-ramp-slight-left", "\xEE\xA4\x9F"},
{"off-ramp-slight-right", "\xEE\xA4\xA0"},
{"on-ramp-left", "\xEE\xA4\x85"},
{"on-ramp-right", "\xEE\xA4\x86"},
{"on-ramp-sharp-left", "\xEE\xA4\x9B"},
{"on-ramp-sharp-right", "\xEE\xA4\xA5"},
{"on-ramp-slight-left", "\xEE\xA4\x87"},
{"on-ramp-slight-right", "\xEE\xA4\x88"},
{"on-ramp-straight", "\xEE\xA4\x84"},
{"rotary", "\xEE\xA4\xA1"},
{"rotary-left", "\xEE\xA4\xA2"},
{"rotary-right", "\xEE\xA4\xA3"},
{"rotary-sharp-left", "\xEE\xA4\xA4"},
{"rotary-sharp-right", "\xEE\xA4\xA5"},
{"rotary-slight-left", "\xEE\xA4\xA6"},
{"rotary-slight-right", "\xEE\xA4\xA7"},
{"rotary-straight", "\xEE\xA4\xA8"},
{"roundabout", "\xEE\xA4\xA1"},
{"roundabout-left", "\xEE\xA4\xA2"},
{"roundabout-right", "\xEE\xA4\xA3"},
{"roundabout-sharp-left", "\xEE\xA4\xA4"},
{"roundabout-sharp-right", "\xEE\xA4\xA5"},
{"roundabout-slight-left", "\xEE\xA4\xA6"},
{"roundabout-slight-right", "\xEE\xA4\xA7"},
{"roundabout-straight", "\xEE\xA4\xA8"},
{"turn-left", "\xEE\xA4\x85"},
{"turn-right", "\xEE\xA4\x86"},
{"turn-sharp-left", "\xEE\xA4\x9B"},
{"turn-sharp-right", "\xEE\xA4\xA5"},
{"turn-slight-left", "\xEE\xA4\x87"},
{"turn-slight-right", "\xEE\xA4\x88"},
{"turn-straight", "\xEE\xA4\x84"},
{"updown", "\xEE\xA4\xA9"},
{"uturn", "\xEE\xA4\x89"},
}};
}; };
} }
} }

View File

@ -185,10 +185,10 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_pos(container1, 0, 50); lv_obj_set_pos(container1, 0, 50);
lv_obj_set_width(container1, 240); lv_obj_set_size(container1, LV_HOR_RES, 190);
lv_obj_set_height(container1, 190);
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
lv_cont_set_fit(container1, LV_FIT_NONE);
lv_obj_t* alert_count = lv_label_create(lv_scr_act(), nullptr); lv_obj_t* alert_count = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_fmt(alert_count, "%i/%i", notifNr, notifNb); lv_label_set_text_fmt(alert_count, "%i/%i", notifNr, notifNb);
@ -198,7 +198,15 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_obj_set_style_local_text_color(alert_type, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x888888)); lv_obj_set_style_local_text_color(alert_type, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x888888));
if (title == nullptr) if (title == nullptr)
title = "Notification"; title = "Notification";
char* pchar;
pchar = strchr(title, '\n');
while (pchar != nullptr) {
*pchar = ' ';
pchar = strchr(pchar + 1, '\n');
}
lv_label_set_text(alert_type, title); lv_label_set_text(alert_type, title);
lv_label_set_long_mode(alert_type, LV_LABEL_LONG_SROLL_CIRC);
lv_obj_set_width(alert_type, 180);
lv_obj_align(alert_type, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 16); lv_obj_align(alert_type, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 16);
///////// /////////
@ -211,6 +219,7 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_label_set_text(alert_subject, msg); lv_label_set_text(alert_subject, msg);
} break; } break;
case Controllers::NotificationManager::Categories::IncomingCall: { case Controllers::NotificationManager::Categories::IncomingCall: {
lv_obj_set_height(container1, 108);
lv_obj_t* alert_subject = lv_label_create(container1, nullptr); lv_obj_t* alert_subject = lv_label_create(container1, nullptr);
lv_obj_set_style_local_text_color(alert_subject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); lv_obj_set_style_local_text_color(alert_subject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK); lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK);
@ -223,38 +232,29 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_obj_set_width(alert_caller, LV_HOR_RES - 20); lv_obj_set_width(alert_caller, LV_HOR_RES - 20);
lv_label_set_text(alert_caller, msg); lv_label_set_text(alert_caller, msg);
lv_obj_t* callBtnContainer = lv_cont_create(container1, NULL); bt_accept = lv_btn_create(lv_scr_act(), nullptr);
lv_obj_set_width(callBtnContainer, 240);
lv_obj_set_height(callBtnContainer, 90);
lv_cont_set_layout(callBtnContainer, LV_LAYOUT_ROW_MID);
lv_obj_set_style_local_bg_color(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x222222));
lv_obj_set_style_local_pad_all(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_margin_top(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 40);
lv_obj_set_style_local_margin_left(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, -8);
lv_obj_set_style_local_pad_inner(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
lv_obj_set_style_local_border_width(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
bt_accept = lv_btn_create(callBtnContainer, nullptr);
bt_accept->user_data = this; bt_accept->user_data = this;
lv_obj_set_event_cb(bt_accept, AcceptIncomingCallEventHandler); lv_obj_set_event_cb(bt_accept, AcceptIncomingCallEventHandler);
lv_obj_set_size(bt_accept, (LV_HOR_RES / 3) - 5, 80); lv_obj_set_size(bt_accept, 76, 76);
lv_obj_align(bt_accept, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
label_accept = lv_label_create(bt_accept, nullptr); label_accept = lv_label_create(bt_accept, nullptr);
lv_label_set_text(label_accept, Symbols::phone); lv_label_set_text(label_accept, Symbols::phone);
lv_obj_set_style_local_bg_color(bt_accept, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); lv_obj_set_style_local_bg_color(bt_accept, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
bt_reject = lv_btn_create(callBtnContainer, nullptr); bt_reject = lv_btn_create(lv_scr_act(), nullptr);
bt_reject->user_data = this; bt_reject->user_data = this;
lv_obj_set_event_cb(bt_reject, RejectIncomingCallEventHandler); lv_obj_set_event_cb(bt_reject, RejectIncomingCallEventHandler);
lv_obj_set_size(bt_reject, (LV_HOR_RES / 3) - 5, 80); lv_obj_set_size(bt_reject, 76, 76);
lv_obj_align(bt_reject, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
label_reject = lv_label_create(bt_reject, nullptr); label_reject = lv_label_create(bt_reject, nullptr);
lv_label_set_text(label_reject, Symbols::phoneSlash); lv_label_set_text(label_reject, Symbols::phoneSlash);
lv_obj_set_style_local_bg_color(bt_reject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_color(bt_reject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
bt_mute = lv_btn_create(callBtnContainer, nullptr); bt_mute = lv_btn_create(lv_scr_act(), nullptr);
bt_mute->user_data = this; bt_mute->user_data = this;
lv_obj_set_event_cb(bt_mute, MuteIncomingCallEventHandler); lv_obj_set_event_cb(bt_mute, MuteIncomingCallEventHandler);
lv_obj_set_size(bt_mute, (LV_HOR_RES / 3) - 5, 80); lv_obj_set_size(bt_mute, 76, 76);
lv_obj_align(bt_mute, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
label_mute = lv_label_create(bt_mute, nullptr); label_mute = lv_label_create(bt_mute, nullptr);
lv_label_set_text(label_mute, Symbols::volumMute); lv_label_set_text(label_mute, Symbols::volumMute);
lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);

View File

@ -4,98 +4,31 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
namespace {
const uint8_t paddle_map[] = {
0xfc, 0xfe, 0xfc, 0xff, /*Color of index 0*/
0xff, 0xff, 0xff, 0xff, /*Color of index 1*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t ball_map[] = {
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
};
}
Paddle::Paddle(Pinetime::Applications::DisplayApp* app, Pinetime::Components::LittleVgl& lvgl) : Screen(app), lvgl {lvgl} { Paddle::Paddle(Pinetime::Applications::DisplayApp* app, Pinetime::Components::LittleVgl& lvgl) : Screen(app), lvgl {lvgl} {
app->SetTouchMode(DisplayApp::TouchModes::Polling); app->SetTouchMode(DisplayApp::TouchModes::Polling);
background = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_size(background, LV_HOR_RES + 1, LV_VER_RES);
lv_obj_set_pos(background, -1, 0);
lv_obj_set_style_local_radius(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_bg_color(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_set_style_local_border_color(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_border_width(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 1);
points = lv_label_create(lv_scr_act(), nullptr); points = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(points, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_label_set_text(points, "0000"); lv_label_set_text(points, "0000");
lv_obj_set_style_local_text_color(points, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x444444)); lv_obj_align(points, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 10);
lv_obj_align(points, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0);
paddle.header.always_zero = 0; paddle = lv_obj_create(lv_scr_act(), nullptr);
paddle.header.w = 4; lv_obj_set_style_local_bg_color(paddle, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
paddle.header.h = 60; lv_obj_set_style_local_radius(paddle, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
paddle.data_size = 68; lv_obj_set_size(paddle, 4, 60);
paddle.header.cf = LV_IMG_CF_INDEXED_1BIT;
paddle.data = paddle_map;
paddle_image = lv_img_create(lv_scr_act(), nullptr);
lv_img_set_src(paddle_image, &paddle);
ball.header.always_zero = 0; ball = lv_obj_create(lv_scr_act(), nullptr);
ball.header.w = 24; lv_obj_set_style_local_bg_color(ball, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
ball.header.h = 24; lv_obj_set_style_local_radius(ball, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
ball.data_size = 24 * 24 * LV_COLOR_SIZE / 8; lv_obj_set_size(ball, ballSize, ballSize);
ball.header.cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
ball.data = ball_map;
ball_image = lv_img_create(lv_scr_act(), nullptr);
lv_img_set_src(ball_image, &ball);
} }
Paddle::~Paddle() { Paddle::~Paddle() {
@ -105,41 +38,37 @@ Paddle::~Paddle() {
} }
bool Paddle::Refresh() { bool Paddle::Refresh() {
if ((counter++ % 5) == 0) { ballX += dx;
counter = 0; ballY += dy;
ballX += dx; lv_obj_set_pos(ball, ballX, ballY);
ballY += dy;
lv_obj_set_pos(ball_image, ballX, ballY); // checks if it has touched the sides (floor and ceiling)
if (ballY <= 1 || ballY >= LV_VER_RES - ballSize - 2) {
dy *= -1;
}
// checks if it has touched the sides (floor and ceiling) // checks if it has touched the side (left side)
if (ballY <= 0 || ballY >= 215) { if (ballX >= LV_VER_RES - ballSize - 1) {
dy *= -1; dx *= -1;
} }
// checks if it has touched the side (left side) // checks if it is in the position of the paddle
if (ballX >= 215) { if (dx < 0 && ballX <= 4) {
dx *= -1; if (ballX >= -ballSize / 4) {
} if (ballY <= (paddlePos + 30 - ballSize / 4) && ballY >= (paddlePos - 30 - ballSize + ballSize / 4)) {
// checks if it is in the position of the paddle
if (ballY <= (paddleBottomY + 16) && ballY >= (paddleTopY - 8)) {
if (ballX >= 0 && ballX < 4) {
lv_obj_set_pos(ball_image, 5, ballY);
dx *= -1; dx *= -1;
score++; score++;
} }
} }
// checks if it has gone behind the paddle // checks if it has gone behind the paddle
else if (ballX <= -40) { else if (ballX <= -ballSize * 2) {
ballX = 107; ballX = (LV_HOR_RES - ballSize) / 2;
ballY = 107; ballY = (LV_VER_RES - ballSize) / 2;
score = 0; score = 0;
} }
lv_label_set_text_fmt(points, "%04d", score);
} }
lv_label_set_text_fmt(points, "%04d", score);
return running; return running;
} }
@ -148,11 +77,8 @@ bool Paddle::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
} }
bool Paddle::OnTouchEvent(uint16_t x, uint16_t y) { bool Paddle::OnTouchEvent(uint16_t x, uint16_t y) {
lv_obj_set_pos( // sets the center paddle pos. (30px offset) with the the y_coordinate of the finger
paddle_image, lv_obj_set_pos(paddle, 0, y - 30);
0, paddlePos = y;
y - 30); // sets the center paddle pos. (30px offset) with the the y_coordinate of the finger and defaults the x_coordinate to 0
paddleTopY = y - 30; // refreshes the upper extreme of the paddle
paddleBottomY = y + 30; // refreshes the lower extreme of the paddle
return true; return true;
} }

View File

@ -24,24 +24,22 @@ namespace Pinetime {
private: private:
Pinetime::Components::LittleVgl& lvgl; Pinetime::Components::LittleVgl& lvgl;
int paddleBottomY = 90; // bottom extreme of the paddle const uint8_t ballSize = 16;
int paddleTopY = 150; // top extreme of the paddle
int ballX = 107; // Initial x_coordinate for the ball (12px offset from the center to counteract the ball's 24px size) uint16_t paddlePos = 30; // Paddle center
int ballY = 107; // Initial y_coordinate for the ball
int dx = 2; // Velocity of the ball in the x_coordinate int16_t ballX = (LV_HOR_RES - ballSize) / 2;
int dy = 3; // Velocity of the ball in the y_coordinate int16_t ballY = (LV_VER_RES - ballSize) / 2;
int counter = 0; // init Frame refresh limit counter int8_t dx = 2; // Velocity of the ball in the x_coordinate
int score = 0; int8_t dy = 3; // Velocity of the ball in the y_coordinate
lv_img_dsc_t paddle; uint16_t score = 0;
lv_img_dsc_t ball;
lv_obj_t* points; lv_obj_t* points;
lv_obj_t* paddle_image; // pointer to paddle image lv_obj_t* paddle;
lv_obj_t* ball_image; // pointer to ball image lv_obj_t* ball;
lv_obj_t* background;
}; };
} }
} }

View File

@ -0,0 +1,340 @@
/*
* This file is part of the Infinitime distribution (https://github.com/JF002/Infinitime).
* Copyright (c) 2021 Kieran Cawthray.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* PineTimeStyle watchface for Infinitime created by Kieran Cawthray
* Based on WatchFaceDigital
* Style/layout copied from TimeStyle for Pebble by Dan Tilden (github.com/tilden)
*/
#include "PineTimeStyle.h"
#include <date/date.h>
#include <lvgl/lvgl.h>
#include <cstdio>
#include "BatteryIcon.h"
#include "BleIcon.h"
#include "NotificationIcon.h"
#include "Symbols.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/motion/MotionController.h"
#include "components/settings/Settings.h"
#include "../DisplayApp.h"
using namespace Pinetime::Applications::Screens;
PineTimeStyle::PineTimeStyle(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController,
Controllers::MotionController& motionController)
: Screen(app),
currentDateTime {{}},
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
notificatioManager {notificatioManager},
settingsController {settingsController},
motionController {motionController} {
/* This sets the watchface number to return to after leaving the menu */
settingsController.SetClockFace(2);
displayedChar[0] = 0;
displayedChar[1] = 0;
displayedChar[2] = 0;
displayedChar[3] = 0;
displayedChar[4] = 0;
/* Create a 200px wide background rectangle */
timebar = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(timebar, 200, 240);
lv_obj_align(timebar, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 0);
/* Display the time */
timeDD1 = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_label_set_text(timeDD1, "12");
lv_obj_align(timeDD1, timebar, LV_ALIGN_IN_TOP_MID, 5, 5);
timeDD2 = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_label_set_text(timeDD2, "34");
lv_obj_align(timeDD2, timebar, LV_ALIGN_IN_BOTTOM_MID, 5, -5);
timeAMPM = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_obj_set_style_local_text_line_space(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, -3);
lv_label_set_text(timeAMPM, "");
lv_obj_align(timeAMPM, timebar, LV_ALIGN_IN_BOTTOM_LEFT, 2, -20);
/* Create a 40px wide bar down the right side of the screen */
sidebar = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_obj_set_style_local_radius(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(sidebar, 40, 240);
lv_obj_align(sidebar, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
/* Display icons */
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(batteryIcon, Symbols::batteryFull);
lv_obj_align(batteryIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 2);
batteryPlug = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_align(batteryPlug, sidebar, LV_ALIGN_IN_TOP_MID, 0, 2);
bleIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25);
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 40);
/* Calendar icon */
calendarOuter = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarOuter, 34, 34);
lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 0);
calendarInner = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
lv_obj_set_style_local_radius(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarInner, 27, 27);
lv_obj_align(calendarInner, calendarOuter, LV_ALIGN_CENTER, 0, 0);
calendarBar1 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarBar1, 3, 12);
lv_obj_align(calendarBar1, calendarOuter, LV_ALIGN_IN_TOP_MID, -6, -3);
calendarBar2 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarBar2, 3, 12);
lv_obj_align(calendarBar2, calendarOuter, LV_ALIGN_IN_TOP_MID, 6, -3);
calendarCrossBar1 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarCrossBar1, 8, 3);
lv_obj_align(calendarCrossBar1, calendarBar1, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
calendarCrossBar2 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarCrossBar2, 8, 3);
lv_obj_align(calendarCrossBar2, calendarBar2, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
/* Display date */
dateDayOfWeek = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(dateDayOfWeek, "THU");
lv_obj_align(dateDayOfWeek, sidebar, LV_ALIGN_CENTER, 0, -34);
dateDay = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateDay, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(dateDay, "25");
lv_obj_align(dateDay, sidebar, LV_ALIGN_CENTER, 0, 3);
dateMonth = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateMonth, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(dateMonth, "MAR");
lv_obj_align(dateMonth, sidebar, LV_ALIGN_CENTER, 0, 32);
// Step count gauge
needle_colors[0] = LV_COLOR_WHITE;
stepGauge = lv_gauge_create(lv_scr_act(), nullptr);
lv_gauge_set_needle_count(stepGauge, 1, needle_colors);
lv_obj_set_size(stepGauge, 40, 40);
lv_obj_align(stepGauge, sidebar, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_gauge_set_scale(stepGauge, 360, 11, 0);
lv_gauge_set_angle_offset(stepGauge, 180);
lv_gauge_set_critical_value(stepGauge, 100);
lv_gauge_set_range(stepGauge, 0, 100);
lv_gauge_set_value(stepGauge, 0, 0);
lv_obj_set_style_local_pad_right(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_pad_left(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_pad_bottom(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_opa(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_obj_set_style_local_scale_width(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_line_width(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_line_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_set_style_local_line_opa(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_obj_set_style_local_line_width(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_pad_inner(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, 4);
backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_click(backgroundLabel, true);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, 240, 240);
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text(backgroundLabel, "");
}
PineTimeStyle::~PineTimeStyle() {
lv_obj_clean(lv_scr_act());
}
bool PineTimeStyle::Refresh() {
batteryPercentRemaining = batteryController.PercentRemaining();
if (batteryPercentRemaining.IsUpdated()) {
auto batteryPercent = batteryPercentRemaining.Get();
if (batteryController.IsCharging()) {
auto isCharging = batteryController.IsCharging() || batteryController.IsPowerPresent();
lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging));
lv_obj_realign(batteryPlug);
lv_label_set_text(batteryIcon, "");
} else {
lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent));
lv_label_set_text(batteryPlug, "");
}
}
bleState = bleController.IsConnected();
if (bleState.IsUpdated()) {
if (bleState.Get() == true) {
lv_label_set_text(bleIcon, BleIcon::GetIcon(true));
lv_obj_realign(bleIcon);
} else {
lv_label_set_text(bleIcon, BleIcon::GetIcon(false));
}
}
notificationState = notificatioManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
if (notificationState.Get() == true) {
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
lv_obj_realign(notificationIcon);
} else {
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
}
}
currentDateTime = dateTimeController.CurrentDateTime();
if (currentDateTime.IsUpdated()) {
auto newDateTime = currentDateTime.Get();
auto dp = date::floor<date::days>(newDateTime);
auto time = date::make_time(newDateTime - dp);
auto yearMonthDay = date::year_month_day(dp);
auto year = (int) yearMonthDay.year();
auto month = static_cast<Pinetime::Controllers::DateTime::Months>((unsigned) yearMonthDay.month());
auto day = (unsigned) yearMonthDay.day();
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
int hour = time.hours().count();
auto minute = time.minutes().count();
char minutesChar[3];
sprintf(minutesChar, "%02d", static_cast<int>(minute));
char hoursChar[3];
char ampmChar[5];
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
sprintf(hoursChar, "%02d", hour);
} else {
if (hour == 0 && hour != 12) {
hour = 12;
sprintf(ampmChar, "A\nM");
} else if (hour == 12 && hour != 0) {
hour = 12;
sprintf(ampmChar, "P\nM");
} else if (hour < 12 && hour != 0) {
sprintf(ampmChar, "A\nM");
} else if (hour > 12 && hour != 0) {
hour = hour - 12;
sprintf(ampmChar, "P\nM");
}
sprintf(hoursChar, "%02d", hour);
}
if (hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] ||
minutesChar[1] != displayedChar[3]) {
displayedChar[0] = hoursChar[0];
displayedChar[1] = hoursChar[1];
displayedChar[2] = minutesChar[0];
displayedChar[3] = minutesChar[1];
char hourStr[3];
char minStr[3];
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
lv_label_set_text(timeAMPM, ampmChar);
}
/* Display the time as 2 pairs of digits */
sprintf(hourStr, "%c%c", hoursChar[0], hoursChar[1]);
lv_label_set_text(timeDD1, hourStr);
sprintf(minStr, "%c%c", minutesChar[0], minutesChar[1]);
lv_label_set_text(timeDD2, minStr);
}
if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
char dayOfWeekStr[4];
char dayStr[3];
char monthStr[4];
sprintf(dayOfWeekStr, "%s", dateTimeController.DayOfWeekShortToString());
sprintf(dayStr, "%d", day);
sprintf(monthStr, "%s", dateTimeController.MonthShortToString());
lv_label_set_text(dateDayOfWeek, dayOfWeekStr);
lv_label_set_text(dateDay, dayStr);
lv_obj_realign(dateDay);
lv_label_set_text(dateMonth, monthStr);
currentYear = year;
currentMonth = month;
currentDayOfWeek = dayOfWeek;
currentDay = day;
}
}
stepCount = motionController.NbSteps();
motionSensorOk = motionController.IsSensorOk();
if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
lv_gauge_set_value(stepGauge, 0, (stepCount.Get() / (settingsController.GetStepsGoal() / 100)));
lv_obj_realign(stepGauge);
if (stepCount.Get() > settingsController.GetStepsGoal()) {
lv_obj_set_style_local_line_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_scale_grad_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
}
}
return running;
}

View File

@ -0,0 +1,86 @@
#pragma once
#include <lvgl/src/lv_core/lv_obj.h>
#include <chrono>
#include <cstdint>
#include <memory>
#include "Screen.h"
#include "ScreenList.h"
#include "components/datetime/DateTimeController.h"
namespace Pinetime {
namespace Controllers {
class Settings;
class Battery;
class Ble;
class NotificationManager;
class HeartRateController;
}
namespace Applications {
namespace Screens {
class PineTimeStyle : public Screen {
public:
PineTimeStyle(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController,
Controllers::MotionController& motionController);
~PineTimeStyle() override;
bool Refresh() override;
void OnObjectEvent(lv_obj_t* pObj, lv_event_t i);
private:
char displayedChar[5];
uint16_t currentYear = 1970;
Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0;
DirtyValue<uint8_t> batteryPercentRemaining {};
DirtyValue<bool> bleState {};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
DirtyValue<bool> motionSensorOk {};
DirtyValue<uint32_t> stepCount {};
DirtyValue<bool> notificationState {};
lv_obj_t* timebar;
lv_obj_t* sidebar;
lv_obj_t* timeDD1;
lv_obj_t* timeDD2;
lv_obj_t* timeAMPM;
lv_obj_t* dateDayOfWeek;
lv_obj_t* dateDay;
lv_obj_t* dateMonth;
lv_obj_t* backgroundLabel;
lv_obj_t* batteryIcon;
lv_obj_t* bleIcon;
lv_obj_t* batteryPlug;
lv_obj_t* calendarOuter;
lv_obj_t* calendarInner;
lv_obj_t* calendarBar1;
lv_obj_t* calendarBar2;
lv_obj_t* calendarCrossBar1;
lv_obj_t* calendarCrossBar2;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* heartbeatBpm;
lv_obj_t* notificationIcon;
lv_obj_t* stepGauge;
lv_color_t needle_colors[1];
Controllers::DateTime& dateTimeController;
Controllers::Battery& batteryController;
Controllers::Ble& bleController;
Controllers::NotificationManager& notificatioManager;
Controllers::Settings& settingsController;
Controllers::MotionController& motionController;
};
}
}
}

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