diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 1932a9d4..b243a64a 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,7 +1,6 @@ # VS Code Dev Container -This is a docker-based interactive development environment using VS Code and Docker Dev Containers removing the need to install any tools locally* - +This is a docker-based interactive development environment using VS Code and Docker Dev Containers removing the need to install any tools locally\* ## Requirements @@ -16,45 +15,41 @@ This is a docker-based interactive development environment using VS Code and Doc 1. Clone InfiniTime and update submodules 2. Launch VS Code -3. Open InfiniTime directory, -4. Allow VS Code to open folder with devcontainer. +3. Open InfiniTime directory, +4. Allow VS Code to open folder with devcontainer. -After this the environment will be built if you do not currently have a container setup, it will install all the necessary tools and extra VSCode extensions. +After this the environment will be built if you do not currently have a container setup, it will install all the necessary tools and extra VSCode extensions. In order to build InfiniTime we need to run the initial submodule init and CMake commands. -#### Manually +#### Manually - You can use the VS Code terminal to run the CMake commands as outlined in the [build instructions](blob/develop/doc/buildAndProgram.md) +You can use the VS Code terminal to run the CMake commands as outlined in the [build instructions](blob/develop/doc/buildAndProgram.md) #### Script The dev environment comes with some scripts to make this easier, They are located in /opt/. -There are also VS Code tasks provided should you desire to use those. +There are also VS Code tasks provided should you desire to use those. The task "update submodules" will update the git submodules - - ### Build You can use the build.sh script located in /opt/ CMake is also configured and controls for the CMake plugin are available in VS Code - - ### Debugging Docker on windows does not support passing USB devices to the underlying WSL2 subsystem, To get around this we use OpenOCD in server mode running on the host. `openocd -f -f ` -This will launch OpenOCD in server mode and attach it to the MCU. +This will launch OpenOCD in server mode and attach it to the MCU. The default launch.json file expects OpenOCD to be listening on port 3333, edit if needed - ## Current Issues -Currently WSL2 Has some real performance issues with IO on a windows host. Accessing files on the virtualized filesystem is much faster. Using VS Codes "clone in container" feature of the Remote - Containers will get around this. After the container is built you will need to update the submodules and follow the build instructions like normal \ No newline at end of file + +Currently WSL2 Has some real performance issues with IO on a windows host. Accessing files on the virtualized filesystem is much faster. Using VS Codes "clone in container" feature of the Remote - Containers will get around this. After the container is built you will need to update the submodules and follow the build instructions like normal diff --git a/.github/workflows/lv_sim.yml b/.github/workflows/lv_sim.yml index 7b02fb1c..c950168c 100644 --- a/.github/workflows/lv_sim.yml +++ b/.github/workflows/lv_sim.yml @@ -7,13 +7,11 @@ on: branches: [ master, develop ] paths-ignore: - 'doc/**' - - 'images/**' - '**.md' pull_request: branches: [ develop ] paths-ignore: - 'doc/**' - - 'images/**' - '**.md' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0f0686b6..81054bcb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,13 +10,11 @@ on: branches: [ master, develop ] paths-ignore: - 'doc/**' - - 'images/**' - '**.md' pull_request: branches: [ develop ] paths-ignore: - 'doc/**' - - 'images/**' - '**.md' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/README.md b/README.md index 7c52cb44..a3d2229b 100644 --- a/README.md +++ b/README.md @@ -2,70 +2,71 @@ [![Build PineTime Firmware](https://github.com/InfiniTimeOrg/InfiniTime/workflows/Build%20PineTime%20Firmware/badge.svg?branch=master)](https://github.com/InfiniTimeOrg/InfiniTime/actions) -![InfiniTime logo](images/infinitime-logo-small.jpg "InfiniTime Logo") +![InfiniTime logo](doc/logo/infinitime-logo-small.jpg "InfiniTime Logo") Fast open-source firmware for the [PineTime smartwatch](https://www.pine64.org/pinetime/) with many features, written in modern C++. ## New to InfiniTime? - - [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) - - [Updating the software](doc/gettingStarted/updating-software.md) - - [About the firmware and bootloader](doc/gettingStarted/about-software.md) +- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) +- [Updating the software](doc/gettingStarted/updating-software.md) +- [About the firmware and bootloader](doc/gettingStarted/about-software.md) ### Companion apps - - [Gadgetbridge](https://gadgetbridge.org/) (Android) - - [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS) - - [Siglo](https://github.com/alexr4535/siglo) (Linux) - - [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS) **[Looking for a new maintainer]** - - [ITD](https://gitea.arsenm.dev/Arsen6331/itd) (Linux) +- [Gadgetbridge](https://gadgetbridge.org/) (Android) +- [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS) +- [Siglo](https://github.com/alexr4535/siglo) (Linux) +- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS) **[Looking for a new maintainer]** +- [ITD](https://gitea.arsenm.dev/Arsen6331/itd) (Linux) ## Development - - [InfiniTime Vision](doc/InfiniTimeVision.md) - - [Rough structure of the code](doc/code/Intro.md) - - [How to implement an application](doc/code/Apps.md) - - [Generate the fonts and symbols](src/displayapp/fonts/README.md) - - [Tips on designing an app UI](doc/ui_guidelines.md) - - [Bootloader, OTA and DFU](bootloader/README.md) - - [Versioning](doc/versioning.md) - - [Project branches](doc/branches.md) - - [Files included in the release notes](doc/filesInReleaseNotes.md) +- [InfiniTime Vision](doc/InfiniTimeVision.md) +- [Rough structure of the code](doc/code/Intro.md) +- [How to implement an application](doc/code/Apps.md) +- [Generate the fonts and symbols](src/displayapp/fonts/README.md) +- [Tips on designing an app UI](doc/ui_guidelines.md) +- [Bootloader, OTA and DFU](bootloader/README.md) +- [Versioning](doc/versioning.md) +- [Project branches](doc/branches.md) +- [Files included in the release notes](doc/filesInReleaseNotes.md) ### Contributing - - [How to contribute?](doc/contribute.md) - - [Coding conventions](doc/coding-convention.md) +- [How to contribute?](doc/contribute.md) +- [Coding conventions](doc/coding-convention.md) ### Build, flash and debug - - [InfiniTime simulator](https://github.com/InfiniTimeOrg/InfiniSim) - - [Build the project](doc/buildAndProgram.md) - - [Build the project with Docker](doc/buildWithDocker.md) - - [Build the project with VSCode](doc/buildWithVScode.md) - - [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md) - - [Flash the firmware using SWD interface](doc/SWD.md) - - [Flash the firmware using JLink](doc/jlink.md) - - [Flash the firmware using GDB](doc/gdb.md) - - [Stub using NRF52-DK](doc/PinetimeStubWithNrf52DK.md) +- [InfiniTime simulator](https://github.com/InfiniTimeOrg/InfiniSim) +- [Build the project](doc/buildAndProgram.md) +- [Build the project with Docker](doc/buildWithDocker.md) +- [Build the project with VSCode](doc/buildWithVScode.md) +- [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md) +- [Flash the firmware using SWD interface](doc/SWD.md) +- [Flash the firmware using JLink](doc/jlink.md) +- [Flash the firmware using GDB](doc/gdb.md) +- [Stub using NRF52-DK](doc/PinetimeStubWithNrf52DK.md) ### API - - [BLE implementation and API](doc/ble.md) +- [BLE implementation and API](doc/ble.md) ### Architecture and technical topics - - [Memory analysis](doc/MemoryAnalysis.md) +- [Memory analysis](doc/MemoryAnalysis.md) ## Licenses This project is released under the GNU General Public License version 3 or, at your option, any later version. It integrates the following projects: - - RTOS : **[FreeRTOS](https://freertos.org)** 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 - - Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license + +- RTOS : **[FreeRTOS](https://freertos.org)** 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 +- Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license ## Credits @@ -73,6 +74,6 @@ I’m not working alone on this project. First, many people create PR for this p Here are some people I would like to highlight: - - [Atc1441](https://github.com/atc1441/) : He works on an Arduino based firmware for the Pinetime and many other smartwatches based on similar hardware. He was of great help when I was implementing support for the BMA421 motion sensor and I²C driver. - - [Koen](https://github.com/bosmoment) : He’s working on a firmware based on RiotOS. He integrated similar libs as me : NimBLE, LittleVGL,… His help was invaluable too! - - [Lup Yuen Lee](https://github.com/lupyuen) : He is everywhere: he works on a Rust firmware, builds a MCUBoot based bootloader for the Pinetime, designs a Flutter based companion app for smartphones and writes a lot of articles about the Pinetime! +- [Atc1441](https://github.com/atc1441/) : He works on an Arduino based firmware for the Pinetime and many other smartwatches based on similar hardware. He was of great help when I was implementing support for the BMA421 motion sensor and I²C driver. +- [Koen](https://github.com/bosmoment) : He’s working on a firmware based on RiotOS. He integrated similar libs as me : NimBLE, LittleVGL,… His help was invaluable too! +- [Lup Yuen Lee](https://github.com/lupyuen) : He is everywhere: he works on a Rust firmware, builds a MCUBoot based bootloader for the Pinetime, designs a Flutter based companion app for smartphones and writes a lot of articles about the Pinetime! diff --git a/bootloader/README.md b/bootloader/README.md index 2cb6b8bf..51ddfa6b 100644 --- a/bootloader/README.md +++ b/bootloader/README.md @@ -1,4 +1,5 @@ # About this bootloader + The [bootloader](https://github.com/lupyuen/pinetime-rust-mynewt/tree/master/libs/pinetime_boot/src) is mostly developed by [Lup Yuen](https://github.com/lupyuen). It is based on [MCUBoot](https://www.mcuboot.com) and [Mynewt](https://mynewt.apache.org/). The goal of this project is to provide a common bootloader for multiple (all?) Pinetime projects. It allows to upgrade the current bootloader and even replace the current application by another one that supports the same bootloader. @@ -12,6 +13,7 @@ When it is run, this bootloader looks in the SPI flash memory if a new firmware As this bootloader does not provide any OTA capability, it is not able to actually download a new version of the application. Providing OTA functionality is thus the responsibility of the application firmware. # About MCUBoot + MCUBoot is run at boot time. In normal operation, it just jumps to the reset handler of the application firmware to run it. Once the application firmware is running, MCUBoot does not run at all. ![MCUBoot boot sequence diagram](../doc/bootloader/boot.png "MCUBoot boot sequence diagram") @@ -19,8 +21,9 @@ MCUBoot is run at boot time. In normal operation, it just jumps to the reset han But MCUBoot does much more than that : it can upgrade the firmware that is currently running by a new one, and it is also able to revert to the previous version of the firmware in case the new one does not run properly. To do this, it uses 2 memory 'slots' : - - **The primary slot** : it contains the current firmware, the one that will be executed by MCUBoot - - **The secondary slot** : it is used to store the upgraded version of the firmware, when available. + +- **The primary slot** : it contains the current firmware, the one that will be executed by MCUBoot +- **The secondary slot** : it is used to store the upgraded version of the firmware, when available. At boot time, MCUBoot detects that a new version of the firmware is available in the secondary slot and swaps them : the current version of the firmware is copied from the primary to the secondary slot and vice-versa. @@ -35,6 +38,7 @@ The next time MCUBoot will be run (after a MCU reset), MCUBoot will check if the Note than MCUBoot **does not** provide any means to download and store the new version of the firmware into the secondary slot. This must be implemented by the application firmware. # Degraded cases + This chapter describes degraded cases that are handled by our bootloader and those that are not supported. Case | Current bootloader | Solution @@ -50,72 +54,73 @@ New firmware does not run properly but sets the valid bit and refreshes the watc # Using the bootloader ## Bootloader graphic + The bootloader loads a graphic (Pinetime logo) from the SPI Flash memory. If this graphic is not loaded in the memory, the LCD will display garbage (the content of the SPI flash memory). The SPI Flash memory is not accessible via the SWD debugger. Use the firmware 'pinetime-graphics' to load the graphic into memory. All you have to do is build it and program it at address 0x00 : - - Build: -``` -$ make pinetime-graphics +- Build: + +```sh +make pinetime-graphics ``` - - Program (using OpenOCD for example) : +- Program (using OpenOCD for example) : + ``` program pinetime-graphics.bin 0 ``` - - Let it run for ~10s (it does nothing for 5 seconds, then write the logo into the SPI memory, then (slowly) displays it on the LCD). +- Let it run for ~10s (it does nothing for 5 seconds, then write the logo into the SPI memory, then (slowly) displays it on the LCD). ## Bootloader binary + The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4 It must be flash at address **0x00** in the internal flash memory. Using OpenOCD: -` -program bootloader-5.0.4.bin 0 -` +`program bootloader-5.0.4.bin 0` ## Application firmware image + Build the binary compatible with the booloader: -` -make pinetime-mcuboot-app -` +`make pinetime-mcuboot-app` The binary is located in */src/pinetime-mcuboot-app.bin*. It must me converted into a MCUBoot image using *imgtool.py* from [MCUBoot](https://github.com/mcu-tools/mcuboot/tree/master/scripts). Simply checkout the project and run the script /scripts/imgtool.py with the following command line: -` -imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header /src/pinetime-mcuboot-app.bin image.bin -` +```sh +imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header /src/pinetime-mcuboot-app.bin image.bin +``` The image must be then flashed at address **0x8000** in the internal flash memory. Using OpenOCD: -` -program image.bin 0x8000 -` +`program image.bin 0x8000` ## OTA and DFU + Pack the image into a .zip file for the NRF DFU protocol: -` +```sh adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application image.bin dfu.zip -` +``` Use NRFConnect or dfu.py (in /bootloader/ota-dfu-python) to upload the zip file to the device: -` +```sh sudo dfu.py -z /home/jf/nrf52/bootloader/dfu.zip -a --legacy -` +``` **Note** : dfu.py is a slightly modified version of [this repo](https://github.com/daniel-thompson/ota-dfu-python). ### Firmware validation + Once the OTA is done, InfiniTime will reset the watch to apply the update. When the watch reboots, the new firmware is running. One last step is needed to finalize the upgrade : the new firmware must be manually validated. If the watch resets while the image is not validated, the bootloader will automatically revert to the previous version of the firmware. diff --git a/bootloader/ota-dfu-python/README.md b/bootloader/ota-dfu-python/README.md index 3397db4d..c38e597a 100644 --- a/bootloader/ota-dfu-python/README.md +++ b/bootloader/ota-dfu-python/README.md @@ -1,13 +1,13 @@ # Python nRF5 OTA DFU Controller So... this is my fork of dingara's fork of astronomer80's fork of -foldedtoad's Python OTA DFU utility. +foldedtoad's Python OTA DFU utility. -My own contribution is little more than a brute force conversion to -python3. It is sparsely tested so there are likely to be a few +My own contribution is little more than a brute force conversion to +python3. It is sparsely tested so there are likely to be a few remaining bytes versus string bugs remaining in the places I didn't test . I used it primarily as part of -[wasp-os](https://github.com/daniel-thompson/wasp-os) as a way to +[wasp-os](https://github.com/daniel-thompson/wasp-os) as a way to deliver OTA updates to nRF52-based smart watches, especially the [Pine64 PineTime](https://www.pine64.org/pinetime/). @@ -17,24 +17,24 @@ This is a Python program that uses `gatttool` (provided with the Linux BlueZ dri ### Main features: -* Perform OTA DFU to an nRF5 peripheral without an external USB BLE dongle. -* Ability to detect if the peripheral is running in application mode or bootloader, and automatically switch if needed (buttonless). -* Support for both Legacy (SDK <= 11) and Secure (SDK >= 12) bootloader. +- Perform OTA DFU to an nRF5 peripheral without an external USB BLE dongle. +- Ability to detect if the peripheral is running in application mode or bootloader, and automatically switch if needed (buttonless). +- Support for both Legacy (SDK <= 11) and Secure (SDK >= 12) bootloader. Before using this utility the nRF5 peripheral device needs to be programmed with a DFU bootloader (see Nordic Semiconductor documentation/examples for instructions on that). ## Prerequisites -* BlueZ 5.4 or above -* Python 3.6 -* Python `pexpect` module (available via pip) -* Python `intelhex` module (available via pip) +- BlueZ 5.4 or above +- Python 3.6 +- Python `pexpect` module (available via pip) +- Python `intelhex` module (available via pip) ## Firmware Build Requirement -* Your nRF5 peripheral firmware build method will produce a firmware file ending with either `*.hex` or `*.bin`. -* Your nRF5 firmware build method will produce an Init file ending with `.dat`. -* The typical naming convention is `application.bin` and `application.dat`, but this utility will accept other names. +- Your nRF5 peripheral firmware build method will produce a firmware file ending with either `*.hex` or `*.bin`. +- Your nRF5 firmware build method will produce an Init file ending with `.dat`. +- The typical naming convention is `application.bin` and `application.dat`, but this utility will accept other names. ## Generating init files @@ -75,14 +75,13 @@ You can use the `hcitool lescan` to figure out the address of a DFU target, for CD:E3:4A:47:1C:E4 CD:E3:4A:47:1C:E4 (unknown) - ## Example Output ================================ == == == DFU Server == == == - ================================ + ================================ Sending file application.bin to CD:E3:4A:47:1C:E4 bin array size: 60788 @@ -105,14 +104,14 @@ You can use the `hcitool lescan` to figure out the address of a DFU target, for ## TODO: -* Implement link-loss procedure for Legacy Controller. -* Update example output in readme. -* Add makefile examples. -* More code cleanup. +- Implement link-loss procedure for Legacy Controller. +- Update example output in readme. +- Add makefile examples. +- More code cleanup. ## Info & References -* [Nordic Legacy DFU Service](http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v11.0.0/bledfu_transport_bleservice.html?cp=4_0_3_4_3_1_4_1) -* [Nordic Legacy DFU sequence diagrams](http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v11.0.0/bledfu_transport_bleprofile.html?cp=4_0_3_4_3_1_4_0_1_6#ota_profile_pkt_rcpt_notif) -* [Nordic Secure DFU bootloader](http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v12.2.0/lib_dfu_transport_ble.html?cp=4_0_1_3_5_2_2) -* [nrfutil](https://github.com/NordicSemiconductor/pc-nrfutil) +- [Nordic Legacy DFU Service](http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v11.0.0/bledfu_transport_bleservice.html?cp=4_0_3_4_3_1_4_1) +- [Nordic Legacy DFU sequence diagrams](http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v11.0.0/bledfu_transport_bleprofile.html?cp=4_0_3_4_3_1_4_0_1_6#ota_profile_pkt_rcpt_notif) +- [Nordic Secure DFU bootloader](http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v12.2.0/lib_dfu_transport_ble.html?cp=4_0_1_3_5_2_2) +- [nrfutil](https://github.com/NordicSemiconductor/pc-nrfutil) diff --git a/doc/BLEFS.md b/doc/BLEFS.md index 519d84a9..5374fd87 100644 --- a/doc/BLEFS.md +++ b/doc/BLEFS.md @@ -1,4 +1,5 @@ # BLE FS + --- The BLE FS protocol in InfiniTime is mostly Adafruit's BLE file transfer protocol, as described in [adafruit/Adafruit_CircuitPython_BLE_File_Transfer](https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer). There are some deviations, such as the status codes. These will be described later in the document. @@ -13,7 +14,7 @@ There are two relevant UUIDs in this protocol: the version characteristic, and t UUID: `adaf0100-4669-6c65-5472-616e73666572` -The version characteristic returns the version of the protocol to which the sender adheres. It returns a single unsigned 32-bit integer. The latest version at the time of writing this is 4. +The version characteristic returns the version of the protocol to which the sender adheres. It returns a single unsigned 32-bit integer. The latest version at the time of writing this is 4. ### Transfer @@ -125,7 +126,7 @@ Paths returned by this command are relative to the path given in the request - Unsigned 16-bit integer encoding the length of the file path. - File path: UTF-8 encoded string that is _not_ null terminated. -The response to this packet will be as follows. Responses will be sent until the final entry, which will have entry number == total entries +The response to this packet will be as follows. Responses will be sent until the final entry, which will have entry number == total entries - Command (single byte): `0x51` - Status (signed 8-bit integer) @@ -133,9 +134,9 @@ The response to this packet will be as follows. Responses will be sent until the - Unsigned 32-bit integer encoding the entry number - Unsigned 32-bit integer encoding the total amount of entries - Flags: unsigned 32-bit integer - + Bit 0: Set when entry is a directory - + Bits 1-7: Reserved -- Unsigned 64-bit integer encoding the unix timestamp of the modification time with nanosecond resolution + - Bit 0: Set when entry is a directory + - Bits 1-7: Reserved +- Unsigned 64-bit integer encoding the unix timestamp of the modification time with nanosecond resolution - Unsigned 32-bit integer encoding the size of the file - Path: UTF-8 encoded string that is _not_ null terminated. @@ -164,4 +165,4 @@ This section describes the differences between Adafruit's spec and InfiniTime's The status codes returned by InfiniTime are a signed 8-bit integer, rather than an unsigned one as described in the spec. -InfiniTime uses LittleFS error codes rather than the ones described in the spec. Those codes can be found in [lfs.h](https://github.com/littlefs-project/littlefs/blob/master/lfs.h#L70). \ No newline at end of file +InfiniTime uses LittleFS error codes rather than the ones described in the spec. Those codes can be found in [lfs.h](https://github.com/littlefs-project/littlefs/blob/master/lfs.h#L70). diff --git a/doc/MemoryAnalysis.md b/doc/MemoryAnalysis.md index 846d5079..0feb0f9e 100644 --- a/doc/MemoryAnalysis.md +++ b/doc/MemoryAnalysis.md @@ -1,25 +1,28 @@ # Memory analysis + The PineTime is equipped with the following memories: - - The internal RAM : **64KB** - - The internal Flash : **512KB** - - The external (SPI) Flash : **4MB** + +- 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/InfiniTimeOrg/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) - +- **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. @@ -41,62 +44,71 @@ Using this tool, you can compare the relative size of symbols. This can be helpf Also, as Linkermapviz is written in Python, you can easily modify and adapt it to your firmware or export data in another format. For example, [here it is modified to parse the contents of the MAP file and export it in a CSV file](https://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-842338620). This file could later be opened in LibreOffice Calc where sort/filter functionality could be used 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-10.3-2021.10/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-10.3-2021.10/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/ +- 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-10.3-2021.10/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-10.3-2021.10/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. In this case, unsurprisingly, fonts and graphics are the largest use of flash memory. ![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. +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/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-842338620 - - Analysis with Puncover : https://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-847311392 + +- Analysis with linkermapviz : https://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-842338620 +- Analysis with Puncover : https://github.com/InfiniTimeOrg/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] +uint8_t buffer[1024] int main() { - int a; + 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. + +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/InfiniTimeOrg/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 /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: ``` @@ -138,6 +150,7 @@ bne .L_fill ``` #### Dump RAM memory and check usage + Dumping the content of the ram is easy using JLink debugger and `nrfjprog`: ``` @@ -193,13 +206,16 @@ On the following dump, the maximum stack usage is 520 bytes (0xFFFF - 0xFDF8): 000fff0 7fc0 2000 4217 0001 3f0a 0001 0000 6100 ``` -#### Analysis +#### 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/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-851035070 + +- https://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-851035070 ### Heap + The heap is declared in the [linker script](https://github.com/InfiniTimeOrg/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: @@ -210,6 +226,7 @@ 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 exceeds 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: @@ -225,6 +242,7 @@ void* __wrap_malloc(size_t 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): @@ -239,12 +257,14 @@ Using this technique, I was able to trace all malloc calls at boot (boot -> digi - hr task = 304 #### Links - - https://github.com/InfiniTimeOrg/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/ + +- https://github.com/InfiniTimeOrg/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 to 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. @@ -262,11 +282,13 @@ 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/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-850890064 +### Links + +- https://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-850890064 ## 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 uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes...). @@ -276,7 +298,6 @@ The function `xPortGetFreeHeapSize()` returns the amount of memory available in NRF_LOG_INFO("Free heap : %d", xPortGetFreeHeapSize()); ``` - The function `uxTaskGetSystemState()` fetches some information about the running tasks like its name and the minimum amount of stack space that has remained for the task since the task was created: ``` @@ -285,5 +306,3 @@ auto nb = uxTaskGetSystemState(tasksStatus, 10, NULL); for (int i = 0; i < nb; i++) { NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark); ``` - - diff --git a/doc/MotionService.md b/doc/MotionService.md index 7cec3fba..429794ac 100644 --- a/doc/MotionService.md +++ b/doc/MotionService.md @@ -1,17 +1,23 @@ # Motion Service + ## Introduction + The motion service exposes step count and raw X/Y/Z motion value as READ and NOTIFY characteristics. ## Service + The service UUID is **00030000-78fc-48fe-8e23-433b3a1942d0** ## Characteristics + ### Step count (UUID 00030001-78fc-48fe-8e23-433b3a1942d0) + The current number of steps represented as a single `uint32_t` (4 bytes) value. ### Raw motion values (UUID 00030002-78fc-48fe-8e23-433b3a1942d0) + The current raw motion values. This is a 3 `int16_t` array: - - [0] : X - - [1] : Y - - [2] : Z +- [0] : X +- [1] : Y +- [2] : Z diff --git a/doc/NavigationService.md b/doc/NavigationService.md index d890cb26..e2a82b33 100644 --- a/doc/NavigationService.md +++ b/doc/NavigationService.md @@ -1,5 +1,7 @@ # Navigation Service + ## Introduction + The navigation ble service provides 4 characteristics to allow the watch to display navigation instructions from a companion application. This service is intended to be used when performing some outdoor activities, for example running or cycling. The 4 characteristics are: @@ -9,110 +11,117 @@ manDist (string) - Manouvre Distance, the distance to the upcoming change progress (uint8) - Percent complete of total route, value 0-100 ## Service + The service UUID is 00010000-78fc-48fe-8e23-433b3a1942d0 ## Characteristics + ## Flags (UUID 00010001-78fc-48fe-8e23-433b3a1942d0) + All included icons are from pure-maps, which provides the actual routing from the client. The icon names ultimately come from the mapbox project "direction-icons", See https://github.com/rinigus/pure-maps/tree/master/qml/icons/navigation See the end of this document for the full list of supported icon names. ## Narrative (UUID 00010002-78fc-48fe-8e23-433b3a1942d0) + This is a client supplied string describing the upcoming instruction such as "At the roundabout take the first exit". ## Man Dist (UUID 00010003-78fc-48fe-8e23-433b3a1942d0) + This is a short string describing the distance to the upcoming instruction such as "50 m". ## Progress (UUID 00010004-78fc-48fe-8e23-433b3a1942d0) + The percent complete in a uint8. The watch displays this as an overall progress in a progress bar. ## Full icon list -* arrive -* arrive-left -* arrive-right -* arrive-straight -* close -* continue -* continue-left -* continue-right -* continue-slight-left -* continue-slight-right -* continue-straight -* continue-uturn -* depart -* depart-left -* depart-right -* depart-straight -* end-of-road-left -* end-of-road-right -* ferry -* flag -* fork -* fork-left -* fork-right -* fork-slight-left -* fork-slight-right -* fork-straight -* invalid -* invalid-left -* invalid-right -* invalid-slight-left -* invalid-slight-right -* invalid-straight -* invalid-uturn -* merge-left -* merge-right -* merge-slight-left -* merge-slight-right -* merge-straight -* new-name-left -* new-name-right -* new-name-sharp-left -* new-name-sharp-right -* new-name-slight-left -* new-name-slight-right -* new-name-straight -* notification-left -* notification-right -* notification-sharp-left -* notification-sharp-right -* notification-slight-left -* notification-slight-right -* notification-straight -* off-ramp-left -* off-ramp-right -* off-ramp-sharp-left -* off-ramp-sharp-right -* off-ramp-slight-left -* off-ramp-slight-right -* off-ramp-straight -* on-ramp-left -* on-ramp-right -* on-ramp-sharp-left -* on-ramp-sharp-right -* on-ramp-slight-left -* on-ramp-slight-right -* on-ramp-straight -* rotary -* rotary-left -* rotary-right -* rotary-sharp-left -* rotary-sharp-right -* rotary-slight-left -* rotary-slight-right -* rotary-straight -* roundabout -* roundabout-left -* roundabout-right -* roundabout-sharp-left -* roundabout-sharp-right -* roundabout-slight-left -* roundabout-slight-right -* roundabout-straight -* turn-left -* turn-right -* turn-sharp-left -* turn-sharp-right -* turn-slight-left -* turn-slight-right -* turn-stright -* updown -* uturn + +- arrive +- arrive-left +- arrive-right +- arrive-straight +- close +- continue +- continue-left +- continue-right +- continue-slight-left +- continue-slight-right +- continue-straight +- continue-uturn +- depart +- depart-left +- depart-right +- depart-straight +- end-of-road-left +- end-of-road-right +- ferry +- flag +- fork +- fork-left +- fork-right +- fork-slight-left +- fork-slight-right +- fork-straight +- invalid +- invalid-left +- invalid-right +- invalid-slight-left +- invalid-slight-right +- invalid-straight +- invalid-uturn +- merge-left +- merge-right +- merge-slight-left +- merge-slight-right +- merge-straight +- new-name-left +- new-name-right +- new-name-sharp-left +- new-name-sharp-right +- new-name-slight-left +- new-name-slight-right +- new-name-straight +- notification-left +- notification-right +- notification-sharp-left +- notification-sharp-right +- notification-slight-left +- notification-slight-right +- notification-straight +- off-ramp-left +- off-ramp-right +- off-ramp-sharp-left +- off-ramp-sharp-right +- off-ramp-slight-left +- off-ramp-slight-right +- off-ramp-straight +- on-ramp-left +- on-ramp-right +- on-ramp-sharp-left +- on-ramp-sharp-right +- on-ramp-slight-left +- on-ramp-slight-right +- on-ramp-straight +- rotary +- rotary-left +- rotary-right +- rotary-sharp-left +- rotary-sharp-right +- rotary-slight-left +- rotary-slight-right +- rotary-straight +- roundabout +- roundabout-left +- roundabout-right +- roundabout-sharp-left +- roundabout-sharp-right +- roundabout-slight-left +- roundabout-slight-right +- roundabout-straight +- turn-left +- turn-right +- turn-sharp-left +- turn-sharp-right +- turn-slight-left +- turn-slight-right +- turn-stright +- updown +- uturn diff --git a/doc/PinetimeStubWithNrf52DK.md b/doc/PinetimeStubWithNrf52DK.md index afa8a74d..6fa0d545 100644 --- a/doc/PinetimeStubWithNrf52DK.md +++ b/doc/PinetimeStubWithNrf52DK.md @@ -1,33 +1,36 @@ # Build a stub for PineTime using NRF52-DK + [NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK) is the official development kit for the NRF52832 SoC from Nordic Semiconductor used in the PineTime. This development kit can be very useful for PineTime development: - * You can use its embedded JLink SWD programmer/debugger to program and debug your 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 as the PineTime. - + +- You can use its embedded JLink SWD programmer/debugger to program and debug your 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 as the PineTime. + This page is about the 2nd point. We will build a stub that will allow us to run the same code you can run on the PineTime. This will allow you to work more easily if you don't have a PineTime dev kit around, if you don't want to modify your dev kit for SWD programming, or if you want to use some feature from the NRF52-DK (like power measurement). -This stub only implements the display, the button and the BLE radio. The other features from the pintime are missing: - * heart rate sensor - * SPI flash - * touchpad - * accelerometer +This stub only implements the display, the button and the BLE radio. The other features from the pintime are missing: + +- heart rate sensor +- SPI flash +- touchpad +- accelerometer These devices could be added on this stub, but I do not have the parts to try them out for now. ![Pinetime stub](../images/pinetimestub1.jpg "PinetimeStub") - Here are the parts you need to build this simulator: - * [NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK) - * An ST7889 display (I bought [this one](https://www.aliexpress.com/item/32859772356.html?spm=a2g0s.9042311.0.0.1b774c4dSoc4Xz)) - * A push-button (the one I use comes from a previous project build around ESP8266 board Wemos D1 Mini). - * Dupont wires - + +- [NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK) +- An ST7889 display (I bought [this one](https://www.aliexpress.com/item/32859772356.html?spm=a2g0s.9042311.0.0.1b774c4dSoc4Xz)) +- A push-button (the one I use comes from a previous project build around ESP8266 board Wemos D1 Mini). +- Dupont wires + You just need to make the following connections: | NRF52-DK | ST7889 display | -| ---------|--------------- | +| -------- | -------------- | | VDD | VCC | | GND | GND | | P0.03 | SDA | @@ -35,11 +38,10 @@ You just need to make the following connections: | P0.02 | SCL | | P0.18 | DC | - -| NRF52-DK | Push Button | -| ---------|----------------------- | -| P0.13 | Button IN (D3 in my case) | -| GND | GND | +| NRF52-DK | Push Button | +| -------- | ------------------------- | +| P0.13 | Button IN (D3 in my case) | +| GND | GND | You also need to enable the I/O expander to disconnect pins from the buttons and LED on the NRF52-DK and leave them available on the pin headers: @@ -47,4 +49,4 @@ You also need to enable the I/O expander to disconnect pins from the buttons and | --------- | --------- | | 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 PineTime. \ No newline at end of file +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. diff --git a/doc/SPI-LCD-driver.md b/doc/SPI-LCD-driver.md index 29f3bbfa..758b4d69 100644 --- a/doc/SPI-LCD-driver.md +++ b/doc/SPI-LCD-driver.md @@ -1,16 +1,20 @@ # The SPI LCD driver + ## Introduction + The LCD controller that drives the display of the Pinetime is the [Sitronix ST7789V](https://wiki.pine64.org/images/5/54/ST7789V_v1.6.pdf). This controller is easy to integrate with an MCU thanks to its SPI interface, and has some interesting features like: + - an on-chip display data RAM that can store the whole framebuffer - partial screen update - hardware assisted vertical scrolling -- interrupt pin, allowing to drive the display with DMA and IRQ +- interrupt pin, allowing to drive the display with DMA and IRQ - ... When you want to write a device driver for a specific component, its datasheet is your holy bible. This document contains a lot of information about the chip, its specification, characteristics, features and functionalities. -Luckily for us, the datasheet of the ST7789 is great! It contains everything we need to write a nice driver for our beloved Pinetime. +Luckily for us, the datasheet of the ST7789 is great! It contains everything we need to write a nice driver for our beloved Pinetime. In this document, I'll try to explain the process I've followed to write a device driver for the LCD. There were multiple iterations: + - First, I tried to find the correct initialization sequence so that the controller is configured correctly according to the hardware configuration; - Then, I tried to display some pixels on the screen; - Next, I wanted to display squares, colors and text; @@ -20,12 +24,14 @@ In this document, I'll try to explain the process I've followed to write a devic I'll describe all these steps in the following chapters. ## The datasheet + As I said in the introduction, the datasheet will be your bedside book during your journey as a device driver designer. You'll read it from the beginning to the end once, twice, maybe ten times. Then, each time you'll want to do something new, you'll reopen the file and search for that specific paragraph or diagram than explains how the controller works so that you can figure out how to use it. The schematic of your board (the Pinetime schematics in this case) will also be very important, as you'll need to know how the LCD controller is physically connected to the MCU. How to read the datasheet? I recommend to read it from the beginning to the end (no joke) at least once. You certainly do not need to read everything in details, but it's good to know what information is available and where in the document. It'll be very useful during the development phase. You'll want to read some part with more attention : + - Data color coding in 4-Line Serial Interface : how to send the pixel to be display to the controller - Display Data Ram : how is the memory organized - Power On/Off sequence @@ -33,7 +39,6 @@ You'll want to read some part with more attention : ## One Pixel at a time - ## Bulk transfers ## DMA @@ -41,6 +46,7 @@ You'll want to read some part with more attention : ## IRQ ## Bare metal integration + Integration customisée dans la lib GFX que j'ai écrite -## Integration with LittleVGL \ No newline at end of file +## Integration with LittleVGL diff --git a/doc/SWD.md b/doc/SWD.md index 86e27517..7fc86958 100644 --- a/doc/SWD.md +++ b/doc/SWD.md @@ -1,4 +1,5 @@ # How to flash InfiniTime using the SWD interface + Download the files **bootloader.bin**, **image-x.y.z.bin** and **pinetime-graphics-x.y.z.bin** from the release page: ![Image file](gettingStarted/imageFile.png) @@ -8,7 +9,7 @@ Using your SWD tool, flash **pinetime-graphics-x.y.z.bin** at offset **0x0000**. Then, using your SWD tool, flash these file at the following offsets: - - bootloader.bin : **0x0000** - - image-x.y.z.bin : **0x8000** +- bootloader.bin : **0x0000** +- image-x.y.z.bin : **0x8000** Reset and voilà, you're running InfiniTime on your PineTime! diff --git a/doc/ble.md b/doc/ble.md index bd9e66f5..9c170418 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -1,5 +1,7 @@ -# Bluetooth Low-Energy : +# Bluetooth Low-Energy : + ## Introduction + This page describes the BLE implementation and API built in this firmware. --- @@ -38,6 +40,7 @@ This page describes the BLE implementation and API built in this firmware. --- ## BLE Connection + When starting, the firmware starts 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, Raspberry Pi, smartphone, etc.) which receives this advertising packet can request a connection to the device. This connection procedure allows the 2 devices to negotiate communication parameters, security keys, etc. @@ -58,7 +61,8 @@ The documentation for BLE FS can be found here: --- ## BLE UUIDs -When possible, InfiniTime tries to implement BLE services defined by the BLE specification. + +When possible, InfiniTime tries to implement BLE services defined by the BLE specification. When the service does not exist in the BLE specification, InfiniTime implements custom services. Custom services are identified by a UUID, as are all BLE services. Here is how to define the UUID of custom services in InfiniTime: @@ -70,34 +74,38 @@ When the service does not exist in the BLE specification, InfiniTime implements The following custom services are implemented in InfiniTime: - - Since InfiniTime 0.8: - * Music Service : 00000000-78fc-48fe-8e23-433b3a1942d0 - +- Since InfiniTime 0.8: - - Since InfiniTime 0.11: - * [Navigation Service](NavigationService.md) : 00010000-78fc-48fe-8e23-433b3a1942d0 + - Music Service : `00000000-78fc-48fe-8e23-433b3a1942d0` +- Since InfiniTime 0.11: + + - [Navigation Service](NavigationService.md) : `00010000-78fc-48fe-8e23-433b3a1942d0` - Since InfiniTime 0.13 - * Call characteristic (extension to the Alert Notification Service): 00020001-78fc-48fe-8e23-433b3a1942d0 + - Call characteristic (extension to the Alert Notification Service): `00020001-78fc-48fe-8e23-433b3a1942d0` - Since InfiniTime 1.7: - * [Motion Service](MotionService.md): 00030000-78fc-48fe-8e23-433b3a1942d0 + - [Motion Service](MotionService.md): `00030000-78fc-48fe-8e23-433b3a1942d0` - Since InfiniTime 1.8: - * [Weather Service](/src/components/ble/weather/WeatherService.h): 00040000-78fc-48fe-8e23-433b3a1942d0 + + - [Weather Service](/src/components/ble/weather/WeatherService.h): `00040000-78fc-48fe-8e23-433b3a1942d0` --- ## BLE services + [List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/) ### CTS + [Current Time Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.current_time.xml) ### ANS + [Alert Notification Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.alert_notification.xml) ![ANS sequence diagram](./ble/ans_sequence.png "ANS sequence diagram") diff --git a/doc/branches.md b/doc/branches.md index c12c764a..cf98684b 100644 --- a/doc/branches.md +++ b/doc/branches.md @@ -1,12 +1,14 @@ # Branches + The branching model of this project is based on the workflow named [Git flow](https://nvie.com/posts/a-successful-git-branching-model/). The project is based on 2 main branches: - - **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. + +- **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. 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 successfully reviewed and accepted. To release a new version of the application, when develop is considered stable, a **release** branch is created from **develop**. This can be considered as a *release candidate* branch. When everything is OK, this release branch is merged into **master** and the release is generated (a tag is applied to git, the release note is finalized, binaries are built,...) from **master**. -Git flow also supports the creation of **hotfix** branches when a bug is discovered in a released version. The **hotfix** branch is created from **master** and will be used only to implement a fix to this bug. Multiple hotfix branches can be created for the same release if multiple bugs are discovered. \ No newline at end of file +Git flow also supports the creation of **hotfix** branches when a bug is discovered in a released version. The **hotfix** branch is created from **master** and will be used only to implement a fix to this bug. Multiple hotfix branches can be created for the same release if multiple bugs are discovered. diff --git a/doc/buildAndProgram.md b/doc/buildAndProgram.md index 510f053d..58d0f72e 100644 --- a/doc/buildAndProgram.md +++ b/doc/buildAndProgram.md @@ -1,24 +1,29 @@ # Build + ## Dependencies + To build this project, you'll need: - - A cross-compiler : [ARM-GCC (10.3-2021.10)](https://developer.arm.com/downloads/-/gnu-rm) - - The NRF52 SDK 15.3.0 : [nRF-SDK v15.3.0](https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip) - - The Python 3 modules `cbor`, `intelhex`, `click` and `cryptography` modules for the `mcuboot` tool (see [requirements.txt](../tools/mcuboot/requirements.txt)) - - To keep the system clean, you can install python modules into a python virtual environment (`venv`) - ```sh - python -m venv .venv - source .venv/bin/activate - python -m pip install wheel - python -m pip install -r tools/mcuboot/requirements.txt - ``` - - A reasonably recent version of CMake (I use 3.16.5) - - lv_font_conv, to generate the font .c files - - see [lv_font_conv](https://github.com/lvgl/lv_font_conv#install-the-script) - - install npm (commonly done via the package manager, ensure node's version is at least 12) - - install lv_font_conv: `npm install lv_font_conv` + +- A cross-compiler : [ARM-GCC (10.3-2021.10)](https://developer.arm.com/downloads/-/gnu-rm) +- The NRF52 SDK 15.3.0 : [nRF-SDK v15.3.0](https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip) +- The Python 3 modules `cbor`, `intelhex`, `click` and `cryptography` modules for the `mcuboot` tool (see [requirements.txt](../tools/mcuboot/requirements.txt)) + - To keep the system clean, you can install python modules into a python virtual environment (`venv`) + ```sh + python -m venv .venv + source .venv/bin/activate + python -m pip install wheel + python -m pip install -r tools/mcuboot/requirements.txt + ``` +- A reasonably recent version of CMake (I use 3.16.5) +- lv_font_conv, to generate the font .c files + - see [lv_font_conv](https://github.com/lvgl/lv_font_conv#install-the-script) + - install npm (commonly done via the package manager, ensure node's version is at least 12) + - install lv_font_conv: `npm install lv_font_conv` ## Build steps + ### Clone the repo + ``` git clone https://github.com/InfiniTimeOrg/InfiniTime.git cd InfiniTime @@ -26,7 +31,9 @@ git submodule update --init mkdir build cd build ``` + ### Project generation using CMake + CMake configures the project according to variables you specify the command line. The variables are: Variable | Description | Example| @@ -41,31 +48,36 @@ CMake configures the project according to variables you specify the command line **BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1` **TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY-TFK5, MOY-TIN5, MOY-TON5, MOY-UNK`|`-DTARGET_DEVICE=PINETIME` (Default) -####(**) 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/InfiniTimeOrg/InfiniTime/releases) new versions of InfiniTime. The *Debug* mode disables all optimizations, which makes the code easier to debug. However, the binary size will likely be too big to fit in the internal flash memory. If you want to build and debug a *Debug* binary, you'll need to disable some parts of the code. For example, the icons for the **Navigation** app use a lot of memory space. You can comment the content of `m_iconMap` in the [Navigation](https://github.com/InfiniTimeOrg/InfiniTime/blob/develop/src/displayapp/screens/Navigation.h#L148) application to free some memory. -####(**) Note about **BUILD_DFU**: +#### (\*\*) Note about **BUILD_DFU** DFU files are the files you'll need to install your build of InfiniTime using OTA (over-the-air) mechanism. To generate the DFU file, the Python tool [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil) is needed on your system. Check that this tool is properly installed before enabling this option. #### CMake command line for JLink + ``` cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../ ``` #### CMake command line for GDB Client (Black Magic Probe) + ``` cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_GDB_CLIENT=1 -DGDB_CLIENT_BIN_PATH=... -DGDB_CLIENT_TARGET_REMOTE=... ../ ``` #### CMake command line for OpenOCD + ``` cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DGDB_CLIENT_BIN_PATH=[optional] ../ ``` ### Build the project + During the project generation, CMake created the following targets: + - **FLASH_ERASE** : mass erase the flash memory of the NRF52. - **FLASH_pinetime-app** : flash the firmware into the NRF52. - **pinetime-app** : build the standalone (without bootloader support) version of the firmware. @@ -78,58 +90,69 @@ During the project generation, CMake created the following targets: If you just want to build the project and run it on the Pinetime, using *pinetime-app* is recommended. See [this page](../bootloader/README.md) for more info about bootloader support. Build: + ``` make -j pinetime-app ``` List of files generated: Binary files are generated into the folder `src`: - - **pinetime-app.bin, .hex and .out** : standalone firmware in bin, hex and out formats. - - **pinetime-app.map** : map file - - **pinetime-mcuboot-app.bin, .hex and .out** : firmware with bootloader support in bin, hex and out formats. - - **pinetime-mcuboot-app.map** : map file - - **pinetime-mcuboot-app-image** : MCUBoot image of the firmware - - **pinetime-mcuboot-ap-dfu** : DFU file of the firmware -The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader** +- **pinetime-app.bin, .hex and .out** : standalone firmware in bin, hex and out formats. +- **pinetime-app.map** : map file +- **pinetime-mcuboot-app.bin, .hex and .out** : firmware with bootloader support in bin, hex and out formats. +- **pinetime-mcuboot-app.map** : map file +- **pinetime-mcuboot-app-image** : MCUBoot image of the firmware +- **pinetime-mcuboot-ap-dfu** : DFU file of the firmware + +The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader** - ### Program and run #### Using CMake targets + These target have been configured during the project generation by CMake according to the parameters you provided to the command line. Mass erase: + ``` make FLASH_ERASE -``` +``` Flash the application: + ``` make FLASH_pinetime-app ``` ### How to generate files needed by the factory + These files are needed by the Pine64 factory to flash InfiniTime as the default firmware on the PineTimes. Two files are needed: an **HEX (.hex)** file that contains the content of the internal flash memory (bootloader + InfiniTime) and a **binary (.bin)** file that contains the content of the external flash memory (recovery firmware). #### merged-internal.hex + First, convert the bootloader to hex: - ``` - /bin/arm-none-eabi-objcopy -I binary -O ihex ./bootloader.bin ./bootloader.hex - ``` + +``` +/bin/arm-none-eabi-objcopy -I binary -O ihex ./bootloader.bin ./bootloader.hex +``` + where `bootloader.bin` is the [last stable version](https://github.com/JF002/pinetime-mcuboot-bootloader/releases) of the [bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader). Then, convert the MCUBoot image of InfiniTime: + ``` /bin/arm-none-eabi-objcopy -I binary -O ihex --change-addresses 0x8000 ./pinetime-mcuboot-app-image-1.6.0.bin ./pinetime-mcuboot-app-image-1.6.0.hex ``` + where `pinetime-mcuboot-app-image-1.6.0.bin` is [the bin of the last MCUBoot image](https://github.com/InfiniTimeOrg/InfiniTime/releases) of [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime). Pay attention to the parameter `--change-addresses 0x8000`. It's needed to ensure the image will be flashed at the offset expected by the bootloader (0x8000). Finally, merge them together with **mergehex**: + ``` /opt/mergehex/mergehex -m ./bootloader.hex ./pinetime-mcuboot-app-image-1.6.0.hex -o merged-internal.hex ``` @@ -137,4 +160,5 @@ Finally, merge them together with **mergehex**: This file must be flashed at offset **0x00** of the internal memory of the NRF52832. #### spinor.bin + This file is the MCUBoot image of the last stable version of the recovery firmware. It must be flashed at offset **0x00** of the external SPINOR flash memory. diff --git a/doc/buildWithDocker.md b/doc/buildWithDocker.md index 1bea8380..20bf85d4 100644 --- a/doc/buildWithDocker.md +++ b/doc/buildWithDocker.md @@ -1,24 +1,24 @@ # Build the project using Docker -A [Docker image (Dockerfile)](../docker) containing all the build environment is available for X86_64 and AMD64 architectures. +A [Docker image (Dockerfile)](../docker) containing all the build environment is available for X86_64 and AMD64 architectures. These images make the build of the firmware and the generation of the DFU file for OTA quite easy, as well as preventing clashes with any other toolchains or development environments you may have installed. Based on Ubuntu 22.04 with the following build dependencies: -* ARM GCC Toolchain -* nRF SDK -* MCUBoot -* adafruit-nrfutil -* lv_font_conv +- ARM GCC Toolchain +- nRF SDK +- MCUBoot +- adafruit-nrfutil +- lv_font_conv ## Run a container to build the project -The `infinitime-build` image contains all the dependencies you need. +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. Before continuing, make sure you first build the image as indicated in the [Build the image](#build-the-image) section, or check the [Using the image from Docker Hub](#using-the-image-from-docker-hub) section if you prefer to use a pre-made image. -This example will build the firmware, generate the MCUBoot image and generate the DFU file. +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 **/build/output**: ```bash @@ -26,12 +26,12 @@ cd # e.g. cd ./work/Pinetime docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime-build ``` -By default, the container runs as `root`, which is not convenient as all the files generated by the build will also belong to `root`. -The parameter `--user` overrides that default behavior. -The command above will run as your current user. +By default, the container runs as `root`, which is not convenient as all the files generated by the build will also belong to `root`. +The parameter `--user` overrides that default behavior. +The command above will run as your current user. -If you only want to build a single CMake target, you can pass it in as the first parameter to the build script. -This means calling the script explicitly as it will override the `CMD`. +If you only want to build a single CMake target, you can pass it in as the first parameter to the build script. +This means calling the script explicitly as it will override the `CMD`. Here's an example for `pinetime-app`: ```bash @@ -50,9 +50,9 @@ docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime/infin The default `latest` tag *should* automatically identify the correct image architecture, but if for some reason Docker does not, you can specify it manually: -* For AMD64 (x86_64) systems: `docker pull --platform linux/amd64 infinitime/infinitime-build` +- For AMD64 (x86_64) systems: `docker pull --platform linux/amd64 infinitime/infinitime-build` -* For ARM64v8 (ARM64/aarch64) systems: `docker pull --platform linux/arm64 infinitime/infinitime-build` +- For ARM64v8 (ARM64/aarch64) systems: `docker pull --platform linux/arm64 infinitime/infinitime-build` ## Build the image @@ -62,4 +62,4 @@ The following commands must be run from the root of the project. This operation ```bash docker build -t infinitime-build ./docker -``` \ No newline at end of file +``` diff --git a/doc/buildWithVScode.md b/doc/buildWithVScode.md index 48b8923f..8e136511 100644 --- a/doc/buildWithVScode.md +++ b/doc/buildWithVScode.md @@ -26,27 +26,22 @@ We leverage a few VS Code extensions for ease of development. Cortex-Debug is only required for interactive debugging using VS Codes built in GDB support. - - ## VS Code/Docker DevContainer The .devcontainer folder contains the configuration and scripts for using a Docker dev container for building InfiniTime -Using the [Remote-Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension is recommended. It will handle configuring the Docker virtual machine and setting everything up. +Using the [Remote-Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension is recommended. It will handle configuring the Docker virtual machine and setting everything up. -More documentation is available in the [readme in .devcontainer](.devcontainer/readme.md) +More documentation is available in the [readme in .devcontainer](.devcontainer/readme.md) ### DevContainer on Ubuntu -To use the DevContainer configuration on Ubuntu based systems two changes need to be made: - -1. Modify the file ``.devcontainer/devcontainer.json`` and add the argument ``"--net=host"`` to the ``"runArgs"`` parameter making the line look like this: -`` "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined", "--net=host"], -`` -2. Modify the file ``.vscode/launch.json`` and change the argument of ``"gdbTarget"`` to ``"127.0.0.1:3333"``, making the line look like: -``"gdbTarget": "127.0.0.1:3333",`` -3. To start debugging launch openocd on your host system with the appropriate configuration, for example with a stlink-v2 the command is: -``openocd -f interface/stlink.cfg -f target/nrf52.cfg``. This launches openocd with the default ports ``3333``, ``4444`` and ``6666``. -4. In VsCode go to the Debug pane on the left of the screen and select the configuration ``Debug - Openocd docker Remote`` and hit the play button on the left. - +To use the DevContainer configuration on Ubuntu based systems two changes need to be made: +1. Modify the file `.devcontainer/devcontainer.json` and add the argument `"--net=host"` to the `"runArgs"` parameter making the line look like this: + `"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined", "--net=host"],` +2. Modify the file `.vscode/launch.json` and change the argument of `"gdbTarget"` to `"127.0.0.1:3333"`, making the line look like: + `"gdbTarget": "127.0.0.1:3333",` +3. To start debugging launch openocd on your host system with the appropriate configuration, for example with a stlink-v2 the command is: + `openocd -f interface/stlink.cfg -f target/nrf52.cfg`. This launches openocd with the default ports `3333`, `4444` and `6666`. +4. In VsCode go to the Debug pane on the left of the screen and select the configuration `Debug - Openocd docker Remote` and hit the play button on the left. diff --git a/doc/code/Apps.md b/doc/code/Apps.md index f067b58b..c756a8b4 100644 --- a/doc/code/Apps.md +++ b/doc/code/Apps.md @@ -1,5 +1,7 @@ # Apps + This page will teach you: + - what screens and apps are in InfiniTime - how to implement your own app @@ -14,6 +16,7 @@ Apps are responsible for everything drawn on the screen when they are running. By default, apps only do something (as in a function is executed) when they are created or when a touch event is detected. ## Interface + Every app class has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen. Other parameters should be references to controllers that the app needs. @@ -24,10 +27,12 @@ it does not need to override any of these functions, as LVGL can also handle tou If you have any doubts, you can always look at how the other apps function for reference. ### Continuous updating + If your app needs to be updated continuously, you can do so by overriding the `Refresh()` function in your class and calling `lv_task_create` inside the constructor. An example call could look like this: + ```cpp taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); ``` @@ -37,9 +42,11 @@ Remember to delete the task again using `lv_task_del`. The function `RefreshTaskCallback` is inherited from `Screen` and just calls your `Refresh` function. ## Creating your own app + A minimal app could look like this: MyApp.h: + ```cpp #pragma once @@ -60,6 +67,7 @@ namespace Pinetime { ``` MyApp.cpp: + ```cpp #include "displayapp/screens/MyApp.h" #include "displayapp/DisplayApp.h" @@ -77,6 +85,7 @@ MyApp::~MyApp() { lv_obj_clean(lv_scr_act()); } ``` + Both of these files should be in [displayapp/screens/](/src/displayapp/screens/) or [displayapp/screens/settings/](/src/displayapp/screens/settings/) if it's a setting app. diff --git a/doc/code/Intro.md b/doc/code/Intro.md index 23b3ade1..34adc5fa 100644 --- a/doc/code/Intro.md +++ b/doc/code/Intro.md @@ -1,7 +1,9 @@ # Introduction to the code + This page is meant to guide you through the source code, so you can find the relevant files for what you're working on. ## FreeRTOS + Infinitime is based on FreeRTOS, a real-time operating system. FreeRTOS provides several quality of life abstractions (for example easy software timers) and most importantly supports multiple tasks. @@ -12,6 +14,7 @@ The task scheduler is responsible for giving every task enough cpu time. As there is only one core on the SoC of the PineTime, real concurrency is impossible and the scheduler has to swap tasks in and out to emulate it. ### Tasks + Tasks are created by calling `xTaskCreate` and passing a function with the signature `void functionName(void*)`. For more info on task creation see the [FreeRTOS Documentation](https://www.freertos.org/a00125.html). In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. @@ -29,6 +32,7 @@ it will need instead of just typing in a large-ish number. You can use `configMINIMAL_STACK_SIZE` which is currently set to 120 words. ## Controllers + Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. Some of them interface with drivers, others are the driver for the resource. The resources provided don't have to be hardware-based. @@ -37,7 +41,9 @@ Some controllers can be passed by reference to apps that need access to the reso They reside in [components/](/src/components/) inside their own subfolder. ## Apps + For more detail see the [Apps page](./Apps.md) ## Bluetooth + Header files with short documentation for the functions are inside [libs/mynewt-nimble/nimble/host/include/host/](/src/libs/mynewt-nimble/nimble/host/include/host/). diff --git a/doc/coding-convention.md b/doc/coding-convention.md index 7198fa4f..9d218ba2 100644 --- a/doc/coding-convention.md +++ b/doc/coding-convention.md @@ -1,41 +1,29 @@ -# Coding convention +# Coding style -## Language +## Use these tools to find and fix issues. -The language of this project is **C++**, and all new code must be written in C++. (Modern) C++ provides a lot of useful tools and functionalities that are beneficial for embedded software development like `constexpr`, `template` and anything that provides zero-cost abstraction. +- Use `clang-format` to format the code. +- Use `clang-tidy` to check the code for other potential issues. -C code is accepted if it comes from another library like FreeRTOS, NimBLE, LVGL or the NRF-SDK. +## Follow these guidelines while writing code. -## Coding style - -The most important rule to follow is to try to keep the code as easy to read and maintain as possible. - -Using an autoformatter is highly recommended, but make sure it's configured properly. - -There are preconfigured autoformatter rules for: - - * CLion (IntelliJ) in [.idea/codeStyles/Project.xml](/.idea/codeStyles/Project.xml) - * `clang-format` - -Also use `clang-tidy` to check the code for other issues. - -If there are no preconfigured rules for your IDE, you can use one of the existing ones to configure your IDE. - - - **Indentation** : 2 spaces, no tabulation - - **Opening brace** at the end of the line - - **Naming** : Choose self-describing variable name - - **class** : PascalCase - - **namespace** : PascalCase - - **variable** : camelCase, **no** prefix/suffix ('_', 'm_',...) for class members - - **Include guard** : `#pragma once` (no `#ifdef __MODULE__ / #define __MODULE__ / #endif`) - - **Includes** : - - files from the project : `#include "relative/path/to/the/file.h"` - - external files and std : `#include ` - - use includes relative to included directories like `src`, not relative to the current file. Don't do: `#include "../file.h"` - - Only use [primary spellings for operators and tokens](https://en.cppreference.com/w/cpp/language/operator_alternative) - - Use auto sparingly. Don't use auto for [fundamental/built-in types](https://en.cppreference.com/w/cpp/language/types) and [fixed width integer types](https://en.cppreference.com/w/cpp/types/integer), except when initializing with a cast to avoid duplicating the type name. - - Examples: - - `auto* app = static_cast(instance);` - - `auto number = static_cast(variable);` - - `uint8_t returnValue = MyFunction();` - - Use nullptr instead of NULL +- **Indentation** : 2 spaces, no tabulation +- **Opening brace** at the end of the line +- **Naming** : Choose self-describing variable name + - **class** : PascalCase + - **namespace** : PascalCase + - **variable** : camelCase, **no** prefix/suffix (`_`, `m_`,...) for class members +- **Include guard** : `#pragma once` (no `#ifdef __MODULE__ / #define __MODULE__ / #endif`) +- **Includes** : + - files from the project : `#include "relative/path/to/the/file.h"` + - external files and std : `#include ` + - use includes relative to included directories like `src`, not relative to the current file. Don't do: `#include "../file.h"` +- Only use [primary spellings for operators and tokens](https://en.cppreference.com/w/cpp/language/operator_alternative) +- Use `auto` sparingly. Don't use `auto` for [fundamental/built-in types](https://en.cppreference.com/w/cpp/language/types) and [fixed width integer types](https://en.cppreference.com/w/cpp/types/integer), except when initializing with a cast to avoid duplicating the type name. + ```c++ + // Examples: + auto* app = static_cast(instance); + auto number = static_cast(variable); + uint8_t returnValue = MyFunction(); + ``` +- Use `nullptr` instead of `NULL` diff --git a/doc/contribute.md b/doc/contribute.md index f2a4aeaa..f4e241a1 100644 --- a/doc/contribute.md +++ b/doc/contribute.md @@ -1,61 +1,19 @@ # How to contribute? -## Report bugs +- [Report bugs](https://github.com/InfiniTimeOrg/InfiniTime/issues/new?assignees=&labels=bug&template=bug-report.yaml) +- Write and improve documentation + - Documentation might be incomplete, or not clear enough, and it is always possible to improve it with better wording, pictures, videos,... + - As the documentation is part of the source code, you can submit changes to the documentation by creating a pull request (see below) +- Fix bugs, add functionalities and improve the code + - See *How to create a pull request* below -Have you found a bug in the firmware? [Create an issue on Github](https://github.com/InfiniTimeOrg/InfiniTime/issues) explaining the bug, how to reproduce it, the version of the firmware you use... +## How to create a pull request? -## Write and improve documentation - -Documentation might be incomplete, or not clear enough, and it is always possible to improve it with better wording, pictures, photo, video,... - -As the documentation is part of the source code, you can submit your improvements to the documentation by submitting a pull request (see below). - -## Fix bugs, add functionalities and improve the code - -You want to fix a bug, add a cool new functionality or improve the code? See *How to submit a pull request below*. - -# How to submit a pull request? - -## TL;DR - - - Create a branch from develop - - Work on a single subject in this branch. Create multiple branches/pulls-requests if you want to work on multiple subjects (bugs, features,...) - - Test your modifications on the actual hardware - - Check your code against the [coding conventions](/doc/coding-convention.md) and [clang-format](../.clang-format) and [clang-tidy](../.clang-tidy) - - Clean your code and remove files that are not needed - - Write documentation related to your new feature if applicable - - Create a pull request and write a great description about it: what does your PR do, why, how,... Add pictures and video if possible - - Wait for someone to review your PR and take part in the review process - - Your PR will eventually be merged :) +1. Fork the project, create a [feature branch](branches.md) from develop and give it a short name that explains the topic of your changes + - Any feature branch should focus on one topic only +2. Make changes in this branch + - Write code that satisfies the [coding conventions](/doc/coding-convention.md) + - Test your changes on a PineTime or the [InfiniTime simulator](https://github.com/InfiniTimeOrg/InfiniSim) +3. Create a pull request, participate in the discussion and make more changes to the feature branch if necessary Your contributions are more than welcome! - -If you want to fix a bug, add functionality or improve the code, you'll first need to create a branch from the **develop** branch (see [this page about the branching model](./branches.md)). This branch is called a feature branch, and you should choose a name that explains what you are working on (ex: "add-doc-about-contributions"). In this branch, **focus on only one topic, bug or feature**. For example, if you created this branch to work on the UI of a specific application, do not commit modifications about the SPI driver. If you want to work on multiple topics, create one branch for each topic. - -When your feature branch is ready, **make sure it actually works** and **do not forget to write documentation** about it if it's relevant. - -**Creating a pull request containing modifications that haven't been tested is strongly discouraged.** If for any reason you cannot test your modifications, but want to publish them anyway, **please mention it in the description**. This way, other contributors might be willing to test it and provide feedback about your code. - -Before submitting a PR, check your code against the [coding conventions](/doc/coding-convention.md). This project also provides [clang-format](../.clang-format) and [clang-tidy](../.clang-tidy) configuration files. You should use them to ensure correct formatting of your code. - -Don't forget to check the files you are going to commit and remove those which aren't necessary (config files from your IDE, for example). Remove old comments, commented code,... - -Then, you can submit a pull request for review. Try to **describe your pull request as much as possible**: what did you do in this branch, how does it work, how it is designed, are there any limitations,... This will help the contributors to understand and review your code easily. You can add pictures and video to the description so that contributors will have a quick overview of your work. - -Other contributors can post comments about the pull request, maybe ask for more info or adjustments in the code. - -Once the pull request is reviewed and accepted, it'll be merged into **develop** and will be released in the next version of the firmware. - -## Why all these rules? - -Reviewing pull requests is a **very time consuming task**. Everything you do to make reviewing easier will **get your PR merged faster**. - -Reviewers will first look at the **description**. If it's easy to understand what the PR does, why the modification is needed or interesting and how it's done, a good part of the work is already done : we understand the PR and its context. - -Reviewing **a few files that were modified for a single purpose** is a lot easier than reviewing 30 files modified for many reasons (bug fix, UI improvements, typos in doc,...), even if all the changes make sense. Also, it's possible that we agree on some modification but not on another, so we won't be able to merge the PR because of the changes that are not accepted. - -The code base should be kept as consistent as possible. If the formatting of your code is not consistent with the rest of the code base, we'll ask you to review it. - -Lastly the changes are tested. If it doesn't work out of the box, we'll ask you to review your code and to ensure that it works as expected. - -It's totally normal for a PR to need some more work even after it was created, that's why we review them. But every round trip takes time, so it's good practice to try to reduce them as much as possible by following those simple rules. diff --git a/doc/filesInReleaseNotes.md b/doc/filesInReleaseNotes.md index 6a5873c0..4cd0d3c5 100644 --- a/doc/filesInReleaseNotes.md +++ b/doc/filesInReleaseNotes.md @@ -1,4 +1,5 @@ # Using the releases + For each new *stable* version of IniniTime, a [release note](https://github.com/InfiniTimeOrg/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. @@ -8,49 +9,52 @@ This page describes the files from the release notes and how to use them. ## Files included in the release notes ### Standalone firmware + 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 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. - - **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.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. +- **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,... + **This firmware must be flashed at address 0x00 in the main flash memory** ### 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). - - **bootloader.hex** : Firmware in Intel HEX file format. - - **This firmware must be flashed at address 0x00 in the main flash memory** +- **bootloader.hex** : Firmware in Intel HEX file format. +**This firmware must be flashed at address 0x00 in the main flash memory** + +### Graphics firmware -### Graphics firmware This firmware is a small utility firmware that writes the boot graphic in the external SPI flash memory. You need it if you want to use the [bootloader](../bootloader/README.md). - - **pinetime-graphics.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB. - - **pinetime-graphics.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-graphics.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed. - - **pinetime-graphics.map** : Map file containing all the symbols, addresses in memory,... - +- **pinetime-graphics.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB. +- **pinetime-graphics.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-graphics.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed. +- **pinetime-graphics.map** : Map file containing all the symbols, addresses in memory,... + **This firmware must be flashed at address 0x00 in the main flash memory** ### Firmware with bootloader + 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 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. +- **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: - - **pinetime-mcuboot-app.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB. - - **pinetime-mcuboot-app.hex** : Firmware in Intel HEX file format. - - **pinetime-mcuboot-app.bin** : Firmware in binary format. - - **pinetime-mcuboot-app.map** : Map file containing all the symbols, addresses in memory,... +- **pinetime-mcuboot-app.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB. +- **pinetime-mcuboot-app.hex** : Firmware in Intel HEX file format. +- **pinetime-mcuboot-app.bin** : Firmware in binary format. +- **pinetime-mcuboot-app.map** : Map file containing all the symbols, addresses in memory,... ### OTA (Update the firmware Over-The-Air) + Once the bootloader and application firmware are running, it is possible to update the current firmware or even replace it with another firmware **that uses the same bootloader based on MCUBoot**. **NOTE :** Use this file **only** if you programmed our [MCUBoot-based bootloader](../bootloader/README.md). This file is not compatible with other bootloaders like the one that is based on the closed source NRF SoftDevice ! - - **pinetime-app-dfu.zip** : This is the file you must provide toNRFConnect to update the firmware over BLE. - +- **pinetime-app-dfu.zip** : This is the file you must provide toNRFConnect to update the firmware over BLE. diff --git a/doc/gdb.md b/doc/gdb.md index eb9b73f1..209ba652 100644 --- a/doc/gdb.md +++ b/doc/gdb.md @@ -12,6 +12,7 @@ run ``` Example : + ``` $ /home/jf/nrf52/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gdb @@ -45,4 +46,3 @@ Loading section .sec7, size 0xdf08 lma 0x40000 Start address 0x0, load size 314200 Transfer rate: 45 KB/sec, 969 bytes/write. ``` - diff --git a/doc/gettingStarted/about-software.md b/doc/gettingStarted/about-software.md index e935d938..54bcaf14 100644 --- a/doc/gettingStarted/about-software.md +++ b/doc/gettingStarted/about-software.md @@ -6,9 +6,9 @@ A **firmware** is software running on the embedded hardware of a device. InfiniTime has three distinct firmwares: - - **[InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime)** is the operating system. - - **[The bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader)** is responsible for safely applying firmware updates and runs before booting into InfiniTime. - - **The recovery firmware** is a special *application firmware* than can be loaded by the bootloader on user request. This firmware can be useful in case of serious issue, when the main application firmware cannot perform an OTA update correctly. +- **[InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime)** is the operating system. +- **[The bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader)** is responsible for safely applying firmware updates and runs before booting into InfiniTime. +- **The recovery firmware** is a special *application firmware* than can be loaded by the bootloader on user request. This firmware can be useful in case of serious issue, when the main application firmware cannot perform an OTA update correctly. **OTA** (**O**ver **T**he **A**ir) refers to updating of the firmware over BLE (**B**luetooth **L**ow **E**nergy). This is a functionality that allows the user to update the firmware on their device wirelessly. @@ -20,7 +20,7 @@ Most of the time, the bootloader just runs without your intervention (updating a However, you can use the bootloader to rollback to the previous firmware, or load the recovery firmware using the push button: - - Press and hold 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 current one. - - Press and hold 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. +- Press and hold 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 current one. +- Press and hold 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). diff --git a/doc/gettingStarted/gettingStarted-1.0.md b/doc/gettingStarted/gettingStarted-1.0.md index 29433921..1586de56 100644 --- a/doc/gettingStarted/gettingStarted-1.0.md +++ b/doc/gettingStarted/gettingStarted-1.0.md @@ -12,9 +12,9 @@ By default, InfiniTime starts on the digital watchface. It'll probably display t You can sync the time using companion apps. - - Gadgetbridge automatically synchronizes the time when you connect it to your watch. More information on Gadgetbridge [here](/doc/gettingStarted/ota-gadgetbridge.md) - - [Sync the time with NRFConnect](/doc/gettingStarted/time-nrfconnect.md) - - Sync the time with your browser https://hubmartin.github.io/WebBLEWatch/ +- Gadgetbridge automatically synchronizes the time when you connect it to your watch. More information on Gadgetbridge [here](/doc/gettingStarted/ota-gadgetbridge.md) +- [Sync the time with NRFConnect](/doc/gettingStarted/time-nrfconnect.md) +- Sync the time with your browser https://hubmartin.github.io/WebBLEWatch/ You can also set the time in the settings without a companion app. (version >1.7.0) @@ -30,9 +30,9 @@ The indicator on the top left is visible if you have unread notifications On the top right there are status icons - - The battery icon shows roughly how much charge is remaining - - The Bluetooth icon is visible when the watch is connected to a companion app - - A plug icon is shown when the watch is plugged into a charger. +- The battery icon shows roughly how much charge is remaining +- The Bluetooth icon is visible when the watch is connected to a companion app +- A plug icon is shown when the watch is plugged into a charger. On the bottom left you can see your heart rate if you have the measurement enabled in the heart rate app. @@ -45,13 +45,13 @@ On the bottom right you can see how many steps you have taken today. ![Quick actions](ui/quicksettings.jpg) ![Settings](ui/settings.jpg) - - Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu. - - Swipe **down** to display the notification panel. Notification sent by your companion app will be displayed here. - - Swipe **right** to display the Quick Actions menu. This menu allows you to - - Set the brightness of the display - - Start the **flashlight** app - - Enable/disable notifications (Do Not Disturb mode) - - Enter the **settings** menu - - Swipe up and down to see all options - - Click the button to go back a screen. - - You can hold the button for a short time to return to the watch face. (version >1.7.0) +- Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu. +- Swipe **down** to display the notification panel. Notification sent by your companion app will be displayed here. +- Swipe **right** to display the Quick Actions menu. This menu allows you to + - Set the brightness of the display + - Start the **flashlight** app + - Enable/disable notifications (Do Not Disturb mode) + - Enter the **settings** menu + - Swipe up and down to see all options +- Click the button to go back a screen. +- You can hold the button for a short time to return to the watch face. (version >1.7.0) diff --git a/doc/gettingStarted/ota-nrfconnect.md b/doc/gettingStarted/ota-nrfconnect.md index 800bd6bc..292e7fc4 100644 --- a/doc/gettingStarted/ota-nrfconnect.md +++ b/doc/gettingStarted/ota-nrfconnect.md @@ -19,4 +19,5 @@ Don't forget to **validate** your firmware. In the InfiniTime go to the settings ![NRFConnect 3](nrfconnect3.jpg) # Demo + [This video](https://seafile.codingfield.com/f/a52b69683a05472a90c7/) shows how to use NRFConnect to update the firmware running on the Pinetime. diff --git a/doc/gettingStarted/updating-software.md b/doc/gettingStarted/updating-software.md index 7a05073a..d302607e 100644 --- a/doc/gettingStarted/updating-software.md +++ b/doc/gettingStarted/updating-software.md @@ -26,8 +26,8 @@ To update the firmware, you need to download the DFU of the firmware version tha We have prepared instructions for flashing InfiniTime with Gadgetbridge and NRFConnect. - - [Updating with Gadgetbridge](/doc/gettingStarted/ota-gadgetbridge.md) - - [Updating with NRFConnect](/doc/gettingStarted/ota-nrfconnect.md) +- [Updating with Gadgetbridge](/doc/gettingStarted/ota-gadgetbridge.md) +- [Updating with NRFConnect](/doc/gettingStarted/ota-nrfconnect.md) ## Firmware validation @@ -35,7 +35,7 @@ Firmware updates must be manually validated. If the firmware isn't validated and You can validate your updated firmware on InfiniTime >= 1.0 by following this simple procedure: - - From the watchface, swipe **right** to display the *quick settings menu* - - Open settings by tapping the cogwheel on the bottom right - - Swipe up until you find an entry named **Firmware** and tap on it - - If the firmware is not validated yet, you can either validate the running firmware, or reset and revert to the previous firmware version +- From the watchface, swipe **right** to display the *quick settings menu* +- Open settings by tapping the cogwheel on the bottom right +- Swipe up until you find an entry named **Firmware** and tap on it +- If the firmware is not validated yet, you can either validate the running firmware, or reset and revert to the previous firmware version diff --git a/doc/jlink.md b/doc/jlink.md index eebcac49..b69c4c46 100644 --- a/doc/jlink.md +++ b/doc/jlink.md @@ -1,6 +1,7 @@ # Flashing the firmware with JLink Start JLinkExe: + ``` $ /opt/SEGGER/JLink/JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1 SEGGER J-Link Commander V6.70d (Compiled Apr 16 2020 17:59:37) @@ -43,6 +44,7 @@ J-Link> ``` Use the command loadfile to program the .hex file: + ``` J-Link>loadfile pinetime-app.hex Downloading file [pinetime-app.hex]... @@ -56,6 +58,7 @@ O.K. ``` Then reset (r) and start (g) the CPU: + ``` J-Link>r Reset delay: 0 ms @@ -66,17 +69,18 @@ J-Link>g ``` # JLink RTT + 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) - - Start JLinkExe +- Program the MCU with the code (see above) +- Start JLinkExe ``` $ JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1 ``` Start JLinkRTTClient + ``` $ JLinkRTTClient ``` - diff --git a/images/infinitime-logo-small.jpg b/doc/logo/infinitime-logo-small.jpg similarity index 100% rename from images/infinitime-logo-small.jpg rename to doc/logo/infinitime-logo-small.jpg diff --git a/images/infinitime-logo.jpg b/doc/logo/infinitime-logo.jpg similarity index 100% rename from images/infinitime-logo.jpg rename to doc/logo/infinitime-logo.jpg diff --git a/doc/openOCD.md b/doc/openOCD.md index df24b30b..34f62425 100644 --- a/doc/openOCD.md +++ b/doc/openOCD.md @@ -1,4 +1,5 @@ # OpenOCD and STLink + OpenOCD (**Open O**n **C**hip **D**ebugger) is an open source tool that interfaces with many SWD/JTAG debugger to provide debugging and *in-system* programming for embedded target devices. OpenOCD supports the **NRF52** (the CPU of the PineTime) and the **STLinkV2**, a cheap SWD debugger. @@ -6,9 +7,10 @@ OpenOCD supports the **NRF52** (the CPU of the PineTime) and the **STLinkV2**, a OpenOCD works on X86 computers, ARM/ARM64 computers, and SBCs (like the RaspberryPi and Pine64 Pinebook Pro)! ## Installation + We will build OpenOCD from sources, as packages from Linux distributions are most of the time outdated and do not support the NRF52 properly. - - Fetch the sources from GIT, and build and install it: +- Fetch the sources from GIT, and build and install it: ``` git clone https://git.code.sf.net/p/openocd/code openocd-code @@ -21,13 +23,14 @@ make -j 4 sudo make install ``` - - Configure UDEV to allow OpenOCD to open the interface to your STLinkV2: +- Configure UDEV to allow OpenOCD to open the interface to your STLinkV2: + ``` sudo cp contrib/60-openocd.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules ``` - - You can now plug your STLinkV2 into a USB port and run OpenOCD to see if it's working correctly: +- You can now plug your STLinkV2 into a USB port and run OpenOCD to see if it's working correctly: ``` $ openocd -f interface/stlink.cfg -f target/nrf52.cfg @@ -50,11 +53,14 @@ Info : STLINK V2J34S7 (API v2) VID:PID 0483:3748 Info : Target voltage: 3.294340 Error: init mode failed (unable to connect to the target) ``` + Ok, OpenOCD is running and it detects my STLinkV2. The last error shows that I've not connected the STLinkV2 to the PineTime. ## Configuration files + OpenOCD is configured using configuration files. First, we need a common configuration file for the project : **openocd-stlink.ocd**: + ``` source [find interface/stlink.cfg] @@ -63,11 +69,13 @@ gdb_breakpoint_override hard source [find target/nrf52.cfg] ``` + This file specifies to OpenOCD which debugger and target it will be connected to. Then, we use various *user files* to use OpenOCD to flash InfiniTime binary files. This files flashes the bootloader and the application firmware : **flash_bootloader_app.ocd**: + ``` init @@ -78,6 +86,7 @@ reset ``` And this one flashes the graphics flasher (it writes the bootloader graphics into the SPI NOR flash memory) : **flash_graphics.ocd**: + ``` init @@ -87,19 +96,23 @@ reset ``` ## Examples + ### Flash bootloader and application + ``` openocd -f ./openocd-stlink.ocd -f ./flash_bootloader_app.ocd ``` ### Flash graphics flasher + ``` openocd -f ./openocd-stlink.ocd -f ./flash_graphics.ocd ``` ## Connect the STLinkV2 to the PineTime + Here is an example using the pogo pins: -![SWD pinout](../images/swd_pinout.jpg) -![Pogo pins](../images/pogopins.jpg) +![SWD pinout](openOCD/swd_pinout.jpg) +![Pogo pins](openOCD/pogopins.jpg) You can find more information about the SWD wiring [on the wiki](https://wiki.pine64.org/index.php?title=PineTime_devkit_wiring). diff --git a/images/pogopins.jpg b/doc/openOCD/pogopins.jpg similarity index 100% rename from images/pogopins.jpg rename to doc/openOCD/pogopins.jpg diff --git a/images/swd_pinout.jpg b/doc/openOCD/swd_pinout.jpg similarity index 100% rename from images/swd_pinout.jpg rename to doc/openOCD/swd_pinout.jpg diff --git a/doc/ui_guidelines.md b/doc/ui_guidelines.md index 296497af..1a93fada 100644 --- a/doc/ui_guidelines.md +++ b/doc/ui_guidelines.md @@ -5,9 +5,9 @@ - Buttons should generally be on the bottom edge - Make interactable objects **big** - When using a page indicator, leave 8px for it on the right side - - It is acceptable to leave 8px on the left side as well to center the content + - It is acceptable to leave 8px on the left side as well to center the content - Top bar takes at least 20px + padding - - Top bar right icons move 8px to the left when using a page indicator + - Top bar right icons move 8px to the left when using a page indicator - A black background helps to hide the screen border, allowing the UI to look less cramped when utilizing the entire display area. - A switch should be twice as wide as it is tall. diff --git a/doc/versioning.md b/doc/versioning.md index 2fa36ab9..28ca085c 100644 --- a/doc/versioning.md +++ b/doc/versioning.md @@ -1,6 +1,7 @@ # Versioning + The versioning of this project is based on [Semantic versioning](https://semver.org/): - - The **patch** is incremented when a bug is fixed on a **released** version (most of the time using a **hotfix** branch). - - The **minor** is incremented when a new version with new features is released. It corresponds to a merge of **develop** into **master**. - - The **major** should be incremented when a breaking change is made to the application. We still have to define what is a breaking change in the context of this project. +- The **patch** is incremented when a bug is fixed on a **released** version (most of the time using a **hotfix** branch). +- The **minor** is incremented when a new version with new features is released. It corresponds to a merge of **develop** into **master**. +- The **major** should be incremented when a breaking change is made to the application. We still have to define what is a breaking change in the context of this project. diff --git a/docker/README.md b/docker/README.md index 71ad258a..5b897b2b 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,2 +1,2 @@ Docker images and build script for building the project using Docker. -See [this page for more info](../doc/buildWithDocker.md). \ No newline at end of file +See [this page for more info](../doc/buildWithDocker.md). diff --git a/images/0.14.0/collage1.png b/images/0.14.0/collage1.png deleted file mode 100644 index fd5f27ba..00000000 Binary files a/images/0.14.0/collage1.png and /dev/null differ diff --git a/images/0.14.0/collage2.png b/images/0.14.0/collage2.png deleted file mode 100644 index 293147dd..00000000 Binary files a/images/0.14.0/collage2.png and /dev/null differ diff --git a/images/0.7.0/montage.jpg b/images/0.7.0/montage.jpg deleted file mode 100644 index 81716cc9..00000000 Binary files a/images/0.7.0/montage.jpg and /dev/null differ diff --git a/images/1.0.0/collage.png b/images/1.0.0/collage.png deleted file mode 100644 index b9ffaa88..00000000 Binary files a/images/1.0.0/collage.png and /dev/null differ diff --git a/images/pinetime.jpg b/images/pinetime.jpg deleted file mode 100644 index 7f729fa5..00000000 Binary files a/images/pinetime.jpg and /dev/null differ diff --git a/images/pinetimestub1.jpg b/images/pinetimestub1.jpg deleted file mode 100644 index 3ebc34e5..00000000 Binary files a/images/pinetimestub1.jpg and /dev/null differ diff --git a/images/v0.3.0.png b/images/v0.3.0.png deleted file mode 100644 index 4a2bcdd2..00000000 Binary files a/images/v0.3.0.png and /dev/null differ diff --git a/images/v0.3.0/Gauge.jpg b/images/v0.3.0/Gauge.jpg deleted file mode 100644 index aa12a01c..00000000 Binary files a/images/v0.3.0/Gauge.jpg and /dev/null differ diff --git a/images/v0.3.0/Helloworld.jpg b/images/v0.3.0/Helloworld.jpg deleted file mode 100644 index b3b09009..00000000 Binary files a/images/v0.3.0/Helloworld.jpg and /dev/null differ diff --git a/images/v0.3.0/Meter.jpg b/images/v0.3.0/Meter.jpg deleted file mode 100644 index a2c28e14..00000000 Binary files a/images/v0.3.0/Meter.jpg and /dev/null differ diff --git a/images/v0.3.0/Version.jpg b/images/v0.3.0/Version.jpg deleted file mode 100644 index d126d741..00000000 Binary files a/images/v0.3.0/Version.jpg and /dev/null differ diff --git a/images/v0.3.0/Watchface.jpg b/images/v0.3.0/Watchface.jpg deleted file mode 100644 index 1cfaa976..00000000 Binary files a/images/v0.3.0/Watchface.jpg and /dev/null differ diff --git a/images/v0.3.0/Watchface2.jpg b/images/v0.3.0/Watchface2.jpg deleted file mode 100644 index 63783051..00000000 Binary files a/images/v0.3.0/Watchface2.jpg and /dev/null differ diff --git a/images/v0.3.0/menu.jpg b/images/v0.3.0/menu.jpg deleted file mode 100644 index bf94a2e5..00000000 Binary files a/images/v0.3.0/menu.jpg and /dev/null differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6971a56..db4a8e2a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -477,7 +477,7 @@ list(APPEND SOURCE_FILES FreeRTOS/port_cmsis.c displayapp/LittleVgl.cpp - displayapp/lv_pinetime_theme.c + displayapp/InfiniTimeTheme.cpp systemtask/SystemTask.cpp systemtask/SystemMonitor.cpp @@ -663,7 +663,7 @@ set(INCLUDE_FILES libs/date/include/date/ptz.h libs/date/include/date/tz_private.h displayapp/LittleVgl.h - displayapp/lv_pinetime_theme.h + displayapp/InfiniTimeTheme.h systemtask/SystemTask.h systemtask/SystemMonitor.h displayapp/screens/Symbols.h @@ -810,6 +810,7 @@ elseif(TARGET_DEVICE STREQUAL "MOY-UNK") # P8b mirrored add_definitions(-DCLOCK_CONFIG_LF_SRC=0) # RC add_definitions(-DMYNEWT_VAL_BLE_LL_SCA=500) add_definitions(-DCLOCK_CONFIG_LF_CAL_ENABLED=1) + add_definitions(-DDRIVER_DISPLAY_MIRROR) else() message(FATAL_ERROR "Invalid TARGET_DEVICE") endif() diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index 3b113ead..478408f6 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -9,7 +9,7 @@ namespace Pinetime { class Settings { public: enum class ClockType : uint8_t { H24, H12 }; - enum class Notification : uint8_t { ON, OFF }; + enum class Notification : uint8_t { On, Off, Sleep }; enum class ChimesOption : uint8_t { None, Hours, HalfHours }; enum class WakeUpMode : uint8_t { SingleTap = 0, @@ -219,7 +219,7 @@ namespace Pinetime { uint32_t screenTimeOut = 15000; ClockType clockType = ClockType::H24; - Notification notificationStatus = Notification::ON; + Notification notificationStatus = Notification::On; uint8_t clockFace = 0; ChimesOption chimesOption = ChimesOption::None; diff --git a/src/displayapp/lv_pinetime_theme.c b/src/displayapp/InfiniTimeTheme.cpp similarity index 94% rename from src/displayapp/lv_pinetime_theme.c rename to src/displayapp/InfiniTimeTheme.cpp index f712004a..4290d87f 100644 --- a/src/displayapp/lv_pinetime_theme.c +++ b/src/displayapp/InfiniTimeTheme.cpp @@ -1,4 +1,4 @@ -#include "displayapp/lv_pinetime_theme.h" +#include "displayapp/InfiniTimeTheme.h" static void theme_apply(lv_obj_t* obj, lv_theme_style_t name); @@ -31,13 +31,14 @@ static lv_style_t style_cb_bullet; static bool inited; static void style_init_reset(lv_style_t* style) { - if (inited) + if (inited) { lv_style_reset(style); - else + } else { lv_style_init(style); + } } -static void basic_init(void) { +static void basic_init() { style_init_reset(&style_bg); lv_style_set_bg_opa(&style_bg, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_color(&style_bg, LV_STATE_DEFAULT, LV_COLOR_BLACK); @@ -46,7 +47,7 @@ static void basic_init(void) { style_init_reset(&style_box); lv_style_set_bg_opa(&style_box, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_radius(&style_box, LV_STATE_DEFAULT, 10); - lv_style_set_value_color(&style_box, LV_STATE_DEFAULT, IT_COLOR_BG); + lv_style_set_value_color(&style_box, LV_STATE_DEFAULT, Colors::bg); lv_style_set_value_font(&style_box, LV_STATE_DEFAULT, theme.font_normal); style_init_reset(&style_label_white); @@ -56,9 +57,9 @@ static void basic_init(void) { style_init_reset(&style_btn); lv_style_set_radius(&style_btn, LV_STATE_DEFAULT, 10); lv_style_set_bg_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_COVER); - lv_style_set_bg_color(&style_btn, LV_STATE_DEFAULT, IT_COLOR_BG); - lv_style_set_bg_color(&style_btn, LV_STATE_CHECKED, IT_COLOR_SEL); - lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED, IT_COLOR_BG_DARK); + lv_style_set_bg_color(&style_btn, LV_STATE_DEFAULT, Colors::bg); + lv_style_set_bg_color(&style_btn, LV_STATE_CHECKED, Colors::highlight); + lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED, Colors::bgDark); lv_style_set_border_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE); lv_style_set_border_width(&style_btn, LV_STATE_DEFAULT, 0); @@ -96,9 +97,9 @@ static void basic_init(void) { style_init_reset(&style_list_btn); lv_style_set_bg_opa(&style_list_btn, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_color(&style_list_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE); - lv_style_set_text_color(&style_list_btn, LV_STATE_DEFAULT, IT_COLOR_BG); + lv_style_set_text_color(&style_list_btn, LV_STATE_DEFAULT, Colors::bg); lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED, LV_COLOR_WHITE); - lv_style_set_image_recolor(&style_list_btn, LV_STATE_DEFAULT, IT_COLOR_BG); + lv_style_set_image_recolor(&style_list_btn, LV_STATE_DEFAULT, Colors::bg); lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED, LV_COLOR_WHITE); lv_style_set_pad_left(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25); lv_style_set_pad_right(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25); @@ -110,21 +111,21 @@ static void basic_init(void) { // Causes lag unfortunately, so we'll have to live with the selected item overflowing the corner // lv_style_set_clip_corner(&style_ddlist_list, LV_STATE_DEFAULT, true); lv_style_set_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25); - lv_style_set_bg_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_style_set_bg_color(&style_ddlist_list, LV_STATE_DEFAULT, Colors::lightGray); lv_style_set_pad_all(&style_ddlist_list, LV_STATE_DEFAULT, 20); style_init_reset(&style_ddlist_selected); lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER); - lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, IT_COLOR_BG); + lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, Colors::bg); style_init_reset(&style_sw_bg); lv_style_set_bg_opa(&style_sw_bg, LV_STATE_DEFAULT, LV_OPA_COVER); - lv_style_set_bg_color(&style_sw_bg, LV_STATE_DEFAULT, IT_COLOR_BG); + lv_style_set_bg_color(&style_sw_bg, LV_STATE_DEFAULT, Colors::bg); lv_style_set_radius(&style_sw_bg, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); style_init_reset(&style_sw_indic); lv_style_set_bg_opa(&style_sw_indic, LV_STATE_DEFAULT, LV_OPA_COVER); - lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, IT_COLOR_SEL); + lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, Colors::highlight); style_init_reset(&style_sw_knob); lv_style_set_bg_opa(&style_sw_knob, LV_STATE_DEFAULT, LV_OPA_COVER); @@ -152,12 +153,12 @@ static void basic_init(void) { lv_style_set_pad_right(&style_slider_knob, LV_STATE_PRESSED, 14); style_init_reset(&style_arc_indic); - lv_style_set_line_color(&style_arc_indic, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_style_set_line_color(&style_arc_indic, LV_STATE_DEFAULT, Colors::lightGray); lv_style_set_line_width(&style_arc_indic, LV_STATE_DEFAULT, LV_DPX(25)); lv_style_set_line_rounded(&style_arc_indic, LV_STATE_DEFAULT, true); style_init_reset(&style_arc_bg); - lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, IT_COLOR_BG); + lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, Colors::bg); 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_pad_all(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(5)); @@ -215,8 +216,8 @@ static void basic_init(void) { lv_style_set_transition_prop_6(&style_cb_bg, LV_STATE_DEFAULT, LV_STYLE_OUTLINE_OPA); lv_style_reset(&style_cb_bullet); - lv_style_set_outline_opa(&style_cb_bullet, LV_STATE_FOCUSED, LV_OPA_TRANSP); lv_style_set_radius(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(4)); + lv_style_set_pattern_image(&style_cb_bullet, LV_STATE_CHECKED, LV_SYMBOL_OK); lv_style_set_pattern_recolor(&style_cb_bullet, LV_STATE_CHECKED, LV_COLOR_WHITE); lv_style_set_pad_left(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8)); lv_style_set_pad_right(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8)); @@ -262,9 +263,7 @@ lv_theme_t* lv_pinetime_theme_init(lv_color_t color_primary, static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) { lv_style_list_t* list; - /*To avoid warnings*/ - uint32_t name_int = (uint32_t) name; - switch (name_int) { + switch (name) { case LV_THEME_NONE: break; @@ -402,7 +401,7 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) { _lv_style_list_add_style(list, &style_scrollbar); break; - case LV_THEME_TABLE: + case LV_THEME_TABLE: { list = lv_obj_get_style_list(obj, LV_TABLE_PART_BG); _lv_style_list_add_style(list, &style_bg); @@ -415,7 +414,7 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) { _lv_style_list_add_style(list, &style_table_cell); _lv_style_list_add_style(list, &style_label_white); } - break; + } break; case LV_THEME_LINEMETER: list = lv_obj_get_style_list(obj, LV_LINEMETER_PART_MAIN); diff --git a/src/displayapp/lv_pinetime_theme.h b/src/displayapp/InfiniTimeTheme.h similarity index 65% rename from src/displayapp/lv_pinetime_theme.h rename to src/displayapp/InfiniTimeTheme.h index b68b7380..5709b007 100644 --- a/src/displayapp/lv_pinetime_theme.h +++ b/src/displayapp/InfiniTimeTheme.h @@ -1,14 +1,17 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include -#define IT_COLOR_BG LV_COLOR_MAKE(0x5d, 0x69, 0x7e) -#define IT_COLOR_BG_DARK LV_COLOR_MAKE(0x18, 0x18, 0x18) -#define IT_COLOR_SEL LV_COLOR_MAKE(0x0, 0xb0, 0x0) +namespace Colors { + static constexpr lv_color_t orange = LV_COLOR_MAKE(0xff, 0xb0, 0x0); + static constexpr lv_color_t green = LV_COLOR_MAKE(0x0, 0xb0, 0x0); + static constexpr lv_color_t lightGray = LV_COLOR_MAKE(0xb0, 0xb0, 0xb0); + + static constexpr lv_color_t bg = LV_COLOR_MAKE(0x5d, 0x69, 0x7e); + static constexpr lv_color_t bgAlt = LV_COLOR_MAKE(0x38, 0x38, 0x38); + static constexpr lv_color_t bgDark = LV_COLOR_MAKE(0x18, 0x18, 0x18); + static constexpr lv_color_t highlight = green; +}; /** * Initialize the default @@ -28,6 +31,3 @@ lv_theme_t* lv_pinetime_theme_init(lv_color_t color_primary, const lv_font_t* font_normal, const lv_font_t* font_subtitle, const lv_font_t* font_title); -#ifdef __cplusplus -} /* extern "C" */ -#endif diff --git a/src/displayapp/LittleVgl.cpp b/src/displayapp/LittleVgl.cpp index 64c99261..d5f31848 100644 --- a/src/displayapp/LittleVgl.cpp +++ b/src/displayapp/LittleVgl.cpp @@ -1,5 +1,5 @@ #include "displayapp/LittleVgl.h" -#include "displayapp/lv_pinetime_theme.h" +#include "displayapp/InfiniTimeTheme.h" #include #include diff --git a/src/displayapp/fonts/README.md b/src/displayapp/fonts/README.md index 9d5ec282..b2669a78 100644 --- a/src/displayapp/fonts/README.md +++ b/src/displayapp/fonts/README.md @@ -1,19 +1,19 @@ # Fonts -* [Jetbrains Mono](https://www.jetbrains.com/lp/mono/) -* [Font Awesome](https://fontawesome.com/v5/cheatsheet/free/solid) -* [Open Sans Light](https://fonts.google.com/specimen/Open+Sans) -* [Material Symbols](https://fonts.google.com/icons) +- [Jetbrains Mono](https://www.jetbrains.com/lp/mono/) +- [Font Awesome](https://fontawesome.com/v5/cheatsheet/free/solid) +- [Open Sans Light](https://fonts.google.com/specimen/Open+Sans) +- [Material Symbols](https://fonts.google.com/icons) ### How to add new symbols: -* Browse the cheat sheets and pick symbols - * [Font Awesome](https://fontawesome.com/v5/cheatsheet/free/solid) - * [Material Symbols](https://fonts.google.com/icons) -* For each symbol, add its hex code (0xf641 for the 'Ad' icon, for example) to the *Range* list in the `fonts.json` file -* Convert this hex value into a UTF-8 code +- Browse the cheat sheets and pick symbols + - [Font Awesome](https://fontawesome.com/v5/cheatsheet/free/solid) + - [Material Symbols](https://fonts.google.com/icons) +- For each symbol, add its hex code (0xf641 for the 'Ad' icon, for example) to the *Range* list in the `fonts.json` file +- Convert this hex value into a UTF-8 code using [this site](http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=f185&mode=hex) -* Define the new symbols in `src/displayapp/screens/Symbols.h`: +- Define the new symbols in `src/displayapp/screens/Symbols.h`: ``` static constexpr const char* newSymbol = "\xEF\x86\x85"; @@ -23,13 +23,13 @@ static constexpr const char* newSymbol = "\xEF\x86\x85"; inside `fonts`, there is a dictionary of fonts, and for each font there is: -* sources - list of file,range(,symbols) wanted (as a dictionary of those) -* bpp - bits per pixel. -* size - size. -* patches - list of extra "patches" to run: a path to a .patch file. (may be relative) -* compress - optional. default disabled. add `"compress": true` to enable + +- sources - list of file,range(,symbols) wanted (as a dictionary of those) +- bpp - bits per pixel. +- size - size. +- patches - list of extra "patches" to run: a path to a .patch file. (may be relative) +- compress - optional. default disabled. add `"compress": true` to enable ### Navigation font `navigtion.ttf` is created with the web app [icomoon](https://icomoon.io/app) by importing the svg files from `src/displayapp/icons/navigation/unique` and generating the font. `lv_font_navi_80.json` is a project file for the site, which you can import to add or remove icons. - diff --git a/src/displayapp/fonts/fonts.json b/src/displayapp/fonts/fonts.json index 9f228fb6..006b8849 100644 --- a/src/displayapp/fonts/fonts.json +++ b/src/displayapp/fonts/fonts.json @@ -7,7 +7,7 @@ }, { "file": "FontAwesome5-Solid+Brands+Regular.woff", - "range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf201, 0xf06e, 0xf015" + "range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf201, 0xf06e, 0xf015, 0xf00c" } ], "bpp": 1, @@ -58,7 +58,7 @@ "sources": [ { "file": "material-design-icons/MaterialIcons-Regular.ttf", - "range": "0xf00b, 0xe3aa-0xe3ac, 0xe7f6-0xe7f7, 0xe8b8" + "range": "0xf00b, 0xe3aa-0xe3ac, 0xe7f6-0xe7f7, 0xe8b8, 0xef44" } ], "bpp": 1, diff --git a/src/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp index 427650c6..d6371ce6 100644 --- a/src/displayapp/screens/Alarm.cpp +++ b/src/displayapp/screens/Alarm.cpp @@ -18,6 +18,7 @@ #include "displayapp/screens/Alarm.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; using Pinetime::Controllers::AlarmController; @@ -79,7 +80,7 @@ Alarm::Alarm(DisplayApp* app, lv_label_set_text_static(txtStop, Symbols::stop); lv_obj_set_hidden(btnStop, true); - static constexpr lv_color_t bgColor = LV_COLOR_MAKE(0x38, 0x38, 0x38); + static constexpr lv_color_t bgColor = Colors::bgAlt; btnRecur = lv_btn_create(lv_scr_act(), nullptr); btnRecur->user_data = this; diff --git a/src/displayapp/screens/BatteryInfo.cpp b/src/displayapp/screens/BatteryInfo.cpp index d9d479f8..9febda61 100644 --- a/src/displayapp/screens/BatteryInfo.cpp +++ b/src/displayapp/screens/BatteryInfo.cpp @@ -1,6 +1,7 @@ #include "displayapp/screens/BatteryInfo.h" #include "displayapp/DisplayApp.h" #include "components/battery/BatteryController.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -16,9 +17,9 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont lv_obj_align(charging_bar, nullptr, LV_ALIGN_CENTER, 0, 10); 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_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, Colors::bgAlt); 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_RED); lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON); status = lv_label_create(lv_scr_act(), nullptr); @@ -33,7 +34,7 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60); 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_MAKE(0xff, 0xb0, 0x0)); + lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange); lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10); lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER); lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95); @@ -62,7 +63,7 @@ void BatteryInfo::Refresh() { 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"); } else { - lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, Colors::highlight); lv_label_set_text_static(status, "Discharging"); } diff --git a/src/displayapp/screens/FirmwareValidation.cpp b/src/displayapp/screens/FirmwareValidation.cpp index a3c97616..a2314690 100644 --- a/src/displayapp/screens/FirmwareValidation.cpp +++ b/src/displayapp/screens/FirmwareValidation.cpp @@ -3,6 +3,7 @@ #include "Version.h" #include "components/firmwarevalidator/FirmwareValidator.h" #include "displayapp/DisplayApp.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -42,7 +43,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, lv_obj_set_size(buttonValidate, 115, 50); lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); lv_obj_set_event_cb(buttonValidate, ButtonEventHandler); - lv_obj_set_style_local_bg_color(buttonValidate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + lv_obj_set_style_local_bg_color(buttonValidate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); labelButtonValidate = lv_label_create(buttonValidate, nullptr); lv_label_set_text_static(labelButtonValidate, "Validate"); @@ -51,7 +52,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, buttonReset->user_data = this; lv_obj_set_size(buttonReset, 115, 50); lv_obj_align(buttonReset, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); - lv_obj_set_style_local_bg_color(buttonReset, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0x0, 0x0)); + lv_obj_set_style_local_bg_color(buttonReset, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_event_cb(buttonReset, ButtonEventHandler); labelButtonReset = lv_label_create(buttonReset, nullptr); diff --git a/src/displayapp/screens/FlashLight.cpp b/src/displayapp/screens/FlashLight.cpp index b00bb0fa..e06b59b5 100644 --- a/src/displayapp/screens/FlashLight.cpp +++ b/src/displayapp/screens/FlashLight.cpp @@ -1,6 +1,7 @@ #include "displayapp/screens/FlashLight.h" #include "displayapp/DisplayApp.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -58,7 +59,7 @@ FlashLight::~FlashLight() { void FlashLight::SetColors() { lv_color_t bgColor = isOn ? LV_COLOR_WHITE : LV_COLOR_BLACK; - lv_color_t fgColor = isOn ? LV_COLOR_MAKE(0xb0, 0xb0, 0xb0) : LV_COLOR_WHITE; + lv_color_t fgColor = isOn ? Colors::lightGray : LV_COLOR_WHITE; lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, bgColor); lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, fgColor); diff --git a/src/displayapp/screens/HeartRate.cpp b/src/displayapp/screens/HeartRate.cpp index 35e06bbc..305e0c4b 100644 --- a/src/displayapp/screens/HeartRate.cpp +++ b/src/displayapp/screens/HeartRate.cpp @@ -3,6 +3,7 @@ #include #include "displayapp/DisplayApp.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -36,10 +37,11 @@ HeartRate::HeartRate(Pinetime::Applications::DisplayApp* app, lv_obj_set_style_local_text_font(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); - if (isHrRunning) - lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); - else - lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + if (isHrRunning) { + lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); + } else { + lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + } lv_label_set_text_static(label_hr, "000"); lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40); @@ -97,12 +99,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) { heartRateController.Start(); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping); - lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); } else { heartRateController.Stop(); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); - lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); } } } diff --git a/src/displayapp/screens/InfiniPaint.cpp b/src/displayapp/screens/InfiniPaint.cpp index 733b4e27..0b6864e8 100644 --- a/src/displayapp/screens/InfiniPaint.cpp +++ b/src/displayapp/screens/InfiniPaint.cpp @@ -1,6 +1,7 @@ #include "displayapp/screens/InfiniPaint.h" #include "displayapp/DisplayApp.h" #include "displayapp/LittleVgl.h" +#include "displayapp/InfiniTimeTheme.h" #include // std::fill @@ -26,7 +27,7 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) { selectColor = LV_COLOR_MAGENTA; break; case 1: - selectColor = LV_COLOR_MAKE(0x0, 0xb0, 0x0); + selectColor = Colors::green; break; case 2: selectColor = LV_COLOR_WHITE; diff --git a/src/displayapp/screens/Metronome.cpp b/src/displayapp/screens/Metronome.cpp index 2ffc52dd..174ac1b6 100644 --- a/src/displayapp/screens/Metronome.cpp +++ b/src/displayapp/screens/Metronome.cpp @@ -1,5 +1,6 @@ #include "displayapp/screens/Metronome.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -12,7 +13,7 @@ namespace { lv_obj_t* createLabel(const char* name, lv_obj_t* reference, lv_align_t align, lv_font_t* font, uint8_t x, uint8_t y) { 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_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); lv_label_set_text(label, name); lv_obj_align(label, reference, align, x, y); diff --git a/src/displayapp/screens/Motion.cpp b/src/displayapp/screens/Motion.cpp index f7ffcc7c..c2dc4dac 100644 --- a/src/displayapp/screens/Motion.cpp +++ b/src/displayapp/screens/Motion.cpp @@ -1,6 +1,7 @@ #include "displayapp/screens/Motion.h" #include #include "displayapp/DisplayApp.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -19,7 +20,7 @@ Motion::Motion(Pinetime::Applications::DisplayApp* app, Controllers::MotionContr /*Add 3 data series*/ ser1 = lv_chart_add_series(chart, LV_COLOR_RED); - ser2 = lv_chart_add_series(chart, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + ser2 = lv_chart_add_series(chart, Colors::green); ser3 = lv_chart_add_series(chart, LV_COLOR_YELLOW); lv_chart_init_points(chart, ser1, 0); diff --git a/src/displayapp/screens/Navigation.cpp b/src/displayapp/screens/Navigation.cpp index 5779df3a..f6389734 100644 --- a/src/displayapp/screens/Navigation.cpp +++ b/src/displayapp/screens/Navigation.cpp @@ -19,6 +19,7 @@ #include #include "displayapp/DisplayApp.h" #include "components/ble/NavigationService.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -192,7 +193,7 @@ void Navigation::Refresh() { if (progress > 90) { lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED); } else { - lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xff, 0xb0, 0x0)); + lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, Colors::orange); } } } diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index 768ac290..90a010f5 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -4,6 +4,7 @@ #include "components/ble/AlertNotificationService.h" #include "displayapp/screens/Symbols.h" #include +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; extern lv_font_t jetbrains_mono_extrabold_compressed; @@ -257,7 +258,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, lv_obj_set_style_local_border_width(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); subject_container = lv_cont_create(container, nullptr); - lv_obj_set_style_local_bg_color(subject_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x38, 0x38, 0x38)); + lv_obj_set_style_local_bg_color(subject_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); lv_obj_set_style_local_pad_all(subject_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); lv_obj_set_style_local_pad_inner(subject_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); lv_obj_set_style_local_border_width(subject_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); @@ -272,7 +273,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, lv_obj_align(alert_count, NULL, LV_ALIGN_IN_TOP_RIGHT, 0, 16); lv_obj_t* alert_type = lv_label_create(container, nullptr); - lv_obj_set_style_local_text_color(alert_type, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xff, 0xb0, 0x0)); + lv_obj_set_style_local_text_color(alert_type, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange); if (title == nullptr) { lv_label_set_text_static(alert_type, "Notification"); } else { @@ -314,7 +315,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, lv_obj_align(bt_accept, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); label_accept = lv_label_create(bt_accept, nullptr); lv_label_set_text_static(label_accept, Symbols::phone); - lv_obj_set_style_local_bg_color(bt_accept, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + lv_obj_set_style_local_bg_color(bt_accept, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); bt_reject = lv_btn_create(container, nullptr); bt_reject->user_data = this; @@ -332,7 +333,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, lv_obj_align(bt_mute, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); label_mute = lv_label_create(bt_mute, nullptr); lv_label_set_text_static(label_mute, Symbols::volumMute); - lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); } break; } } diff --git a/src/displayapp/screens/PassKey.cpp b/src/displayapp/screens/PassKey.cpp index 4057a7eb..5d255e44 100644 --- a/src/displayapp/screens/PassKey.cpp +++ b/src/displayapp/screens/PassKey.cpp @@ -5,7 +5,7 @@ using namespace Pinetime::Applications::Screens; PassKey::PassKey(Pinetime::Applications::DisplayApp* app, uint32_t key) : Screen(app) { passkeyLabel = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(passkeyLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFF00)); + lv_obj_set_style_local_text_color(passkeyLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW); lv_obj_set_style_local_text_font(passkeyLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); lv_label_set_text_fmt(passkeyLabel, "%06u", key); lv_obj_align(passkeyLabel, nullptr, LV_ALIGN_CENTER, 0, -20); diff --git a/src/displayapp/screens/Steps.cpp b/src/displayapp/screens/Steps.cpp index 0dcdcf59..10ccf1aa 100644 --- a/src/displayapp/screens/Steps.cpp +++ b/src/displayapp/screens/Steps.cpp @@ -20,7 +20,7 @@ Steps::Steps(Pinetime::Applications::DisplayApp* app, lv_obj_set_style_local_bg_opa(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0); lv_obj_set_style_local_border_width(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 2); lv_obj_set_style_local_radius(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 0); - lv_obj_set_style_local_line_color(stepsArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0x0000FF)); + lv_obj_set_style_local_line_color(stepsArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE); lv_arc_set_end_angle(stepsArc, 200); lv_obj_set_size(stepsArc, 240, 240); lv_arc_set_range(stepsArc, 0, 500); @@ -32,7 +32,7 @@ Steps::Steps(Pinetime::Applications::DisplayApp* app, lv_arc_set_value(stepsArc, int16_t(500 * stepsCount / settingsController.GetStepsGoal())); lSteps = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00)); + lv_obj_set_style_local_text_color(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME); lv_obj_set_style_local_text_font(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); lv_label_set_text_fmt(lSteps, "%li", stepsCount); lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -40); diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index e705fcb0..c68cd854 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -1,6 +1,7 @@ #include "displayapp/screens/StopWatch.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -30,13 +31,13 @@ StopWatch::StopWatch(DisplayApp* app, System::SystemTask& systemTask) : Screen(a time = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); - lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); lv_label_set_text_static(time, "00:00"); lv_obj_align(time, lv_scr_act(), LV_ALIGN_CENTER, 0, -45); msecTime = lv_label_create(lv_scr_act(), nullptr); // lv_obj_set_style_local_text_font(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20); - lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); lv_label_set_text_static(msecTime, "00"); lv_obj_align(msecTime, lv_scr_act(), LV_ALIGN_CENTER, 0, 3); @@ -75,8 +76,8 @@ StopWatch::~StopWatch() { void StopWatch::Reset() { currentState = States::Init; oldTimeElapsed = 0; - lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); - lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); lv_label_set_text_static(time, "00:00"); lv_label_set_text_static(msecTime, "00"); @@ -90,8 +91,8 @@ void StopWatch::Reset() { void StopWatch::Start() { lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT); lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT); - lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); - lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); lv_label_set_text_static(txtPlayPause, Symbols::pause); lv_label_set_text_static(txtStopLap, Symbols::lapsFlag); startTime = xTaskGetTickCount(); diff --git a/src/displayapp/screens/Styles.cpp b/src/displayapp/screens/Styles.cpp index bcfd584f..cebdc70c 100644 --- a/src/displayapp/screens/Styles.cpp +++ b/src/displayapp/screens/Styles.cpp @@ -1,8 +1,9 @@ #include "Styles.h" +#include "displayapp/InfiniTimeTheme.h" void Pinetime::Applications::Screens::SetRadioButtonStyle(lv_obj_t* checkbox) { lv_obj_set_style_local_radius(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_obj_set_style_local_border_width(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED, 9); - lv_obj_set_style_local_border_color(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + lv_obj_set_style_local_border_color(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED, Colors::highlight); lv_obj_set_style_local_bg_color(checkbox, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED, LV_COLOR_WHITE); } diff --git a/src/displayapp/screens/Symbols.h b/src/displayapp/screens/Symbols.h index c6e76e36..1180ec6f 100644 --- a/src/displayapp/screens/Symbols.h +++ b/src/displayapp/screens/Symbols.h @@ -37,6 +37,7 @@ namespace Pinetime { static constexpr const char* chartLine = "\xEF\x88\x81"; static constexpr const char* eye = "\xEF\x81\xAE"; static constexpr const char* home = "\xEF\x80\x95"; + static constexpr const char* sleep = "\xEE\xBD\x84"; // lv_font_sys_48.c static constexpr const char* settings = "\xEE\xA2\xB8"; diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index 54059796..01c35195 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -12,6 +12,7 @@ #include "components/datetime/DateTimeController.h" #include "components/motion/MotionController.h" #include "drivers/Watchdog.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -218,7 +219,7 @@ std::unique_ptr SystemInfo::CreateScreen4() { lv_table_set_col_cnt(infoTask, 4); lv_table_set_row_cnt(infoTask, maxTaskCount + 1); lv_obj_set_style_local_pad_all(infoTask, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, 0); - lv_obj_set_style_local_border_color(infoTask, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_border_color(infoTask, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, Colors::lightGray); lv_table_set_cell_value(infoTask, 0, 0, "#"); lv_table_set_col_width(infoTask, 0, 30); diff --git a/src/displayapp/screens/Tile.cpp b/src/displayapp/screens/Tile.cpp index bf0780f1..a60076ed 100644 --- a/src/displayapp/screens/Tile.cpp +++ b/src/displayapp/screens/Tile.cpp @@ -2,6 +2,7 @@ #include "displayapp/DisplayApp.h" #include "displayapp/screens/BatteryIcon.h" #include "components/ble/BleController.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -71,7 +72,7 @@ Tile::Tile(uint8_t screenID, lv_obj_set_style_local_bg_opa(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DEFAULT, LV_OPA_50); lv_obj_set_style_local_bg_color(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DEFAULT, LV_COLOR_AQUA); lv_obj_set_style_local_bg_opa(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DISABLED, LV_OPA_50); - lv_obj_set_style_local_bg_color(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DISABLED, lv_color_hex(0x111111)); + lv_obj_set_style_local_bg_color(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DISABLED, Colors::bgDark); lv_obj_set_style_local_pad_all(btnm1, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 0); lv_obj_set_style_local_pad_inner(btnm1, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 10); diff --git a/src/displayapp/screens/Timer.cpp b/src/displayapp/screens/Timer.cpp index a25be1c4..136d6b52 100644 --- a/src/displayapp/screens/Timer.cpp +++ b/src/displayapp/screens/Timer.cpp @@ -1,6 +1,7 @@ #include "displayapp/screens/Timer.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/InfiniTimeTheme.h" #include using namespace Pinetime::Applications::Screens; @@ -54,7 +55,7 @@ Timer::Timer(DisplayApp* app, Controllers::TimerController& timerController) : S btnPlayPause = lv_btn_create(btnObjectMask, nullptr); btnPlayPause->user_data = this; lv_obj_set_style_local_radius(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); - lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x38, 0x38, 0x38)); + lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); lv_obj_set_event_cb(btnPlayPause, btnEventHandler); lv_obj_set_size(btnPlayPause, LV_HOR_RES, 50); diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp index 251a560f..5ebb3304 100644 --- a/src/displayapp/screens/WatchFaceAnalog.cpp +++ b/src/displayapp/screens/WatchFaceAnalog.cpp @@ -6,6 +6,7 @@ #include "displayapp/screens/Symbols.h" #include "displayapp/screens/NotificationIcon.h" #include "components/settings/Settings.h" +#include "displayapp/InfiniTimeTheme.h" LV_IMG_DECLARE(bg_clock); @@ -73,14 +74,14 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app, lv_obj_align(plugIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0); notificationIcon = lv_label_create(lv_scr_act(), NULL); - lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00)); + lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME); lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false)); lv_obj_align(notificationIcon, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); // Date - Day / Week day label_date_day = lv_label_create(lv_scr_act(), NULL); - lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xff, 0xb0, 0x0)); + lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange); lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day()); lv_label_set_align(label_date_day, LV_LABEL_ALIGN_CENTER); lv_obj_align(label_date_day, NULL, LV_ALIGN_CENTER, 50, 0); diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index 7e876d8f..705272f7 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -34,7 +34,7 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, statusIcons.Create(); 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(0x00FF00)); + lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME); lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false)); lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); diff --git a/src/displayapp/screens/WatchFacePineTimeStyle.cpp b/src/displayapp/screens/WatchFacePineTimeStyle.cpp index 63b421da..ed09f5dd 100644 --- a/src/displayapp/screens/WatchFacePineTimeStyle.cpp +++ b/src/displayapp/screens/WatchFacePineTimeStyle.cpp @@ -104,11 +104,11 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app, lv_obj_align(plugIcon, 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_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_label_set_text_static(bleIcon, ""); notificationIcon = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000)); + lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_label_set_text_static(notificationIcon, ""); // Calendar icon diff --git a/src/displayapp/screens/settings/QuickSettings.cpp b/src/displayapp/screens/settings/QuickSettings.cpp index f7560066..b76affc9 100644 --- a/src/displayapp/screens/settings/QuickSettings.cpp +++ b/src/displayapp/screens/settings/QuickSettings.cpp @@ -2,20 +2,29 @@ #include "displayapp/DisplayApp.h" #include "displayapp/screens/Symbols.h" #include "displayapp/screens/BatteryIcon.h" -#include +#include "components/ble/BleController.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; namespace { void ButtonEventHandler(lv_obj_t* obj, lv_event_t event) { auto* screen = static_cast(obj->user_data); - screen->OnButtonEvent(obj, event); + if (event == LV_EVENT_CLICKED) { + screen->OnButtonEvent(obj); + } } void lv_update_task(struct _lv_task_t* task) { auto* user_data = static_cast(task->user_data); user_data->UpdateScreen(); } + + enum class ButtonState : lv_state_t { + NotificationsOn = LV_STATE_CHECKED, + NotificationsOff = LV_STATE_DEFAULT, + Sleep = 0x40, + }; } QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app, @@ -50,7 +59,7 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app, lv_style_init(&btn_style); lv_style_set_radius(&btn_style, LV_STATE_DEFAULT, buttonHeight / 4); - lv_style_set_bg_color(&btn_style, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x38, 0x38, 0x38)); + lv_style_set_bg_color(&btn_style, LV_STATE_DEFAULT, Colors::bgAlt); btn1 = lv_btn_create(lv_scr_act(), nullptr); btn1->user_data = this; @@ -78,19 +87,24 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app, btn3 = lv_btn_create(lv_scr_act(), nullptr); btn3->user_data = this; lv_obj_set_event_cb(btn3, ButtonEventHandler); - lv_btn_set_checkable(btn3, true); lv_obj_add_style(btn3, LV_BTN_PART_MAIN, &btn_style); + lv_obj_set_style_local_bg_color(btn3, LV_BTN_PART_MAIN, static_cast(ButtonState::NotificationsOff), LV_COLOR_RED); + static constexpr lv_color_t violet = LV_COLOR_MAKE(0x60, 0x00, 0xff); + lv_obj_set_style_local_bg_color(btn3, LV_BTN_PART_MAIN, static_cast(ButtonState::Sleep), violet); lv_obj_set_size(btn3, buttonWidth, buttonHeight); lv_obj_align(btn3, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, buttonXOffset, 0); btn3_lvl = lv_label_create(btn3, nullptr); lv_obj_set_style_local_text_font(btn3_lvl, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48); - if (settingsController.GetNotificationStatus() == Controllers::Settings::Notification::ON) { - lv_obj_add_state(btn3, LV_STATE_CHECKED); + if (settingsController.GetNotificationStatus() == Controllers::Settings::Notification::On) { lv_label_set_text_static(btn3_lvl, Symbols::notificationsOn); - } else { + lv_obj_set_state(btn3, static_cast(ButtonState::NotificationsOn)); + } else if (settingsController.GetNotificationStatus() == Controllers::Settings::Notification::Off) { lv_label_set_text_static(btn3_lvl, Symbols::notificationsOff); + } else { + lv_label_set_text_static(btn3_lvl, Symbols::sleep); + lv_obj_set_state(btn3, static_cast(ButtonState::Sleep)); } btn4 = lv_btn_create(lv_scr_act(), nullptr); @@ -121,31 +135,33 @@ void QuickSettings::UpdateScreen() { statusIcons.Update(); } -void QuickSettings::OnButtonEvent(lv_obj_t* object, lv_event_t event) { - if (object == btn2 && event == LV_EVENT_CLICKED) { - - running = false; +void QuickSettings::OnButtonEvent(lv_obj_t* object) { + if (object == btn2) { app->StartApp(Apps::FlashLight, DisplayApp::FullRefreshDirections::Up); - - } else if (object == btn1 && event == LV_EVENT_CLICKED) { + } else if (object == btn1) { brightness.Step(); lv_label_set_text_static(btn1_lvl, brightness.GetIcon()); settingsController.SetBrightness(brightness.Level()); - } else if (object == btn3 && event == LV_EVENT_VALUE_CHANGED) { + } else if (object == btn3) { - if (lv_obj_get_state(btn3, LV_BTN_PART_MAIN) & LV_STATE_CHECKED) { - settingsController.SetNotificationStatus(Controllers::Settings::Notification::ON); - motorController.RunForDuration(35); - lv_label_set_text_static(btn3_lvl, Symbols::notificationsOn); - } else { - settingsController.SetNotificationStatus(Controllers::Settings::Notification::OFF); + if (settingsController.GetNotificationStatus() == Controllers::Settings::Notification::On) { + settingsController.SetNotificationStatus(Controllers::Settings::Notification::Off); lv_label_set_text_static(btn3_lvl, Symbols::notificationsOff); + lv_obj_set_state(btn3, static_cast(ButtonState::NotificationsOff)); + } else if (settingsController.GetNotificationStatus() == Controllers::Settings::Notification::Off) { + settingsController.SetNotificationStatus(Controllers::Settings::Notification::Sleep); + lv_label_set_text_static(btn3_lvl, Symbols::sleep); + lv_obj_set_state(btn3, static_cast(ButtonState::Sleep)); + } else { + settingsController.SetNotificationStatus(Controllers::Settings::Notification::On); + lv_label_set_text_static(btn3_lvl, Symbols::notificationsOn); + lv_obj_set_state(btn3, static_cast(ButtonState::NotificationsOn)); + motorController.RunForDuration(35); } - } else if (object == btn4 && event == LV_EVENT_CLICKED) { - running = false; + } else if (object == btn4) { settingsController.SetSettingsMenu(0); app->StartApp(Apps::Settings, DisplayApp::FullRefreshDirections::Up); } diff --git a/src/displayapp/screens/settings/QuickSettings.h b/src/displayapp/screens/settings/QuickSettings.h index cd342b0c..f555d034 100644 --- a/src/displayapp/screens/settings/QuickSettings.h +++ b/src/displayapp/screens/settings/QuickSettings.h @@ -27,7 +27,7 @@ namespace Pinetime { ~QuickSettings() override; - void OnButtonEvent(lv_obj_t* object, lv_event_t event); + void OnButtonEvent(lv_obj_t* object); void UpdateScreen(); diff --git a/src/displayapp/screens/settings/SettingSetTime.cpp b/src/displayapp/screens/settings/SettingSetTime.cpp index 7581f184..47b786e4 100644 --- a/src/displayapp/screens/settings/SettingSetTime.cpp +++ b/src/displayapp/screens/settings/SettingSetTime.cpp @@ -5,6 +5,7 @@ #include "displayapp/DisplayApp.h" #include "displayapp/screens/Symbols.h" #include "components/settings/Settings.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -67,8 +68,7 @@ SettingSetTime::SettingSetTime(Pinetime::Applications::DisplayApp* app, lv_obj_set_size(btnSetTime, 120, 50); lv_obj_align(btnSetTime, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); lv_obj_set_style_local_value_str(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Set"); - lv_obj_set_style_local_bg_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x38, 0x38, 0x38)); - lv_obj_set_style_local_bg_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_MAKE(0x18, 0x18, 0x18)); + lv_obj_set_style_local_bg_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); lv_obj_set_style_local_value_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_GRAY); lv_obj_set_event_cb(btnSetTime, SetTimeEventHandler); diff --git a/src/displayapp/screens/settings/SettingShakeThreshold.cpp b/src/displayapp/screens/settings/SettingShakeThreshold.cpp index aac1eaff..de46f7de 100644 --- a/src/displayapp/screens/settings/SettingShakeThreshold.cpp +++ b/src/displayapp/screens/settings/SettingShakeThreshold.cpp @@ -3,6 +3,7 @@ #include "displayapp/DisplayApp.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; @@ -123,8 +124,7 @@ void SettingShakeThreshold::UpdateSelected(lv_obj_t* object, lv_event_t event) { vCalTime = xTaskGetTickCount(); lv_label_set_text_static(calLabel, "Ready!"); lv_obj_set_click(positionArc, false); - lv_obj_set_style_local_bg_color(calButton, LV_BTN_PART_MAIN, LV_STATE_CHECKED, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); - lv_obj_set_style_local_bg_color(calButton, LV_BTN_PART_MAIN, LV_STATE_CHECKED, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); + lv_obj_set_style_local_bg_color(calButton, LV_BTN_PART_MAIN, LV_STATE_CHECKED, Colors::highlight); } else if (lv_btn_get_state(calButton) == LV_BTN_STATE_RELEASED) { calibrating = 0; lv_obj_set_click(positionArc, true); diff --git a/src/displayapp/screens/settings/SettingSteps.cpp b/src/displayapp/screens/settings/SettingSteps.cpp index e92600c2..af5bd6e9 100644 --- a/src/displayapp/screens/settings/SettingSteps.cpp +++ b/src/displayapp/screens/settings/SettingSteps.cpp @@ -17,7 +17,6 @@ SettingSteps::SettingSteps(Pinetime::Applications::DisplayApp* app, Pinetime::Co lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr); - // lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); diff --git a/src/displayapp/screens/settings/SettingWatchFace.cpp b/src/displayapp/screens/settings/SettingWatchFace.cpp index 3cb2a364..be595a74 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.cpp +++ b/src/displayapp/screens/settings/SettingWatchFace.cpp @@ -21,7 +21,6 @@ SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app, Pine lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr); - // lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); diff --git a/src/displayapp/widgets/Counter.cpp b/src/displayapp/widgets/Counter.cpp index d8a1626e..e95178ec 100644 --- a/src/displayapp/widgets/Counter.cpp +++ b/src/displayapp/widgets/Counter.cpp @@ -1,5 +1,6 @@ #include "displayapp/widgets/Counter.h" #include "components/datetime/DateTimeController.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Widgets; @@ -17,9 +18,17 @@ namespace { widget->DownBtnPressed(); } } + constexpr int digitCount(int number) { + int digitCount = 0; + while (number > 0) { + digitCount++; + number /= 10; + } + return digitCount; + } } -Counter::Counter(int min, int max, lv_font_t& font) : min {min}, max {max}, value {min}, font {font} { +Counter::Counter(int min, int max, lv_font_t& font) : min {min}, max {max}, value {min}, font {font}, leadingZeroCount {digitCount(max)} { } void Counter::UpBtnPressed() { @@ -71,14 +80,14 @@ void Counter::UpdateLabel() { if (value == 0) { lv_label_set_text_static(number, "12"); } else if (value <= 12) { - lv_label_set_text_fmt(number, "%.2i", value); + lv_label_set_text_fmt(number, "%.*i", leadingZeroCount, value); } else { - lv_label_set_text_fmt(number, "%.2i", value - 12); + lv_label_set_text_fmt(number, "%.*i", leadingZeroCount, value - 12); } } else if (monthMode) { lv_label_set_text(number, Controllers::DateTime::MonthShortToStringLow(static_cast(value))); } else { - lv_label_set_text_fmt(number, "%.2i", value); + lv_label_set_text_fmt(number, "%.*i", leadingZeroCount, value); } } @@ -94,6 +103,8 @@ void Counter::EnableMonthMode() { monthMode = true; } +// Counter cannot be resized after creation, +// so the newMax value must have the same number of digits as the old one void Counter::SetMax(int newMax) { max = newMax; if (value > max) { @@ -108,10 +119,8 @@ void Counter::SetValueChangedEventCallback(void* userData, void (*handler)(void* } void Counter::Create() { - constexpr lv_color_t bgColor = LV_COLOR_MAKE(0x38, 0x38, 0x38); - counterContainer = lv_obj_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_bg_color(counterContainer, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor); + lv_obj_set_style_local_bg_color(counterContainer, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); number = lv_label_create(counterContainer, nullptr); lv_obj_set_style_local_text_font(number, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &font); @@ -133,7 +142,7 @@ void Counter::Create() { UpdateLabel(); upBtn = lv_btn_create(counterContainer, nullptr); - lv_obj_set_style_local_bg_color(upBtn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor); + lv_obj_set_style_local_bg_color(upBtn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); lv_obj_set_size(upBtn, width, btnHeight); lv_obj_align(upBtn, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0); upBtn->user_data = this; @@ -145,7 +154,7 @@ void Counter::Create() { lv_obj_align(upLabel, nullptr, LV_ALIGN_CENTER, 0, 0); downBtn = lv_btn_create(counterContainer, nullptr); - lv_obj_set_style_local_bg_color(downBtn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor); + lv_obj_set_style_local_bg_color(downBtn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); lv_obj_set_size(downBtn, width, btnHeight); lv_obj_align(downBtn, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0); downBtn->user_data = this; diff --git a/src/displayapp/widgets/Counter.h b/src/displayapp/widgets/Counter.h index d38dd9d7..825860b8 100644 --- a/src/displayapp/widgets/Counter.h +++ b/src/displayapp/widgets/Counter.h @@ -41,6 +41,7 @@ namespace Pinetime { int min; int max; int value; + const int leadingZeroCount; bool twelveHourMode = false; bool monthMode = false; lv_font_t& font; diff --git a/src/displayapp/widgets/PageIndicator.cpp b/src/displayapp/widgets/PageIndicator.cpp index 06058beb..84d03e7e 100644 --- a/src/displayapp/widgets/PageIndicator.cpp +++ b/src/displayapp/widgets/PageIndicator.cpp @@ -1,4 +1,5 @@ #include "displayapp/widgets/PageIndicator.h" +#include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Widgets; @@ -13,7 +14,7 @@ void PageIndicator::Create() { pageIndicatorBase = lv_line_create(lv_scr_act(), nullptr); lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111)); + lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, Colors::bgDark); lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2); const int16_t indicatorSize = LV_VER_RES / nScreens; @@ -26,6 +27,6 @@ void PageIndicator::Create() { pageIndicator = lv_line_create(lv_scr_act(), nullptr); lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); - lv_obj_set_style_local_line_color(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); + lv_obj_set_style_local_line_color(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); lv_line_set_points(pageIndicator, pageIndicatorPoints, 2); } diff --git a/src/displayapp/widgets/StatusIcons.cpp b/src/displayapp/widgets/StatusIcons.cpp index c1004b77..607f3745 100644 --- a/src/displayapp/widgets/StatusIcons.cpp +++ b/src/displayapp/widgets/StatusIcons.cpp @@ -19,7 +19,7 @@ void StatusIcons::Create() { lv_label_set_text_static(bleIcon, Screens::Symbols::bluetooth); batteryPlug = lv_label_create(container, nullptr); - lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFF0000)); + lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); lv_label_set_text_static(batteryPlug, Screens::Symbols::plug); batteryIcon.Create(container); diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index 3ed1beb4..cfd5bd2c 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -21,7 +21,10 @@ void St7789::Init() { MemoryDataAccessControl(); ColumnAddressSet(); RowAddressSet(); +// P8B Mirrored version does not need display inversion. +#ifndef DRIVER_DISPLAY_MIRROR DisplayInversionOn(); +#endif NormalModeOn(); SetVdv(); DisplayOn(); @@ -62,7 +65,18 @@ void St7789::ColMod() { void St7789::MemoryDataAccessControl() { WriteCommand(static_cast(Commands::MemoryDataAccessControl)); +#ifdef DRIVER_DISPLAY_MIRROR + // [7] = MY = Page Address Order, 0 = Top to bottom, 1 = Bottom to top + // [6] = MX = Column Address Order, 0 = Left to right, 1 = Right to left + // [5] = MV = Page/Column Order, 0 = Normal mode, 1 = Reverse mode + // [4] = ML = Line Address Order, 0 = LCD refresh from top to bottom, 1 = Bottom to top + // [3] = RGB = RGB/BGR Order, 0 = RGB, 1 = BGR + // [2] = MH = Display Data Latch Order, 0 = LCD refresh from left to right, 1 = Right to left + // [0 .. 1] = Unused + WriteData(0b01000000); +#else WriteData(0x00); +#endif } void St7789::ColumnAddressSet() { diff --git a/src/libs/lv_conf.h b/src/libs/lv_conf.h index b3ff8f57..00f6a1df 100644 --- a/src/libs/lv_conf.h +++ b/src/libs/lv_conf.h @@ -161,7 +161,7 @@ typedef void* lv_anim_user_data_t; #define LV_USE_OUTLINE 0 /*1: enable pattern drawing on rectangles*/ -#define LV_USE_PATTERN 0 +#define LV_USE_PATTERN 1 /*1: enable value string drawing on rectangles*/ #define LV_USE_VALUE_STR 1 diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index d3f12789..1c871fd2 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -251,7 +251,8 @@ void SystemTask::Work() { case Messages::TouchWakeUp: { if (touchHandler.GetNewTouchInfo()) { auto gesture = touchHandler.GestureGet(); - if (gesture != Pinetime::Applications::TouchEvents::None && + if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep && + gesture != Pinetime::Applications::TouchEvents::None && ((gesture == Pinetime::Applications::TouchEvents::DoubleTap && settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) || (gesture == Pinetime::Applications::TouchEvents::Tap && @@ -280,7 +281,7 @@ void SystemTask::Work() { } break; case Messages::OnNewNotification: - if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::ON) { + if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::On) { if (state == SystemTaskState::Sleeping) { GoToRunning(); } else { @@ -388,7 +389,8 @@ void SystemTask::Work() { break; case Messages::OnNewHour: using Pinetime::Controllers::AlarmController; - if (settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours && + if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep && + settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours && alarmController.State() != AlarmController::AlarmState::Alerting) { if (state == SystemTaskState::Sleeping) { GoToRunning(); @@ -399,7 +401,8 @@ void SystemTask::Work() { break; case Messages::OnNewHalfHour: using Pinetime::Controllers::AlarmController; - if (settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours && + if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep && + settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours && alarmController.State() != AlarmController::AlarmState::Alerting) { if (state == SystemTaskState::Sleeping) { GoToRunning(); @@ -483,13 +486,13 @@ void SystemTask::UpdateMotion() { motionController.IsSensorOk(motionSensor.IsOk()); motionController.Update(motionValues.x, motionValues.y, motionValues.z, motionValues.steps); - if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) && - motionController.Should_RaiseWake(state == SystemTaskState::Sleeping)) { - GoToRunning(); - } - if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) && - motionController.Should_ShakeWake(settingsController.GetShakeThreshold())) { - GoToRunning(); + if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep) { + if ((settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) && + motionController.Should_RaiseWake(state == SystemTaskState::Sleeping)) || + (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) && + motionController.Should_ShakeWake(settingsController.GetShakeThreshold()))) { + GoToRunning(); + } } }