Merge branch 'develop' into infineat-external-resources

# Conflicts:
#	src/displayapp/screens/Symbols.h
#	src/displayapp/screens/settings/SettingWatchFace.cpp
#	src/displayapp/screens/settings/SettingWatchFace.h
This commit is contained in:
Jean-François Milants 2022-09-11 14:59:49 +02:00
commit ada2c09581
160 changed files with 3250 additions and 46018 deletions

View file

@ -1,7 +1,6 @@
# VS Code Dev Container # 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 ## Requirements
@ -25,7 +24,7 @@ In order to build InfiniTime we need to run the initial submodule init and CMake
#### 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 #### Script
@ -35,16 +34,12 @@ There are also VS Code tasks provided should you desire to use those.
The task "update submodules" will update the git submodules The task "update submodules" will update the git submodules
### Build ### Build
You can use the build.sh script located in /opt/ 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 CMake is also configured and controls for the CMake plugin are available in VS Code
### Debugging ### 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. 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.
@ -55,6 +50,6 @@ 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 The default launch.json file expects OpenOCD to be listening on port 3333, edit if needed
## Current Issues ## 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 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

70
.github/workflows/docker.yml vendored Normal file
View file

@ -0,0 +1,70 @@
name: Build and push Docker image
on:
push:
branches: [ develop ]
paths:
- 'docker/**'
pull_request:
branches: [ develop ]
paths:
- 'docker/**'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
env:
USERNAME: infinitime
steps:
- uses: actions/checkout@v3
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_LOGIN_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ secrets.DOCKER_HUB_IMAGE_USERNAME || env.USERNAME }}/infinitime-build
tags: |
type=sha
type=raw,value=latest
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
- name: Build and push
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v3
with:
context: ./docker/
file: ./docker/Dockerfile
platforms: linux/amd64,linux/arm64
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_IMAGE_USERNAME || env.USERNAME }}/infinitime-build:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_IMAGE_USERNAME || env.USERNAME }}/infinitime-build:buildcache,mode=max
- name: Build
if: github.event_name == 'pull_request'
uses: docker/build-push-action@v3
with:
context: ./docker/
file: ./docker/Dockerfile
platforms: linux/amd64,linux/arm64
builder: ${{ steps.buildx.outputs.name }}
push: false
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_IMAGE_USERNAME || env.USERNAME }}/infinitime-build:buildcache

View file

@ -2,11 +2,12 @@ name: Code formatting
on: on:
pull_request: pull_request:
branches: [ master, develop ] branches: [ develop ]
paths: paths:
- '**.cpp' - '**.cpp'
- '**.h' - '**.h'
- '!src/libs/**' - '!src/libs/**'
- '!src/FreeRTOS/**'
jobs: jobs:
test-format: test-format:
@ -18,8 +19,6 @@ jobs:
- name: Configure git - name: Configure git
run: | run: |
git config --global user.email "-"
git config --global user.name "Autoformatter"
git fetch origin "$GITHUB_BASE_REF":"$GITHUB_BASE_REF" --depth=1000 git fetch origin "$GITHUB_BASE_REF":"$GITHUB_BASE_REF" --depth=1000
- name: Install clang-format - name: Install clang-format

View file

@ -1,66 +0,0 @@
# GitHub Actions Workflow to build Simulator for PineTime Smart Watch LVGL Interface
name: Build PineTime LVGL Simulator
on:
push:
branches: [ master, develop ]
pull_request:
branches: [ master, develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
#########################################################################################
# Download and Install Dependencies
- name: Install cmake
uses: lukka/get-cmake@v3.18.3
- name: Install SDL2 development package
run: |
sudo apt-get update
sudo apt-get -y install libsdl2-dev
- name: Install lv_font_conv
run:
npm i -g lv_font_conv@1.5.2
#########################################################################################
# Checkout
- name: Checkout source files
uses: actions/checkout@v2
with:
submodules: recursive
#########################################################################################
# get InfiniSim repo
- name: Get InfiniSim repo
run: |
git clone https://github.com/InfiniTimeOrg/InfiniSim.git --depth 1 --branch main
git -C InfiniSim submodule update --init lv_drivers libpng
#########################################################################################
# CMake
- name: CMake
run: |
cmake -G Ninja -S InfiniSim -B build_lv_sim -DInfiniTime_DIR="${PWD}"
#########################################################################################
# Build and Upload simulator
- name: Build simulator executable
run: |
cmake --build build_lv_sim
- name: Upload simulator executable
uses: actions/upload-artifact@v3
with:
name: infinisim-${{ github.head_ref }}
path: build_lv_sim/infinisim

View file

@ -1,18 +1,18 @@
# GitHub Actions Workflow to build FreeRTOS Firmware for PineTime Smart Watch name: CI
# See https://lupyuen.github.io/pinetime-rust-mynewt/articles/cloud
# Based on https://github.com/JF002/InfiniTime/blob/master/doc/buildAndProgram.md
# and https://github.com/JF002/InfiniTime/blob/master/bootloader/README.md
name: Build PineTime Firmware
# Run this workflow whenever the build may be affected
on: on:
push: push:
branches: [ master, develop ] branches: [ master, develop ]
paths-ignore:
- 'doc/**'
pull_request: pull_request:
branches: [ master, develop ] branches: [ develop ]
paths-ignore:
- 'doc/**'
jobs: jobs:
build: build-firmware:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: infinitime/infinitime-build image: infinitime/infinitime-build
@ -45,3 +45,42 @@ jobs:
with: with:
name: InfiniTime MCUBoot image ${{ github.head_ref }} name: InfiniTime MCUBoot image ${{ github.head_ref }}
path: ./build/output/pinetime-mcuboot-app-image-*.bin path: ./build/output/pinetime-mcuboot-app-image-*.bin
build-simulator:
runs-on: ubuntu-latest
steps:
- name: Install cmake
uses: lukka/get-cmake@v3.18.3
- name: Install SDL2 development package
run: |
sudo apt-get update
sudo apt-get -y install libsdl2-dev
- name: Install lv_font_conv
run:
npm i -g lv_font_conv@1.5.2
- name: Checkout source files
uses: actions/checkout@v2
with:
submodules: recursive
- name: Get InfiniSim repo
run: |
git clone https://github.com/InfiniTimeOrg/InfiniSim.git --depth 1 --branch main
git -C InfiniSim submodule update --init lv_drivers libpng
- name: CMake
run: |
cmake -G Ninja -S InfiniSim -B build_lv_sim -DInfiniTime_DIR="${PWD}"
- name: Build simulator executable
run: |
cmake --build build_lv_sim
- name: Upload simulator executable
uses: actions/upload-artifact@v3
with:
name: infinisim-${{ github.head_ref }}
path: build_lv_sim/infinisim

3
.gitmodules vendored
View file

@ -7,3 +7,6 @@
[submodule "src/libs/QCBOR"] [submodule "src/libs/QCBOR"]
path = src/libs/QCBOR path = src/libs/QCBOR
url = https://github.com/laurencelundblade/QCBOR.git url = https://github.com/laurencelundblade/QCBOR.git
[submodule "src/libs/date"]
path = src/libs/date
url = https://github.com/HowardHinnant/date.git

2
.vscode/launch.json vendored
View file

@ -52,7 +52,7 @@
"servertype": "openocd", "servertype": "openocd",
"runToMain": true, "runToMain": true,
// Only use armToolchainPath if your arm-none-eabi-gdb is not in your path (some GCC packages does not contain arm-none-eabi-gdb) // Only use armToolchainPath if your arm-none-eabi-gdb is not in your path (some GCC packages does not contain arm-none-eabi-gdb)
"armToolchainPath": "${workspaceRoot}/../gcc-arm-none-eabi-9-2020-q2-update/bin", "armToolchainPath": "${workspaceRoot}/../gcc-arm-none-eabi-10.3-2021.10/bin",
"svdFile": "${workspaceRoot}/nrf52.svd", "svdFile": "${workspaceRoot}/nrf52.svd",
"configFiles": [ "configFiles": [
"interface/stlink.cfg", "interface/stlink.cfg",

View file

@ -1,5 +1,8 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(pinetime VERSION 1.9.0 LANGUAGES C CXX ASM)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
project(pinetime VERSION 1.10.0 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
@ -51,14 +54,13 @@ if(BUILD_DFU)
set(BUILD_DFU true) set(BUILD_DFU true)
endif() endif()
option(WATCH_COLMI_P8 "Build for the Colmi P8" OFF) if(BUILD_RESOURCES)
set(TARGET_DEVICE "PineTime") set(BUILD_RESOURCES true)
if(WATCH_COLMI_P8)
set(TARGET_DEVICE "Colmi P8")
add_definitions(-DWATCH_P8)
endif() endif()
set(TARGET_DEVICE "PINETIME" CACHE STRING "Target device")
set_property(CACHE TARGET_DEVICE PROPERTY STRINGS PINETIME MOY-TFK5 MOY-TIN5 MOY-TON5 MOY-UNK)
set(PROJECT_GIT_COMMIT_HASH "") set(PROJECT_GIT_COMMIT_HASH "")
execute_process(COMMAND git rev-parse --short HEAD execute_process(COMMAND git rev-parse --short HEAD
@ -70,8 +72,10 @@ string(STRIP "${PROJECT_GIT_COMMIT_HASH}" PROJECT_GIT_COMMIT_HASH)
message("PROJECT_GIT_COMMIT_HASH_SUCCESS? " ${PROJECT_GIT_COMMIT_HASH_SUCCESS}) message("PROJECT_GIT_COMMIT_HASH_SUCCESS? " ${PROJECT_GIT_COMMIT_HASH_SUCCESS})
message("")
message("BUILD CONFIGURATION") message("BUILD CONFIGURATION")
message("-------------------") message("-------------------")
message(" * Mode : " ${CMAKE_BUILD_TYPE})
message(" * Version : " ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) message(" * Version : " ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
message(" * Toolchain : " ${ARM_NONE_EABI_TOOLCHAIN_PATH}) message(" * Toolchain : " ${ARM_NONE_EABI_TOOLCHAIN_PATH})
message(" * GitRef(S) : " ${PROJECT_GIT_COMMIT_HASH}) message(" * GitRef(S) : " ${PROJECT_GIT_COMMIT_HASH})
@ -98,6 +102,11 @@ if(BUILD_DFU)
else() else()
message(" * Build DFU (using adafruit-nrfutil) : Disabled") message(" * Build DFU (using adafruit-nrfutil) : Disabled")
endif() endif()
if(BUILD_RESOURCES)
message(" * Build resources : Enabled")
else()
message(" * Build resources : Disabled")
endif()
set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!") set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/Version.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/Version.h)

View file

@ -2,68 +2,71 @@
[![Build PineTime Firmware](https://github.com/InfiniTimeOrg/InfiniTime/workflows/Build%20PineTime%20Firmware/badge.svg?branch=master)](https://github.com/InfiniTimeOrg/InfiniTime/actions) [![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++. Fast open-source firmware for the [PineTime smartwatch](https://www.pine64.org/pinetime/) with many features, written in modern C++.
## New to InfiniTime? ## New to InfiniTime?
- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) - [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md)
- [Updating the software](doc/gettingStarted/updating-software.md) - [Updating the software](doc/gettingStarted/updating-software.md)
- [About the firmware and bootloader](doc/gettingStarted/about-software.md) - [About the firmware and bootloader](doc/gettingStarted/about-software.md)
### Companion apps ### Companion apps
- [Gadgetbridge](https://gadgetbridge.org/) (Android)
- [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS) - [Gadgetbridge](https://gadgetbridge.org/) (Android)
- [Siglo](https://github.com/alexr4535/siglo) (Linux) - [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS)
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) **[Experimental]** **[Unmaintained, looking for developers/maintainers]** (iOS) - [Siglo](https://github.com/alexr4535/siglo) (Linux)
- [ITD](https://gitea.arsenm.dev/Arsen6331/itd) (Linux) - [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS) **[Looking for a new maintainer]**
- [ITD](https://gitea.arsenm.dev/Arsen6331/itd) (Linux)
## Development ## Development
- [Rough structure of the code](doc/code/Intro.md) - [InfiniTime Vision](doc/InfiniTimeVision.md)
- [How to implement an application](doc/code/Apps.md) - [Rough structure of the code](doc/code/Intro.md)
- [Generate the fonts and symbols](src/displayapp/fonts/README.md) - [How to implement an application](doc/code/Apps.md)
- [Creating a stopwatch in Pinetime(article)](https://pankajraghav.com/2021/04/03/PINETIME-STOPCLOCK.html) - [Generate the fonts and symbols](src/displayapp/fonts/README.md)
- [Tips on designing an app UI](doc/ui_guidelines.md) - [Tips on designing an app UI](doc/ui_guidelines.md)
- [Bootloader, OTA and DFU](bootloader/README.md)
### InfiniSim Simulator - [Versioning](doc/versioning.md)
Use the [InfiniSim Simulator](https://github.com/InfiniTimeOrg/InfiniSim) to experience the `InfiniTime` user interface directly on your PC, to shorten the time until you get your hands on a real [PineTime smartwatch](https://www.pine64.org/pinetime/). - [Project branches](doc/branches.md)
Or use it to develop new Watchfaces, new Screens, or quickly iterate on the user interface. - [Files included in the release notes](doc/filesInReleaseNotes.md)
### Contributing ### 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 ### Build, flash and debug
- [Project branches](doc/branches.md) - [InfiniTime simulator](https://github.com/InfiniTimeOrg/InfiniSim)
- [Versioning](doc/versioning.md) - [Build the project](doc/buildAndProgram.md)
- [Files included in the release notes](doc/filesInReleaseNotes.md) - [Build the project with Docker](doc/buildWithDocker.md)
- [Build the project](doc/buildAndProgram.md) - [Build the project with VSCode](doc/buildWithVScode.md)
- [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md) - [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md)
- [Flash the firmware using SWD interface](doc/SWD.md) - [Flash the firmware using SWD interface](doc/SWD.md)
- [Build the project with Docker](doc/buildWithDocker.md) - [Flash the firmware using JLink](doc/jlink.md)
- [Build the project with VSCode](doc/buildWithVScode.md) - [Flash the firmware using GDB](doc/gdb.md)
- [Bootloader, OTA and DFU](./bootloader/README.md) - [Stub using NRF52-DK](doc/PinetimeStubWithNrf52DK.md)
- [Stub using NRF52-DK](./doc/PinetimeStubWithNrf52DK.md)
### API ### API
- [BLE implementation and API](./doc/ble.md) - [BLE implementation and API](doc/ble.md)
### Architecture and technical topics ### Architecture and technical topics
- [Memory analysis](./doc/MemoryAnalysis.md) - [Memory analysis](doc/MemoryAnalysis.md)
## Licenses ## Licenses
This project is released under the GNU General Public License version 3 or, at your option, any later version. This project is released under the GNU General Public License version 3 or, at your option, any later version.
It integrates the following projects: It integrates the following projects:
- RTOS : **[FreeRTOS](https://freertos.org)** under the MIT license
- UI : **[LittleVGL/LVGL](https://lvgl.io/)** under the MIT license - RTOS : **[FreeRTOS](https://freertos.org)** under the MIT license
- BLE stack : **[NimBLE](https://github.com/apache/mynewt-nimble)** under the Apache 2.0 license - UI : **[LittleVGL/LVGL](https://lvgl.io/)** under the MIT license
- Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license - BLE stack : **[NimBLE](https://github.com/apache/mynewt-nimble)** under the Apache 2.0 license
- Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license
## Credits ## Credits
@ -71,8 +74,6 @@ Im not working alone on this project. First, many people create PR for this p
Here are some people I would like to highlight: 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. - [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) : Hes working on a firmware based on RiotOS. He integrated similar libs as me : NimBLE, LittleVGL,… His help was invaluable too! - [Koen](https://github.com/bosmoment) : Hes 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! - [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!
*If you feel like you should appear on this list, just get in touch with me or submit a PR :)*

View file

@ -1,4 +1,5 @@
# About this bootloader # 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 [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. 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. 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 # 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 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") ![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. 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' : 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. 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. 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 # Degraded cases
This chapter describes degraded cases that are handled by our bootloader and those that are not supported. This chapter describes degraded cases that are handled by our bootloader and those that are not supported.
Case | Current bootloader | Solution 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 # Using the bootloader
## Bootloader graphic ## 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 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 : 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: - Build:
```
$ make pinetime-graphics ```sh
make pinetime-graphics
``` ```
- Program (using OpenOCD for example) : - Program (using OpenOCD for example) :
``` ```
program pinetime-graphics.bin 0 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 ## Bootloader binary
The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4 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. It must be flash at address **0x00** in the internal flash memory.
Using OpenOCD: Using OpenOCD:
` `program bootloader-5.0.4.bin 0`
program bootloader-5.0.4.bin 0
`
## Application firmware image ## Application firmware image
Build the binary compatible with the booloader: Build the binary compatible with the booloader:
` `make pinetime-mcuboot-app`
make pinetime-mcuboot-app
`
The binary is located in *<build directory>/src/pinetime-mcuboot-app.bin*. The binary is located in *<build directory>/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 <mcuboot root>/scripts/imgtool.py with the following command line: 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 <mcuboot root>/scripts/imgtool.py with the following command line:
` ```sh
imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header <build directory>/src/pinetime-mcuboot-app.bin image.bin imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header <build directory>/src/pinetime-mcuboot-app.bin image.bin
` ```
The image must be then flashed at address **0x8000** in the internal flash memory. The image must be then flashed at address **0x8000** in the internal flash memory.
Using OpenOCD: Using OpenOCD:
` `program image.bin 0x8000`
program image.bin 0x8000
`
## OTA and DFU ## OTA and DFU
Pack the image into a .zip file for the NRF DFU protocol: 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 adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application image.bin dfu.zip
` ```
Use NRFConnect or dfu.py (in <project root>/bootloader/ota-dfu-python) to upload the zip file to the device: Use NRFConnect or dfu.py (in <project root>/bootloader/ota-dfu-python) to upload the zip file to the device:
` ```sh
sudo dfu.py -z /home/jf/nrf52/bootloader/dfu.zip -a <pinetime MAC address> --legacy sudo dfu.py -z /home/jf/nrf52/bootloader/dfu.zip -a <pinetime MAC address> --legacy
` ```
**Note** : dfu.py is a slightly modified version of [this repo](https://github.com/daniel-thompson/ota-dfu-python). **Note** : dfu.py is a slightly modified version of [this repo](https://github.com/daniel-thompson/ota-dfu-python).
### Firmware validation ### 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. 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. 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.

View file

@ -17,24 +17,24 @@ This is a Python program that uses `gatttool` (provided with the Linux BlueZ dri
### Main features: ### Main features:
* Perform OTA DFU to an nRF5 peripheral without an external USB BLE dongle. - 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). - 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. - 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). 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 ## Prerequisites
* BlueZ 5.4 or above - BlueZ 5.4 or above
* Python 3.6 - Python 3.6
* Python `pexpect` module (available via pip) - Python `pexpect` module (available via pip)
* Python `intelhex` module (available via pip) - Python `intelhex` module (available via pip)
## Firmware Build Requirement ## Firmware Build Requirement
* Your nRF5 peripheral firmware build method will produce a firmware file ending with either `*.hex` or `*.bin`. - 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`. - 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. - The typical naming convention is `application.bin` and `application.dat`, but this utility will accept other names.
## Generating init files ## Generating init files
@ -75,7 +75,6 @@ You can use the `hcitool lescan` to figure out the address of a DFU target, for
CD:E3:4A:47:1C:E4 <TARGET_NAME> CD:E3:4A:47:1C:E4 <TARGET_NAME>
CD:E3:4A:47:1C:E4 (unknown) CD:E3:4A:47:1C:E4 (unknown)
## Example Output ## Example Output
================================ ================================
@ -105,14 +104,14 @@ You can use the `hcitool lescan` to figure out the address of a DFU target, for
## TODO: ## TODO:
* Implement link-loss procedure for Legacy Controller. - Implement link-loss procedure for Legacy Controller.
* Update example output in readme. - Update example output in readme.
* Add makefile examples. - Add makefile examples.
* More code cleanup. - More code cleanup.
## Info & References ## 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 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 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) - [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) - [nrfutil](https://github.com/NordicSemiconductor/pc-nrfutil)

View file

@ -1,4 +1,5 @@
# BLE FS # 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. 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.
@ -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. - Unsigned 16-bit integer encoding the length of the file path.
- File path: UTF-8 encoded string that is _not_ null terminated. - 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` - Command (single byte): `0x51`
- Status (signed 8-bit integer) - 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 entry number
- Unsigned 32-bit integer encoding the total amount of entries - Unsigned 32-bit integer encoding the total amount of entries
- Flags: unsigned 32-bit integer - Flags: unsigned 32-bit integer
+ Bit 0: Set when entry is a directory - Bit 0: Set when entry is a directory
+ Bits 1-7: Reserved - Bits 1-7: Reserved
- Unsigned 64-bit integer encoding the unix timestamp of the modification time with nanosecond resolution - 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 - Unsigned 32-bit integer encoding the size of the file
- Path: UTF-8 encoded string that is _not_ null terminated. - Path: UTF-8 encoded string that is _not_ null terminated.

33
doc/InfiniTimeVision.md Normal file
View file

@ -0,0 +1,33 @@
# InfiniTime Vision
The purpose of this document is to steer efforts towards a common goal, and as such should be taken into consideration with all developments.
## InfiniTime
InfiniTime is a community-built smartwatch firmware.
It offers freedom and privacy advantages unavailable to users of proprietary wearable technology.
InfiniTime is not to be used for medical or other health tracking purposes.
## Core Principles
- Keep It Simple
- Reliability
- Battery efficiency
- Easy and simple navigation
- Behaviour should be predictable and easy to understand
- Prefer solid default experience over customization
- Personalization is achieved through custom watchfaces.
More options may be available through a companion app.
- Use standard protocols and methods
## Long term vision
The perfect version of InfiniTime would include:
- Capability to sideload apps and watchfaces
- Only a minimal feature set in the flashed firmware.
Users would add the features they want.
- Ports to other devices
- Translations
- Great user documentation

View file

@ -1,25 +1,28 @@
# Memory analysis # Memory analysis
The PineTime is equipped with the following memories: The PineTime is equipped with the following memories:
- The internal RAM : **64KB**
- The internal Flash : **512KB** - The internal RAM : **64KB**
- The external (SPI) Flash : **4MB** - 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... 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). 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 ## 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. 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: Here is a small description of these sections and where they end up in memory:
- **TEXT** = code (FLASH) - **TEXT** = code (FLASH)
- **RODATA** = constants (FLASH) - **RODATA** = constants (FLASH)
- **DATA** = initialized variables (FLASH + RAM) - **DATA** = initialized variables (FLASH + RAM)
- **BSS** = uninitialized variables (RAM) - **BSS** = uninitialized variables (RAM)
## Internal FLASH ## Internal FLASH
The internal flash memory stores the whole firmware: code, variable that are not default-initialized, constants... 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. 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,25 +44,27 @@ 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... 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
[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](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](./memoryAnalysis/puncover.png)
Puncover is really easy to install: Puncover is really easy to install:
- Clone the repo and cd into the cloned directory - Clone the repo and cd into the cloned directory
- Setup a venv - Setup a venv
- `python -m virtualenv venv` - `python -m virtualenv venv`
- `source venv/bin/activate` - `source venv/bin/activate`
- Install : `pip install .` - Install : `pip install .`
- Run : `puncover --gcc_tools_base=/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi- --elf_file /path/to/build/directory/src/pinetime-app-1.1.0.out --src_root /path/to/sources --build_dir /path/to/build/directory` - 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 - Replace
* `/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin` with the path to your gcc-arm-none-eabi toolchain - `/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/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/sources` with the path to the root folder of the sources (checkout directory)
* `/path/to/build/directory` with the path to the build directory - `/path/to/build/directory` with the path to the build directory
- Launch a browser at http://localhost:5000/ - Launch a browser at http://localhost:5000/
### Analysis ### 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. 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) ![Puncover](./memoryAnalysis/puncover-all-symbols.png)
@ -69,18 +74,22 @@ This way, you can easily check what needs to be optimized. We should find a way
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 ### 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
RAM memory contains all the data that can be modified at run-time: variables, stack, heap... RAM memory contains all the data that can be modified at run-time: variables, stack, heap...
### Data ### Data
RAM memory can be *statically* allocated, meaning that the size and position of the data are known at compile-time: 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. 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. 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]
@ -90,13 +99,16 @@ int main() {
``` ```
#### Analysis #### 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 ### 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). 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. 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: #### Fill the stack section by a known pattern:
Edit <NRFSDK>/modules/nrfx/mdk/gcc_startup_nrf52.S and add the following code after the copy of the data from read only memory to RAM at around line 243: Edit <NRFSDK>/modules/nrfx/mdk/gcc_startup_nrf52.S and add the following code after the copy of the data from read only memory to RAM at around line 243:
``` ```
@ -138,6 +150,7 @@ bne .L_fill
``` ```
#### Dump RAM memory and check usage #### Dump RAM memory and check usage
Dumping the content of the ram is easy using JLink debugger and `nrfjprog`: Dumping the content of the ram is easy using JLink debugger and `nrfjprog`:
``` ```
@ -194,12 +207,15 @@ On the following dump, the maximum stack usage is 520 bytes (0xFFFF - 0xFDF8):
``` ```
#### Analysis #### Analysis
According to my experimentations, we don't use the stack that much, and 8192 bytes is probably way too big for InfiniTime! According to my experimentations, we don't use the stack that much, and 8192 bytes is probably way too big for InfiniTime!
#### Links #### Links
- https://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-851035070
- https://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-851035070
### Heap ### 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`...). 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: 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 #### 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. 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: 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()`. 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): 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 - hr task = 304
#### Links #### 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://github.com/InfiniTimeOrg/InfiniTime/issues/313#issuecomment-851035625
- 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-1-calculating-stack-size/
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-3-avoiding-heap-errors/ - 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 ## LVGL
I did a deep analysis of the usage of the buffer dedicated to lvgl (managed by lv_mem). 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. 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. 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). 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 heap and task stack
FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h* FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h*
FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes...). FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes...).
@ -276,7 +298,6 @@ The function `xPortGetFreeHeapSize()` returns the amount of memory available in
NRF_LOG_INFO("Free heap : %d", xPortGetFreeHeapSize()); 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: 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++) { for (int i = 0; i < nb; i++) {
NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark); NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark);
``` ```

View file

@ -1,17 +1,23 @@
# Motion Service # Motion Service
## Introduction ## Introduction
The motion service exposes step count and raw X/Y/Z motion value as READ and NOTIFY characteristics. The motion service exposes step count and raw X/Y/Z motion value as READ and NOTIFY characteristics.
## Service ## Service
The service UUID is **00030000-78fc-48fe-8e23-433b3a1942d0** The service UUID is **00030000-78fc-48fe-8e23-433b3a1942d0**
## Characteristics ## Characteristics
### Step count (UUID 00030001-78fc-48fe-8e23-433b3a1942d0) ### Step count (UUID 00030001-78fc-48fe-8e23-433b3a1942d0)
The current number of steps represented as a single `uint32_t` (4 bytes) value. The current number of steps represented as a single `uint32_t` (4 bytes) value.
### Raw motion values (UUID 00030002-78fc-48fe-8e23-433b3a1942d0) ### Raw motion values (UUID 00030002-78fc-48fe-8e23-433b3a1942d0)
The current raw motion values. This is a 3 `int16_t` array: The current raw motion values. This is a 3 `int16_t` array:
- [0] : X - [0] : X
- [1] : Y - [1] : Y
- [2] : Z - [2] : Z

View file

@ -1,5 +1,7 @@
# Navigation Service # Navigation Service
## Introduction ## 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 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: 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 progress (uint8) - Percent complete of total route, value 0-100
## Service ## Service
The service UUID is 00010000-78fc-48fe-8e23-433b3a1942d0 The service UUID is 00010000-78fc-48fe-8e23-433b3a1942d0
## Characteristics ## Characteristics
## Flags (UUID 00010001-78fc-48fe-8e23-433b3a1942d0) ## 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. 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) ## 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". 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) ## Man Dist (UUID 00010003-78fc-48fe-8e23-433b3a1942d0)
This is a short string describing the distance to the upcoming instruction such as "50 m". This is a short string describing the distance to the upcoming instruction such as "50 m".
## Progress (UUID 00010004-78fc-48fe-8e23-433b3a1942d0) ## 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. The percent complete in a uint8. The watch displays this as an overall progress in a progress bar.
## Full icon list ## Full icon list
* arrive
* arrive-left - arrive
* arrive-right - arrive-left
* arrive-straight - arrive-right
* close - arrive-straight
* continue - close
* continue-left - continue
* continue-right - continue-left
* continue-slight-left - continue-right
* continue-slight-right - continue-slight-left
* continue-straight - continue-slight-right
* continue-uturn - continue-straight
* depart - continue-uturn
* depart-left - depart
* depart-right - depart-left
* depart-straight - depart-right
* end-of-road-left - depart-straight
* end-of-road-right - end-of-road-left
* ferry - end-of-road-right
* flag - ferry
* fork - flag
* fork-left - fork
* fork-right - fork-left
* fork-slight-left - fork-right
* fork-slight-right - fork-slight-left
* fork-straight - fork-slight-right
* invalid - fork-straight
* invalid-left - invalid
* invalid-right - invalid-left
* invalid-slight-left - invalid-right
* invalid-slight-right - invalid-slight-left
* invalid-straight - invalid-slight-right
* invalid-uturn - invalid-straight
* merge-left - invalid-uturn
* merge-right - merge-left
* merge-slight-left - merge-right
* merge-slight-right - merge-slight-left
* merge-straight - merge-slight-right
* new-name-left - merge-straight
* new-name-right - new-name-left
* new-name-sharp-left - new-name-right
* new-name-sharp-right - new-name-sharp-left
* new-name-slight-left - new-name-sharp-right
* new-name-slight-right - new-name-slight-left
* new-name-straight - new-name-slight-right
* notification-left - new-name-straight
* notification-right - notification-left
* notification-sharp-left - notification-right
* notification-sharp-right - notification-sharp-left
* notification-slight-left - notification-sharp-right
* notification-slight-right - notification-slight-left
* notification-straight - notification-slight-right
* off-ramp-left - notification-straight
* off-ramp-right - off-ramp-left
* off-ramp-sharp-left - off-ramp-right
* off-ramp-sharp-right - off-ramp-sharp-left
* off-ramp-slight-left - off-ramp-sharp-right
* off-ramp-slight-right - off-ramp-slight-left
* off-ramp-straight - off-ramp-slight-right
* on-ramp-left - off-ramp-straight
* on-ramp-right - on-ramp-left
* on-ramp-sharp-left - on-ramp-right
* on-ramp-sharp-right - on-ramp-sharp-left
* on-ramp-slight-left - on-ramp-sharp-right
* on-ramp-slight-right - on-ramp-slight-left
* on-ramp-straight - on-ramp-slight-right
* rotary - on-ramp-straight
* rotary-left - rotary
* rotary-right - rotary-left
* rotary-sharp-left - rotary-right
* rotary-sharp-right - rotary-sharp-left
* rotary-slight-left - rotary-sharp-right
* rotary-slight-right - rotary-slight-left
* rotary-straight - rotary-slight-right
* roundabout - rotary-straight
* roundabout-left - roundabout
* roundabout-right - roundabout-left
* roundabout-sharp-left - roundabout-right
* roundabout-sharp-right - roundabout-sharp-left
* roundabout-slight-left - roundabout-sharp-right
* roundabout-slight-right - roundabout-slight-left
* roundabout-straight - roundabout-slight-right
* turn-left - roundabout-straight
* turn-right - turn-left
* turn-sharp-left - turn-right
* turn-sharp-right - turn-sharp-left
* turn-slight-left - turn-sharp-right
* turn-slight-right - turn-slight-left
* turn-stright - turn-slight-right
* updown - turn-stright
* uturn - updown
- uturn

View file

@ -1,33 +1,36 @@
# Build a stub for PineTime using NRF52-DK # Build a stub for PineTime using NRF52-DK
[NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK) is the official development kit for the NRF52832 SoC from Nordic Semiconductor used in the PineTime. [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: 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 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: 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 - heart rate sensor
* touchpad - SPI flash
* accelerometer - touchpad
- accelerometer
These devices could be added on this stub, but I do not have the parts to try them out for now. 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") ![Pinetime stub](../images/pinetimestub1.jpg "PinetimeStub")
Here are the parts you need to build this simulator: 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)) - [NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK)
* A push-button (the one I use comes from a previous project build around ESP8266 board Wemos D1 Mini). - An ST7889 display (I bought [this one](https://www.aliexpress.com/item/32859772356.html?spm=a2g0s.9042311.0.0.1b774c4dSoc4Xz))
* Dupont wires - 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: You just need to make the following connections:
| NRF52-DK | ST7889 display | | NRF52-DK | ST7889 display |
| ---------|--------------- | | -------- | -------------- |
| VDD | VCC | | VDD | VCC |
| GND | GND | | GND | GND |
| P0.03 | SDA | | P0.03 | SDA |
@ -35,11 +38,10 @@ You just need to make the following connections:
| P0.02 | SCL | | P0.02 | SCL |
| P0.18 | DC | | P0.18 | DC |
| NRF52-DK | Push Button |
| NRF52-DK | Push Button | | -------- | ------------------------- |
| ---------|----------------------- | | P0.13 | Button IN (D3 in my case) |
| P0.13 | Button IN (D3 in my case) | | GND | GND |
| 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: 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:

View file

@ -1,6 +1,9 @@
# The SPI LCD driver # The SPI LCD driver
## Introduction ## 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: 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 - an on-chip display data RAM that can store the whole framebuffer
- partial screen update - partial screen update
- hardware assisted vertical scrolling - hardware assisted vertical scrolling
@ -11,6 +14,7 @@ When you want to write a device driver for a specific component, its datasheet i
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: 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; - 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; - Then, I tried to display some pixels on the screen;
- Next, I wanted to display squares, colors and text; - 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. I'll describe all these steps in the following chapters.
## The datasheet ## 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. 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. 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. 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 : 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 - 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 - Display Data Ram : how is the memory organized
- Power On/Off sequence - Power On/Off sequence
@ -33,7 +39,6 @@ You'll want to read some part with more attention :
## One Pixel at a time ## One Pixel at a time
## Bulk transfers ## Bulk transfers
## DMA ## DMA
@ -41,6 +46,7 @@ You'll want to read some part with more attention :
## IRQ ## IRQ
## Bare metal integration ## Bare metal integration
Integration customisée dans la lib GFX que j'ai écrite Integration customisée dans la lib GFX que j'ai écrite
## Integration with LittleVGL ## Integration with LittleVGL

View file

@ -1,4 +1,5 @@
# How to flash InfiniTime using the SWD interface # 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: 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) ![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: Then, using your SWD tool, flash these file at the following offsets:
- bootloader.bin : **0x0000** - bootloader.bin : **0x0000**
- image-x.y.z.bin : **0x8000** - image-x.y.z.bin : **0x8000**
Reset and voilà, you're running InfiniTime on your PineTime! Reset and voilà, you're running InfiniTime on your PineTime!

View file

@ -1,8 +1,8 @@
# Bluetooth Low-Energy : # Bluetooth Low-Energy :
## Introduction
This page describes the BLE implementation and API built in this firmware.
**Note**: I'm a beginner in BLE related technologies and the information in this document reflects my current knowledge and understanding of the BLE stack. This information might be erroneous or incomplete. Feel free to submit a PR if you think you can improve it. ## Introduction
This page describes the BLE implementation and API built in this firmware.
--- ---
@ -40,6 +40,7 @@ This page describes the BLE implementation and API built in this firmware.
--- ---
## BLE Connection ## 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. 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. 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.
@ -60,6 +61,7 @@ The documentation for BLE FS can be found here:
--- ---
## BLE UUIDs ## 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: 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:
@ -72,34 +74,38 @@ When the service does not exist in the BLE specification, InfiniTime implements
The following custom services are implemented in InfiniTime: The following custom services are implemented in InfiniTime:
- Since InfiniTime 0.8: - Since InfiniTime 0.8:
* Music Service : 00000000-78fc-48fe-8e23-433b3a1942d0
- Music Service : `00000000-78fc-48fe-8e23-433b3a1942d0`
- Since InfiniTime 0.11: - Since InfiniTime 0.11:
* [Navigation Service](NavigationService.md) : 00010000-78fc-48fe-8e23-433b3a1942d0
- [Navigation Service](NavigationService.md) : `00010000-78fc-48fe-8e23-433b3a1942d0`
- Since InfiniTime 0.13 - 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: - 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: - 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 ## BLE services
[List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/) [List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/)
### CTS ### CTS
[Current Time Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.current_time.xml) [Current Time Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.current_time.xml)
### ANS ### ANS
[Alert Notification Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.alert_notification.xml) [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") ![ANS sequence diagram](./ble/ans_sequence.png "ANS sequence diagram")

View file

@ -1,9 +1,11 @@
# Branches # 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 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: 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. 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.

View file

@ -1,24 +1,29 @@
# Build # Build
## Dependencies ## Dependencies
To build this project, you'll need: To build this project, you'll need:
- A cross-compiler : [ARM-GCC (9-2020-q2-update)](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads/9-2020-q2-update)
- 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) - A cross-compiler : [ARM-GCC (10.3-2021.10)](https://developer.arm.com/downloads/-/gnu-rm)
- The Python 3 modules `cbor`, `intelhex`, `click` and `cryptography` modules for the `mcuboot` tool (see [requirements.txt](../tools/mcuboot/requirements.txt)) - 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)
- To keep the system clean, you can install python modules into a python virtual environment (`venv`) - The Python 3 modules `cbor`, `intelhex`, `click` and `cryptography` modules for the `mcuboot` tool (see [requirements.txt](../tools/mcuboot/requirements.txt))
```sh - To keep the system clean, you can install python modules into a python virtual environment (`venv`)
python -m venv .venv ```sh
source .venv/bin/activate python -m venv .venv
python -m pip install wheel source .venv/bin/activate
python -m pip install -r tools/mcuboot/requirements.txt 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 - A reasonably recent version of CMake (I use 3.16.5)
- see [lv_font_conv](https://github.com/lvgl/lv_font_conv#install-the-script) - lv_font_conv, to generate the font .c files
- install npm (commonly done via the package manager, ensure node's version is at least 12) - see [lv_font_conv](https://github.com/lvgl/lv_font_conv#install-the-script)
- install lv_font_conv: `npm install lv_font_conv` - 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 ## Build steps
### Clone the repo ### Clone the repo
``` ```
git clone https://github.com/InfiniTimeOrg/InfiniTime.git git clone https://github.com/InfiniTimeOrg/InfiniTime.git
cd InfiniTime cd InfiniTime
@ -26,12 +31,14 @@ git submodule update --init
mkdir build mkdir build
cd build cd build
``` ```
### Project generation using CMake ### Project generation using CMake
CMake configures the project according to variables you specify the command line. The variables are: CMake configures the project according to variables you specify the command line. The variables are:
Variable | Description | Example| Variable | Description | Example|
----------|-------------|--------| ----------|-------------|--------|
**ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`-DARM_NONE_EABI_TOOLCHAIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2020-q2-update/`| **ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`-DARM_NONE_EABI_TOOLCHAIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-10.3-2021.10/`|
**NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`| **NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`|
**USE_JLINK, USE_GDB_CLIENT and USE_OPENOCD**|Enable *JLink* mode, *GDB Client* (Black Magic Probe) mode or *OpenOCD* mode (set the one you want to use to `1`)|`-DUSE_JLINK=1` **USE_JLINK, USE_GDB_CLIENT and USE_OPENOCD**|Enable *JLink* mode, *GDB Client* (Black Magic Probe) mode or *OpenOCD* mode (set the one you want to use to `1`)|`-DUSE_JLINK=1`
**CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug` **CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
@ -39,33 +46,39 @@ CMake configures the project according to variables you specify the command line
**GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb` **GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb`
**GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0` **GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0`
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1` **BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
**WATCH_COLMI_P8**|Use pin configuration for Colmi P8 watch|`-DWATCH_COLMI_P8=1` **BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [lv_img_conv](https://github.com/lvgl/lv_img_conv). |`-DBUILD_RESOURCES=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. 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. 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. 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 command line for JLink
``` ```
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../ cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../
``` ```
#### CMake command line for GDB Client (Black Magic Probe) #### 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 -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 command line for OpenOCD
``` ```
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DGDB_CLIENT_BIN_PATH=[optional] ../ cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DGDB_CLIENT_BIN_PATH=[optional] ../
``` ```
### Build the project ### Build the project
During the project generation, CMake created the following targets: During the project generation, CMake created the following targets:
- **FLASH_ERASE** : mass erase the flash memory of the NRF52. - **FLASH_ERASE** : mass erase the flash memory of the NRF52.
- **FLASH_pinetime-app** : flash the firmware into the NRF52. - **FLASH_pinetime-app** : flash the firmware into the NRF52.
- **pinetime-app** : build the standalone (without bootloader support) version of the firmware. - **pinetime-app** : build the standalone (without bootloader support) version of the firmware.
@ -78,185 +91,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. 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: Build:
``` ```
make -j pinetime-app make -j pinetime-app
``` ```
List of files generated: List of files generated:
Binary files are generated into the folder `src`: 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-app.bin, .hex and .out** : standalone firmware in bin, hex and out formats.
- **pinetime-mcuboot-app.bin, .hex and .out** : firmware with bootloader support in bin, hex and out formats. - **pinetime-app.map** : map file
- **pinetime-mcuboot-app.map** : map file - **pinetime-mcuboot-app.bin, .hex and .out** : firmware with bootloader support in bin, hex and out formats.
- **pinetime-mcuboot-app-image** : MCUBoot image of the firmware - **pinetime-mcuboot-app.map** : map file
- **pinetime-mcuboot-ap-dfu** : DFU file of the firmware - **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** The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader**
### Program and run ### Program and run
#### Using CMake targets #### Using CMake targets
These target have been configured during the project generation by CMake according to the parameters you provided to the command line. These target have been configured during the project generation by CMake according to the parameters you provided to the command line.
Mass erase: Mass erase:
``` ```
make FLASH_ERASE make FLASH_ERASE
``` ```
Flash the application: Flash the application:
``` ```
make FLASH_pinetime-app make FLASH_pinetime-app
``` ```
### Using 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)
DLL version V6.70d, compiled Apr 16 2020 17:59:25
Connecting to J-Link via USB...O.K.
Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Mar 17 2020 14:43:00
Hardware version: V1.00
S/N: 682579153
License(s): RDI, FlashBP, FlashDL, JFlash, GDB
VTref=3.300V
Device "NRF52" selected.
Connecting to target via SWD
InitTarget() start
InitTarget() end
Found SW-DP with ID 0x2BA01477
DPIDR: 0x2BA01477
Scanning AP map to find all available APs
AP[2]: Stopped AP scan as end of AP map has been reached
AP[0]: AHB-AP (IDR: 0x24770011)
AP[1]: JTAG-AP (IDR: 0x02880000)
Iterating through AP map to find AHB-AP to use
AP[0]: Core found
AP[0]: AHB-AP ROM base: 0xE00FF000
CPUID register: 0x410FC241. Implementer code: 0x41 (ARM)
Found Cortex-M4 r0p1, Little endian.
FPUnit: 6 code (BP) slots and 2 literal slots
CoreSight components:
ROMTbl[0] @ E00FF000
ROMTbl[0][0]: E000E000, CID: B105E00D, PID: 000BB00C SCS-M7
ROMTbl[0][1]: E0001000, CID: B105E00D, PID: 003BB002 DWT
ROMTbl[0][2]: E0002000, CID: B105E00D, PID: 002BB003 FPB
ROMTbl[0][3]: E0000000, CID: B105E00D, PID: 003BB001 ITM
ROMTbl[0][4]: E0040000, CID: B105900D, PID: 000BB9A1 TPIU
ROMTbl[0][5]: E0041000, CID: B105900D, PID: 000BB925 ETM
Cortex-M4 identified.
J-Link>
```
Use the command loadfile to program the .hex file:
```
J-Link>loadfile pinetime-app.hex
Downloading file [pinetime-app.hex]...
Comparing flash [100%] Done.
Erasing flash [100%] Done.
Programming flash [100%] Done.
Verifying flash [100%] Done.
J-Link: Flash download: Bank 0 @ 0x00000000: 1 range affected (4096 bytes)
J-Link: Flash download: Total time needed: 0.322s (Prepare: 0.043s, Compare: 0.202s, Erase: 0.003s, Program: 0.064s, Verify: 0.000s, Restore: 0.007s)
O.K.
```
Then reset (r) and start (g) the CPU:
```
J-Link>r
Reset delay: 0 ms
Reset type NORMAL: Resets core & peripherals via SYSRESETREQ & VECTRESET bit.
Reset: Halt core after reset via DEMCR.VC_CORERESET.
Reset: Reset device via AIRCR.SYSRESETREQ.
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
```
$ JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1
```
Start JLinkRTTClient
```
$ JLinkRTTClient
```
### Using GDB and Black Magic Probe (BMP)
Enter the following command into GDB:
```
target extended-remote /dev/ttyACM0
monitor swdp_scan
attach 1
file ./pinetime-app-full.hex
load
run
```
Example :
```
$ /home/jf/nrf52/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gdb
(gdb) target extended-remote /dev/ttyACM0
Remote debugging using /dev/ttyACM0
(gdb) monitor swdp_scan
Target voltage: ABSENT!
Available Targets:
No. Att Driver
1 Nordic nRF52 M3/M4
2 Nordic nRF52 Access Port
(gdb) attach 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0xfffffffe in ?? ()
(gdb) file ./pinetime-app-full.hex
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from ./pinetime-app-full.hex...
(No debugging symbols found in ./pinetime-app-full.hex)
(gdb) load
Loading section .sec1, size 0xb00 lma 0x0
Loading section .sec2, size 0xf000 lma 0x1000
Loading section .sec3, size 0x10000 lma 0x10000
Loading section .sec4, size 0x5150 lma 0x20000
Loading section .sec5, size 0xa000 lma 0x26000
Loading section .sec6, size 0x10000 lma 0x30000
Loading section .sec7, size 0xdf08 lma 0x40000
Start address 0x0, load size 314200
Transfer rate: 45 KB/sec, 969 bytes/write.
```
### How to generate files needed by the factory ### 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. 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). 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 #### merged-internal.hex
First, convert the bootloader to hex: First, convert the bootloader to hex:
```
<ARM TOOLCHAIN>/bin/arm-none-eabi-objcopy -I binary -O ihex ./bootloader.bin ./bootloader.hex ```
``` <ARM TOOLCHAIN>/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). 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: Then, convert the MCUBoot image of InfiniTime:
``` ```
<ARM TOOLCHAIN>/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 <ARM TOOLCHAIN>/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). 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). 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**: Finally, merge them together with **mergehex**:
``` ```
/opt/mergehex/mergehex -m ./bootloader.hex ./pinetime-mcuboot-app-image-1.6.0.hex -o merged-internal.hex /opt/mergehex/mergehex -m ./bootloader.hex ./pinetime-mcuboot-app-image-1.6.0.hex -o merged-internal.hex
``` ```
@ -264,4 +161,5 @@ Finally, merge them together with **mergehex**:
This file must be flashed at offset **0x00** of the internal memory of the NRF52832. This file must be flashed at offset **0x00** of the internal memory of the NRF52832.
#### spinor.bin #### 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. 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.

View file

@ -5,11 +5,11 @@ These images make the build of the firmware and the generation of the DFU file f
Based on Ubuntu 22.04 with the following build dependencies: Based on Ubuntu 22.04 with the following build dependencies:
* ARM GCC Toolchain - ARM GCC Toolchain
* nRF SDK - nRF SDK
* MCUBoot - MCUBoot
* adafruit-nrfutil - adafruit-nrfutil
* lv_font_conv - lv_font_conv
## Run a container to build the project ## Run a container to build the project
@ -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: 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 ## Build the image

View file

@ -8,7 +8,7 @@ To support as many setups as possible the VS Code configuration files expect the
Variable | Description | Example Variable | Description | Example
----------|-------------|-------- ----------|-------------|--------
**ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`export ARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update` **ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`export ARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-10.3-2021.10`
**NRF5_SDK_PATH**|path to the NRF52 SDK|`export NRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345` **NRF5_SDK_PATH**|path to the NRF52 SDK|`export NRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345`
## VS Code Extensions ## VS Code Extensions
@ -26,8 +26,6 @@ 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. Cortex-Debug is only required for interactive debugging using VS Codes built in GDB support.
## VS Code/Docker DevContainer ## VS Code/Docker DevContainer
The .devcontainer folder contains the configuration and scripts for using a Docker dev container for building InfiniTime The .devcontainer folder contains the configuration and scripts for using a Docker dev container for building InfiniTime
@ -37,16 +35,13 @@ Using the [Remote-Containers](https://marketplace.visualstudio.com/items?itemNam
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 ### DevContainer on Ubuntu
To use the DevContainer configuration on Ubuntu based systems two changes need to be made: 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: 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"], `"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:
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",`
``"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:
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`.
``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.
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.

View file

@ -1,5 +1,7 @@
# Apps # Apps
This page will teach you: This page will teach you:
- what screens and apps are in InfiniTime - what screens and apps are in InfiniTime
- how to implement your own app - 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. By default, apps only do something (as in a function is executed) when they are created or when a touch event is detected.
## Interface ## Interface
Every app class has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. 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. 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. 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. If you have any doubts, you can always look at how the other apps function for reference.
### Continuous updating ### Continuous updating
If your app needs to be updated continuously, you can do so by overriding the `Refresh()` function in your class 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. and calling `lv_task_create` inside the constructor.
An example call could look like this: An example call could look like this:
```cpp ```cpp
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); 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. The function `RefreshTaskCallback` is inherited from `Screen` and just calls your `Refresh` function.
## Creating your own app ## Creating your own app
A minimal app could look like this: A minimal app could look like this:
MyApp.h: MyApp.h:
```cpp ```cpp
#pragma once #pragma once
@ -60,6 +67,7 @@ namespace Pinetime {
``` ```
MyApp.cpp: MyApp.cpp:
```cpp ```cpp
#include "displayapp/screens/MyApp.h" #include "displayapp/screens/MyApp.h"
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
@ -77,6 +85,7 @@ MyApp::~MyApp() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
``` ```
Both of these files should be in [displayapp/screens/](/src/displayapp/screens/) 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. or [displayapp/screens/settings/](/src/displayapp/screens/settings/) if it's a setting app.

View file

@ -1,7 +1,9 @@
# Introduction to the code # 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. 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 ## FreeRTOS
Infinitime is based on FreeRTOS, a real-time operating system. Infinitime is based on FreeRTOS, a real-time operating system.
FreeRTOS provides several quality of life abstractions (for example easy software timers) FreeRTOS provides several quality of life abstractions (for example easy software timers)
and most importantly supports multiple tasks. 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. 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
Tasks are created by calling `xTaskCreate` and passing a function with the signature `void functionName(void*)`. 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). 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**. 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. You can use `configMINIMAL_STACK_SIZE` which is currently set to 120 words.
## Controllers ## Controllers
Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. 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. Some of them interface with drivers, others are the driver for the resource.
The resources provided don't have to be hardware-based. 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. They reside in [components/](/src/components/) inside their own subfolder.
## Apps ## Apps
For more detail see the [Apps page](./Apps.md) For more detail see the [Apps page](./Apps.md)
## Bluetooth ## 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/). 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/).

View file

@ -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 - **Indentation** : 2 spaces, no tabulation
- **Opening brace** at the end of the line
The most important rule to follow is to try to keep the code as easy to read and maintain as possible. - **Naming** : Choose self-describing variable name
- **class** : PascalCase
Using an autoformatter is highly recommended, but make sure it's configured properly. - **namespace** : PascalCase
- **variable** : camelCase, **no** prefix/suffix (`_`, `m_`,...) for class members
There are preconfigured autoformatter rules for: - **Include guard** : `#pragma once` (no `#ifdef __MODULE__ / #define __MODULE__ / #endif`)
- **Includes** :
* CLion (IntelliJ) in [.idea/codeStyles/Project.xml](/.idea/codeStyles/Project.xml) - files from the project : `#include "relative/path/to/the/file.h"`
* `clang-format` - external files and std : `#include <file.h>`
- use includes relative to included directories like `src`, not relative to the current file. Don't do: `#include "../file.h"`
Also use `clang-tidy` to check the code for other issues. - 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.
If there are no preconfigured rules for your IDE, you can use one of the existing ones to configure your IDE. ```c++
// Examples:
- **Indentation** : 2 spaces, no tabulation auto* app = static_cast<DisplayApp*>(instance);
- **Opening brace** at the end of the line auto number = static_cast<uint8_t>(variable);
- **Naming** : Choose self-describing variable name uint8_t returnValue = MyFunction();
- **class** : PascalCase ```
- **namespace** : PascalCase - Use `nullptr` instead of `NULL`
- **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 <file.h>`
- 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<DisplayApp*>(instance);`
- `auto number = static_cast<uint8_t>(variable);`
- `uint8_t returnValue = MyFunction();`
- Use nullptr instead of NULL

View file

@ -1,61 +1,19 @@
# How to contribute? # 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 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
Documentation might be incomplete, or not clear enough, and it is always possible to improve it with better wording, pictures, photo, video,... 2. Make changes in this branch
- Write code that satisfies the [coding conventions](/doc/coding-convention.md)
As the documentation is part of the source code, you can submit your improvements to the documentation by submitting a pull request (see below). - 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
## 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 :)
Your contributions are more than welcome! 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.

View file

@ -1,4 +1,5 @@
# Using the releases # 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. 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. 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 ## Files included in the release notes
### Standalone firmware ### Standalone firmware
This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flashed at offset 0, meaning it will erase any bootloader that might be present in memory. This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flashed at offset 0, meaning it will erase any bootloader that might be present in memory.
- **pinetime-app.out** : Output file of GCC containing debug symbols, useful if you want to debug the firmware using GDB. - **pinetime-app.out** : Output file of GCC containing debug symbols, useful if you want to debug the firmware using GDB.
- **pinetime-app.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it. - **pinetime-app.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it.
- **pintime-app.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed. - **pintime-app.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed.
- **pinetime-app.map** : Map file containing all the symbols, addresses in memory,... - **pinetime-app.map** : Map file containing all the symbols, addresses in memory,...
**This firmware must be flashed at address 0x00 in the main flash memory** **This firmware must be flashed at address 0x00 in the main flash memory**
### Bootloader ### Bootloader
The bootloader is maintained by [lupyuen](https://github.com/lupyuen) and is a binary version of [this release](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4). The bootloader is maintained by [lupyuen](https://github.com/lupyuen) and is a binary version of [this release](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4).
- **bootloader.hex** : Firmware in Intel HEX file format. - **bootloader.hex** : Firmware in Intel HEX file format.
**This firmware must be flashed at address 0x00 in the main flash memory**
**This firmware must be flashed at address 0x00 in the main flash memory**
### 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). 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.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. - **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. - **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.map** : Map file containing all the symbols, addresses in memory,...
**This firmware must be flashed at address 0x00 in the main flash memory** **This firmware must be flashed at address 0x00 in the main flash memory**
### Firmware with bootloader ### Firmware with bootloader
This firmware is intended to be used with our [MCUBoot-based bootloader](../bootloader/README.md). This firmware is intended to be used with our [MCUBoot-based bootloader](../bootloader/README.md).
- **pinetime-mcuboot-app-image.hex**: Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed 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: 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.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.hex** : Firmware in Intel HEX file format.
- **pinetime-mcuboot-app.bin** : Firmware in binary 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.map** : Map file containing all the symbols, addresses in memory,...
### OTA (Update the firmware Over-The-Air) ### 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**. 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 ! **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.

48
doc/gdb.md Normal file
View file

@ -0,0 +1,48 @@
# Flashing the firmware with GDB and Black Magic Probe (BMP)
Enter the following command into GDB:
```
target extended-remote /dev/ttyACM0
monitor swdp_scan
attach 1
file ./pinetime-app-full.hex
load
run
```
Example :
```
$ /home/jf/nrf52/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gdb
(gdb) target extended-remote /dev/ttyACM0
Remote debugging using /dev/ttyACM0
(gdb) monitor swdp_scan
Target voltage: ABSENT!
Available Targets:
No. Att Driver
1 Nordic nRF52 M3/M4
2 Nordic nRF52 Access Port
(gdb) attach 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0xfffffffe in ?? ()
(gdb) file ./pinetime-app-full.hex
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from ./pinetime-app-full.hex...
(No debugging symbols found in ./pinetime-app-full.hex)
(gdb) load
Loading section .sec1, size 0xb00 lma 0x0
Loading section .sec2, size 0xf000 lma 0x1000
Loading section .sec3, size 0x10000 lma 0x10000
Loading section .sec4, size 0x5150 lma 0x20000
Loading section .sec5, size 0xa000 lma 0x26000
Loading section .sec6, size 0x10000 lma 0x30000
Loading section .sec7, size 0xdf08 lma 0x40000
Start address 0x0, load size 314200
Transfer rate: 45 KB/sec, 969 bytes/write.
```

View file

@ -6,9 +6,9 @@ A **firmware** is software running on the embedded hardware of a device.
InfiniTime has three distinct firmwares: InfiniTime has three distinct firmwares:
- **[InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime)** is the operating system. - **[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 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. - **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. **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: 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 **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 **red** to load the recovery firmware. This recovery firmware only provides BLE connectivity and OTA functionality.
More info about the bootloader in [its project page](https://github.com/JF002/pinetime-mcuboot-bootloader/blob/master/README.md). More info about the bootloader in [its project page](https://github.com/JF002/pinetime-mcuboot-bootloader/blob/master/README.md).

View file

@ -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. 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) - 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 NRFConnect](/doc/gettingStarted/time-nrfconnect.md)
- Sync the time with your browser https://hubmartin.github.io/WebBLEWatch/ - 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) 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 On the top right there are status icons
- The battery icon shows roughly how much charge is remaining - The battery icon shows roughly how much charge is remaining
- The Bluetooth icon is visible when the watch is connected to a companion app - 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. - 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. 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) ![Quick actions](ui/quicksettings.jpg)
![Settings](ui/settings.jpg) ![Settings](ui/settings.jpg)
- Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu. - 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 **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 - Swipe **right** to display the Quick Actions menu. This menu allows you to
- Set the brightness of the display - Set the brightness of the display
- Start the **flashlight** app - Start the **flashlight** app
- Enable/disable notifications (Do Not Disturb mode) - Enable/disable notifications (Do Not Disturb mode)
- Enter the **settings** menu - Enter the **settings** menu
- Swipe up and down to see all options - Swipe up and down to see all options
- Click the button to go back a screen. - 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) - You can hold the button for a short time to return to the watch face. (version >1.7.0)

View file

@ -19,4 +19,5 @@ Don't forget to **validate** your firmware. In the InfiniTime go to the settings
![NRFConnect 3](nrfconnect3.jpg) ![NRFConnect 3](nrfconnect3.jpg)
# Demo # Demo
[This video](https://seafile.codingfield.com/f/a52b69683a05472a90c7/) shows how to use NRFConnect to update the firmware running on the Pinetime. [This video](https://seafile.codingfield.com/f/a52b69683a05472a90c7/) shows how to use NRFConnect to update the firmware running on the Pinetime.

View file

@ -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. We have prepared instructions for flashing InfiniTime with Gadgetbridge and NRFConnect.
- [Updating with Gadgetbridge](/doc/gettingStarted/ota-gadgetbridge.md) - [Updating with Gadgetbridge](/doc/gettingStarted/ota-gadgetbridge.md)
- [Updating with NRFConnect](/doc/gettingStarted/ota-nrfconnect.md) - [Updating with NRFConnect](/doc/gettingStarted/ota-nrfconnect.md)
## Firmware validation ## 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: 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* - From the watchface, swipe **right** to display the *quick settings menu*
- Open settings by tapping the cogwheel on the bottom right - Open settings by tapping the cogwheel on the bottom right
- Swipe up until you find an entry named **Firmware** and tap on it - 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 - If the firmware is not validated yet, you can either validate the running firmware, or reset and revert to the previous firmware version

86
doc/jlink.md Normal file
View file

@ -0,0 +1,86 @@
# 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)
DLL version V6.70d, compiled Apr 16 2020 17:59:25
Connecting to J-Link via USB...O.K.
Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Mar 17 2020 14:43:00
Hardware version: V1.00
S/N: 682579153
License(s): RDI, FlashBP, FlashDL, JFlash, GDB
VTref=3.300V
Device "NRF52" selected.
Connecting to target via SWD
InitTarget() start
InitTarget() end
Found SW-DP with ID 0x2BA01477
DPIDR: 0x2BA01477
Scanning AP map to find all available APs
AP[2]: Stopped AP scan as end of AP map has been reached
AP[0]: AHB-AP (IDR: 0x24770011)
AP[1]: JTAG-AP (IDR: 0x02880000)
Iterating through AP map to find AHB-AP to use
AP[0]: Core found
AP[0]: AHB-AP ROM base: 0xE00FF000
CPUID register: 0x410FC241. Implementer code: 0x41 (ARM)
Found Cortex-M4 r0p1, Little endian.
FPUnit: 6 code (BP) slots and 2 literal slots
CoreSight components:
ROMTbl[0] @ E00FF000
ROMTbl[0][0]: E000E000, CID: B105E00D, PID: 000BB00C SCS-M7
ROMTbl[0][1]: E0001000, CID: B105E00D, PID: 003BB002 DWT
ROMTbl[0][2]: E0002000, CID: B105E00D, PID: 002BB003 FPB
ROMTbl[0][3]: E0000000, CID: B105E00D, PID: 003BB001 ITM
ROMTbl[0][4]: E0040000, CID: B105900D, PID: 000BB9A1 TPIU
ROMTbl[0][5]: E0041000, CID: B105900D, PID: 000BB925 ETM
Cortex-M4 identified.
J-Link>
```
Use the command loadfile to program the .hex file:
```
J-Link>loadfile pinetime-app.hex
Downloading file [pinetime-app.hex]...
Comparing flash [100%] Done.
Erasing flash [100%] Done.
Programming flash [100%] Done.
Verifying flash [100%] Done.
J-Link: Flash download: Bank 0 @ 0x00000000: 1 range affected (4096 bytes)
J-Link: Flash download: Total time needed: 0.322s (Prepare: 0.043s, Compare: 0.202s, Erase: 0.003s, Program: 0.064s, Verify: 0.000s, Restore: 0.007s)
O.K.
```
Then reset (r) and start (g) the CPU:
```
J-Link>r
Reset delay: 0 ms
Reset type NORMAL: Resets core & peripherals via SYSRESETREQ & VECTRESET bit.
Reset: Halt core after reset via DEMCR.VC_CORERESET.
Reset: Reset device via AIRCR.SYSRESETREQ.
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
```
$ JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1
```
Start JLinkRTTClient
```
$ JLinkRTTClient
```

View file

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -1,4 +1,5 @@
# OpenOCD and STLink # 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 (**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. 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)! OpenOCD works on X86 computers, ARM/ARM64 computers, and SBCs (like the RaspberryPi and Pine64 Pinebook Pro)!
## Installation ## 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. 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 git clone https://git.code.sf.net/p/openocd/code openocd-code
@ -21,13 +23,14 @@ make -j 4
sudo make install 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 cp contrib/60-openocd.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules 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 $ 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 Info : Target voltage: 3.294340
Error: init mode failed (unable to connect to the target) 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. 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 ## Configuration files
OpenOCD is configured using configuration files. OpenOCD is configured using configuration files.
First, we need a common configuration file for the project : **openocd-stlink.ocd**: First, we need a common configuration file for the project : **openocd-stlink.ocd**:
``` ```
source [find interface/stlink.cfg] source [find interface/stlink.cfg]
@ -63,11 +69,13 @@ gdb_breakpoint_override hard
source [find target/nrf52.cfg] source [find target/nrf52.cfg]
``` ```
This file specifies to OpenOCD which debugger and target it will be connected to. 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. 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**: This files flashes the bootloader and the application firmware : **flash_bootloader_app.ocd**:
``` ```
init 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**: And this one flashes the graphics flasher (it writes the bootloader graphics into the SPI NOR flash memory) : **flash_graphics.ocd**:
``` ```
init init
@ -87,19 +96,23 @@ reset
``` ```
## Examples ## Examples
### Flash bootloader and application ### Flash bootloader and application
``` ```
openocd -f ./openocd-stlink.ocd -f ./flash_bootloader_app.ocd openocd -f ./openocd-stlink.ocd -f ./flash_bootloader_app.ocd
``` ```
### Flash graphics flasher ### Flash graphics flasher
``` ```
openocd -f ./openocd-stlink.ocd -f ./flash_graphics.ocd openocd -f ./openocd-stlink.ocd -f ./flash_graphics.ocd
``` ```
## Connect the STLinkV2 to the PineTime ## Connect the STLinkV2 to the PineTime
Here is an example using the pogo pins: Here is an example using the pogo pins:
![SWD pinout](../images/swd_pinout.jpg) ![SWD pinout](openOCD/swd_pinout.jpg)
![Pogo pins](../images/pogopins.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). You can find more information about the SWD wiring [on the wiki](https://wiki.pine64.org/index.php?title=PineTime_devkit_wiring).

View file

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

View file

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View file

@ -5,9 +5,9 @@
- Buttons should generally be on the bottom edge - Buttons should generally be on the bottom edge
- Make interactable objects **big** - Make interactable objects **big**
- When using a page indicator, leave 8px for it on the right side - 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 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 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. - A switch should be twice as wide as it is tall.

View file

@ -1,6 +1,7 @@
# Versioning # Versioning
The versioning of this project is based on [Semantic versioning](https://semver.org/): 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 **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 **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 **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.

View file

@ -22,6 +22,12 @@ RUN apt-get update -qq \
python3-dev \ python3-dev \
git \ git \
apt-utils \ apt-utils \
pkg-config \
libpixman-1-dev \
libcairo2-dev \
libpango-1.0-0 \
ibpango1.0-dev \
libpangocairo-1.0-0 \
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \ && curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \ && apt-get install -y nodejs \
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*; && rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
@ -33,6 +39,10 @@ RUN pip3 install -Iv cryptography==3.3
RUN pip3 install cbor RUN pip3 install cbor
RUN npm i lv_font_conv@1.5.2 -g RUN npm i lv_font_conv@1.5.2 -g
RUN npm i ts-node@10.9.1 -g
RUN npm i @swc/core -g
RUN npm i lv_img_conv@0.3.0 -g
# build.sh knows how to compile # build.sh knows how to compile
COPY build.sh /opt/ COPY build.sh /opt/

View file

@ -11,19 +11,25 @@ export SOURCES_DIR="${SOURCES_DIR:=/sources}"
export BUILD_DIR="${BUILD_DIR:=$SOURCES_DIR/build}" export BUILD_DIR="${BUILD_DIR:=$SOURCES_DIR/build}"
export OUTPUT_DIR="${OUTPUT_DIR:=$SOURCES_DIR/build/output}" export OUTPUT_DIR="${OUTPUT_DIR:=$SOURCES_DIR/build/output}"
# Specify a folder with read/write access to NPM
export NPM_DIR="$BUILD_DIR/npm"
export npm_config_cache="${NPM_DIR}"
export BUILD_TYPE=${BUILD_TYPE:=Release} export BUILD_TYPE=${BUILD_TYPE:=Release}
export GCC_ARM_VER=${GCC_ARM_VER:="gcc-arm-none-eabi-9-2020-q2-update"} export GCC_ARM_VER=${GCC_ARM_VER:="10.3-2021.10"}
export NRF_SDK_VER=${NRF_SDK_VER:="nRF5_SDK_15.3.0_59ac345"} export NRF_SDK_VER=${NRF_SDK_VER:="nRF5_SDK_15.3.0_59ac345"}
MACHINE="$(uname -m)" MACHINE="$(uname -m)"
[[ "$MACHINE" == "arm64" ]] && MACHINE="aarch64" [[ "$MACHINE" == "arm64" ]] && MACHINE="aarch64"
export GCC_ARM_PATH="gcc-arm-none-eabi-$GCC_ARM_VER"
main() { main() {
local target="$1" local target="$1"
mkdir -p "$TOOLS_DIR" mkdir -p "$TOOLS_DIR"
[[ ! -d "$TOOLS_DIR/$GCC_ARM_VER" ]] && GetGcc [[ ! -d "$TOOLS_DIR/$GCC_ARM_PATH" ]] && GetGcc
[[ ! -d "$TOOLS_DIR/$NRF_SDK_VER" ]] && GetNrfSdk [[ ! -d "$TOOLS_DIR/$NRF_SDK_VER" ]] && GetNrfSdk
[[ ! -d "$TOOLS_DIR/mcuboot" ]] && GetMcuBoot [[ ! -d "$TOOLS_DIR/mcuboot" ]] && GetMcuBoot
@ -38,8 +44,7 @@ main() {
} }
GetGcc() { GetGcc() {
GCC_SRC="$GCC_ARM_VER-$MACHINE-linux.tar.bz" wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/$GCC_ARM_VER/$GCC_ARM_PATH-$MACHINE-linux.tar.bz2 -O - | tar -xj -C $TOOLS_DIR/
wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/$GCC_SRC -O - | tar -xj -C $TOOLS_DIR/
} }
GetMcuBoot() { GetMcuBoot() {
@ -59,9 +64,10 @@ CmakeGenerate() {
-B "$BUILD_DIR" \ -B "$BUILD_DIR" \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DUSE_OPENOCD=1 \ -DUSE_OPENOCD=1 \
-DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_VER" \ -DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_PATH" \
-DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \ -DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \
-DBUILD_DFU=1 -DBUILD_DFU=1 \
-DBUILD_RESOURCES=1
} }
CmakeBuild() { CmakeBuild() {

View file

@ -15,6 +15,8 @@ cp "$BUILD_DIR/src/pinetime-mcuboot-app-dfu-$PROJECT_VERSION.zip" "$OUTPUT_DIR/p
cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin" cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin"
cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip" cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip"
cp "$BUILD_DIR/src/resources/infinitime-resources-$PROJECT_VERSION.zip" "$OUTPUT_DIR/infinitime-resources-$PROJECT_VERSION.zip"
mkdir -p "$OUTPUT_DIR/src" mkdir -p "$OUTPUT_DIR/src"
cp $BUILD_DIR/src/*.bin "$OUTPUT_DIR/src/" cp $BUILD_DIR/src/*.bin "$OUTPUT_DIR/src/"
cp $BUILD_DIR/src/*.hex "$OUTPUT_DIR/src/" cp $BUILD_DIR/src/*.hex "$OUTPUT_DIR/src/"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

View file

@ -9,15 +9,15 @@ using namespace Pinetime;
uint32_t BootloaderVersion::version = 0; uint32_t BootloaderVersion::version = 0;
char BootloaderVersion::versionString[BootloaderVersion::VERSION_STR_LEN] = "0.0.0"; char BootloaderVersion::versionString[BootloaderVersion::VERSION_STR_LEN] = "0.0.0";
const uint32_t BootloaderVersion::Major() { uint32_t BootloaderVersion::Major() {
return (BootloaderVersion::version >> 16u) & 0xff; return (BootloaderVersion::version >> 16u) & 0xff;
} }
const uint32_t BootloaderVersion::Minor() { uint32_t BootloaderVersion::Minor() {
return (BootloaderVersion::version >> 8u) & 0xff; return (BootloaderVersion::version >> 8u) & 0xff;
} }
const uint32_t BootloaderVersion::Patch() { uint32_t BootloaderVersion::Patch() {
return BootloaderVersion::version & 0xff; return BootloaderVersion::version & 0xff;
} }
@ -25,7 +25,7 @@ const char* BootloaderVersion::VersionString() {
return BootloaderVersion::versionString; return BootloaderVersion::versionString;
} }
const bool BootloaderVersion::IsValid() { bool BootloaderVersion::IsValid() {
return BootloaderVersion::version >= 0x00010000; return BootloaderVersion::version >= 0x00010000;
} }

View file

@ -6,11 +6,11 @@
namespace Pinetime { namespace Pinetime {
class BootloaderVersion { class BootloaderVersion {
public: public:
static const uint32_t Major(); static uint32_t Major();
static const uint32_t Minor(); static uint32_t Minor();
static const uint32_t Patch(); static uint32_t Patch();
static const char* VersionString(); static const char* VersionString();
static const bool IsValid(); static bool IsValid();
static void SetVersion(uint32_t v); static void SetVersion(uint32_t v);
private: private:

View file

@ -9,21 +9,21 @@ set(NRF_BOARD pca10040)
# check if all the necessary tools paths have been provided. # check if all the necessary tools paths have been provided.
if (NOT NRF5_SDK_PATH) if (NOT NRF5_SDK_PATH)
message(FATAL_ERROR "The path to the nRF5 SDK (NRF5_SDK_PATH) must be set.") message(FATAL_ERROR "The path to the nRF5 SDK (NRF5_SDK_PATH) must be set.")
endif () endif ()
if (DEFINED ARM_NONE_EABI_TOOLCHAIN_PATH) if (DEFINED ARM_NONE_EABI_TOOLCHAIN_PATH)
set(ARM_NONE_EABI_TOOLCHAIN_BIN_PATH ${ARM_NONE_EABI_TOOLCHAIN_PATH}/bin) set(ARM_NONE_EABI_TOOLCHAIN_BIN_PATH ${ARM_NONE_EABI_TOOLCHAIN_PATH}/bin)
endif () endif ()
if (NOT NRF_TARGET MATCHES "nrf52") if (NOT NRF_TARGET MATCHES "nrf52")
message(FATAL_ERROR "Only rRF52 boards are supported right now") message(FATAL_ERROR "Only rRF52 boards are supported right now")
endif () endif ()
# Setup toolchain # Setup toolchain
include(${CMAKE_SOURCE_DIR}/cmake-nRF5x/arm-gcc-toolchain.cmake) include(${CMAKE_SOURCE_DIR}/cmake-nRF5x/arm-gcc-toolchain.cmake)
if (NOT DEFINED ARM_GCC_TOOLCHAIN) if (NOT DEFINED ARM_GCC_TOOLCHAIN)
message(FATAL_ERROR "The toolchain must be set up before calling this macro") message(FATAL_ERROR "The toolchain must be set up before calling this macro")
endif () endif ()
set(CMAKE_OSX_SYSROOT "/") set(CMAKE_OSX_SYSROOT "/")
set(CMAKE_OSX_DEPLOYMENT_TARGET "") set(CMAKE_OSX_DEPLOYMENT_TARGET "")
@ -406,6 +406,8 @@ list(APPEND SOURCE_FILES
displayapp/screens/Styles.cpp displayapp/screens/Styles.cpp
displayapp/Colors.cpp displayapp/Colors.cpp
displayapp/widgets/Counter.cpp displayapp/widgets/Counter.cpp
displayapp/widgets/PageIndicator.cpp
displayapp/widgets/StatusIcons.cpp
## Settings ## Settings
displayapp/screens/settings/QuickSettings.cpp displayapp/screens/settings/QuickSettings.cpp
@ -478,7 +480,7 @@ list(APPEND SOURCE_FILES
FreeRTOS/port_cmsis.c FreeRTOS/port_cmsis.c
displayapp/LittleVgl.cpp displayapp/LittleVgl.cpp
displayapp/lv_pinetime_theme.c displayapp/InfiniTimeTheme.cpp
systemtask/SystemTask.cpp systemtask/SystemTask.cpp
systemtask/SystemMonitor.cpp systemtask/SystemMonitor.cpp
@ -613,6 +615,8 @@ set(INCLUDE_FILES
displayapp/screens/Alarm.h displayapp/screens/Alarm.h
displayapp/Colors.h displayapp/Colors.h
displayapp/widgets/Counter.h displayapp/widgets/Counter.h
displayapp/widgets/PageIndicator.h
displayapp/widgets/StatusIcons.h
drivers/St7789.h drivers/St7789.h
drivers/SpiNorFlash.h drivers/SpiNorFlash.h
drivers/SpiMaster.h drivers/SpiMaster.h
@ -654,16 +658,16 @@ set(INCLUDE_FILES
drivers/Cst816s.h drivers/Cst816s.h
FreeRTOS/portmacro.h FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h FreeRTOS/portmacro_cmsis.h
libs/date/includes/date/tz.h libs/date/include/date/tz.h
libs/date/includes/date/chrono_io.h libs/date/include/date/chrono_io.h
libs/date/includes/date/date.h libs/date/include/date/date.h
libs/date/includes/date/islamic.h libs/date/include/date/islamic.h
libs/date/includes/date/iso_week.h libs/date/include/date/iso_week.h
libs/date/includes/date/julian.h libs/date/include/date/julian.h
libs/date/includes/date/ptz.h libs/date/include/date/ptz.h
libs/date/includes/date/tz_private.h libs/date/include/date/tz_private.h
displayapp/LittleVgl.h displayapp/LittleVgl.h
displayapp/lv_pinetime_theme.h displayapp/InfiniTimeTheme.h
systemtask/SystemTask.h systemtask/SystemTask.h
systemtask/SystemMonitor.h systemtask/SystemMonitor.h
displayapp/screens/Symbols.h displayapp/screens/Symbols.h
@ -684,7 +688,7 @@ include_directories(
../ ../
libs/ libs/
FreeRTOS/ FreeRTOS/
libs/date/includes libs/date/include
libs/mynewt-nimble/porting/npl/freertos/include libs/mynewt-nimble/porting/npl/freertos/include
libs/mynewt-nimble/nimble/include libs/mynewt-nimble/nimble/include
libs/mynewt-nimble/porting/nimble/include libs/mynewt-nimble/porting/nimble/include
@ -782,14 +786,60 @@ add_definitions(-DNRF52 -DNRF52832 -DNRF52832_XXAA -DNRF52_PAN_74 -DNRF52_PAN_64
add_definitions(-DFREERTOS) add_definitions(-DFREERTOS)
add_definitions(-D__STACK_SIZE=1024) add_definitions(-D__STACK_SIZE=1024)
add_definitions(-D__HEAP_SIZE=4096) add_definitions(-D__HEAP_SIZE=4096)
add_definitions(-DMYNEWT_VAL_BLE_LL_RFMGMT_ENABLE_TIME=1500)
# NOTE : Add the following defines to enable debug mode of the NRF SDK: # Note: Only use this for debugging
#add_definitions(-DDEBUG) # Derive the low frequency clock from the main clock (SYNT)
#add_definitions(-DDEBUG_NRF_USER) # add_definitions(-DCLOCK_CONFIG_LF_SRC=2)
if (NOT CMAKE_BUILD_TYPE) # Target hardware configuration options
set(CMAKE_BUILD_TYPE "Release") add_definitions(-DTARGET_DEVICE_${TARGET_DEVICE})
endif () add_definitions(-DTARGET_DEVICE_NAME="${TARGET_DEVICE}")
if(TARGET_DEVICE STREQUAL "PINETIME")
add_definitions(-DDRIVER_PINMAP_PINETIME)
add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL
elseif(TARGET_DEVICE STREQUAL "MOY-TFK5") # P8a
add_definitions(-DDRIVER_PINMAP_P8)
add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL
elseif(TARGET_DEVICE STREQUAL "MOY-TIN5") # P8a variant 2
add_definitions(-DDRIVER_PINMAP_P8)
add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL
elseif(TARGET_DEVICE STREQUAL "MOY-TON5") # P8b
add_definitions(-DDRIVER_PINMAP_P8)
add_definitions(-DCLOCK_CONFIG_LF_SRC=0) # RC
add_definitions(-DMYNEWT_VAL_BLE_LL_SCA=500)
add_definitions(-DCLOCK_CONFIG_LF_CAL_ENABLED=1)
elseif(TARGET_DEVICE STREQUAL "MOY-UNK") # P8b mirrored
add_definitions(-DDRIVER_PINMAP_P8)
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()
# Debug configuration
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
add_definitions(-DDEBUG)
add_definitions(-DDEBUG_NRF_USER)
# NRF SDK Logging
add_definitions(-DNRF_LOG_ENABLED=1)
# add_definitions(-DNRF_LOG_BACKEND_RTT_ENABLED=1)
# add_definitions(-DNRF_LOG_BACKEND_SERIAL_USES_RTT=1)
# NRF SDK individual modules logging
# add_definitions(-DCLOCK_CONFIG_LOG_ENABLED=1)
# add_definitions(-DCLOCK_CONFIG_LOG_LEVEL=4)
# add_definitions(-DRTC_CONFIG_LOG_ENABLED=1)
# add_definitions(-DRTC_CONFIG_LOG_LEVEL=4)
# Nimble Logging
add_definitions(-DMYNEWT_VAL_NEWT_FEATURE_LOGCFG=1)
# add_definitions(-DMYNEWT_VAL_LOG_LEVEL=0)
# add_definitions(-DMYNEWT_VAL_BLE_HS_LOG_LVL=0)
endif()
add_subdirectory(displayapp/fonts) add_subdirectory(displayapp/fonts)
target_compile_options(infinitime_fonts PUBLIC target_compile_options(infinitime_fonts PUBLIC
@ -894,10 +944,15 @@ add_custom_command(TARGET ${EXECUTABLE_NAME}
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_FILE_NAME}.out "${EXECUTABLE_FILE_NAME}.hex" COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_FILE_NAME}.out "${EXECUTABLE_FILE_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_FILE_NAME}") COMMENT "post build steps for ${EXECUTABLE_FILE_NAME}")
if(BUILD_RESOURCES)
add_dependencies(${EXECUTABLE_NAME} GenerateResources)
endif()
# Build binary intended to be used by bootloader # Build binary intended to be used by bootloader
set(EXECUTABLE_MCUBOOT_NAME "pinetime-mcuboot-app") set(EXECUTABLE_MCUBOOT_NAME "pinetime-mcuboot-app")
set(EXECUTABLE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}) set(EXECUTABLE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
set(IMAGE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.hex) set(IMAGE_MCUBOOT_FILE_NAME_HEX ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.hex)
set(IMAGE_MCUBOOT_FILE_NAME_BIN ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.bin)
set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld") set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld")
add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES}) add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES})
@ -921,16 +976,21 @@ add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME}
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_MCUBOOT_FILE_NAME}.out COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_MCUBOOT_FILE_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_FILE_NAME}.bin" COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_FILE_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_FILE_NAME}.hex" COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_FILE_NAME}.hex"
COMMAND ${CMAKE_SOURCE_DIR}/tools/mcuboot/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header ${EXECUTABLE_MCUBOOT_FILE_NAME}.hex ${IMAGE_MCUBOOT_FILE_NAME} COMMAND ${CMAKE_SOURCE_DIR}/tools/mcuboot/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header ${EXECUTABLE_MCUBOOT_FILE_NAME}.hex ${IMAGE_MCUBOOT_FILE_NAME_HEX}
COMMAND ${CMAKE_SOURCE_DIR}/tools/mcuboot/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header ${EXECUTABLE_MCUBOOT_FILE_NAME}.bin ${IMAGE_MCUBOOT_FILE_NAME_BIN}
COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_FILE_NAME}" COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_FILE_NAME}"
) )
if(BUILD_RESOURCES)
add_dependencies(${EXECUTABLE_MCUBOOT_NAME} GenerateResources)
endif()
if(BUILD_DFU) if(BUILD_DFU)
add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME} add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME}
POST_BUILD POST_BUILD
COMMAND adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application ${IMAGE_MCUBOOT_FILE_NAME} ${DFU_MCUBOOT_FILE_NAME} COMMAND adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application ${IMAGE_MCUBOOT_FILE_NAME_HEX} ${DFU_MCUBOOT_FILE_NAME}
COMMENT "post build (DFU) steps for ${EXECUTABLE_MCUBOOT_FILE_NAME}" COMMENT "post build (DFU) steps for ${EXECUTABLE_MCUBOOT_FILE_NAME}"
) )
endif() endif()
# InfiniTime recovery firmware (autonomous) # InfiniTime recovery firmware (autonomous)
@ -964,7 +1024,8 @@ add_custom_command(TARGET ${EXECUTABLE_RECOVERY_NAME}
# InfiniTime recovery firmware (mcuboot) # InfiniTime recovery firmware (mcuboot)
set(EXECUTABLE_RECOVERY_MCUBOOT_NAME "pinetime-mcuboot-recovery") set(EXECUTABLE_RECOVERY_MCUBOOT_NAME "pinetime-mcuboot-recovery")
set(EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}) set(EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.hex) set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME_HEX ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.hex)
set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES}) add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES})
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs QCBOR infinitime_fonts) target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs QCBOR infinitime_fonts)
@ -988,18 +1049,18 @@ add_custom_command(TARGET ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.out COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.bin" COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.hex" COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.out "${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.hex"
COMMAND ${CMAKE_SOURCE_DIR}/tools/mcuboot/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.hex ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME} COMMAND ${CMAKE_SOURCE_DIR}/tools/mcuboot/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}.hex ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME_HEX}
COMMAND ${CMAKE_OBJCOPY} -I ihex -O binary ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME} "${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.bin" COMMAND ${CMAKE_OBJCOPY} -I ihex -O binary ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME_HEX} "${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.bin"
COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/bin2c.py ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.bin recoveryImage > recoveryImage.h COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/bin2c.py ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.bin recoveryImage > recoveryImage.h
COMMENT "post build steps for ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}" COMMENT "post build steps for ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}"
) )
if(BUILD_DFU) if(BUILD_DFU)
add_custom_command(TARGET ${EXECUTABLE_RECOVERY_MCUBOOT_NAME} add_custom_command(TARGET ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}
POST_BUILD POST_BUILD
COMMAND adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME} ${DFU_RECOVERY_MCUBOOT_FILE_NAME} COMMAND adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME_HEX} ${DFU_RECOVERY_MCUBOOT_FILE_NAME}
COMMENT "post build (DFU) steps for ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}" COMMENT "post build (DFU) steps for ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME}"
) )
endif() endif()
# Build binary that writes the recovery image into the SPI flash memory # Build binary that writes the recovery image into the SPI flash memory
@ -1036,7 +1097,8 @@ add_custom_command(TARGET ${EXECUTABLE_RECOVERYLOADER_NAME}
# Build binary that writes the recovery image (MCUBoot version) # Build binary that writes the recovery image (MCUBoot version)
set(EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME "pinetime-mcuboot-recovery-loader") set(EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME "pinetime-mcuboot-recovery-loader")
set(EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}) set(EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
set(IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.hex) set(IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
set(IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME_HEX ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.hex)
set(DFU_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip) set(DFU_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
add_executable(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE_FILES}) add_executable(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE_FILES})
target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk QCBOR infinitime_fonts) target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk QCBOR infinitime_fonts)
@ -1063,82 +1125,86 @@ add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}
COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.out COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.out
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.bin" COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.bin"
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.hex" COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.out "${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.hex"
COMMAND ${CMAKE_SOURCE_DIR}/tools/mcuboot/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.hex ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME} COMMAND ${CMAKE_SOURCE_DIR}/tools/mcuboot/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.hex ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME_HEX}
COMMAND ${CMAKE_OBJCOPY} -I ihex -O binary ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME} "${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.bin" COMMAND ${CMAKE_OBJCOPY} -I ihex -O binary ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME_HEX} "${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.bin"
COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/bin2c.py ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.bin recoveryLoaderImage > recoveryLoaderImage.h COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/bin2c.py ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.bin recoveryLoaderImage > recoveryLoaderImage.h
COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}" COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}"
) )
if(BUILD_DFU) if(BUILD_DFU)
add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}
POST_BUILD POST_BUILD
COMMAND adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME} ${DFU_MCUBOOT_RECOVERYLOADER_FILE_NAME} COMMAND adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME_HEX} ${DFU_MCUBOOT_RECOVERYLOADER_FILE_NAME}
COMMENT "post build (DFU) steps for ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}" COMMENT "post build (DFU) steps for ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME}"
) )
endif()
if(BUILD_RESOURCES)
add_subdirectory(resources)
endif() endif()
# FLASH # FLASH
if (USE_JLINK) if (USE_JLINK)
add_custom_target(FLASH_ERASE add_custom_target(FLASH_ERASE
COMMAND ${NRFJPROG} --eraseall -f ${NRF_TARGET} COMMAND ${NRFJPROG} --eraseall -f ${NRF_TARGET}
COMMENT "erasing flashing" COMMENT "erasing flashing"
) )
add_custom_target("FLASH_${EXECUTABLE_NAME}" add_custom_target("FLASH_${EXECUTABLE_NAME}"
DEPENDS ${EXECUTABLE_NAME} DEPENDS ${EXECUTABLE_NAME}
COMMAND ${NRFJPROG} --program ${EXECUTABLE_FILE_NAME}.hex -f ${NRF_TARGET} --sectorerase COMMAND ${NRFJPROG} --program ${EXECUTABLE_FILE_NAME}.hex -f ${NRF_TARGET} --sectorerase
COMMAND sleep 0.5s COMMAND sleep 0.5s
COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET} COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET}
COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex" COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex"
) )
elseif (USE_GDB_CLIENT) elseif (USE_GDB_CLIENT)
add_custom_target(FLASH_ERASE
COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'mon erase_mass'
COMMENT "erasing flashing"
)
add_custom_target("FLASH_${EXECUTABLE_NAME}"
DEPENDS ${EXECUTABLE_NAME}
COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'load' -ex 'kill' ${EXECUTABLE_FILE_NAME}.hex
COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex"
)
elseif (USE_OPENOCD)
if (USE_CMSIS_DAP)
add_custom_target(FLASH_ERASE add_custom_target(FLASH_ERASE
COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'mon erase_mass' COMMAND ${OPENOCD_BIN_PATH} -c 'source [find interface/cmsis-dap.cfg]' -c 'transport select swd'
-c 'source [find target/nrf52.cfg]'
-c 'init'
-c 'halt'
-c 'nrf5 mass_erase'
-c 'halt'
-c 'reset'
-c 'exit'
COMMENT "erasing flashing" COMMENT "erasing flashing"
) )
add_custom_target("FLASH_${EXECUTABLE_NAME}" add_custom_target("FLASH_${EXECUTABLE_NAME}"
DEPENDS ${EXECUTABLE_NAME} DEPENDS ${EXECUTABLE_NAME}
COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'load' -ex 'kill' ${EXECUTABLE_FILE_NAME}.hex COMMAND ${OPENOCD_BIN_PATH}
-c 'tcl_port disabled'
-c 'gdb_port 3333'
-c 'telnet_port 4444'
-c 'source [find interface/cmsis-dap.cfg]'
-c 'transport select swd'
-c 'source [find target/nrf52.cfg]'
-c 'halt'
-c "program \"${EXECUTABLE_FILE_NAME}.hex\""
-c 'reset'
-c 'shutdown'
COMMENT "flashing ${EXECUTABLE_BIN_NAME}.hex"
)
else ()
add_custom_target(FLASH_ERASE
COMMAND ${OPENOCD_BIN_PATH} -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c init -c halt -c 'nrf5 mass_erase' -c reset -c shutdown
COMMENT "erasing flashing"
)
add_custom_target("FLASH_${EXECUTABLE_NAME}"
DEPENDS ${EXECUTABLE_NAME}
COMMAND ${OPENOCD_BIN_PATH} -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c "program \"${EXECUTABLE_FILE_NAME}.hex\"" -c reset -c shutdown
COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex" COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex"
) )
elseif (USE_OPENOCD) endif ()
if (USE_CMSIS_DAP)
add_custom_target(FLASH_ERASE
COMMAND ${OPENOCD_BIN_PATH} -c 'source [find interface/cmsis-dap.cfg]' -c 'transport select swd'
-c 'source [find target/nrf52.cfg]'
-c 'init'
-c 'halt'
-c 'nrf5 mass_erase'
-c 'halt'
-c 'reset'
-c 'exit'
COMMENT "erasing flashing"
)
add_custom_target("FLASH_${EXECUTABLE_NAME}"
DEPENDS ${EXECUTABLE_NAME}
COMMAND ${OPENOCD_BIN_PATH}
-c 'tcl_port disabled'
-c 'gdb_port 3333'
-c 'telnet_port 4444'
-c 'source [find interface/cmsis-dap.cfg]'
-c 'transport select swd'
-c 'source [find target/nrf52.cfg]'
-c 'halt'
-c "program \"${EXECUTABLE_FILE_NAME}.hex\""
-c 'reset'
-c 'shutdown'
COMMENT "flashing ${EXECUTABLE_BIN_NAME}.hex"
)
else ()
add_custom_target(FLASH_ERASE
COMMAND ${OPENOCD_BIN_PATH} -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c init -c halt -c 'nrf5 mass_erase' -c reset -c shutdown
COMMENT "erasing flashing"
)
add_custom_target("FLASH_${EXECUTABLE_NAME}"
DEPENDS ${EXECUTABLE_NAME}
COMMAND ${OPENOCD_BIN_PATH} -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c "program \"${EXECUTABLE_FILE_NAME}.hex\"" -c reset -c shutdown
COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex"
)
endif ()
endif () endif ()

View file

@ -1,6 +1,7 @@
#include "components/ble/NotificationManager.h" #include "components/ble/NotificationManager.h"
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <cassert>
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
@ -9,73 +10,117 @@ constexpr uint8_t NotificationManager::MessageSize;
void NotificationManager::Push(NotificationManager::Notification&& notif) { void NotificationManager::Push(NotificationManager::Notification&& notif) {
notif.id = GetNextId(); notif.id = GetNextId();
notif.valid = true; notif.valid = true;
notifications[writeIndex] = std::move(notif);
writeIndex = (writeIndex + 1 < TotalNbNotifications) ? writeIndex + 1 : 0;
if (!empty)
readIndex = (readIndex + 1 < TotalNbNotifications) ? readIndex + 1 : 0;
else
empty = false;
newNotification = true; newNotification = true;
} if (beginIdx > 0) {
--beginIdx;
NotificationManager::Notification NotificationManager::GetLastNotification() { } else {
NotificationManager::Notification notification = notifications[readIndex]; beginIdx = notifications.size() - 1;
notification.index = 1; }
return notification; notifications[beginIdx] = std::move(notif);
if (size < notifications.size()) {
size++;
}
} }
NotificationManager::Notification::Id NotificationManager::GetNextId() { NotificationManager::Notification::Id NotificationManager::GetNextId() {
return nextId++; return nextId++;
} }
NotificationManager::Notification NotificationManager::GetNext(NotificationManager::Notification::Id id) { NotificationManager::Notification NotificationManager::GetLastNotification() const {
auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n) { if (this->IsEmpty()) {
return n.valid && n.id == id;
});
if (currentIterator == notifications.end() || currentIterator->id != id)
return Notification {};
auto& lastNotification = notifications[readIndex];
NotificationManager::Notification result;
if (currentIterator == (notifications.end() - 1))
result = *(notifications.begin());
else
result = *(currentIterator + 1);
if (result.id <= id)
return {}; return {};
}
result.index = (lastNotification.id - result.id) + 1; return this->At(0);
return result;
} }
NotificationManager::Notification NotificationManager::GetPrevious(NotificationManager::Notification::Id id) { const NotificationManager::Notification& NotificationManager::At(NotificationManager::Notification::Idx idx) const {
auto currentIterator = std::find_if(notifications.begin(), notifications.end(), [id](const Notification& n) { if (idx >= notifications.size()) {
return n.valid && n.id == id; assert(false);
}); return notifications.at(beginIdx); // this should not happen
if (currentIterator == notifications.end() || currentIterator->id != id) }
return Notification {}; size_t read_idx = (beginIdx + idx) % notifications.size();
return notifications.at(read_idx);
auto& lastNotification = notifications[readIndex];
NotificationManager::Notification result;
if (currentIterator == notifications.begin())
result = *(notifications.end() - 1);
else
result = *(currentIterator - 1);
if (result.id >= id)
return {};
result.index = (lastNotification.id - result.id) + 1;
return result;
} }
bool NotificationManager::AreNewNotificationsAvailable() { NotificationManager::Notification& NotificationManager::At(NotificationManager::Notification::Idx idx) {
if (idx >= notifications.size()) {
assert(false);
return notifications.at(beginIdx); // this should not happen
}
size_t read_idx = (beginIdx + idx) % notifications.size();
return notifications.at(read_idx);
}
NotificationManager::Notification::Idx NotificationManager::IndexOf(NotificationManager::Notification::Id id) const {
for (NotificationManager::Notification::Idx idx = 0; idx < this->size; idx++) {
const NotificationManager::Notification& notification = this->At(idx);
if (notification.id == id) {
return idx;
}
}
return size;
}
NotificationManager::Notification NotificationManager::Get(NotificationManager::Notification::Id id) const {
NotificationManager::Notification::Idx idx = this->IndexOf(id);
if (idx == this->size) {
return {};
}
return this->At(idx);
}
NotificationManager::Notification NotificationManager::GetNext(NotificationManager::Notification::Id id) const {
NotificationManager::Notification::Idx idx = this->IndexOf(id);
if (idx == this->size) {
return {};
}
if (idx == 0 || idx > notifications.size()) {
return {};
}
return this->At(idx - 1);
}
NotificationManager::Notification NotificationManager::GetPrevious(NotificationManager::Notification::Id id) const {
NotificationManager::Notification::Idx idx = this->IndexOf(id);
if (idx == this->size) {
return {};
}
if (static_cast<size_t>(idx + 1) >= notifications.size()) {
return {};
}
return this->At(idx + 1);
}
void NotificationManager::DismissIdx(NotificationManager::Notification::Idx idx) {
if (this->IsEmpty()) {
return;
}
if (idx >= size) {
assert(false);
return; // this should not happen
}
if (idx == 0) { // just remove the first element, don't need to change the other elements
notifications.at(beginIdx).valid = false;
beginIdx = (beginIdx + 1) % notifications.size();
} else {
// overwrite the specified entry by moving all later messages one index to the front
for (size_t i = idx; i < size - 1; ++i) {
this->At(i) = this->At(i + 1);
}
this->At(size - 1).valid = false;
}
--size;
}
void NotificationManager::Dismiss(NotificationManager::Notification::Id id) {
NotificationManager::Notification::Idx idx = this->IndexOf(id);
if (idx == this->size) {
return;
}
this->DismissIdx(idx);
}
bool NotificationManager::AreNewNotificationsAvailable() const {
return newNotification; return newNotification;
} }
@ -84,9 +129,7 @@ bool NotificationManager::ClearNewNotificationFlag() {
} }
size_t NotificationManager::NbNotifications() const { size_t NotificationManager::NbNotifications() const {
return std::count_if(notifications.begin(), notifications.end(), [](const Notification& n) { return size;
return n.valid;
});
} }
const char* NotificationManager::Notification::Message() const { const char* NotificationManager::Notification::Message() const {

View file

@ -26,9 +26,9 @@ namespace Pinetime {
struct Notification { struct Notification {
using Id = uint8_t; using Id = uint8_t;
Id id; using Idx = uint8_t;
Id id = 0;
bool valid = false; bool valid = false;
uint8_t index;
uint8_t size; uint8_t size;
std::array<char, MessageSize + 1> message; std::array<char, MessageSize + 1> message;
Categories category = Categories::Unknown; Categories category = Categories::Unknown;
@ -36,27 +36,38 @@ namespace Pinetime {
const char* Message() const; const char* Message() const;
const char* Title() const; const char* Title() const;
}; };
Notification::Id nextId {0};
void Push(Notification&& notif); void Push(Notification&& notif);
Notification GetLastNotification(); Notification GetLastNotification() const;
Notification GetNext(Notification::Id id); Notification Get(Notification::Id id) const;
Notification GetPrevious(Notification::Id id); Notification GetNext(Notification::Id id) const;
Notification GetPrevious(Notification::Id id) const;
// Return the index of the notification with the specified id, if not found return NbNotifications()
Notification::Idx IndexOf(Notification::Id id) const;
bool ClearNewNotificationFlag(); bool ClearNewNotificationFlag();
bool AreNewNotificationsAvailable(); bool AreNewNotificationsAvailable() const;
void Dismiss(Notification::Id id);
static constexpr size_t MaximumMessageSize() { static constexpr size_t MaximumMessageSize() {
return MessageSize; return MessageSize;
}; };
bool IsEmpty() const {
return size == 0;
}
size_t NbNotifications() const; size_t NbNotifications() const;
private: private:
Notification::Id nextId {0};
Notification::Id GetNextId(); Notification::Id GetNextId();
const Notification& At(Notification::Idx idx) const;
Notification& At(Notification::Idx idx);
void DismissIdx(Notification::Idx idx);
static constexpr uint8_t TotalNbNotifications = 5; static constexpr uint8_t TotalNbNotifications = 5;
std::array<Notification, TotalNbNotifications> notifications; std::array<Notification, TotalNbNotifications> notifications;
uint8_t readIndex = 0; size_t beginIdx = TotalNbNotifications - 1; // index of the newest notification
uint8_t writeIndex = 0; size_t size = 0; // number of valid notifications in buffer
bool empty = true;
std::atomic<bool> newNotification {false}; std::atomic<bool> newNotification {false};
}; };
} }

View file

@ -74,14 +74,6 @@ BrightnessController::Levels BrightnessController::Level() const {
return level; return level;
} }
void BrightnessController::Backup() {
backupLevel = level;
}
void BrightnessController::Restore() {
Set(backupLevel);
}
void BrightnessController::Step() { void BrightnessController::Step() {
switch (level) { switch (level) {
case Levels::Low: case Levels::Low:

View file

@ -15,15 +15,11 @@ namespace Pinetime {
void Higher(); void Higher();
void Step(); void Step();
void Backup();
void Restore();
const char* GetIcon(); const char* GetIcon();
const char* ToString(); const char* ToString();
private: private:
Levels level = Levels::High; Levels level = Levels::High;
Levels backupLevel = Levels::High;
}; };
} }
} }

View file

@ -9,7 +9,7 @@ namespace Pinetime {
class Settings { class Settings {
public: public:
enum class ClockType : uint8_t { H24, H12 }; 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 ChimesOption : uint8_t { None, Hours, HalfHours };
enum class WakeUpMode : uint8_t { enum class WakeUpMode : uint8_t {
SingleTap = 0, SingleTap = 0,
@ -251,7 +251,7 @@ namespace Pinetime {
uint32_t screenTimeOut = 15000; uint32_t screenTimeOut = 15000;
ClockType clockType = ClockType::H24; ClockType clockType = ClockType::H24;
Notification notificationStatus = Notification::ON; Notification notificationStatus = Notification::On;
uint8_t clockFace = 0; uint8_t clockFace = 0;
ChimesOption chimesOption = ChimesOption::None; ChimesOption chimesOption = ChimesOption::None;

View file

@ -129,6 +129,10 @@ void DisplayApp::InitHw() {
} }
void DisplayApp::Refresh() { void DisplayApp::Refresh() {
auto LoadPreviousScreen = [this]() {
LoadApp(returnToApp, returnDirection);
};
TickType_t queueTimeout; TickType_t queueTimeout;
switch (state) { switch (state) {
case States::Idle: case States::Idle:
@ -136,7 +140,7 @@ void DisplayApp::Refresh() {
break; break;
case States::Running: case States::Running:
if (!currentScreen->IsRunning()) { if (!currentScreen->IsRunning()) {
LoadApp(returnToApp, returnDirection); LoadPreviousScreen();
} }
queueTimeout = lv_task_handler(); queueTimeout = lv_task_handler();
break; break;
@ -149,12 +153,10 @@ void DisplayApp::Refresh() {
if (xQueueReceive(msgQueue, &msg, queueTimeout)) { if (xQueueReceive(msgQueue, &msg, queueTimeout)) {
switch (msg) { switch (msg) {
case Messages::DimScreen: case Messages::DimScreen:
// Backup brightness is the brightness to return to after dimming or sleeping
brightnessController.Backup();
brightnessController.Set(Controllers::BrightnessController::Levels::Low); brightnessController.Set(Controllers::BrightnessController::Levels::Low);
break; break;
case Messages::RestoreBrightness: case Messages::RestoreBrightness:
brightnessController.Restore(); brightnessController.Set(settingsController.GetBrightness());
break; break;
case Messages::GoToSleep: case Messages::GoToSleep:
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) { while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
@ -165,7 +167,7 @@ void DisplayApp::Refresh() {
state = States::Idle; state = States::Idle;
break; break;
case Messages::GoToRunning: case Messages::GoToRunning:
brightnessController.Restore(); brightnessController.Set(settingsController.GetBrightness());
state = States::Running; state = States::Running;
break; break;
case Messages::UpdateTimeOut: case Messages::UpdateTimeOut:
@ -181,7 +183,7 @@ void DisplayApp::Refresh() {
case Messages::TimerDone: case Messages::TimerDone:
if (currentApp == Apps::Timer) { if (currentApp == Apps::Timer) {
auto* timer = static_cast<Screens::Timer*>(currentScreen.get()); auto* timer = static_cast<Screens::Timer*>(currentScreen.get());
timer->SetDone(); timer->Reset();
} else { } else {
LoadApp(Apps::Timer, DisplayApp::FullRefreshDirections::Down); LoadApp(Apps::Timer, DisplayApp::FullRefreshDirections::Down);
} }
@ -224,9 +226,7 @@ void DisplayApp::Refresh() {
break; break;
} }
} else if (returnTouchEvent == gesture) { } else if (returnTouchEvent == gesture) {
LoadApp(returnToApp, returnDirection); LoadPreviousScreen();
brightnessController.Set(settingsController.GetBrightness());
brightnessController.Backup();
} }
} else { } else {
touchHandler.CancelTap(); touchHandler.CancelTap();
@ -237,9 +237,7 @@ void DisplayApp::Refresh() {
if (currentApp == Apps::Clock) { if (currentApp == Apps::Clock) {
PushMessageToSystemTask(System::Messages::GoToSleep); PushMessageToSystemTask(System::Messages::GoToSleep);
} else { } else {
LoadApp(returnToApp, returnDirection); LoadPreviousScreen();
brightnessController.Set(settingsController.GetBrightness());
brightnessController.Backup();
} }
} }
break; break;
@ -303,6 +301,8 @@ void DisplayApp::ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction
void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) { void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) {
touchHandler.CancelTap(); touchHandler.CancelTap();
brightnessController.Set(settingsController.GetBrightness());
currentScreen.reset(nullptr); currentScreen.reset(nullptr);
SetFullRefresh(direction); SetFullRefresh(direction);
@ -311,7 +311,8 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
switch (app) { switch (app) {
case Apps::Launcher: case Apps::Launcher:
currentScreen = std::make_unique<Screens::ApplicationList>(this, settingsController, batteryController, dateTimeController); currentScreen =
std::make_unique<Screens::ApplicationList>(this, settingsController, batteryController, bleController, dateTimeController);
ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::SwipeDown); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break; break;
case Apps::None: case Apps::None:
@ -367,7 +368,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
currentScreen = std::make_unique<Screens::Timer>(this, timerController); currentScreen = std::make_unique<Screens::Timer>(this, timerController);
break; break;
case Apps::Alarm: case Apps::Alarm:
currentScreen = std::make_unique<Screens::Alarm>(this, alarmController, settingsController, *systemTask); currentScreen = std::make_unique<Screens::Alarm>(this, alarmController, settingsController.GetClockType(), *systemTask);
break; break;
// Settings // Settings
@ -377,7 +378,8 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
dateTimeController, dateTimeController,
brightnessController, brightnessController,
motorController, motorController,
settingsController); settingsController,
bleController);
ReturnApp(Apps::Clock, FullRefreshDirections::LeftAnim, TouchEvents::SwipeLeft); ReturnApp(Apps::Clock, FullRefreshDirections::LeftAnim, TouchEvents::SwipeLeft);
break; break;
case Apps::Settings: case Apps::Settings:

View file

@ -1,41 +1,13 @@
/** #include "displayapp/InfiniTimeTheme.h"
* @file lv_pinetime_theme.c
*
*/
/*********************
* INCLUDES
*********************/
#include "displayapp/lv_pinetime_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void theme_apply(lv_obj_t* obj, lv_theme_style_t name); static void theme_apply(lv_obj_t* obj, lv_theme_style_t name);
/**********************
* STATIC VARIABLES
**********************/
static lv_theme_t theme; static lv_theme_t theme;
static lv_style_t style_circle;
static lv_style_t style_bg; static lv_style_t style_bg;
static lv_style_t style_box; static lv_style_t style_box;
static lv_style_t style_box_border;
static lv_style_t style_btn; static lv_style_t style_btn;
static lv_style_t style_btn_border;
static lv_style_t style_title;
static lv_style_t style_label_white; static lv_style_t style_label_white;
static lv_style_t style_back;
static lv_style_t style_icon; static lv_style_t style_icon;
static lv_style_t style_bar_indic; static lv_style_t style_bar_indic;
static lv_style_t style_slider_knob; static lv_style_t style_slider_knob;
@ -51,7 +23,6 @@ static lv_style_t style_arc_knob;
static lv_style_t style_arc_indic; static lv_style_t style_arc_indic;
static lv_style_t style_table_cell; static lv_style_t style_table_cell;
static lv_style_t style_pad_small; static lv_style_t style_pad_small;
static lv_style_t style_bg_grad;
static lv_style_t style_lmeter; static lv_style_t style_lmeter;
static lv_style_t style_chart_serie; static lv_style_t style_chart_serie;
static lv_style_t style_cb_bg; static lv_style_t style_cb_bg;
@ -59,26 +30,15 @@ static lv_style_t style_cb_bullet;
static bool inited; static bool inited;
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init_reset(lv_style_t* style) { static void style_init_reset(lv_style_t* style) {
if (inited) if (inited) {
lv_style_reset(style); lv_style_reset(style);
else } else {
lv_style_init(style); lv_style_init(style);
}
} }
static void basic_init(void) { static void basic_init() {
style_init_reset(&style_circle);
lv_style_set_radius(&style_circle, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
style_init_reset(&style_bg); style_init_reset(&style_bg);
lv_style_set_bg_opa(&style_bg, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_bg, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_bg, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_style_set_bg_color(&style_bg, LV_STATE_DEFAULT, LV_COLOR_BLACK);
@ -87,41 +47,27 @@ static void basic_init(void) {
style_init_reset(&style_box); style_init_reset(&style_box);
lv_style_set_bg_opa(&style_box, LV_STATE_DEFAULT, LV_OPA_COVER); 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_radius(&style_box, LV_STATE_DEFAULT, 10);
lv_style_set_value_color(&style_box, LV_STATE_DEFAULT, LV_PINETIME_BLUE); 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); lv_style_set_value_font(&style_box, LV_STATE_DEFAULT, theme.font_normal);
style_init_reset(&style_box_border);
lv_style_set_bg_opa(&style_box_border, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_border_width(&style_box_border, LV_STATE_DEFAULT, 2);
lv_style_set_border_color(&style_box_border, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_text_color(&style_box, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
style_init_reset(&style_title);
lv_style_set_text_color(&style_title, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_text_font(&style_title, LV_STATE_DEFAULT, theme.font_subtitle);
style_init_reset(&style_label_white); style_init_reset(&style_label_white);
lv_style_set_text_color(&style_label_white, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_text_color(&style_label_white, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_text_color(&style_label_white, LV_STATE_DISABLED, LV_COLOR_GRAY);
style_init_reset(&style_btn); style_init_reset(&style_btn);
lv_style_set_radius(&style_btn, LV_STATE_DEFAULT, 10); 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_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_btn, LV_STATE_DEFAULT, LV_PINETIME_BLUE); lv_style_set_bg_color(&style_btn, LV_STATE_DEFAULT, Colors::bg);
lv_style_set_bg_color(&style_btn, LV_STATE_CHECKED, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); lv_style_set_bg_color(&style_btn, LV_STATE_CHECKED, Colors::highlight);
lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED, LV_PINETIME_BLUE); lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED, Colors::bgDark);
lv_style_set_bg_color(&style_btn, LV_STATE_DISABLED | LV_STATE_CHECKED, lv_color_hex3(0x888)); lv_style_set_border_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_border_color(&style_btn, LV_STATE_DEFAULT, theme.color_primary);
lv_style_set_border_width(&style_btn, LV_STATE_DEFAULT, 0); lv_style_set_border_width(&style_btn, LV_STATE_DEFAULT, 0);
lv_style_set_text_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff)); lv_style_set_text_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff)); lv_style_set_text_color(&style_btn, LV_STATE_DISABLED, LV_COLOR_GRAY);
lv_style_set_text_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
lv_style_set_text_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888));
lv_style_set_value_color(&style_btn, LV_STATE_DEFAULT, lv_color_hex(0xffffff)); lv_style_set_value_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff)); lv_style_set_value_color(&style_btn, LV_STATE_DISABLED, LV_COLOR_GRAY);
lv_style_set_value_color(&style_btn, LV_STATE_CHECKED, lv_color_hex(0xffffff));
lv_style_set_value_color(&style_btn, LV_STATE_DISABLED, lv_color_hex(0x888888));
lv_style_set_pad_left(&style_btn, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_pad_left(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_pad_right(&style_btn, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_pad_right(&style_btn, LV_STATE_DEFAULT, LV_DPX(20));
@ -130,27 +76,12 @@ static void basic_init(void) {
lv_style_set_pad_inner(&style_btn, LV_STATE_DEFAULT, LV_DPX(15)); lv_style_set_pad_inner(&style_btn, LV_STATE_DEFAULT, LV_DPX(15));
lv_style_set_outline_width(&style_btn, LV_STATE_DEFAULT, LV_DPX(2)); lv_style_set_outline_width(&style_btn, LV_STATE_DEFAULT, LV_DPX(2));
lv_style_set_outline_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_0); lv_style_set_outline_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_0);
lv_style_set_outline_color(&style_btn, LV_STATE_DEFAULT, theme.color_primary); lv_style_set_outline_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_transition_time(&style_btn, LV_STATE_DEFAULT, 0); lv_style_set_transition_time(&style_btn, LV_STATE_DEFAULT, 0);
lv_style_set_transition_delay(&style_btn, LV_STATE_DEFAULT, 0); lv_style_set_transition_delay(&style_btn, LV_STATE_DEFAULT, 0);
style_init_reset(&style_btn_border);
lv_style_set_radius(&style_btn_border, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_border_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_border_width(&style_btn_border, LV_STATE_DEFAULT, 2);
lv_style_set_bg_opa(&style_btn_border, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_bg_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_text_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_value_color(&style_btn_border, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_transition_prop_3(&style_btn_border, LV_STATE_DEFAULT, LV_STYLE_BG_OPA);
style_init_reset(&style_icon); style_init_reset(&style_icon);
lv_style_set_text_color(&style_icon, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_text_color(&style_icon, LV_STATE_DEFAULT, LV_COLOR_WHITE);
style_init_reset(&style_back);
lv_style_set_value_color(&style_back, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_value_str(&style_back, LV_STATE_DEFAULT, LV_SYMBOL_LEFT);
lv_style_set_value_font(&style_back, LV_STATE_DEFAULT, theme.font_subtitle);
style_init_reset(&style_bar_indic); style_init_reset(&style_bar_indic);
lv_style_set_bg_opa(&style_bar_indic, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_bar_indic, LV_STATE_DEFAULT, LV_OPA_COVER);
@ -165,15 +96,11 @@ static void basic_init(void) {
style_init_reset(&style_list_btn); style_init_reset(&style_list_btn);
lv_style_set_bg_opa(&style_list_btn, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_list_btn, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_bg_color(&style_list_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_bg_color(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_GRAY); lv_style_set_text_color(&style_list_btn, LV_STATE_DEFAULT, Colors::bg);
lv_style_set_bg_color(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, lv_color_darken(LV_PINETIME_GRAY, LV_OPA_20)); lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED, LV_COLOR_WHITE);
lv_style_set_text_color(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_BLUE); lv_style_set_image_recolor(&style_list_btn, LV_STATE_DEFAULT, Colors::bg);
lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_WHITE); lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED, LV_COLOR_WHITE);
lv_style_set_text_color(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, LV_PINETIME_WHITE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_DEFAULT, LV_PINETIME_BLUE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED, LV_PINETIME_WHITE);
lv_style_set_image_recolor(&style_list_btn, LV_STATE_CHECKED | LV_STATE_PRESSED, LV_PINETIME_WHITE);
lv_style_set_pad_left(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25); lv_style_set_pad_left(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25);
lv_style_set_pad_right(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25); lv_style_set_pad_right(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 25);
lv_style_set_pad_top(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 100); lv_style_set_pad_top(&style_list_btn, LV_STATE_DEFAULT, LV_HOR_RES / 100);
@ -182,25 +109,23 @@ static void basic_init(void) {
style_init_reset(&style_ddlist_list); style_init_reset(&style_ddlist_list);
// Causes lag unfortunately, so we'll have to live with the selected item overflowing the corner // 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_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_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25);
lv_style_set_shadow_width(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 20); lv_style_set_bg_color(&style_ddlist_list, LV_STATE_DEFAULT, Colors::lightGray);
lv_style_set_shadow_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0));
lv_style_set_bg_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0));
lv_style_set_pad_all(&style_ddlist_list, LV_STATE_DEFAULT, 20); lv_style_set_pad_all(&style_ddlist_list, LV_STATE_DEFAULT, 20);
style_init_reset(&style_ddlist_selected); style_init_reset(&style_ddlist_selected);
lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, LV_PINETIME_BLUE); lv_style_set_bg_color(&style_ddlist_selected, LV_STATE_DEFAULT, Colors::bg);
style_init_reset(&style_sw_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_opa(&style_sw_bg, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_sw_bg, LV_STATE_DEFAULT, LV_PINETIME_BLUE); 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); lv_style_set_radius(&style_sw_bg, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
style_init_reset(&style_sw_indic); style_init_reset(&style_sw_indic);
lv_style_set_bg_opa(&style_sw_indic, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_sw_indic, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); lv_style_set_bg_color(&style_sw_indic, LV_STATE_DEFAULT, Colors::highlight);
style_init_reset(&style_sw_knob); style_init_reset(&style_sw_knob);
lv_style_set_bg_opa(&style_sw_knob, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_sw_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
@ -228,12 +153,12 @@ static void basic_init(void) {
lv_style_set_pad_right(&style_slider_knob, LV_STATE_PRESSED, 14); lv_style_set_pad_right(&style_slider_knob, LV_STATE_PRESSED, 14);
style_init_reset(&style_arc_indic); style_init_reset(&style_arc_indic);
lv_style_set_line_color(&style_arc_indic, LV_STATE_DEFAULT, LV_PINETIME_BLUE); 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_width(&style_arc_indic, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_line_rounded(&style_arc_indic, LV_STATE_DEFAULT, true); lv_style_set_line_rounded(&style_arc_indic, LV_STATE_DEFAULT, true);
style_init_reset(&style_arc_bg); style_init_reset(&style_arc_bg);
lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); 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_width(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true); lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true);
lv_style_set_pad_all(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(5)); lv_style_set_pad_all(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(5));
@ -245,7 +170,7 @@ static void basic_init(void) {
lv_style_set_pad_all(&style_arc_knob, LV_STATE_DEFAULT, LV_DPX(5)); lv_style_set_pad_all(&style_arc_knob, LV_STATE_DEFAULT, LV_DPX(5));
style_init_reset(&style_table_cell); style_init_reset(&style_table_cell);
lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY); lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1); lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1);
lv_style_set_border_side(&style_table_cell, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); lv_style_set_border_side(&style_table_cell, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL);
lv_style_set_pad_left(&style_table_cell, LV_STATE_DEFAULT, 5); lv_style_set_pad_left(&style_table_cell, LV_STATE_DEFAULT, 5);
@ -261,11 +186,6 @@ static void basic_init(void) {
lv_style_set_pad_bottom(&style_pad_small, LV_STATE_DEFAULT, pad_small_value); lv_style_set_pad_bottom(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
lv_style_set_pad_inner(&style_pad_small, LV_STATE_DEFAULT, pad_small_value); lv_style_set_pad_inner(&style_pad_small, LV_STATE_DEFAULT, pad_small_value);
style_init_reset(&style_bg_grad);
lv_style_set_bg_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 40));
lv_style_set_bg_grad_color(&style_bg_grad, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(10, 10, 20));
lv_style_set_bg_grad_dir(&style_bg_grad, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
style_init_reset(&style_lmeter); style_init_reset(&style_lmeter);
lv_style_set_radius(&style_lmeter, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_style_set_radius(&style_lmeter, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_pad_left(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_pad_left(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(20));
@ -274,30 +194,30 @@ static void basic_init(void) {
lv_style_set_pad_inner(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(30)); lv_style_set_pad_inner(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(30));
lv_style_set_scale_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(25)); lv_style_set_scale_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_line_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary); lv_style_set_line_color(&style_lmeter, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_scale_grad_color(&style_lmeter, LV_STATE_DEFAULT, theme.color_primary); lv_style_set_scale_grad_color(&style_lmeter, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_scale_end_color(&style_lmeter, LV_STATE_DEFAULT, lv_color_hex3(0x888)); lv_style_set_scale_end_color(&style_lmeter, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_style_set_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(10)); lv_style_set_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(10));
lv_style_set_scale_end_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(7)); lv_style_set_scale_end_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(7));
style_init_reset(&style_chart_serie); style_init_reset(&style_chart_serie);
lv_style_set_line_color(&style_chart_serie, LV_STATE_DEFAULT, LV_PINETIME_WHITE); lv_style_set_line_color(&style_chart_serie, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_width(&style_chart_serie, LV_STATE_DEFAULT, 4); lv_style_set_line_width(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_size(&style_chart_serie, LV_STATE_DEFAULT, 4); lv_style_set_size(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_bg_opa(&style_chart_serie, LV_STATE_DEFAULT, 0); lv_style_set_bg_opa(&style_chart_serie, LV_STATE_DEFAULT, 0);
lv_style_reset(&style_cb_bg); lv_style_reset(&style_cb_bg);
lv_style_set_radius(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(4)); lv_style_set_radius(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(4));
lv_style_set_pad_inner(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(10)); lv_style_set_pad_inner(&style_cb_bg, LV_STATE_DEFAULT, 18);
lv_style_set_outline_color(&style_cb_bg, LV_STATE_DEFAULT, theme.color_primary); lv_style_set_outline_color(&style_cb_bg, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_outline_width(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(2)); lv_style_set_outline_width(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(2));
lv_style_set_outline_pad(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(20)); lv_style_set_outline_pad(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(20));
lv_style_set_transition_time(&style_cb_bg, LV_STATE_DEFAULT, 0); lv_style_set_transition_time(&style_cb_bg, LV_STATE_DEFAULT, 0);
lv_style_set_transition_prop_6(&style_cb_bg, LV_STATE_DEFAULT, LV_STYLE_OUTLINE_OPA); lv_style_set_transition_prop_6(&style_cb_bg, LV_STATE_DEFAULT, LV_STYLE_OUTLINE_OPA);
lv_style_reset(&style_cb_bullet); 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_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_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_left(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
lv_style_set_pad_right(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8)); lv_style_set_pad_right(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
@ -305,10 +225,6 @@ static void basic_init(void) {
lv_style_set_pad_bottom(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8)); lv_style_set_pad_bottom(&style_cb_bullet, LV_STATE_DEFAULT, LV_DPX(8));
} }
/**********************
* GLOBAL FUNCTIONS
**********************/
/** /**
* Initialize the default * Initialize the default
* @param color_primary the primary color of the theme * @param color_primary the primary color of the theme
@ -347,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) { static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
lv_style_list_t* list; lv_style_list_t* list;
/*To avoid warnings*/ switch (name) {
uint32_t name_int = (uint32_t) name;
switch (name_int) {
case LV_THEME_NONE: case LV_THEME_NONE:
break; break;
@ -376,7 +290,6 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN); lv_obj_clean_style_list(obj, LV_BTN_PART_MAIN);
list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN); list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN);
_lv_style_list_add_style(list, &style_btn); _lv_style_list_add_style(list, &style_btn);
//_lv_style_list_add_style(list, &style_bg_grad);
break; break;
case LV_THEME_BTNMATRIX: case LV_THEME_BTNMATRIX:
@ -386,8 +299,6 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BTN); list = lv_obj_get_style_list(obj, LV_BTNMATRIX_PART_BTN);
_lv_style_list_add_style(list, &style_btn); _lv_style_list_add_style(list, &style_btn);
//_lv_style_list_add_style(list, &style_bg_grad);
//_lv_style_list_add_style(list, &style_bg_click);
break; break;
case LV_THEME_BAR: case LV_THEME_BAR:
@ -490,7 +401,7 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
_lv_style_list_add_style(list, &style_scrollbar); _lv_style_list_add_style(list, &style_scrollbar);
break; break;
case LV_THEME_TABLE: case LV_THEME_TABLE: {
list = lv_obj_get_style_list(obj, LV_TABLE_PART_BG); list = lv_obj_get_style_list(obj, LV_TABLE_PART_BG);
_lv_style_list_add_style(list, &style_bg); _lv_style_list_add_style(list, &style_bg);
@ -503,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_table_cell);
_lv_style_list_add_style(list, &style_label_white); _lv_style_list_add_style(list, &style_label_white);
} }
break; } break;
case LV_THEME_LINEMETER: case LV_THEME_LINEMETER:
list = lv_obj_get_style_list(obj, LV_LINEMETER_PART_MAIN); list = lv_obj_get_style_list(obj, LV_LINEMETER_PART_MAIN);
@ -533,7 +444,3 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
lv_obj_refresh_style(obj, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL); lv_obj_refresh_style(obj, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
} }
/**********************
* STATIC FUNCTIONS
**********************/

View file

@ -1,39 +1,17 @@
/** #pragma once
* @file lv_pinetime_theme.h
*
*/
#ifndef LV_PINETIME_THEME_H
#define LV_PINETIME_THEME_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <lvgl/lvgl.h> #include <lvgl/lvgl.h>
/********************* namespace Colors {
* DEFINES static constexpr lv_color_t orange = LV_COLOR_MAKE(0xff, 0xb0, 0x0);
*********************/ static constexpr lv_color_t green = LV_COLOR_MAKE(0x0, 0xb0, 0x0);
/*Colors*/ static constexpr lv_color_t lightGray = LV_COLOR_MAKE(0xb0, 0xb0, 0xb0);
#define LV_PINETIME_WHITE lv_color_hex(0xffffff)
#define LV_PINETIME_LIGHT lv_color_hex(0xf3f8fe)
#define LV_PINETIME_GRAY lv_color_hex(0x8a8a8a)
#define LV_PINETIME_LIGHT_GRAY lv_color_hex(0xc4c4c4)
#define LV_PINETIME_BLUE lv_color_hex(0x5d697e)
#define LV_PINETIME_GREEN lv_color_hex(0x4cb242)
#define LV_PINETIME_RED lv_color_hex(0xd51732)
/********************** static constexpr lv_color_t bg = LV_COLOR_MAKE(0x5d, 0x69, 0x7e);
* TYPEDEFS 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;
/********************** };
* GLOBAL PROTOTYPES
**********************/
/** /**
* Initialize the default * Initialize the default
@ -53,12 +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_normal,
const lv_font_t* font_subtitle, const lv_font_t* font_subtitle,
const lv_font_t* font_title); const lv_font_t* font_title);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif

View file

@ -1,5 +1,5 @@
#include "displayapp/LittleVgl.h" #include "displayapp/LittleVgl.h"
#include "displayapp/lv_pinetime_theme.h" #include "displayapp/InfiniTimeTheme.h"
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <task.h> #include <task.h>

View file

@ -6,6 +6,8 @@ find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED
message(STATUS "Using ${LV_FONT_CONV} to generate font files") message(STATUS "Using ${LV_FONT_CONV} to generate font files")
configure_file(${CMAKE_CURRENT_LIST_DIR}/jetbrains_mono_bold_20.c_zero.patch configure_file(${CMAKE_CURRENT_LIST_DIR}/jetbrains_mono_bold_20.c_zero.patch
${CMAKE_CURRENT_BINARY_DIR}/jetbrains_mono_bold_20.c_zero.patch COPYONLY) ${CMAKE_CURRENT_BINARY_DIR}/jetbrains_mono_bold_20.c_zero.patch COPYONLY)
configure_file(${CMAKE_CURRENT_LIST_DIR}/jetbrains_mono_bold_20.c_M.patch
${CMAKE_CURRENT_BINARY_DIR}/jetbrains_mono_bold_20.c_M.patch COPYONLY)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
# FindPython3 module introduces with CMake 3.12 # FindPython3 module introduces with CMake 3.12
# https://cmake.org/cmake/help/latest/module/FindPython3.html # https://cmake.org/cmake/help/latest/module/FindPython3.html

View file

@ -1,16 +1,19 @@
# Fonts # Fonts
* [Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/) - [Jetbrains Mono](https://www.jetbrains.com/lp/mono/)
* [Awesome font from LVGL](https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff) - [Font Awesome](https://fontawesome.com/v5/cheatsheet/free/solid)
* [Open Sans Light from Google](https://fonts.google.com/specimen/Open+Sans) - [Open Sans Light](https://fonts.google.com/specimen/Open+Sans)
- [Material Symbols](https://fonts.google.com/icons)
### How to add new symbols: ### How to add new symbols:
* Browse [this cheatsheet](https://fontawesome.com/cheatsheet/free/solid) and pick symbols - Browse the cheat sheets and pick symbols
* For each symbol, add its hex code (0xf641 for the 'Ad' icon, for example) to the *Range* list (or the symbol list when its simple enough) in the `fonts.json` file - [Font Awesome](https://fontawesome.com/v5/cheatsheet/free/solid)
* Convert this hex value into a UTF-8 code - [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) 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"; static constexpr const char* newSymbol = "\xEF\x86\x85";
@ -20,11 +23,12 @@ static constexpr const char* newSymbol = "\xEF\x86\x85";
inside `fonts`, there is a dictionary of fonts, inside `fonts`, there is a dictionary of fonts,
and for each font there is: and for each font there is:
* sources - list of file,range(,symbols) wanted (as a dictionary of those)
* bpp - bits per pixel. - sources - list of file,range(,symbols) wanted (as a dictionary of those)
* size - size. - bpp - bits per pixel.
* patches - list of extra "patches" to run: a path to a .patch file. (may be relative) - size - size.
* compress - optional. default disabled. add `"compress": true` to enable - 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 ### Navigation font

View file

@ -7,12 +7,12 @@
}, },
{ {
"file": "FontAwesome5-Solid+Brands+Regular.woff", "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, "bpp": 1,
"size": 20, "size": 20,
"patches": ["jetbrains_mono_bold_20.c_zero.patch"] "patches": ["jetbrains_mono_bold_20.c_zero.patch", "jetbrains_mono_bold_20.c_M.patch"]
}, },
"jetbrains_mono_42": { "jetbrains_mono_42": {
"sources": [ "sources": [
@ -57,8 +57,8 @@
"lv_font_sys_48": { "lv_font_sys_48": {
"sources": [ "sources": [
{ {
"file": "icons_sys_48.ttf", "file": "material-design-icons/MaterialIcons-Regular.ttf",
"range": "0xe902, 0xe904-0xe907, 0xe90b-0xe90c" "range": "0xf00b, 0xe3aa-0xe3ac, 0xe7f6-0xe7f7, 0xe8b8, 0xef44"
} }
], ],
"bpp": 1, "bpp": 1,

View file

@ -67,7 +67,7 @@ def main():
subprocess.check_call(line) subprocess.check_call(line)
if patches: if patches:
for patch in patches: for patch in patches:
subprocess.check_call(['/usr/bin/patch', name+'.c', patch]) subprocess.check_call(['/usr/bin/env', 'patch', name+'.c', patch])

View file

@ -0,0 +1,8 @@
@@ -217,7 +217,7 @@
0xc0, 0xe0, 0x70, 0x38, 0x1c, 0xf, 0xff, 0xfc,
/* U+004D "M" */
- 0xf3, 0xfc, 0xfd, 0x3f, 0xcf, 0xff, 0xff, 0xfe,
+ 0xf3, 0xfc, 0xff, 0x3f, 0xcf, 0xff, 0xff, 0xfe,
0xdf, 0xb7, 0xe1, 0xf8, 0x7e, 0x1f, 0x87, 0xe1,
0xf8, 0x70,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -18,10 +18,18 @@
#include "displayapp/screens/Alarm.h" #include "displayapp/screens/Alarm.h"
#include "displayapp/screens/Screen.h" #include "displayapp/screens/Screen.h"
#include "displayapp/screens/Symbols.h" #include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
using Pinetime::Controllers::AlarmController; using Pinetime::Controllers::AlarmController;
namespace {
void ValueChangedHandler(void* userData) {
auto* screen = static_cast<Alarm*>(userData);
screen->OnValueChanged();
}
}
static void btnEventHandler(lv_obj_t* obj, lv_event_t event) { static void btnEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<Alarm*>(obj->user_data); auto* screen = static_cast<Alarm*>(obj->user_data);
screen->OnButtonEvent(obj, event); screen->OnButtonEvent(obj, event);
@ -34,58 +42,33 @@ static void StopAlarmTaskCallback(lv_task_t* task) {
Alarm::Alarm(DisplayApp* app, Alarm::Alarm(DisplayApp* app,
Controllers::AlarmController& alarmController, Controllers::AlarmController& alarmController,
Pinetime::Controllers::Settings& settingsController, Controllers::Settings::ClockType clockType,
System::SystemTask& systemTask) System::SystemTask& systemTask)
: Screen(app), alarmController {alarmController}, settingsController {settingsController}, systemTask {systemTask} { : Screen(app), alarmController {alarmController}, systemTask {systemTask} {
time = lv_label_create(lv_scr_act(), nullptr); hourCounter.Create();
lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); if (clockType == Controllers::Settings::ClockType::H12) {
hourCounter.EnableTwelveHourMode();
alarmHours = alarmController.Hours(); lblampm = lv_label_create(lv_scr_act(), nullptr);
alarmMinutes = alarmController.Minutes(); lv_obj_set_style_local_text_font(lblampm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
lv_label_set_text_fmt(time, "%02hhu:%02hhu", alarmHours, alarmMinutes); lv_label_set_text_static(lblampm, "AM");
lv_label_set_align(lblampm, LV_LABEL_ALIGN_CENTER);
lv_obj_align(lblampm, lv_scr_act(), LV_ALIGN_CENTER, 0, 30);
}
hourCounter.SetValue(alarmController.Hours());
hourCounter.SetValueChangedEventCallback(this, ValueChangedHandler);
lv_obj_align(time, lv_scr_act(), LV_ALIGN_CENTER, 0, -25); minuteCounter.Create();
lv_obj_align(minuteCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
minuteCounter.SetValue(alarmController.Minutes());
minuteCounter.SetValueChangedEventCallback(this, ValueChangedHandler);
lblampm = lv_label_create(lv_scr_act(), nullptr); lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(lblampm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20); lv_obj_set_style_local_text_font(colonLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
lv_obj_set_style_local_text_color(lblampm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); lv_label_set_text_static(colonLabel, ":");
lv_label_set_text_static(lblampm, " "); lv_obj_align(colonLabel, lv_scr_act(), LV_ALIGN_CENTER, 0, -29);
lv_label_set_align(lblampm, LV_LABEL_ALIGN_CENTER);
lv_obj_align(lblampm, lv_scr_act(), LV_ALIGN_CENTER, 0, 30);
btnHoursUp = lv_btn_create(lv_scr_act(), nullptr);
btnHoursUp->user_data = this;
lv_obj_set_event_cb(btnHoursUp, btnEventHandler);
lv_obj_set_size(btnHoursUp, 60, 40);
lv_obj_align(btnHoursUp, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 20, -85);
txtHrUp = lv_label_create(btnHoursUp, nullptr);
lv_label_set_text_static(txtHrUp, "+");
btnHoursDown = lv_btn_create(lv_scr_act(), nullptr);
btnHoursDown->user_data = this;
lv_obj_set_event_cb(btnHoursDown, btnEventHandler);
lv_obj_set_size(btnHoursDown, 60, 40);
lv_obj_align(btnHoursDown, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 20, 35);
txtHrDown = lv_label_create(btnHoursDown, nullptr);
lv_label_set_text_static(txtHrDown, "-");
btnMinutesUp = lv_btn_create(lv_scr_act(), nullptr);
btnMinutesUp->user_data = this;
lv_obj_set_event_cb(btnMinutesUp, btnEventHandler);
lv_obj_set_size(btnMinutesUp, 60, 40);
lv_obj_align(btnMinutesUp, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -20, -85);
txtMinUp = lv_label_create(btnMinutesUp, nullptr);
lv_label_set_text_static(txtMinUp, "+");
btnMinutesDown = lv_btn_create(lv_scr_act(), nullptr);
btnMinutesDown->user_data = this;
lv_obj_set_event_cb(btnMinutesDown, btnEventHandler);
lv_obj_set_size(btnMinutesDown, 60, 40);
lv_obj_align(btnMinutesDown, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -20, 35);
txtMinDown = lv_label_create(btnMinutesDown, nullptr);
lv_label_set_text_static(txtMinDown, "-");
btnStop = lv_btn_create(lv_scr_act(), nullptr); btnStop = lv_btn_create(lv_scr_act(), nullptr);
btnStop->user_data = this; btnStop->user_data = this;
@ -97,6 +80,8 @@ Alarm::Alarm(DisplayApp* app,
lv_label_set_text_static(txtStop, Symbols::stop); lv_label_set_text_static(txtStop, Symbols::stop);
lv_obj_set_hidden(btnStop, true); lv_obj_set_hidden(btnStop, true);
static constexpr lv_color_t bgColor = Colors::bgAlt;
btnRecur = lv_btn_create(lv_scr_act(), nullptr); btnRecur = lv_btn_create(lv_scr_act(), nullptr);
btnRecur->user_data = this; btnRecur->user_data = this;
lv_obj_set_event_cb(btnRecur, btnEventHandler); lv_obj_set_event_cb(btnRecur, btnEventHandler);
@ -104,13 +89,18 @@ Alarm::Alarm(DisplayApp* app,
lv_obj_align(btnRecur, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); lv_obj_align(btnRecur, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
txtRecur = lv_label_create(btnRecur, nullptr); txtRecur = lv_label_create(btnRecur, nullptr);
SetRecurButtonState(); SetRecurButtonState();
lv_obj_set_style_local_bg_color(btnRecur, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor);
btnInfo = lv_btn_create(lv_scr_act(), nullptr); btnInfo = lv_btn_create(lv_scr_act(), nullptr);
btnInfo->user_data = this; btnInfo->user_data = this;
lv_obj_set_event_cb(btnInfo, btnEventHandler); lv_obj_set_event_cb(btnInfo, btnEventHandler);
lv_obj_set_size(btnInfo, 50, 40); lv_obj_set_size(btnInfo, 50, 50);
lv_obj_align(btnInfo, lv_scr_act(), LV_ALIGN_CENTER, 0, -85); lv_obj_align(btnInfo, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, -4);
txtInfo = lv_label_create(btnInfo, nullptr); lv_obj_set_style_local_bg_color(btnInfo, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, bgColor);
lv_obj_set_style_local_border_width(btnInfo, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_border_color(btnInfo, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_t* txtInfo = lv_label_create(btnInfo, nullptr);
lv_label_set_text_static(txtInfo, "i"); lv_label_set_text_static(txtInfo, "i");
enableSwitch = lv_switch_create(lv_scr_act(), nullptr); enableSwitch = lv_switch_create(lv_scr_act(), nullptr);
@ -119,6 +109,7 @@ Alarm::Alarm(DisplayApp* app,
lv_obj_set_size(enableSwitch, 100, 50); lv_obj_set_size(enableSwitch, 100, 50);
// Align to the center of 115px from edge // Align to the center of 115px from edge
lv_obj_align(enableSwitch, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 7, 0); lv_obj_align(enableSwitch, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 7, 0);
lv_obj_set_style_local_bg_color(enableSwitch, LV_SWITCH_PART_BG, LV_STATE_DEFAULT, bgColor);
UpdateAlarmTime(); UpdateAlarmTime();
@ -136,8 +127,14 @@ Alarm::~Alarm() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
void Alarm::DisableAlarm() {
if (alarmController.State() == AlarmController::AlarmState::Set) {
alarmController.DisableAlarm();
lv_switch_off(enableSwitch, LV_ANIM_ON);
}
}
void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) { void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
using Pinetime::Controllers::AlarmController;
if (event == LV_EVENT_CLICKED) { if (event == LV_EVENT_CLICKED) {
if (obj == btnStop) { if (obj == btnStop) {
StopAlerting(); StopAlerting();
@ -159,49 +156,8 @@ void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
} }
return; return;
} }
// If any other button was pressed, disable the alarm
// this is to make it clear that the alarm won't be set until it is turned back on
if (alarmController.State() == AlarmController::AlarmState::Set) {
alarmController.DisableAlarm();
lv_switch_off(enableSwitch, LV_ANIM_ON);
}
if (obj == btnMinutesUp) {
if (alarmMinutes >= 59) {
alarmMinutes = 0;
} else {
alarmMinutes++;
}
UpdateAlarmTime();
return;
}
if (obj == btnMinutesDown) {
if (alarmMinutes == 0) {
alarmMinutes = 59;
} else {
alarmMinutes--;
}
UpdateAlarmTime();
return;
}
if (obj == btnHoursUp) {
if (alarmHours >= 23) {
alarmHours = 0;
} else {
alarmHours++;
}
UpdateAlarmTime();
return;
}
if (obj == btnHoursDown) {
if (alarmHours == 0) {
alarmHours = 23;
} else {
alarmHours--;
}
UpdateAlarmTime();
return;
}
if (obj == btnRecur) { if (obj == btnRecur) {
DisableAlarm();
ToggleRecurrence(); ToggleRecurrence();
} }
} }
@ -224,30 +180,20 @@ bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return alarmController.State() == AlarmController::AlarmState::Alerting && event == TouchEvents::SwipeDown; return alarmController.State() == AlarmController::AlarmState::Alerting && event == TouchEvents::SwipeDown;
} }
void Alarm::OnValueChanged() {
DisableAlarm();
UpdateAlarmTime();
}
void Alarm::UpdateAlarmTime() { void Alarm::UpdateAlarmTime() {
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { if (lblampm != nullptr) {
switch (alarmHours) { if (hourCounter.GetValue() >= 12) {
case 0: lv_label_set_text_static(lblampm, "PM");
lv_label_set_text_static(lblampm, "AM"); } else {
lv_label_set_text_fmt(time, "%02d:%02d", 12, alarmMinutes); lv_label_set_text_static(lblampm, "AM");
break;
case 1 ... 11:
lv_label_set_text_static(lblampm, "AM");
lv_label_set_text_fmt(time, "%02d:%02d", alarmHours, alarmMinutes);
break;
case 12:
lv_label_set_text_static(lblampm, "PM");
lv_label_set_text_fmt(time, "%02d:%02d", 12, alarmMinutes);
break;
case 13 ... 23:
lv_label_set_text_static(lblampm, "PM");
lv_label_set_text_fmt(time, "%02d:%02d", alarmHours - 12, alarmMinutes);
break;
} }
} else {
lv_label_set_text_fmt(time, "%02d:%02d", alarmHours, alarmMinutes);
} }
alarmController.SetAlarmTime(alarmHours, alarmMinutes); alarmController.SetAlarmTime(hourCounter.GetValue(), minuteCounter.GetValue());
} }
void Alarm::SetAlerting() { void Alarm::SetAlerting() {
@ -283,6 +229,9 @@ void Alarm::SetSwitchState(lv_anim_enable_t anim) {
} }
void Alarm::ShowInfo() { void Alarm::ShowInfo() {
if (btnMessage != nullptr) {
return;
}
btnMessage = lv_btn_create(lv_scr_act(), nullptr); btnMessage = lv_btn_create(lv_scr_act(), nullptr);
btnMessage->user_data = this; btnMessage->user_data = this;
lv_obj_set_event_cb(btnMessage, btnEventHandler); lv_obj_set_event_cb(btnMessage, btnEventHandler);

View file

@ -21,6 +21,7 @@
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
#include "displayapp/LittleVgl.h" #include "displayapp/LittleVgl.h"
#include "components/alarm/AlarmController.h" #include "components/alarm/AlarmController.h"
#include "displayapp/widgets/Counter.h"
namespace Pinetime { namespace Pinetime {
namespace Applications { namespace Applications {
@ -29,29 +30,28 @@ namespace Pinetime {
public: public:
Alarm(DisplayApp* app, Alarm(DisplayApp* app,
Controllers::AlarmController& alarmController, Controllers::AlarmController& alarmController,
Pinetime::Controllers::Settings& settingsController, Controllers::Settings::ClockType clockType,
System::SystemTask& systemTask); System::SystemTask& systemTask);
~Alarm() override; ~Alarm() override;
void SetAlerting(); void SetAlerting();
void OnButtonEvent(lv_obj_t* obj, lv_event_t event); void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
bool OnButtonPushed() override; bool OnButtonPushed() override;
bool OnTouchEvent(TouchEvents event) override; bool OnTouchEvent(TouchEvents event) override;
void OnValueChanged();
void StopAlerting(); void StopAlerting();
private: private:
uint8_t alarmHours;
uint8_t alarmMinutes;
Controllers::AlarmController& alarmController; Controllers::AlarmController& alarmController;
Controllers::Settings& settingsController;
System::SystemTask& systemTask; System::SystemTask& systemTask;
lv_obj_t *time, *lblampm, *btnStop, *txtStop, *btnMinutesUp, *btnMinutesDown, *btnHoursUp, *btnHoursDown, *txtMinUp, *txtMinDown, lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch;
*txtHrUp, *txtHrDown, *btnRecur, *txtRecur, *btnInfo, *txtInfo, *enableSwitch; lv_obj_t* lblampm = nullptr;
lv_obj_t* txtMessage = nullptr; lv_obj_t* txtMessage = nullptr;
lv_obj_t* btnMessage = nullptr; lv_obj_t* btnMessage = nullptr;
lv_task_t* taskStopAlarm = nullptr; lv_task_t* taskStopAlarm = nullptr;
enum class EnableButtonState { On, Off, Alerting }; enum class EnableButtonState { On, Off, Alerting };
void DisableAlarm();
void SetRecurButtonState(); void SetRecurButtonState();
void SetSwitchState(lv_anim_enable_t anim); void SetSwitchState(lv_anim_enable_t anim);
void SetAlarm(); void SetAlarm();
@ -59,6 +59,8 @@ namespace Pinetime {
void HideInfo(); void HideInfo();
void ToggleRecurrence(); void ToggleRecurrence();
void UpdateAlarmTime(); void UpdateAlarmTime();
Widgets::Counter hourCounter = Widgets::Counter(0, 23, jetbrains_mono_76);
Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
}; };
}; };
}; };

View file

@ -1,33 +1,34 @@
#include "displayapp/screens/ApplicationList.h" #include "displayapp/screens/ApplicationList.h"
#include <lvgl/lvgl.h> #include <lvgl/lvgl.h>
#include <array> #include <functional>
#include "displayapp/screens/Symbols.h"
#include "displayapp/screens/Tile.h"
#include "displayapp/Apps.h" #include "displayapp/Apps.h"
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
constexpr std::array<Tile::Applications, ApplicationList::applications.size()> ApplicationList::applications;
auto ApplicationList::CreateScreenList() const {
std::array<std::function<std::unique_ptr<Screen>()>, nScreens> screens;
for (size_t i = 0; i < screens.size(); i++) {
screens[i] = [this, i]() -> std::unique_ptr<Screen> {
return CreateScreen(i);
};
}
return screens;
}
ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app, ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app,
Pinetime::Controllers::Settings& settingsController, Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::Battery& batteryController, Pinetime::Controllers::Battery& batteryController,
Pinetime::Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController) Controllers::DateTime& dateTimeController)
: Screen(app), : Screen(app),
settingsController {settingsController}, settingsController {settingsController},
batteryController {batteryController}, batteryController {batteryController},
bleController {bleController},
dateTimeController {dateTimeController}, dateTimeController {dateTimeController},
screens {app, screens {app, settingsController.GetAppMenu(), CreateScreenList(), Screens::ScreenListModes::UpDown} {
settingsController.GetAppMenu(),
{
[this]() -> std::unique_ptr<Screen> {
return CreateScreen1();
},
[this]() -> std::unique_ptr<Screen> {
return CreateScreen2();
},
//[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
},
Screens::ScreenListModes::UpDown} {
} }
ApplicationList::~ApplicationList() { ApplicationList::~ApplicationList() {
@ -38,42 +39,18 @@ bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return screens.OnTouchEvent(event); return screens.OnTouchEvent(event);
} }
std::unique_ptr<Screen> ApplicationList::CreateScreen1() { std::unique_ptr<Screen> ApplicationList::CreateScreen(unsigned int screenNum) const {
std::array<Screens::Tile::Applications, 6> applications {{ std::array<Tile::Applications, appsPerScreen> apps;
{Symbols::stopWatch, Apps::StopWatch}, for (int i = 0; i < appsPerScreen; i++) {
{Symbols::clock, Apps::Alarm}, apps[i] = applications[screenNum * appsPerScreen + i];
{Symbols::hourGlass, Apps::Timer}, }
{Symbols::shoe, Apps::Steps},
{Symbols::heartBeat, Apps::HeartRate},
{Symbols::music, Apps::Music},
}};
return std::make_unique<Screens::Tile>(0, 2, app, settingsController, batteryController, dateTimeController, applications); return std::make_unique<Screens::Tile>(screenNum,
nScreens,
app,
settingsController,
batteryController,
bleController,
dateTimeController,
apps);
} }
std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
std::array<Screens::Tile::Applications, 6> applications {{
{Symbols::paintbrush, Apps::Paint},
{Symbols::paddle, Apps::Paddle},
{"2", Apps::Twos},
{Symbols::chartLine, Apps::Motion},
{Symbols::drum, Apps::Metronome},
{Symbols::map, Apps::Navigation},
}};
return std::make_unique<Screens::Tile>(1, 2, app, settingsController, batteryController, dateTimeController, applications);
}
/*std::unique_ptr<Screen> ApplicationList::CreateScreen3() {
std::array<Screens::Tile::Applications, 6> applications {
{{"A", Apps::Meter},
{"B", Apps::Navigation},
{"C", Apps::Clock},
{"D", Apps::Music},
{"E", Apps::SysInfo},
{"F", Apps::Brightness}
}
};
return std::make_unique<Screens::Tile>(2, 3, app, settingsController, batteryController, dateTimeController, applications);
}*/

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <array>
#include <memory> #include <memory>
#include "displayapp/screens/Screen.h" #include "displayapp/screens/Screen.h"
@ -7,6 +8,8 @@
#include "components/datetime/DateTimeController.h" #include "components/datetime/DateTimeController.h"
#include "components/settings/Settings.h" #include "components/settings/Settings.h"
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/screens/Tile.h"
namespace Pinetime { namespace Pinetime {
namespace Applications { namespace Applications {
@ -16,19 +19,41 @@ namespace Pinetime {
explicit ApplicationList(DisplayApp* app, explicit ApplicationList(DisplayApp* app,
Pinetime::Controllers::Settings& settingsController, Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::Battery& batteryController, Pinetime::Controllers::Battery& batteryController,
Pinetime::Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController); Controllers::DateTime& dateTimeController);
~ApplicationList() override; ~ApplicationList() override;
bool OnTouchEvent(TouchEvents event) override; bool OnTouchEvent(TouchEvents event) override;
private: private:
auto CreateScreenList() const;
std::unique_ptr<Screen> CreateScreen(unsigned int screenNum) const;
Controllers::Settings& settingsController; Controllers::Settings& settingsController;
Pinetime::Controllers::Battery& batteryController; Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::Ble& bleController;
Controllers::DateTime& dateTimeController; Controllers::DateTime& dateTimeController;
ScreenList<2> screens; static constexpr int appsPerScreen = 6;
std::unique_ptr<Screen> CreateScreen1();
std::unique_ptr<Screen> CreateScreen2(); // Increment this when more space is needed
// std::unique_ptr<Screen> CreateScreen3(); static constexpr int nScreens = 2;
static constexpr std::array<Tile::Applications, appsPerScreen * nScreens> applications {{
{Symbols::stopWatch, Apps::StopWatch},
{Symbols::clock, Apps::Alarm},
{Symbols::hourGlass, Apps::Timer},
{Symbols::shoe, Apps::Steps},
{Symbols::heartBeat, Apps::HeartRate},
{Symbols::music, Apps::Music},
{Symbols::paintbrush, Apps::Paint},
{Symbols::paddle, Apps::Paddle},
{"2", Apps::Twos},
{Symbols::chartLine, Apps::Motion},
{Symbols::drum, Apps::Metronome},
{Symbols::map, Apps::Navigation},
}};
ScreenList<nScreens> screens;
}; };
} }
} }

View file

@ -1,6 +1,7 @@
#include "displayapp/screens/BatteryInfo.h" #include "displayapp/screens/BatteryInfo.h"
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; 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_obj_align(charging_bar, nullptr, LV_ALIGN_CENTER, 0, 10);
lv_bar_set_anim_time(charging_bar, 1000); lv_bar_set_anim_time(charging_bar, 1000);
lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, lv_color_hex(0x222222)); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, 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_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); lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON);
status = lv_label_create(lv_scr_act(), nullptr); status = lv_label_create(lv_scr_act(), nullptr);
@ -33,7 +34,7 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60); lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60);
voltage = lv_label_create(lv_scr_act(), nullptr); voltage = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_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_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER); lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER);
lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95); lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95);
@ -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_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_label_set_text_static(status, "Battery low"); lv_label_set_text_static(status, "Battery low");
} else { } 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"); lv_label_set_text_static(status, "Discharging");
} }

View file

@ -3,6 +3,7 @@
#include "Version.h" #include "Version.h"
#include "components/firmwarevalidator/FirmwareValidator.h" #include "components/firmwarevalidator/FirmwareValidator.h"
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
@ -42,7 +43,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app,
lv_obj_set_size(buttonValidate, 115, 50); lv_obj_set_size(buttonValidate, 115, 50);
lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_obj_set_event_cb(buttonValidate, ButtonEventHandler); 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); labelButtonValidate = lv_label_create(buttonValidate, nullptr);
lv_label_set_text_static(labelButtonValidate, "Validate"); lv_label_set_text_static(labelButtonValidate, "Validate");
@ -51,7 +52,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app,
buttonReset->user_data = this; buttonReset->user_data = this;
lv_obj_set_size(buttonReset, 115, 50); lv_obj_set_size(buttonReset, 115, 50);
lv_obj_align(buttonReset, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); lv_obj_align(buttonReset, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
lv_obj_set_style_local_bg_color(buttonReset, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_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); lv_obj_set_event_cb(buttonReset, ButtonEventHandler);
labelButtonReset = lv_label_create(buttonReset, nullptr); labelButtonReset = lv_label_create(buttonReset, nullptr);

View file

@ -1,37 +1,35 @@
#include "displayapp/screens/FlashLight.h" #include "displayapp/screens/FlashLight.h"
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "displayapp/screens/Symbols.h" #include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
namespace { namespace {
void event_handler(lv_obj_t* obj, lv_event_t event) { void EventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<FlashLight*>(obj->user_data); if (event == LV_EVENT_CLICKED) {
screen->OnClickEvent(obj, event); auto* screen = static_cast<FlashLight*>(obj->user_data);
screen->Toggle();
}
} }
} }
FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app, FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app,
System::SystemTask& systemTask, System::SystemTask& systemTask,
Controllers::BrightnessController& brightnessController) Controllers::BrightnessController& brightnessController)
: Screen(app), : Screen(app), systemTask {systemTask}, brightnessController {brightnessController} {
systemTask {systemTask},
brightnessController {brightnessController}
{ brightnessController.Set(Controllers::BrightnessController::Levels::Low);
brightnessController.Backup();
brightnessLevel = brightnessController.Level();
flashLight = lv_label_create(lv_scr_act(), nullptr); flashLight = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48); lv_obj_set_style_local_text_font(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
lv_label_set_text_static(flashLight, Symbols::highlight); lv_label_set_text_static(flashLight, Symbols::flashlight);
lv_obj_align(flashLight, nullptr, LV_ALIGN_CENTER, 0, 0); lv_obj_align(flashLight, nullptr, LV_ALIGN_CENTER, 0, 0);
for (auto& i : indicators) { for (auto& indicator : indicators) {
i = lv_obj_create(lv_scr_act(), nullptr); indicator = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_size(i, 15, 10); lv_obj_set_size(indicator, 15, 10);
lv_obj_set_style_local_border_width(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 2); lv_obj_set_style_local_border_width(indicator, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 2);
} }
lv_obj_align(indicators[1], flashLight, LV_ALIGN_OUT_BOTTOM_MID, 0, 5); lv_obj_align(indicators[1], flashLight, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
@ -48,7 +46,7 @@ FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app,
lv_label_set_text_static(backgroundAction, ""); lv_label_set_text_static(backgroundAction, "");
lv_obj_set_click(backgroundAction, true); lv_obj_set_click(backgroundAction, true);
backgroundAction->user_data = this; backgroundAction->user_data = this;
lv_obj_set_event_cb(backgroundAction, event_handler); lv_obj_set_event_cb(backgroundAction, EventHandler);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
} }
@ -56,27 +54,19 @@ FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app,
FlashLight::~FlashLight() { FlashLight::~FlashLight() {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
brightnessController.Restore();
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
} }
void FlashLight::SetColors() { void FlashLight::SetColors() {
if (isOn) { lv_color_t bgColor = isOn ? LV_COLOR_WHITE : LV_COLOR_BLACK;
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); lv_color_t fgColor = isOn ? Colors::lightGray : LV_COLOR_WHITE;
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0));
for (auto& i : indicators) { lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, bgColor);
lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, fgColor);
lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_WHITE); for (auto& indicator : indicators) {
lv_obj_set_style_local_border_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); lv_obj_set_style_local_bg_color(indicator, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, fgColor);
} lv_obj_set_style_local_bg_color(indicator, LV_OBJ_PART_MAIN, LV_STATE_DISABLED, bgColor);
} else { lv_obj_set_style_local_border_color(indicator, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, fgColor);
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
for (auto& i : indicators) {
lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_bg_color(i, LV_OBJ_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_BLACK);
lv_obj_set_style_local_border_color(i, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
}
} }
} }
@ -95,37 +85,43 @@ void FlashLight::SetIndicators() {
} }
} }
void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) { void FlashLight::Toggle() {
if (obj == backgroundAction && event == LV_EVENT_CLICKED) { isOn = !isOn;
isOn = !isOn; SetColors();
SetColors(); if (isOn) {
brightnessController.Set(brightnessLevel);
} else {
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
} }
} }
bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
auto SetState = [this]() {
if (isOn) {
brightnessController.Set(brightnessLevel);
}
SetIndicators();
};
if (event == TouchEvents::SwipeLeft) { if (event == TouchEvents::SwipeLeft) {
if (brightnessLevel == BrightnessController::Levels::High) { if (brightnessLevel == BrightnessController::Levels::High) {
brightnessLevel = BrightnessController::Levels::Medium; brightnessLevel = BrightnessController::Levels::Medium;
brightnessController.Set(brightnessLevel); SetState();
SetIndicators();
} else if (brightnessLevel == BrightnessController::Levels::Medium) { } else if (brightnessLevel == BrightnessController::Levels::Medium) {
brightnessLevel = BrightnessController::Levels::Low; brightnessLevel = BrightnessController::Levels::Low;
brightnessController.Set(brightnessLevel); SetState();
SetIndicators();
} }
return true; return true;
} }
if (event == TouchEvents::SwipeRight) { if (event == TouchEvents::SwipeRight) {
if (brightnessLevel == BrightnessController::Levels::Low) { if (brightnessLevel == BrightnessController::Levels::Low) {
brightnessLevel = BrightnessController::Levels::Medium; brightnessLevel = BrightnessController::Levels::Medium;
brightnessController.Set(brightnessLevel); SetState();
SetIndicators();
} else if (brightnessLevel == BrightnessController::Levels::Medium) { } else if (brightnessLevel == BrightnessController::Levels::Medium) {
brightnessLevel = BrightnessController::Levels::High; brightnessLevel = BrightnessController::Levels::High;
brightnessController.Set(brightnessLevel); SetState();
SetIndicators();
} }
return true; return true;
} }

View file

@ -17,7 +17,7 @@ namespace Pinetime {
~FlashLight() override; ~FlashLight() override;
bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override; bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;
void OnClickEvent(lv_obj_t* obj, lv_event_t event); void Toggle();
private: private:
void SetIndicators(); void SetIndicators();
@ -26,7 +26,7 @@ namespace Pinetime {
Pinetime::System::SystemTask& systemTask; Pinetime::System::SystemTask& systemTask;
Controllers::BrightnessController& brightnessController; Controllers::BrightnessController& brightnessController;
Controllers::BrightnessController::Levels brightnessLevel; Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
lv_obj_t* flashLight; lv_obj_t* flashLight;
lv_obj_t* backgroundAction; lv_obj_t* backgroundAction;

View file

@ -3,6 +3,7 @@
#include <components/heartrate/HeartRateController.h> #include <components/heartrate/HeartRateController.h>
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; 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); lv_obj_set_style_local_text_font(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
if (isHrRunning) if (isHrRunning) {
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
else } else {
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);
}
lv_label_set_text_static(label_hr, "000"); lv_label_set_text_static(label_hr, "000");
lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40); lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40);
@ -97,12 +99,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
heartRateController.Start(); heartRateController.Start();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x0, 0xb0, 0x0)); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
} else { } else {
heartRateController.Stop(); heartRateController.Stop();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped); UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xb0, 0xb0, 0xb0)); lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
} }
} }
} }

View file

@ -1,6 +1,7 @@
#include "displayapp/screens/InfiniPaint.h" #include "displayapp/screens/InfiniPaint.h"
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "displayapp/LittleVgl.h" #include "displayapp/LittleVgl.h"
#include "displayapp/InfiniTimeTheme.h"
#include <algorithm> // std::fill #include <algorithm> // std::fill
@ -26,7 +27,7 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
selectColor = LV_COLOR_MAGENTA; selectColor = LV_COLOR_MAGENTA;
break; break;
case 1: case 1:
selectColor = LV_COLOR_MAKE(0x0, 0xb0, 0x0); selectColor = Colors::green;
break; break;
case 2: case 2:
selectColor = LV_COLOR_WHITE; selectColor = LV_COLOR_WHITE;

View file

@ -3,34 +3,9 @@
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
Label::Label(uint8_t screenID, uint8_t numScreens, Pinetime::Applications::DisplayApp* app, lv_obj_t* labelText) Label::Label(uint8_t screenID, uint8_t numScreens, Pinetime::Applications::DisplayApp* app, lv_obj_t* labelText)
: Screen(app), labelText {labelText} { : Screen(app), labelText {labelText}, pageIndicator(screenID, numScreens) {
if (numScreens > 1) { pageIndicator.Create();
pageIndicatorBasePoints[0].x = LV_HOR_RES - 1;
pageIndicatorBasePoints[0].y = 0;
pageIndicatorBasePoints[1].x = LV_HOR_RES - 1;
pageIndicatorBasePoints[1].y = LV_VER_RES;
pageIndicatorBase = lv_line_create(lv_scr_act(), NULL);
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
uint16_t indicatorSize = LV_VER_RES / numScreens;
uint16_t indicatorPos = indicatorSize * screenID;
pageIndicatorPoints[0].x = LV_HOR_RES - 1;
pageIndicatorPoints[0].y = indicatorPos;
pageIndicatorPoints[1].x = LV_HOR_RES - 1;
pageIndicatorPoints[1].y = indicatorPos + indicatorSize;
pageIndicator = lv_line_create(lv_scr_act(), NULL);
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_rounded(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
lv_line_set_points(pageIndicator, pageIndicatorPoints, 2);
}
} }
Label::~Label() { Label::~Label() {

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "displayapp/screens/Screen.h" #include "displayapp/screens/Screen.h"
#include "displayapp/widgets/PageIndicator.h"
#include <lvgl/lvgl.h> #include <lvgl/lvgl.h>
namespace Pinetime { namespace Pinetime {
@ -14,10 +15,7 @@ namespace Pinetime {
private: private:
lv_obj_t* labelText = nullptr; lv_obj_t* labelText = nullptr;
lv_point_t pageIndicatorBasePoints[2]; Widgets::PageIndicator pageIndicator;
lv_point_t pageIndicatorPoints[2];
lv_obj_t* pageIndicatorBase;
lv_obj_t* pageIndicator;
}; };
} }
} }

View file

@ -16,37 +16,14 @@ List::List(uint8_t screenID,
DisplayApp* app, DisplayApp* app,
Controllers::Settings& settingsController, Controllers::Settings& settingsController,
std::array<Applications, MAXLISTITEMS>& applications) std::array<Applications, MAXLISTITEMS>& applications)
: Screen(app), settingsController {settingsController} { : Screen(app), settingsController {settingsController}, pageIndicator(screenID, numScreens) {
// Set the background to Black // Set the background to Black
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_make(0, 0, 0)); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_make(0, 0, 0));
settingsController.SetSettingsMenu(screenID); settingsController.SetSettingsMenu(screenID);
if (numScreens > 1) { pageIndicator.Create();
pageIndicatorBasePoints[0].x = LV_HOR_RES - 1;
pageIndicatorBasePoints[0].y = 0;
pageIndicatorBasePoints[1].x = LV_HOR_RES - 1;
pageIndicatorBasePoints[1].y = LV_VER_RES;
pageIndicatorBase = lv_line_create(lv_scr_act(), NULL);
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
const uint16_t indicatorSize = LV_VER_RES / numScreens;
const uint16_t indicatorPos = indicatorSize * screenID;
pageIndicatorPoints[0].x = LV_HOR_RES - 1;
pageIndicatorPoints[0].y = indicatorPos;
pageIndicatorPoints[1].x = LV_HOR_RES - 1;
pageIndicatorPoints[1].y = indicatorPos + indicatorSize;
pageIndicator = lv_line_create(lv_scr_act(), NULL);
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_line_set_points(pageIndicator, pageIndicatorPoints, 2);
}
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr); lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);

View file

@ -4,6 +4,7 @@
#include <cstdint> #include <cstdint>
#include <array> #include <array>
#include "displayapp/screens/Screen.h" #include "displayapp/screens/Screen.h"
#include "displayapp/widgets/PageIndicator.h"
#include "displayapp/Apps.h" #include "displayapp/Apps.h"
#include "components/settings/Settings.h" #include "components/settings/Settings.h"
@ -35,10 +36,7 @@ namespace Pinetime {
lv_obj_t* itemApps[MAXLISTITEMS]; lv_obj_t* itemApps[MAXLISTITEMS];
lv_point_t pageIndicatorBasePoints[2]; Widgets::PageIndicator pageIndicator;
lv_point_t pageIndicatorPoints[2];
lv_obj_t* pageIndicatorBase;
lv_obj_t* pageIndicator;
}; };
} }
} }

View file

@ -1,5 +1,6 @@
#include "displayapp/screens/Metronome.h" #include "displayapp/screens/Metronome.h"
#include "displayapp/screens/Symbols.h" #include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; 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* 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_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_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_label_set_text(label, name);
lv_obj_align(label, reference, align, x, y); lv_obj_align(label, reference, align, x, y);

View file

@ -1,6 +1,7 @@
#include "displayapp/screens/Motion.h" #include "displayapp/screens/Motion.h"
#include <lvgl/lvgl.h> #include <lvgl/lvgl.h>
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
@ -19,7 +20,7 @@ Motion::Motion(Pinetime::Applications::DisplayApp* app, Controllers::MotionContr
/*Add 3 data series*/ /*Add 3 data series*/
ser1 = lv_chart_add_series(chart, LV_COLOR_RED); 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); ser3 = lv_chart_add_series(chart, LV_COLOR_YELLOW);
lv_chart_init_points(chart, ser1, 0); lv_chart_init_points(chart, ser1, 0);

View file

@ -19,6 +19,7 @@
#include <cstdint> #include <cstdint>
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "components/ble/NavigationService.h" #include "components/ble/NavigationService.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
@ -192,7 +193,7 @@ void Navigation::Refresh() {
if (progress > 90) { if (progress > 90) {
lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
} else { } else {
lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xff, 0xb0, 0x0)); lv_obj_set_style_local_bg_color(barProgress, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, Colors::orange);
} }
} }
} }

View file

@ -3,6 +3,8 @@
#include "components/ble/MusicService.h" #include "components/ble/MusicService.h"
#include "components/ble/AlertNotificationService.h" #include "components/ble/AlertNotificationService.h"
#include "displayapp/screens/Symbols.h" #include "displayapp/screens/Symbols.h"
#include <algorithm>
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed; extern lv_font_t jetbrains_mono_extrabold_compressed;
@ -20,30 +22,23 @@ Notifications::Notifications(DisplayApp* app,
motorController {motorController}, motorController {motorController},
systemTask {systemTask}, systemTask {systemTask},
mode {mode} { mode {mode} {
notificationManager.ClearNewNotificationFlag(); notificationManager.ClearNewNotificationFlag();
auto notification = notificationManager.GetLastNotification(); auto notification = notificationManager.GetLastNotification();
if (notification.valid) { if (notification.valid) {
currentId = notification.id; currentId = notification.id;
currentItem = std::make_unique<NotificationItem>(notification.Title(), currentItem = std::make_unique<NotificationItem>(notification.Title(),
notification.Message(), notification.Message(),
notification.index, 1,
notification.category, notification.category,
notificationManager.NbNotifications(), notificationManager.NbNotifications(),
mode,
alertNotificationService, alertNotificationService,
motorController); motorController);
validDisplay = true; validDisplay = true;
} else { } else {
currentItem = std::make_unique<NotificationItem>("Notification", currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
"No notification to display", validDisplay = false;
0,
notification.category,
notificationManager.NbNotifications(),
Modes::Preview,
alertNotificationService,
motorController);
} }
if (mode == Modes::Preview) { if (mode == Modes::Preview) {
systemTask.PushMessage(System::Messages::DisableSleeping); systemTask.PushMessage(System::Messages::DisableSleeping);
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) { if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
@ -77,7 +72,7 @@ Notifications::~Notifications() {
void Notifications::Refresh() { void Notifications::Refresh() {
if (mode == Modes::Preview && timeoutLine != nullptr) { if (mode == Modes::Preview && timeoutLine != nullptr) {
TickType_t tick = xTaskGetTickCount(); TickType_t tick = xTaskGetTickCount();
int32_t pos = 240 - ((tick - timeoutTickCountStart) / (timeoutLength / 240)); int32_t pos = LV_HOR_RES - ((tick - timeoutTickCountStart) / (timeoutLength / LV_HOR_RES));
if (pos <= 0) { if (pos <= 0) {
running = false; running = false;
} else { } else {
@ -85,6 +80,40 @@ void Notifications::Refresh() {
lv_line_set_points(timeoutLine, timeoutLinePoints, 2); lv_line_set_points(timeoutLine, timeoutLinePoints, 2);
} }
} }
if (dismissingNotification) {
dismissingNotification = false;
auto notification = notificationManager.Get(currentId);
if (!notification.valid) {
notification = notificationManager.GetLastNotification();
}
currentId = notification.id;
if (!notification.valid) {
validDisplay = false;
}
currentItem.reset(nullptr);
if (afterDismissNextMessageFromAbove) {
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down);
} else {
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up);
}
if (validDisplay) {
Controllers::NotificationManager::Notification::Idx currentIdx = notificationManager.IndexOf(currentId);
currentItem = std::make_unique<NotificationItem>(notification.Title(),
notification.Message(),
currentIdx + 1,
notification.category,
notificationManager.NbNotifications(),
alertNotificationService,
motorController);
} else {
currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
}
}
running = currentItem->IsRunning() && running; running = currentItem->IsRunning() && running;
} }
@ -108,52 +137,84 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
} }
switch (event) { switch (event) {
case Pinetime::Applications::TouchEvents::SwipeRight:
if (validDisplay) {
Controllers::NotificationManager::Notification previousNotification;
auto previousMessage = notificationManager.GetPrevious(currentId);
auto nextMessage = notificationManager.GetNext(currentId);
if (!previousMessage.valid) {
// dismissed last message (like 5/5), need to go one message down (like 4/4)
afterDismissNextMessageFromAbove = false; // show next message coming from below
} else {
afterDismissNextMessageFromAbove = true; // show next message coming from above
}
notificationManager.Dismiss(currentId);
if (previousMessage.valid) {
currentId = previousMessage.id;
} else if (nextMessage.valid) {
currentId = nextMessage.id;
} else {
// don't update id, won't be found be refresh and try to load latest message or no message box
}
currentItem.reset(nullptr);
app->SetFullRefresh(DisplayApp::FullRefreshDirections::RightAnim);
// create black transition screen to let the notification dismiss to blackness
lv_obj_t* blackBox = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_size(blackBox, LV_HOR_RES, LV_VER_RES);
lv_obj_set_style_local_bg_color(blackBox, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
dismissingNotification = true;
return true;
}
return false;
case Pinetime::Applications::TouchEvents::SwipeDown: { case Pinetime::Applications::TouchEvents::SwipeDown: {
Controllers::NotificationManager::Notification previousNotification; Controllers::NotificationManager::Notification previousNotification;
if (validDisplay) if (validDisplay) {
previousNotification = notificationManager.GetPrevious(currentId); previousNotification = notificationManager.GetPrevious(currentId);
else } else {
previousNotification = notificationManager.GetLastNotification(); previousNotification = notificationManager.GetLastNotification();
}
if (!previousNotification.valid) if (!previousNotification.valid) {
return true; return true;
}
validDisplay = true;
currentId = previousNotification.id; currentId = previousNotification.id;
Controllers::NotificationManager::Notification::Idx currentIdx = notificationManager.IndexOf(currentId);
validDisplay = true;
currentItem.reset(nullptr); currentItem.reset(nullptr);
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down); app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down);
currentItem = std::make_unique<NotificationItem>(previousNotification.Title(), currentItem = std::make_unique<NotificationItem>(previousNotification.Title(),
previousNotification.Message(), previousNotification.Message(),
previousNotification.index, currentIdx + 1,
previousNotification.category, previousNotification.category,
notificationManager.NbNotifications(), notificationManager.NbNotifications(),
mode,
alertNotificationService, alertNotificationService,
motorController); motorController);
} }
return true; return true;
case Pinetime::Applications::TouchEvents::SwipeUp: { case Pinetime::Applications::TouchEvents::SwipeUp: {
Controllers::NotificationManager::Notification nextNotification; Controllers::NotificationManager::Notification nextNotification;
if (validDisplay) if (validDisplay) {
nextNotification = notificationManager.GetNext(currentId); nextNotification = notificationManager.GetNext(currentId);
else } else {
nextNotification = notificationManager.GetLastNotification(); nextNotification = notificationManager.GetLastNotification();
}
if (!nextNotification.valid) { if (!nextNotification.valid) {
running = false; running = false;
return false; return false;
} }
validDisplay = true;
currentId = nextNotification.id; currentId = nextNotification.id;
Controllers::NotificationManager::Notification::Idx currentIdx = notificationManager.IndexOf(currentId);
validDisplay = true;
currentItem.reset(nullptr); currentItem.reset(nullptr);
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up); app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up);
currentItem = std::make_unique<NotificationItem>(nextNotification.Title(), currentItem = std::make_unique<NotificationItem>(nextNotification.Title(),
nextNotification.Message(), nextNotification.Message(),
nextNotification.index, currentIdx + 1,
nextNotification.category, nextNotification.category,
notificationManager.NbNotifications(), notificationManager.NbNotifications(),
mode,
alertNotificationService, alertNotificationService,
motorController); motorController);
} }
@ -170,34 +231,49 @@ namespace {
} }
} }
Notifications::NotificationItem::NotificationItem(Pinetime::Controllers::AlertNotificationService& alertNotificationService,
Pinetime::Controllers::MotorController& motorController)
: NotificationItem("Notification",
"No notification to display",
0,
Controllers::NotificationManager::Categories::Unknown,
0,
alertNotificationService,
motorController) {
}
Notifications::NotificationItem::NotificationItem(const char* title, Notifications::NotificationItem::NotificationItem(const char* title,
const char* msg, const char* msg,
uint8_t notifNr, uint8_t notifNr,
Controllers::NotificationManager::Categories category, Controllers::NotificationManager::Categories category,
uint8_t notifNb, uint8_t notifNb,
Modes mode,
Pinetime::Controllers::AlertNotificationService& alertNotificationService, Pinetime::Controllers::AlertNotificationService& alertNotificationService,
Pinetime::Controllers::MotorController& motorController) Pinetime::Controllers::MotorController& motorController)
: mode {mode}, alertNotificationService {alertNotificationService}, motorController {motorController} { : alertNotificationService {alertNotificationService}, motorController {motorController} {
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), NULL); container = lv_cont_create(lv_scr_act(), nullptr);
lv_obj_set_size(container, LV_HOR_RES, LV_VER_RES);
lv_obj_set_style_local_bg_color(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_set_style_local_pad_all(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_pad_inner(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_border_width(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x38, 0x38, 0x38)); subject_container = lv_cont_create(container, nullptr);
lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); lv_obj_set_style_local_bg_color(subject_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt);
lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); lv_obj_set_style_local_pad_all(subject_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10);
lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); 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);
lv_obj_set_pos(container1, 0, 50); lv_obj_set_pos(subject_container, 0, 50);
lv_obj_set_size(container1, LV_HOR_RES, 190); lv_obj_set_size(subject_container, LV_HOR_RES, LV_VER_RES - 50);
lv_cont_set_layout(subject_container, LV_LAYOUT_COLUMN_LEFT);
lv_cont_set_fit(subject_container, LV_FIT_NONE);
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); lv_obj_t* alert_count = lv_label_create(container, nullptr);
lv_cont_set_fit(container1, LV_FIT_NONE);
lv_obj_t* alert_count = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_fmt(alert_count, "%i/%i", notifNr, notifNb); lv_label_set_text_fmt(alert_count, "%i/%i", notifNr, notifNb);
lv_obj_align(alert_count, NULL, LV_ALIGN_IN_TOP_RIGHT, 0, 16); lv_obj_align(alert_count, NULL, LV_ALIGN_IN_TOP_RIGHT, 0, 16);
lv_obj_t* alert_type = lv_label_create(lv_scr_act(), nullptr); 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(0xb0, 0xb0, 0xb0)); lv_obj_set_style_local_text_color(alert_type, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange);
if (title == nullptr) { if (title == nullptr) {
lv_label_set_text_static(alert_type, "Notification"); lv_label_set_text_static(alert_type, "Notification");
} else { } else {
@ -214,39 +290,34 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_obj_set_width(alert_type, 180); lv_obj_set_width(alert_type, 180);
lv_obj_align(alert_type, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 16); lv_obj_align(alert_type, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 16);
///////// lv_obj_t* alert_subject = lv_label_create(subject_container, nullptr);
lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK);
lv_obj_set_width(alert_subject, LV_HOR_RES - 20);
switch (category) { switch (category) {
default: { default:
lv_obj_t* alert_subject = lv_label_create(container1, nullptr);
lv_obj_set_style_local_text_color(alert_subject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xff, 0xb0, 0x0));
lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK);
lv_obj_set_width(alert_subject, LV_HOR_RES - 20);
lv_label_set_text(alert_subject, msg); lv_label_set_text(alert_subject, msg);
} break; break;
case Controllers::NotificationManager::Categories::IncomingCall: { case Controllers::NotificationManager::Categories::IncomingCall: {
lv_obj_set_height(container1, 108); lv_obj_set_height(subject_container, 108);
lv_obj_t* alert_subject = lv_label_create(container1, nullptr);
lv_obj_set_style_local_text_color(alert_subject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0xff, 0xb0, 0x0));
lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK);
lv_obj_set_width(alert_subject, LV_HOR_RES - 20);
lv_label_set_text_static(alert_subject, "Incoming call from"); lv_label_set_text_static(alert_subject, "Incoming call from");
lv_obj_t* alert_caller = lv_label_create(container1, nullptr); lv_obj_t* alert_caller = lv_label_create(subject_container, nullptr);
lv_obj_align(alert_caller, alert_subject, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); lv_obj_align(alert_caller, alert_subject, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
lv_label_set_long_mode(alert_caller, LV_LABEL_LONG_BREAK); lv_label_set_long_mode(alert_caller, LV_LABEL_LONG_BREAK);
lv_obj_set_width(alert_caller, LV_HOR_RES - 20); lv_obj_set_width(alert_caller, LV_HOR_RES - 20);
lv_label_set_text(alert_caller, msg); lv_label_set_text(alert_caller, msg);
bt_accept = lv_btn_create(lv_scr_act(), nullptr); bt_accept = lv_btn_create(container, nullptr);
bt_accept->user_data = this; bt_accept->user_data = this;
lv_obj_set_event_cb(bt_accept, CallEventHandler); lv_obj_set_event_cb(bt_accept, CallEventHandler);
lv_obj_set_size(bt_accept, 76, 76); lv_obj_set_size(bt_accept, 76, 76);
lv_obj_align(bt_accept, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); lv_obj_align(bt_accept, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
label_accept = lv_label_create(bt_accept, nullptr); label_accept = lv_label_create(bt_accept, nullptr);
lv_label_set_text_static(label_accept, Symbols::phone); 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(lv_scr_act(), nullptr); bt_reject = lv_btn_create(container, nullptr);
bt_reject->user_data = this; bt_reject->user_data = this;
lv_obj_set_event_cb(bt_reject, CallEventHandler); lv_obj_set_event_cb(bt_reject, CallEventHandler);
lv_obj_set_size(bt_reject, 76, 76); lv_obj_set_size(bt_reject, 76, 76);
@ -255,14 +326,14 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_label_set_text_static(label_reject, Symbols::phoneSlash); lv_label_set_text_static(label_reject, Symbols::phoneSlash);
lv_obj_set_style_local_bg_color(bt_reject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_color(bt_reject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
bt_mute = lv_btn_create(lv_scr_act(), nullptr); bt_mute = lv_btn_create(container, nullptr);
bt_mute->user_data = this; bt_mute->user_data = this;
lv_obj_set_event_cb(bt_mute, CallEventHandler); lv_obj_set_event_cb(bt_mute, CallEventHandler);
lv_obj_set_size(bt_mute, 76, 76); lv_obj_set_size(bt_mute, 76, 76);
lv_obj_align(bt_mute, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); lv_obj_align(bt_mute, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
label_mute = lv_label_create(bt_mute, nullptr); label_mute = lv_label_create(bt_mute, nullptr);
lv_label_set_text_static(label_mute, Symbols::volumMute); 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; } break;
} }
} }

View file

@ -33,12 +33,13 @@ namespace Pinetime {
class NotificationItem { class NotificationItem {
public: public:
NotificationItem(Pinetime::Controllers::AlertNotificationService& alertNotificationService,
Pinetime::Controllers::MotorController& motorController);
NotificationItem(const char* title, NotificationItem(const char* title,
const char* msg, const char* msg,
uint8_t notifNr, uint8_t notifNr,
Controllers::NotificationManager::Categories, Controllers::NotificationManager::Categories,
uint8_t notifNb, uint8_t notifNb,
Modes mode,
Pinetime::Controllers::AlertNotificationService& alertNotificationService, Pinetime::Controllers::AlertNotificationService& alertNotificationService,
Pinetime::Controllers::MotorController& motorController); Pinetime::Controllers::MotorController& motorController);
~NotificationItem(); ~NotificationItem();
@ -48,16 +49,17 @@ namespace Pinetime {
void OnCallButtonEvent(lv_obj_t*, lv_event_t event); void OnCallButtonEvent(lv_obj_t*, lv_event_t event);
private: private:
lv_obj_t* container1; lv_obj_t* container;
lv_obj_t* subject_container;
lv_obj_t* bt_accept; lv_obj_t* bt_accept;
lv_obj_t* bt_mute; lv_obj_t* bt_mute;
lv_obj_t* bt_reject; lv_obj_t* bt_reject;
lv_obj_t* label_accept; lv_obj_t* label_accept;
lv_obj_t* label_mute; lv_obj_t* label_mute;
lv_obj_t* label_reject; lv_obj_t* label_reject;
Modes mode;
Pinetime::Controllers::AlertNotificationService& alertNotificationService; Pinetime::Controllers::AlertNotificationService& alertNotificationService;
Pinetime::Controllers::MotorController& motorController; Pinetime::Controllers::MotorController& motorController;
bool running = true; bool running = true;
}; };
@ -68,15 +70,19 @@ namespace Pinetime {
System::SystemTask& systemTask; System::SystemTask& systemTask;
Modes mode = Modes::Normal; Modes mode = Modes::Normal;
std::unique_ptr<NotificationItem> currentItem; std::unique_ptr<NotificationItem> currentItem;
Controllers::NotificationManager::Notification::Id currentId; Pinetime::Controllers::NotificationManager::Notification::Id currentId;
bool validDisplay = false; bool validDisplay = false;
bool afterDismissNextMessageFromAbove = false;
lv_point_t timeoutLinePoints[2] {{0, 1}, {239, 1}}; lv_point_t timeoutLinePoints[2] {{0, 1}, {239, 1}};
lv_obj_t* timeoutLine = nullptr; lv_obj_t* timeoutLine = nullptr;
TickType_t timeoutTickCountStart; TickType_t timeoutTickCountStart;
static const TickType_t timeoutLength = pdMS_TO_TICKS(7000); static const TickType_t timeoutLength = pdMS_TO_TICKS(7000);
bool interacted = true; bool interacted = true;
bool dismissingNotification = false;
lv_task_t* taskRefresh; lv_task_t* taskRefresh;
}; };
} }

View file

@ -5,7 +5,7 @@ using namespace Pinetime::Applications::Screens;
PassKey::PassKey(Pinetime::Applications::DisplayApp* app, uint32_t key) : Screen(app) { PassKey::PassKey(Pinetime::Applications::DisplayApp* app, uint32_t key) : Screen(app) {
passkeyLabel = lv_label_create(lv_scr_act(), nullptr); 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_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_label_set_text_fmt(passkeyLabel, "%06u", key);
lv_obj_align(passkeyLabel, nullptr, LV_ALIGN_CENTER, 0, -20); lv_obj_align(passkeyLabel, nullptr, LV_ALIGN_CENTER, 0, -20);

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