Merge branch 'develop' into pinmap
This commit is contained in:
commit
0d083a2bea
65
.devcontainer/Dockerfile
Normal file
65
.devcontainer/Dockerfile
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
FROM ubuntu:latest
|
||||||
|
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
RUN apt-get update -qq \
|
||||||
|
&& apt-get install -y \
|
||||||
|
# x86_64 / generic packages
|
||||||
|
bash \
|
||||||
|
build-essential \
|
||||||
|
cmake \
|
||||||
|
git \
|
||||||
|
make \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
tar \
|
||||||
|
unzip \
|
||||||
|
wget \
|
||||||
|
curl \
|
||||||
|
dos2unix \
|
||||||
|
clang-format-12 \
|
||||||
|
clang-tidy \
|
||||||
|
locales \
|
||||||
|
libncurses5 \
|
||||||
|
# aarch64 packages
|
||||||
|
libffi-dev \
|
||||||
|
libssl-dev \
|
||||||
|
python3-dev \
|
||||||
|
rustc \
|
||||||
|
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
|
||||||
|
|
||||||
|
#SET LOCALE
|
||||||
|
RUN locale-gen en_US.UTF-8
|
||||||
|
ENV LANG en_US.UTF-8
|
||||||
|
ENV LANGUAGE en_US:en
|
||||||
|
ENV LC_ALL en_US.UTF-8
|
||||||
|
|
||||||
|
RUN pip3 install adafruit-nrfutil
|
||||||
|
# required for McuBoot
|
||||||
|
RUN pip3 install setuptools_rust
|
||||||
|
|
||||||
|
WORKDIR /opt/
|
||||||
|
# build.sh knows how to compile but it problimatic on Win10
|
||||||
|
COPY build.sh .
|
||||||
|
RUN chmod +x build.sh
|
||||||
|
# create_build_openocd.sh uses cmake to crate to build directory
|
||||||
|
COPY create_build_openocd.sh .
|
||||||
|
RUN chmod +x create_build_openocd.sh
|
||||||
|
# Lets get each in a separate docker layer for better downloads
|
||||||
|
# GCC
|
||||||
|
# RUN bash -c "source /opt/build.sh; GetGcc;"
|
||||||
|
RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz2 -O - | tar -xj -C /opt
|
||||||
|
# NrfSdk
|
||||||
|
# RUN bash -c "source /opt/build.sh; GetNrfSdk;"
|
||||||
|
RUN wget -q "https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip" -O /tmp/nRF5_SDK_15.3.0_59ac345
|
||||||
|
RUN unzip -q /tmp/nRF5_SDK_15.3.0_59ac345 -d /opt
|
||||||
|
RUN rm /tmp/nRF5_SDK_15.3.0_59ac345
|
||||||
|
# McuBoot
|
||||||
|
# RUN bash -c "source /opt/build.sh; GetMcuBoot;"
|
||||||
|
RUN git clone https://github.com/JuulLabs-OSS/mcuboot.git
|
||||||
|
RUN pip3 install -r ./mcuboot/scripts/requirements.txt
|
||||||
|
|
||||||
|
RUN adduser infinitime
|
||||||
|
|
||||||
|
ENV NRF5_SDK_PATH /opt/nRF5_SDK_15.3.0_59ac345
|
||||||
|
ENV ARM_NONE_EABI_TOOLCHAIN_PATH /opt/gcc-arm-none-eabi-9-2020-q2-update
|
||||||
|
ENV SOURCES_DIR /workspaces/InfiniTime
|
60
.devcontainer/README.md
Normal file
60
.devcontainer/README.md
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# 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*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- VS Code
|
||||||
|
- [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension
|
||||||
|
- Docker
|
||||||
|
- OpenOCD - For debugging
|
||||||
|
|
||||||
|
## Using
|
||||||
|
|
||||||
|
### Code editing, and building.
|
||||||
|
|
||||||
|
1. Clone InfiniTime and update submodules
|
||||||
|
2. Launch VS Code
|
||||||
|
3. Open InfiniTime directory,
|
||||||
|
4. Allow VS Code to open folder with devcontainer.
|
||||||
|
|
||||||
|
After this the environment will be built if you do not currently have a container setup, it will install all the necessary tools and extra VSCode extensions.
|
||||||
|
|
||||||
|
In order to build InfiniTime we need to run the initial submodule init and CMake commands.
|
||||||
|
|
||||||
|
#### Manually
|
||||||
|
|
||||||
|
You can use the VS Code terminal to run the CMake commands as outlined in the [build instructions](blob/develop/doc/buildAndProgram.md)
|
||||||
|
|
||||||
|
#### Script
|
||||||
|
|
||||||
|
The dev environment comes with some scripts to make this easier, They are located in /opt/.
|
||||||
|
|
||||||
|
There are also VS Code tasks provided should you desire to use those.
|
||||||
|
|
||||||
|
The task "update submodules" will update the git submodules
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
You can use the build.sh script located in /opt/
|
||||||
|
|
||||||
|
CMake is also configured and controls for the CMake plugin are available in VS Code
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
Docker on windows does not support passing USB devices to the underlying WSL2 subsystem, To get around this we use OpenOCD in server mode running on the host.
|
||||||
|
|
||||||
|
`openocd -f <yourinterface> -f <nrf52.cfg target file>`
|
||||||
|
|
||||||
|
This will launch OpenOCD in server mode and attach it to the MCU.
|
||||||
|
|
||||||
|
The default launch.json file expects OpenOCD to be listening on port 3333, edit if needed
|
||||||
|
|
||||||
|
|
||||||
|
## Current Issues
|
||||||
|
Currently WSL2 Has some real performance issues with IO on a windows host. Accessing files on the virtualized filesystem is much faster. Using VS Codes "clone in container" feature of the Remote - Containers will get around this. After the container is built you will need to update the submodules and follow the build instructions like normal
|
78
.devcontainer/build.sh
Normal file
78
.devcontainer/build.sh
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#!/bin/bash
|
||||||
|
(return 0 2>/dev/null) && SOURCED="true" || SOURCED="false"
|
||||||
|
export LC_ALL=C.UTF-8
|
||||||
|
export LANG=C.UTF-8
|
||||||
|
set -x
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Default locations if the var isn't already set
|
||||||
|
export TOOLS_DIR="${TOOLS_DIR:=/opt}"
|
||||||
|
export SOURCES_DIR="${SOURCES_DIR:=/sources}"
|
||||||
|
export BUILD_DIR="${BUILD_DIR:=$SOURCES_DIR/build}"
|
||||||
|
export OUTPUT_DIR="${OUTPUT_DIR:=$BUILD_DIR/output}"
|
||||||
|
|
||||||
|
export BUILD_TYPE=${BUILD_TYPE:=Release}
|
||||||
|
export GCC_ARM_VER=${GCC_ARM_VER:="gcc-arm-none-eabi-9-2020-q2-update"}
|
||||||
|
export NRF_SDK_VER=${NRF_SDK_VER:="nRF5_SDK_15.3.0_59ac345"}
|
||||||
|
|
||||||
|
MACHINE="$(uname -m)"
|
||||||
|
[[ "$MACHINE" == "arm64" ]] && MACHINE="aarch64"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local target="$1"
|
||||||
|
|
||||||
|
mkdir -p "$TOOLS_DIR"
|
||||||
|
|
||||||
|
[[ ! -d "$TOOLS_DIR/$GCC_ARM_VER" ]] && GetGcc
|
||||||
|
[[ ! -d "$TOOLS_DIR/$NRF_SDK_VER" ]] && GetNrfSdk
|
||||||
|
[[ ! -d "$TOOLS_DIR/mcuboot" ]] && GetMcuBoot
|
||||||
|
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
|
||||||
|
CmakeGenerate
|
||||||
|
CmakeBuild $target
|
||||||
|
BUILD_RESULT=$?
|
||||||
|
if [ "$DISABLE_POSTBUILD" != "true" -a "$BUILD_RESULT" == 0 ]; then
|
||||||
|
source "$BUILD_DIR/post_build.sh"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
GetGcc() {
|
||||||
|
GCC_SRC="$GCC_ARM_VER-$MACHINE-linux.tar.bz"
|
||||||
|
wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/$GCC_SRC -O - | tar -xj -C $TOOLS_DIR/
|
||||||
|
}
|
||||||
|
|
||||||
|
GetMcuBoot() {
|
||||||
|
git clone https://github.com/JuulLabs-OSS/mcuboot.git "$TOOLS_DIR/mcuboot"
|
||||||
|
pip3 install -r "$TOOLS_DIR/mcuboot/scripts/requirements.txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
GetNrfSdk() {
|
||||||
|
wget -q "https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/$NRF_SDK_VER.zip" -O /tmp/$NRF_SDK_VER
|
||||||
|
unzip -q /tmp/$NRF_SDK_VER -d "$TOOLS_DIR/"
|
||||||
|
rm /tmp/$NRF_SDK_VER
|
||||||
|
}
|
||||||
|
|
||||||
|
CmakeGenerate() {
|
||||||
|
# We can swap the CD and trailing SOURCES_DIR for -B and -S respectively
|
||||||
|
# once we go to newer CMake (Ubuntu 18.10 gives us CMake 3.10)
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
cmake -G "Unix Makefiles" \
|
||||||
|
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||||
|
-DUSE_OPENOCD=1 \
|
||||||
|
-DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_VER" \
|
||||||
|
-DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \
|
||||||
|
"$SOURCES_DIR"
|
||||||
|
cmake -L -N .
|
||||||
|
}
|
||||||
|
|
||||||
|
CmakeBuild() {
|
||||||
|
local target="$1"
|
||||||
|
[[ -n "$target" ]] && target="--target $target"
|
||||||
|
if cmake --build "$BUILD_DIR" --config $BUILD_TYPE $target -- -j$(nproc)
|
||||||
|
then return 0; else return 1;
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ $SOURCED == "false" ]] && main "$@" || echo "Sourced!"
|
2
.devcontainer/build_app.sh
Normal file
2
.devcontainer/build_app.sh
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
cmake --build /workspaces/Pinetime/build --config Release -- -j6 pinetime-app
|
3
.devcontainer/create_build_openocd.sh
Normal file
3
.devcontainer/create_build_openocd.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
rm -rf build/
|
||||||
|
cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Release -DUSE_OPENOCD=1 -DARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update -DNRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345 -S . -Bbuild
|
36
.devcontainer/devcontainer.json
Normal file
36
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||||
|
// https://github.com/microsoft/vscode-dev-containers/tree/v0.154.2/containers/cpp
|
||||||
|
{
|
||||||
|
// "name": "Pinetime",
|
||||||
|
// "image": "feabhas/pinetime-dev"
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
// Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04
|
||||||
|
// "args": { "VARIANT": "ubuntu-20.04" }
|
||||||
|
},
|
||||||
|
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
|
||||||
|
|
||||||
|
// Set *default* container specific settings.json values on container create.
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.shell.linux": "/bin/bash"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
|
"extensions": [
|
||||||
|
"ms-vscode.cpptools",
|
||||||
|
"ms-vscode.cmake-tools",
|
||||||
|
"marus25.cortex-debug",
|
||||||
|
"notskm.clang-tidy",
|
||||||
|
"mjohns.clang-format"
|
||||||
|
],
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "bash /opt/create_build_openocd.sh",
|
||||||
|
|
||||||
|
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
|
// "remoteUser": "vscode"
|
||||||
|
"remoteUser": "infinitime"
|
||||||
|
}
|
2
.devcontainer/make_build_dir.sh
Normal file
2
.devcontainer/make_build_dir.sh
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Release -DUSE_OPENOCD=1 -DARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update -DNRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345 ${SOURCES_DIR}
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
# CMake
|
# CMake
|
||||||
cmake-build-*
|
cmake-build-*
|
||||||
cmake-*
|
cmake-*/
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
**/CMakeCache.txt
|
**/CMakeCache.txt
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
|
|
20
.vscode/c_cpp_properties.json
vendored
Normal file
20
.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "nrfCC",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**",
|
||||||
|
"${workspaceFolder}/src/**",
|
||||||
|
"${workspaceFolder}/src"
|
||||||
|
],
|
||||||
|
"defines": [],
|
||||||
|
"compilerPath": "${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin/arm-none-eabi-gcc",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++14",
|
||||||
|
"intelliSenseMode": "linux-gcc-arm",
|
||||||
|
"configurationProvider": "ms-vscode.cpp-tools",
|
||||||
|
"compileCommands": "${workspaceFolder}/build/compile_commands.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
62
.vscode/cmake-variants.json
vendored
Normal file
62
.vscode/cmake-variants.json
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"buildType": {
|
||||||
|
"default": "release",
|
||||||
|
"choices": {
|
||||||
|
"debug": {
|
||||||
|
"short": "Debug",
|
||||||
|
"long": "Emit debug information without performing optimizations",
|
||||||
|
"buildType": "Debug"
|
||||||
|
},
|
||||||
|
"release": {
|
||||||
|
"short": "Release",
|
||||||
|
"long": "Perform optimizations",
|
||||||
|
"buildType": "Release"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"programmer":{
|
||||||
|
"default": "OpenOCD",
|
||||||
|
"choices":{
|
||||||
|
"OpenOCD":{
|
||||||
|
"short":"OpenOCD",
|
||||||
|
"long": "Use OpenOCD",
|
||||||
|
"settings":{
|
||||||
|
"USE_OPENOCD":1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"JLink":{
|
||||||
|
"short":"JLink",
|
||||||
|
"long": "Use JLink",
|
||||||
|
"settings":{
|
||||||
|
"USE_JLINK":1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GDB":{
|
||||||
|
"short":"GDB",
|
||||||
|
"long": "Use GDB",
|
||||||
|
"settings":{
|
||||||
|
"USE_GDB_CLIENT":1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DFU": {
|
||||||
|
"default": "no",
|
||||||
|
"choices": {
|
||||||
|
"no": {
|
||||||
|
"short": "No DFU",
|
||||||
|
"long": "Do not build DFU",
|
||||||
|
"settings": {
|
||||||
|
"BUILD_DFU":"0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yes": {
|
||||||
|
"short": "Build DFU",
|
||||||
|
"long": "Build DFU",
|
||||||
|
"settings": {
|
||||||
|
"BUILD_DFU":"1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["ms-vscode.cpptools","ms-vscode.cmake-tools","marus25.cortex-debug"]
|
||||||
|
}
|
46
.vscode/launch.json
vendored
Normal file
46
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"version": "0.1.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug - Openocd docker Remote",
|
||||||
|
"type":"cortex-debug",
|
||||||
|
"cortex-debug.armToolchainPath":"${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"executable": "${command:cmake.launchTargetPath}",
|
||||||
|
"request": "launch",
|
||||||
|
"servertype": "external",
|
||||||
|
// This may need to be arm-none-eabi-gdb depending on your system
|
||||||
|
"gdbPath" : "${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin/arm-none-eabi-gdb",
|
||||||
|
// Connect to an already running OpenOCD instance
|
||||||
|
"gdbTarget": "host.docker.internal:3333",
|
||||||
|
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||||
|
"runToMain": true,
|
||||||
|
// Work around for stopping at main on restart
|
||||||
|
"postRestartCommands": [
|
||||||
|
"break main",
|
||||||
|
"continue"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug - Openocd Local",
|
||||||
|
"type":"cortex-debug",
|
||||||
|
"cortex-debug.armToolchainPath":"${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"executable": "${command:cmake.launchTargetPath}",
|
||||||
|
"request": "launch",
|
||||||
|
"servertype": "openocd",
|
||||||
|
// This may need to be arm-none-eabi-gdb depending on your system
|
||||||
|
"gdbPath" : "${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin/arm-none-eabi-gdb",
|
||||||
|
// Connect to an already running OpenOCD instance
|
||||||
|
"gdbTarget": "localhost:3333",
|
||||||
|
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||||
|
"runToMain": true,
|
||||||
|
// Work around for stopping at main on restart
|
||||||
|
"postRestartCommands": [
|
||||||
|
"break main",
|
||||||
|
"continue"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
64
.vscode/settings.json
vendored
64
.vscode/settings.json
vendored
|
@ -1,59 +1,9 @@
|
||||||
{
|
{
|
||||||
"files.associations": {
|
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||||
"chrono": "cpp",
|
"cmake.configureArgs": [
|
||||||
"list": "cpp",
|
"-DARM_NONE_EABI_TOOLCHAIN_PATH=${env:ARM_NONE_EABI_TOOLCHAIN_PATH}",
|
||||||
"array": "cpp",
|
"-DNRF5_SDK_PATH=${env:NRF5_SDK_PATH}",
|
||||||
"atomic": "cpp",
|
],
|
||||||
"bit": "cpp",
|
"cmake.generator": "Unix Makefiles",
|
||||||
"*.tcc": "cpp",
|
"clang-tidy.buildPath": "build/compile_commands.json"
|
||||||
"cctype": "cpp",
|
|
||||||
"charconv": "cpp",
|
|
||||||
"clocale": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"condition_variable": "cpp",
|
|
||||||
"cstdarg": "cpp",
|
|
||||||
"cstddef": "cpp",
|
|
||||||
"cstdint": "cpp",
|
|
||||||
"cstdio": "cpp",
|
|
||||||
"cstdlib": "cpp",
|
|
||||||
"cstring": "cpp",
|
|
||||||
"ctime": "cpp",
|
|
||||||
"cwchar": "cpp",
|
|
||||||
"cwctype": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"exception": "cpp",
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"memory": "cpp",
|
|
||||||
"memory_resource": "cpp",
|
|
||||||
"netfwd": "cpp",
|
|
||||||
"numeric": "cpp",
|
|
||||||
"optional": "cpp",
|
|
||||||
"random": "cpp",
|
|
||||||
"ratio": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"string_view": "cpp",
|
|
||||||
"system_error": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"type_traits": "cpp",
|
|
||||||
"utility": "cpp",
|
|
||||||
"fstream": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"iosfwd": "cpp",
|
|
||||||
"iostream": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"mutex": "cpp",
|
|
||||||
"new": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"sstream": "cpp",
|
|
||||||
"stdexcept": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"thread": "cpp",
|
|
||||||
"cinttypes": "cpp",
|
|
||||||
"typeinfo": "cpp"
|
|
||||||
}
|
|
||||||
}
|
}
|
44
.vscode/tasks.json
vendored
Normal file
44
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "create openocd build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "/opt/create_build_openocd.sh",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "update submodules",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "git submodule update --init",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "BuildInit",
|
||||||
|
"dependsOn": [
|
||||||
|
"update submodules",
|
||||||
|
"create openocd build"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -93,6 +93,7 @@ As of now, here is the list of achievements of this project:
|
||||||
- [Build the project](doc/buildAndProgram.md)
|
- [Build the project](doc/buildAndProgram.md)
|
||||||
- [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md)
|
- [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md)
|
||||||
- [Build the project with Docker](doc/buildWithDocker.md)
|
- [Build the project with Docker](doc/buildWithDocker.md)
|
||||||
|
- [Build the project with VSCode](doc/buildWithVScode.md)
|
||||||
- [Bootloader, OTA and DFU](./bootloader/README.md)
|
- [Bootloader, OTA and DFU](./bootloader/README.md)
|
||||||
- [Stub using NRF52-DK](./doc/PinetimeStubWithNrf52DK.md)
|
- [Stub using NRF52-DK](./doc/PinetimeStubWithNrf52DK.md)
|
||||||
- Logging with JLink RTT.
|
- Logging with JLink RTT.
|
||||||
|
|
42
doc/buildWithVScode.md
Normal file
42
doc/buildWithVScode.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Build and Develop the project using VS Code
|
||||||
|
|
||||||
|
The .VS Code folder contains configuration files for developing InfiniTime with VS Code. Effort was made to have these rely on Environment variables instead of hardcoded paths.
|
||||||
|
|
||||||
|
## Environment Setup
|
||||||
|
|
||||||
|
To support as many setups as possible the VS Code configuration files expect there to be certain environment variables to be set.
|
||||||
|
|
||||||
|
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`
|
||||||
|
**NRF5_SDK_PATH**|path to the NRF52 SDK|`export NRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345`
|
||||||
|
|
||||||
|
## VS Code Extensions
|
||||||
|
|
||||||
|
We leverage a few VS Code extensions for ease of development.
|
||||||
|
|
||||||
|
#### Required Extensions
|
||||||
|
|
||||||
|
- [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) - C/C++ IntelliSense, debugging, and code browsing.
|
||||||
|
- [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) - Extended CMake support in Visual Studio Code
|
||||||
|
|
||||||
|
#### Optional Extensions
|
||||||
|
|
||||||
|
[Cortex-Debug](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug) - ARM Cortex-M GDB Debugger support for VS Code
|
||||||
|
|
||||||
|
Cortex-Debug is only required for interactive debugging using VS Codes built in GDB support.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## VS Code/Docker DevContainer
|
||||||
|
|
||||||
|
The .devcontainer folder contains the configuration and scripts for using a Docker dev container for building InfiniTime
|
||||||
|
|
||||||
|
Using the [Remote-Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension is recommended. It will handle configuring the Docker virtual machine and setting everything up.
|
||||||
|
|
||||||
|
More documentation is available in the [readme in .devcontainer](.devcontainer/readme.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ void Battery::Update() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Non blocking read
|
// Non blocking read
|
||||||
samples = 0;
|
|
||||||
isReading = true;
|
isReading = true;
|
||||||
SaadcInit();
|
SaadcInit();
|
||||||
|
|
||||||
|
@ -41,9 +40,9 @@ void Battery::SaadcInit() {
|
||||||
|
|
||||||
nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
|
nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
|
||||||
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
|
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
|
||||||
.gain = NRF_SAADC_GAIN1_5,
|
.gain = NRF_SAADC_GAIN1_4,
|
||||||
.reference = NRF_SAADC_REFERENCE_INTERNAL,
|
.reference = NRF_SAADC_REFERENCE_INTERNAL,
|
||||||
.acq_time = NRF_SAADC_ACQTIME_3US,
|
.acq_time = NRF_SAADC_ACQTIME_40US,
|
||||||
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
|
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
|
||||||
.burst = NRF_SAADC_BURST_ENABLED,
|
.burst = NRF_SAADC_BURST_ENABLED,
|
||||||
.pin_p = batteryVoltageAdcInput,
|
.pin_p = batteryVoltageAdcInput,
|
||||||
|
@ -61,22 +60,21 @@ void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
|
||||||
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
|
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
|
||||||
|
|
||||||
// A hardware voltage divider divides the battery voltage by 2
|
// A hardware voltage divider divides the battery voltage by 2
|
||||||
// ADC gain is 1/5
|
// ADC gain is 1/4
|
||||||
// thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 10
|
// thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 8
|
||||||
// reference_voltage is 0.6V
|
// reference_voltage is 600mV
|
||||||
// p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
|
// p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
|
||||||
voltage = p_event->data.done.p_buffer[0] * 6000 / 1024;
|
voltage = p_event->data.done.p_buffer[0] * (8 * 600) / 1024;
|
||||||
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
|
|
||||||
percentRemaining = std::max(percentRemaining, 0);
|
if (voltage > battery_max) {
|
||||||
percentRemaining = std::min(percentRemaining, 100);
|
percentRemaining = 100;
|
||||||
percentRemainingBuffer.Insert(percentRemaining);
|
} else if (voltage < battery_min) {
|
||||||
|
percentRemaining = 0;
|
||||||
|
} else {
|
||||||
|
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
|
||||||
|
}
|
||||||
|
|
||||||
samples++;
|
|
||||||
if (samples > percentRemainingSamples) {
|
|
||||||
nrfx_saadc_uninit();
|
nrfx_saadc_uninit();
|
||||||
isReading = false;
|
isReading = false;
|
||||||
} else {
|
|
||||||
nrfx_saadc_sample();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,38 +7,6 @@
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
|
|
||||||
/** A simple circular buffer that can be used to average
|
|
||||||
out the sensor values. The total capacity of the CircBuffer
|
|
||||||
is given as the template parameter N.
|
|
||||||
*/
|
|
||||||
template <int N> class CircBuffer {
|
|
||||||
public:
|
|
||||||
CircBuffer() : arr {}, sz {}, cap {N}, head {} {
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
insert member function overwrites the next data to the current
|
|
||||||
HEAD and moves the HEAD to the newly inserted value.
|
|
||||||
*/
|
|
||||||
void Insert(const uint8_t num) {
|
|
||||||
head %= cap;
|
|
||||||
arr[head++] = num;
|
|
||||||
if (sz != cap) {
|
|
||||||
sz++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t GetAverage() const {
|
|
||||||
int sum = std::accumulate(arr.begin(), arr.end(), 0);
|
|
||||||
return static_cast<uint8_t>(sum / sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<uint8_t, N> arr; /**< internal array used to store the values*/
|
|
||||||
uint8_t sz; /**< The current size of the array.*/
|
|
||||||
uint8_t cap; /**< Total capacity of the CircBuffer.*/
|
|
||||||
uint8_t head; /**< The current head of the CircBuffer*/
|
|
||||||
};
|
|
||||||
|
|
||||||
class Battery {
|
class Battery {
|
||||||
public:
|
public:
|
||||||
Battery();
|
Battery();
|
||||||
|
@ -47,10 +15,7 @@ namespace Pinetime {
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
uint8_t PercentRemaining() const {
|
uint8_t PercentRemaining() const {
|
||||||
auto avg = percentRemainingBuffer.GetAverage();
|
return percentRemaining;
|
||||||
avg = std::min(avg, static_cast<uint8_t>(100));
|
|
||||||
avg = std::max(avg, static_cast<uint8_t>(0));
|
|
||||||
return avg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Voltage() const {
|
uint16_t Voltage() const {
|
||||||
|
@ -69,12 +34,9 @@ namespace Pinetime {
|
||||||
static Battery* instance;
|
static Battery* instance;
|
||||||
nrf_saadc_value_t saadc_value;
|
nrf_saadc_value_t saadc_value;
|
||||||
|
|
||||||
static constexpr uint8_t percentRemainingSamples = 5;
|
|
||||||
CircBuffer<percentRemainingSamples> percentRemainingBuffer {};
|
|
||||||
|
|
||||||
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
|
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
|
||||||
uint16_t voltage = 0;
|
uint16_t voltage = 0;
|
||||||
int percentRemaining = -1;
|
uint8_t percentRemaining = 0;
|
||||||
|
|
||||||
bool isCharging = false;
|
bool isCharging = false;
|
||||||
bool isPowerPresent = false;
|
bool isPowerPresent = false;
|
||||||
|
@ -85,7 +47,6 @@ namespace Pinetime {
|
||||||
static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
|
static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
|
||||||
|
|
||||||
bool isReading = false;
|
bool isReading = false;
|
||||||
uint8_t samples = 0;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,9 +55,9 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
|
||||||
auto time = date::make_time(currentDateTime - dp);
|
auto time = date::make_time(currentDateTime - dp);
|
||||||
auto yearMonthDay = date::year_month_day(dp);
|
auto yearMonthDay = date::year_month_day(dp);
|
||||||
|
|
||||||
year = (int) yearMonthDay.year();
|
year = static_cast<int>(yearMonthDay.year());
|
||||||
month = static_cast<Months>((unsigned) yearMonthDay.month());
|
month = static_cast<Months>(static_cast<unsigned>(yearMonthDay.month()));
|
||||||
day = (unsigned) yearMonthDay.day();
|
day = static_cast<unsigned>(yearMonthDay.day());
|
||||||
dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding());
|
dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding());
|
||||||
|
|
||||||
hour = time.hours().count();
|
hour = time.hours().count();
|
||||||
|
@ -75,31 +75,31 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DateTime::MonthShortToString() {
|
const char* DateTime::MonthShortToString() {
|
||||||
return DateTime::MonthsString[(uint8_t) month];
|
return DateTime::MonthsString[static_cast<uint8_t>(month)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DateTime::MonthShortToStringLow() {
|
const char* DateTime::MonthShortToStringLow() {
|
||||||
return DateTime::MonthsStringLow[(uint8_t) month];
|
return DateTime::MonthsStringLow[static_cast<uint8_t>(month)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DateTime::MonthsToStringLow() {
|
const char* DateTime::MonthsToStringLow() {
|
||||||
return DateTime::MonthsLow[(uint8_t) month];
|
return DateTime::MonthsLow[static_cast<uint8_t>(month)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DateTime::DayOfWeekToString() {
|
const char* DateTime::DayOfWeekToString() {
|
||||||
return DateTime::DaysString[(uint8_t) dayOfWeek];
|
return DateTime::DaysString[static_cast<uint8_t>(dayOfWeek)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DateTime::DayOfWeekShortToString() {
|
const char* DateTime::DayOfWeekShortToString() {
|
||||||
return DateTime::DaysStringShort[(uint8_t) dayOfWeek];
|
return DateTime::DaysStringShort[static_cast<uint8_t>(dayOfWeek)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DateTime::DayOfWeekToStringLow() {
|
const char* DateTime::DayOfWeekToStringLow() {
|
||||||
return DateTime::DaysStringLow[(uint8_t) dayOfWeek];
|
return DateTime::DaysStringLow[static_cast<uint8_t>(dayOfWeek)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DateTime::DayOfWeekShortToStringLow() {
|
const char* DateTime::DayOfWeekShortToStringLow() {
|
||||||
return DateTime::DaysStringShortLow[(uint8_t) dayOfWeek];
|
return DateTime::DaysStringShortLow[static_cast<uint8_t>(dayOfWeek)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
|
void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
const char* BatteryIcon::GetBatteryIcon(uint8_t batteryPercent) {
|
const char* BatteryIcon::GetBatteryIcon(uint8_t batteryPercent) {
|
||||||
if (batteryPercent > 90)
|
if (batteryPercent > 87)
|
||||||
return Symbols::batteryFull;
|
return Symbols::batteryFull;
|
||||||
if (batteryPercent > 75)
|
if (batteryPercent > 62)
|
||||||
return Symbols::batteryThreeQuarter;
|
return Symbols::batteryThreeQuarter;
|
||||||
if (batteryPercent > 50)
|
if (batteryPercent > 37)
|
||||||
return Symbols::batteryHalf;
|
return Symbols::batteryHalf;
|
||||||
if (batteryPercent > 25)
|
if (batteryPercent > 12)
|
||||||
return Symbols::batteryOneQuarter;
|
return Symbols::batteryOneQuarter;
|
||||||
return Symbols::batteryEmpty;
|
return Symbols::batteryEmpty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
|
|
||||||
#include <date/date.h>
|
#include <date/date.h>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <cstdio>
|
|
||||||
#include "BatteryIcon.h"
|
|
||||||
#include "BleIcon.h"
|
|
||||||
#include "NotificationIcon.h"
|
|
||||||
#include "Symbols.h"
|
|
||||||
#include "components/battery/BatteryController.h"
|
#include "components/battery/BatteryController.h"
|
||||||
#include "components/motion/MotionController.h"
|
#include "components/motion/MotionController.h"
|
||||||
#include "components/ble/BleController.h"
|
#include "components/ble/BleController.h"
|
||||||
|
@ -89,16 +84,3 @@ std::unique_ptr<Screen> Clock::PineTimeStyleScreen() {
|
||||||
settingsController,
|
settingsController,
|
||||||
motionController);
|
motionController);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Examples for more watch faces
|
|
||||||
std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() {
|
|
||||||
return std::make_unique<Screens::WatchFaceMinimal>(app, dateTimeController, batteryController, bleController, notificatioManager,
|
|
||||||
settingsController);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Screen> Clock::WatchFaceCustomScreen() {
|
|
||||||
return std::make_unique<Screens::WatchFaceCustom>(app, dateTimeController, batteryController, bleController, notificatioManager,
|
|
||||||
settingsController);
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -9,9 +9,6 @@
|
||||||
#include "components/datetime/DateTimeController.h"
|
#include "components/datetime/DateTimeController.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Drivers {
|
|
||||||
class BMA421;
|
|
||||||
}
|
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
class Settings;
|
class Settings;
|
||||||
class Battery;
|
class Battery;
|
||||||
|
@ -51,10 +48,6 @@ namespace Pinetime {
|
||||||
std::unique_ptr<Screen> WatchFaceDigitalScreen();
|
std::unique_ptr<Screen> WatchFaceDigitalScreen();
|
||||||
std::unique_ptr<Screen> WatchFaceAnalogScreen();
|
std::unique_ptr<Screen> WatchFaceAnalogScreen();
|
||||||
std::unique_ptr<Screen> PineTimeStyleScreen();
|
std::unique_ptr<Screen> PineTimeStyleScreen();
|
||||||
|
|
||||||
// Examples for more watch faces
|
|
||||||
// std::unique_ptr<Screen> WatchFaceMinimalScreen();
|
|
||||||
// std::unique_ptr<Screen> WatchFaceCustomScreen();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app,
|
||||||
notificatioManager {notificatioManager},
|
notificatioManager {notificatioManager},
|
||||||
settingsController {settingsController},
|
settingsController {settingsController},
|
||||||
motionController {motionController} {
|
motionController {motionController} {
|
||||||
|
|
||||||
/* This sets the watchface number to return to after leaving the menu */
|
/* This sets the watchface number to return to after leaving the menu */
|
||||||
settingsController.SetClockFace(2);
|
settingsController.SetClockFace(2);
|
||||||
|
|
||||||
|
@ -62,7 +61,6 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app,
|
||||||
displayedChar[4] = 0;
|
displayedChar[4] = 0;
|
||||||
|
|
||||||
/* Create a 200px wide background rectangle */
|
/* Create a 200px wide background rectangle */
|
||||||
|
|
||||||
timebar = lv_obj_create(lv_scr_act(), nullptr);
|
timebar = lv_obj_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
||||||
lv_obj_set_style_local_radius(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
|
lv_obj_set_style_local_radius(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
|
||||||
|
@ -70,7 +68,6 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app,
|
||||||
lv_obj_align(timebar, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 0);
|
lv_obj_align(timebar, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 0);
|
||||||
|
|
||||||
/* Display the time */
|
/* Display the time */
|
||||||
|
|
||||||
timeDD1 = lv_label_create(lv_scr_act(), nullptr);
|
timeDD1 = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_set_style_local_text_font(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
|
lv_obj_set_style_local_text_font(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
|
||||||
lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
|
lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
|
||||||
|
@ -90,7 +87,6 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app,
|
||||||
lv_obj_align(timeAMPM, timebar, LV_ALIGN_IN_BOTTOM_LEFT, 2, -20);
|
lv_obj_align(timeAMPM, timebar, LV_ALIGN_IN_BOTTOM_LEFT, 2, -20);
|
||||||
|
|
||||||
/* Create a 40px wide bar down the right side of the screen */
|
/* Create a 40px wide bar down the right side of the screen */
|
||||||
|
|
||||||
sidebar = lv_obj_create(lv_scr_act(), nullptr);
|
sidebar = lv_obj_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
|
lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
|
||||||
lv_obj_set_style_local_radius(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
|
lv_obj_set_style_local_radius(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
|
||||||
|
@ -98,7 +94,6 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app,
|
||||||
lv_obj_align(sidebar, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
lv_obj_align(sidebar, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
||||||
|
|
||||||
/* Display icons */
|
/* Display icons */
|
||||||
|
|
||||||
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
||||||
lv_label_set_text(batteryIcon, Symbols::batteryFull);
|
lv_label_set_text(batteryIcon, Symbols::batteryFull);
|
||||||
|
@ -117,7 +112,6 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app,
|
||||||
lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 40);
|
lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 40);
|
||||||
|
|
||||||
/* Calendar icon */
|
/* Calendar icon */
|
||||||
|
|
||||||
calendarOuter = lv_obj_create(lv_scr_act(), nullptr);
|
calendarOuter = lv_obj_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
||||||
lv_obj_set_style_local_radius(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
|
lv_obj_set_style_local_radius(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
|
||||||
|
@ -155,7 +149,6 @@ PineTimeStyle::PineTimeStyle(DisplayApp* app,
|
||||||
lv_obj_align(calendarCrossBar2, calendarBar2, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
lv_obj_align(calendarCrossBar2, calendarBar2, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
||||||
|
|
||||||
/* Display date */
|
/* Display date */
|
||||||
|
|
||||||
dateDayOfWeek = lv_label_create(lv_scr_act(), nullptr);
|
dateDayOfWeek = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
|
||||||
lv_label_set_text(dateDayOfWeek, "THU");
|
lv_label_set_text(dateDayOfWeek, "THU");
|
||||||
|
@ -223,26 +216,17 @@ bool PineTimeStyle::Refresh() {
|
||||||
|
|
||||||
bleState = bleController.IsConnected();
|
bleState = bleController.IsConnected();
|
||||||
if (bleState.IsUpdated()) {
|
if (bleState.IsUpdated()) {
|
||||||
if (bleState.Get() == true) {
|
lv_label_set_text(bleIcon, BleIcon::GetIcon(bleState.Get()));
|
||||||
lv_label_set_text(bleIcon, BleIcon::GetIcon(true));
|
|
||||||
lv_obj_realign(bleIcon);
|
lv_obj_realign(bleIcon);
|
||||||
} else {
|
|
||||||
lv_label_set_text(bleIcon, BleIcon::GetIcon(false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationState = notificatioManager.AreNewNotificationsAvailable();
|
notificationState = notificatioManager.AreNewNotificationsAvailable();
|
||||||
if (notificationState.IsUpdated()) {
|
if (notificationState.IsUpdated()) {
|
||||||
if (notificationState.Get() == true) {
|
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
|
||||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
|
|
||||||
lv_obj_realign(notificationIcon);
|
lv_obj_realign(notificationIcon);
|
||||||
} else {
|
|
||||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDateTime = dateTimeController.CurrentDateTime();
|
currentDateTime = dateTimeController.CurrentDateTime();
|
||||||
|
|
||||||
if (currentDateTime.IsUpdated()) {
|
if (currentDateTime.IsUpdated()) {
|
||||||
auto newDateTime = currentDateTime.Get();
|
auto newDateTime = currentDateTime.Get();
|
||||||
|
|
||||||
|
@ -250,9 +234,9 @@ bool PineTimeStyle::Refresh() {
|
||||||
auto time = date::make_time(newDateTime - dp);
|
auto time = date::make_time(newDateTime - dp);
|
||||||
auto yearMonthDay = date::year_month_day(dp);
|
auto yearMonthDay = date::year_month_day(dp);
|
||||||
|
|
||||||
auto year = (int) yearMonthDay.year();
|
auto year = static_cast<int>(yearMonthDay.year());
|
||||||
auto month = static_cast<Pinetime::Controllers::DateTime::Months>((unsigned) yearMonthDay.month());
|
auto month = static_cast<Pinetime::Controllers::DateTime::Months>(static_cast<unsigned>(yearMonthDay.month()));
|
||||||
auto day = (unsigned) yearMonthDay.day();
|
auto day = static_cast<unsigned>(yearMonthDay.day());
|
||||||
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
|
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
|
||||||
|
|
||||||
int hour = time.hours().count();
|
int hour = time.hours().count();
|
||||||
|
@ -263,7 +247,6 @@ bool PineTimeStyle::Refresh() {
|
||||||
|
|
||||||
char hoursChar[3];
|
char hoursChar[3];
|
||||||
char ampmChar[5];
|
char ampmChar[5];
|
||||||
|
|
||||||
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
|
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
|
||||||
sprintf(hoursChar, "%02d", hour);
|
sprintf(hoursChar, "%02d", hour);
|
||||||
} else {
|
} else {
|
||||||
|
@ -282,41 +265,26 @@ bool PineTimeStyle::Refresh() {
|
||||||
sprintf(hoursChar, "%02d", hour);
|
sprintf(hoursChar, "%02d", hour);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] ||
|
if (hoursChar[0] != displayedChar[0] or hoursChar[1] != displayedChar[1] or minutesChar[0] != displayedChar[2] or
|
||||||
minutesChar[1] != displayedChar[3]) {
|
minutesChar[1] != displayedChar[3]) {
|
||||||
displayedChar[0] = hoursChar[0];
|
displayedChar[0] = hoursChar[0];
|
||||||
displayedChar[1] = hoursChar[1];
|
displayedChar[1] = hoursChar[1];
|
||||||
displayedChar[2] = minutesChar[0];
|
displayedChar[2] = minutesChar[0];
|
||||||
displayedChar[3] = minutesChar[1];
|
displayedChar[3] = minutesChar[1];
|
||||||
|
|
||||||
char hourStr[3];
|
|
||||||
char minStr[3];
|
|
||||||
|
|
||||||
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
||||||
lv_label_set_text(timeAMPM, ampmChar);
|
lv_label_set_text(timeAMPM, ampmChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Display the time as 2 pairs of digits */
|
lv_label_set_text_fmt(timeDD1, "%s", hoursChar);
|
||||||
sprintf(hourStr, "%c%c", hoursChar[0], hoursChar[1]);
|
lv_label_set_text_fmt(timeDD2, "%s", minutesChar);
|
||||||
lv_label_set_text(timeDD1, hourStr);
|
|
||||||
|
|
||||||
sprintf(minStr, "%c%c", minutesChar[0], minutesChar[1]);
|
|
||||||
lv_label_set_text(timeDD2, minStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
|
if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
|
||||||
char dayOfWeekStr[4];
|
lv_label_set_text_fmt(dateDayOfWeek, "%s", dateTimeController.DayOfWeekShortToString());
|
||||||
char dayStr[3];
|
lv_label_set_text_fmt(dateDay, "%d", day);
|
||||||
char monthStr[4];
|
|
||||||
|
|
||||||
sprintf(dayOfWeekStr, "%s", dateTimeController.DayOfWeekShortToString());
|
|
||||||
sprintf(dayStr, "%d", day);
|
|
||||||
sprintf(monthStr, "%s", dateTimeController.MonthShortToString());
|
|
||||||
|
|
||||||
lv_label_set_text(dateDayOfWeek, dayOfWeekStr);
|
|
||||||
lv_label_set_text(dateDay, dayStr);
|
|
||||||
lv_obj_realign(dateDay);
|
lv_obj_realign(dateDay);
|
||||||
lv_label_set_text(dateMonth, monthStr);
|
lv_label_set_text_fmt(dateMonth, "%s", dateTimeController.MonthShortToString());
|
||||||
|
|
||||||
currentYear = year;
|
currentYear = year;
|
||||||
currentMonth = month;
|
currentMonth = month;
|
||||||
|
|
|
@ -32,8 +32,6 @@ namespace Pinetime {
|
||||||
|
|
||||||
bool Refresh() override;
|
bool Refresh() override;
|
||||||
|
|
||||||
void OnObjectEvent(lv_obj_t* pObj, lv_event_t i);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char displayedChar[5];
|
char displayedChar[5];
|
||||||
|
|
||||||
|
@ -67,9 +65,6 @@ namespace Pinetime {
|
||||||
lv_obj_t* calendarBar2;
|
lv_obj_t* calendarBar2;
|
||||||
lv_obj_t* calendarCrossBar1;
|
lv_obj_t* calendarCrossBar1;
|
||||||
lv_obj_t* calendarCrossBar2;
|
lv_obj_t* calendarCrossBar2;
|
||||||
lv_obj_t* heartbeatIcon;
|
|
||||||
lv_obj_t* heartbeatValue;
|
|
||||||
lv_obj_t* heartbeatBpm;
|
|
||||||
lv_obj_t* notificationIcon;
|
lv_obj_t* notificationIcon;
|
||||||
lv_obj_t* stepGauge;
|
lv_obj_t* stepGauge;
|
||||||
lv_color_t needle_colors[1];
|
lv_color_t needle_colors[1];
|
||||||
|
|
|
@ -10,38 +10,37 @@ LV_IMG_DECLARE(bg_clock);
|
||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
constexpr int16_t HourLength = 70;
|
||||||
constexpr auto HOUR_LENGTH = 70;
|
constexpr int16_t MinuteLength = 90;
|
||||||
constexpr auto MINUTE_LENGTH = 90;
|
constexpr int16_t SecondLength = 110;
|
||||||
constexpr auto SECOND_LENGTH = 110;
|
|
||||||
|
|
||||||
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
|
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
|
||||||
const auto LV_TRIG_SCALE = _lv_trigo_sin(90);
|
const auto LV_TRIG_SCALE = _lv_trigo_sin(90);
|
||||||
|
|
||||||
int16_t cosine(int16_t angle) {
|
int16_t Cosine(int16_t angle) {
|
||||||
return _lv_trigo_sin(angle + 90);
|
return _lv_trigo_sin(angle + 90);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t sine(int16_t angle) {
|
int16_t Sine(int16_t angle) {
|
||||||
return _lv_trigo_sin(angle);
|
return _lv_trigo_sin(angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t coordinate_x_relocate(int16_t x) {
|
int16_t CoordinateXRelocate(int16_t x) {
|
||||||
return (x + LV_HOR_RES / 2);
|
return (x + LV_HOR_RES / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t coordinate_y_relocate(int16_t y) {
|
int16_t CoordinateYRelocate(int16_t y) {
|
||||||
return std::abs(y - LV_HOR_RES / 2);
|
return std::abs(y - LV_HOR_RES / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_point_t coordinate_relocate(int16_t radius, int16_t angle) {
|
lv_point_t CoordinateRelocate(int16_t radius, int16_t angle) {
|
||||||
return lv_point_t{
|
return lv_point_t{
|
||||||
.x = coordinate_x_relocate(radius * static_cast<int32_t>(sine(angle)) / LV_TRIG_SCALE),
|
.x = CoordinateXRelocate(radius * static_cast<int32_t>(Sine(angle)) / LV_TRIG_SCALE),
|
||||||
.y = coordinate_y_relocate(radius * static_cast<int32_t>(cosine(angle)) / LV_TRIG_SCALE)
|
.y = CoordinateYRelocate(radius * static_cast<int32_t>(Cosine(angle)) / LV_TRIG_SCALE)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
}
|
||||||
|
|
||||||
WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
|
WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
|
||||||
Controllers::DateTime& dateTimeController,
|
Controllers::DateTime& dateTimeController,
|
||||||
|
@ -123,7 +122,6 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
|
||||||
}
|
}
|
||||||
|
|
||||||
WatchFaceAnalog::~WatchFaceAnalog() {
|
WatchFaceAnalog::~WatchFaceAnalog() {
|
||||||
|
|
||||||
lv_style_reset(&hour_line_style);
|
lv_style_reset(&hour_line_style);
|
||||||
lv_style_reset(&hour_line_style_trace);
|
lv_style_reset(&hour_line_style_trace);
|
||||||
lv_style_reset(&minute_line_style);
|
lv_style_reset(&minute_line_style);
|
||||||
|
@ -134,18 +132,17 @@ WatchFaceAnalog::~WatchFaceAnalog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WatchFaceAnalog::UpdateClock() {
|
void WatchFaceAnalog::UpdateClock() {
|
||||||
|
|
||||||
hour = dateTimeController.Hours();
|
hour = dateTimeController.Hours();
|
||||||
minute = dateTimeController.Minutes();
|
minute = dateTimeController.Minutes();
|
||||||
second = dateTimeController.Seconds();
|
second = dateTimeController.Seconds();
|
||||||
|
|
||||||
if (sMinute != minute) {
|
if (sMinute != minute) {
|
||||||
auto const angle = minute * 6;
|
auto const angle = minute * 6;
|
||||||
minute_point[0] = coordinate_relocate(30, angle);
|
minute_point[0] = CoordinateRelocate(30, angle);
|
||||||
minute_point[1] = coordinate_relocate(MINUTE_LENGTH, angle);
|
minute_point[1] = CoordinateRelocate(MinuteLength, angle);
|
||||||
|
|
||||||
minute_point_trace[0] = coordinate_relocate(5, angle);
|
minute_point_trace[0] = CoordinateRelocate(5, angle);
|
||||||
minute_point_trace[1] = coordinate_relocate(31, angle);
|
minute_point_trace[1] = CoordinateRelocate(31, angle);
|
||||||
|
|
||||||
lv_line_set_points(minute_body, minute_point, 2);
|
lv_line_set_points(minute_body, minute_point, 2);
|
||||||
lv_line_set_points(minute_body_trace, minute_point_trace, 2);
|
lv_line_set_points(minute_body_trace, minute_point_trace, 2);
|
||||||
|
@ -156,11 +153,11 @@ void WatchFaceAnalog::UpdateClock() {
|
||||||
sMinute = minute;
|
sMinute = minute;
|
||||||
auto const angle = (hour * 30 + minute / 2);
|
auto const angle = (hour * 30 + minute / 2);
|
||||||
|
|
||||||
hour_point[0] = coordinate_relocate(30, angle);
|
hour_point[0] = CoordinateRelocate(30, angle);
|
||||||
hour_point[1] = coordinate_relocate(HOUR_LENGTH, angle);
|
hour_point[1] = CoordinateRelocate(HourLength, angle);
|
||||||
|
|
||||||
hour_point_trace[0] = coordinate_relocate(5, angle);
|
hour_point_trace[0] = CoordinateRelocate(5, angle);
|
||||||
hour_point_trace[1] = coordinate_relocate(31, angle);
|
hour_point_trace[1] = CoordinateRelocate(31, angle);
|
||||||
|
|
||||||
lv_line_set_points(hour_body, hour_point, 2);
|
lv_line_set_points(hour_body, hour_point, 2);
|
||||||
lv_line_set_points(hour_body_trace, hour_point_trace, 2);
|
lv_line_set_points(hour_body_trace, hour_point_trace, 2);
|
||||||
|
@ -170,8 +167,8 @@ void WatchFaceAnalog::UpdateClock() {
|
||||||
sSecond = second;
|
sSecond = second;
|
||||||
auto const angle = second * 6;
|
auto const angle = second * 6;
|
||||||
|
|
||||||
second_point[0] = coordinate_relocate(-20, angle);
|
second_point[0] = CoordinateRelocate(-20, angle);
|
||||||
second_point[1] = coordinate_relocate(SECOND_LENGTH, angle);
|
second_point[1] = CoordinateRelocate(SecondLength, angle);
|
||||||
lv_line_set_points(second_body, second_point, 2);
|
lv_line_set_points(second_body, second_point, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,16 +183,12 @@ bool WatchFaceAnalog::Refresh() {
|
||||||
notificationState = notificationManager.AreNewNotificationsAvailable();
|
notificationState = notificationManager.AreNewNotificationsAvailable();
|
||||||
|
|
||||||
if (notificationState.IsUpdated()) {
|
if (notificationState.IsUpdated()) {
|
||||||
if (notificationState.Get() == true)
|
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
|
||||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
|
|
||||||
else
|
|
||||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDateTime = dateTimeController.CurrentDateTime();
|
currentDateTime = dateTimeController.CurrentDateTime();
|
||||||
|
|
||||||
if (currentDateTime.IsUpdated()) {
|
if (currentDateTime.IsUpdated()) {
|
||||||
|
|
||||||
month = dateTimeController.Month();
|
month = dateTimeController.Month();
|
||||||
day = dateTimeController.Day();
|
day = dateTimeController.Day();
|
||||||
dayOfWeek = dateTimeController.DayOfWeek();
|
dayOfWeek = dateTimeController.DayOfWeek();
|
||||||
|
@ -203,7 +196,6 @@ bool WatchFaceAnalog::Refresh() {
|
||||||
UpdateClock();
|
UpdateClock();
|
||||||
|
|
||||||
if ((month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
|
if ((month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
|
||||||
|
|
||||||
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), day);
|
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), day);
|
||||||
|
|
||||||
currentMonth = month;
|
currentMonth = month;
|
||||||
|
|
|
@ -58,14 +58,12 @@ namespace Pinetime {
|
||||||
lv_obj_t* minute_body_trace;
|
lv_obj_t* minute_body_trace;
|
||||||
lv_obj_t* second_body;
|
lv_obj_t* second_body;
|
||||||
|
|
||||||
// ##
|
|
||||||
lv_point_t hour_point[2];
|
lv_point_t hour_point[2];
|
||||||
lv_point_t hour_point_trace[2];
|
lv_point_t hour_point_trace[2];
|
||||||
lv_point_t minute_point[2];
|
lv_point_t minute_point[2];
|
||||||
lv_point_t minute_point_trace[2];
|
lv_point_t minute_point_trace[2];
|
||||||
lv_point_t second_point[2];
|
lv_point_t second_point[2];
|
||||||
|
|
||||||
// ##
|
|
||||||
lv_style_t hour_line_style;
|
lv_style_t hour_line_style;
|
||||||
lv_style_t hour_line_style_trace;
|
lv_style_t hour_line_style_trace;
|
||||||
lv_style_t minute_line_style;
|
lv_style_t minute_line_style;
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
#include "components/ble/NotificationManager.h"
|
#include "components/ble/NotificationManager.h"
|
||||||
#include "components/heartrate/HeartRateController.h"
|
#include "components/heartrate/HeartRateController.h"
|
||||||
#include "components/motion/MotionController.h"
|
#include "components/motion/MotionController.h"
|
||||||
#include "components/settings/Settings.h"
|
|
||||||
#include "../DisplayApp.h"
|
|
||||||
|
|
||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
||||||
|
@ -36,12 +33,6 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
||||||
motionController {motionController} {
|
motionController {motionController} {
|
||||||
settingsController.SetClockFace(0);
|
settingsController.SetClockFace(0);
|
||||||
|
|
||||||
displayedChar[0] = 0;
|
|
||||||
displayedChar[1] = 0;
|
|
||||||
displayedChar[2] = 0;
|
|
||||||
displayedChar[3] = 0;
|
|
||||||
displayedChar[4] = 0;
|
|
||||||
|
|
||||||
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_label_set_text(batteryIcon, Symbols::batteryFull);
|
lv_label_set_text(batteryIcon, Symbols::batteryFull);
|
||||||
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2);
|
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2);
|
||||||
|
@ -56,7 +47,7 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
||||||
lv_label_set_text(bleIcon, Symbols::bluetooth);
|
lv_label_set_text(bleIcon, Symbols::bluetooth);
|
||||||
lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
||||||
|
|
||||||
notificationIcon = lv_label_create(lv_scr_act(), NULL);
|
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00));
|
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00));
|
||||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
|
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
|
||||||
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0);
|
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0);
|
||||||
|
@ -111,17 +102,13 @@ bool WatchFaceDigital::Refresh() {
|
||||||
if (batteryPercentRemaining.IsUpdated()) {
|
if (batteryPercentRemaining.IsUpdated()) {
|
||||||
auto batteryPercent = batteryPercentRemaining.Get();
|
auto batteryPercent = batteryPercentRemaining.Get();
|
||||||
lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent));
|
lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent));
|
||||||
auto isCharging = batteryController.IsCharging() || batteryController.IsPowerPresent();
|
auto isCharging = batteryController.IsCharging() or batteryController.IsPowerPresent();
|
||||||
lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging));
|
lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging));
|
||||||
}
|
}
|
||||||
|
|
||||||
bleState = bleController.IsConnected();
|
bleState = bleController.IsConnected();
|
||||||
if (bleState.IsUpdated()) {
|
if (bleState.IsUpdated()) {
|
||||||
if (bleState.Get() == true) {
|
lv_label_set_text(bleIcon, BleIcon::GetIcon(bleState.Get()));
|
||||||
lv_label_set_text(bleIcon, BleIcon::GetIcon(true));
|
|
||||||
} else {
|
|
||||||
lv_label_set_text(bleIcon, BleIcon::GetIcon(false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 5);
|
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 5);
|
||||||
lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
||||||
|
@ -129,10 +116,7 @@ bool WatchFaceDigital::Refresh() {
|
||||||
|
|
||||||
notificationState = notificatioManager.AreNewNotificationsAvailable();
|
notificationState = notificatioManager.AreNewNotificationsAvailable();
|
||||||
if (notificationState.IsUpdated()) {
|
if (notificationState.IsUpdated()) {
|
||||||
if (notificationState.Get() == true)
|
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
|
||||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
|
|
||||||
else
|
|
||||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDateTime = dateTimeController.CurrentDateTime();
|
currentDateTime = dateTimeController.CurrentDateTime();
|
||||||
|
@ -144,9 +128,9 @@ bool WatchFaceDigital::Refresh() {
|
||||||
auto time = date::make_time(newDateTime - dp);
|
auto time = date::make_time(newDateTime - dp);
|
||||||
auto yearMonthDay = date::year_month_day(dp);
|
auto yearMonthDay = date::year_month_day(dp);
|
||||||
|
|
||||||
auto year = (int) yearMonthDay.year();
|
auto year = static_cast<int>(yearMonthDay.year());
|
||||||
auto month = static_cast<Pinetime::Controllers::DateTime::Months>((unsigned) yearMonthDay.month());
|
auto month = static_cast<Pinetime::Controllers::DateTime::Months>(static_cast<unsigned>(yearMonthDay.month()));
|
||||||
auto day = (unsigned) yearMonthDay.day();
|
auto day = static_cast<unsigned>(yearMonthDay.day());
|
||||||
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
|
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
|
||||||
|
|
||||||
int hour = time.hours().count();
|
int hour = time.hours().count();
|
||||||
|
@ -175,15 +159,13 @@ bool WatchFaceDigital::Refresh() {
|
||||||
sprintf(hoursChar, "%02d", hour);
|
sprintf(hoursChar, "%02d", hour);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] ||
|
if ((hoursChar[0] != displayedChar[0]) or (hoursChar[1] != displayedChar[1]) or (minutesChar[0] != displayedChar[2]) or
|
||||||
minutesChar[1] != displayedChar[3]) {
|
(minutesChar[1] != displayedChar[3])) {
|
||||||
displayedChar[0] = hoursChar[0];
|
displayedChar[0] = hoursChar[0];
|
||||||
displayedChar[1] = hoursChar[1];
|
displayedChar[1] = hoursChar[1];
|
||||||
displayedChar[2] = minutesChar[0];
|
displayedChar[2] = minutesChar[0];
|
||||||
displayedChar[3] = minutesChar[1];
|
displayedChar[3] = minutesChar[1];
|
||||||
|
|
||||||
char timeStr[6];
|
|
||||||
|
|
||||||
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
||||||
lv_label_set_text(label_time_ampm, ampmChar);
|
lv_label_set_text(label_time_ampm, ampmChar);
|
||||||
if (hoursChar[0] == '0') {
|
if (hoursChar[0] == '0') {
|
||||||
|
@ -191,8 +173,7 @@ bool WatchFaceDigital::Refresh() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(timeStr, "%c%c:%c%c", hoursChar[0], hoursChar[1], minutesChar[0], minutesChar[1]);
|
lv_label_set_text_fmt(label_time, "%s:%s", hoursChar, minutesChar);
|
||||||
lv_label_set_text(label_time, timeStr);
|
|
||||||
|
|
||||||
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
||||||
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
|
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
|
||||||
|
@ -202,13 +183,11 @@ bool WatchFaceDigital::Refresh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
|
if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
|
||||||
char dateStr[22];
|
|
||||||
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
|
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
|
||||||
sprintf(dateStr, "%s %d %s %d", dateTimeController.DayOfWeekShortToString(), day, dateTimeController.MonthShortToString(), year);
|
lv_label_set_text_fmt(label_date, "%s %d %s %d", dateTimeController.DayOfWeekShortToString(), day, dateTimeController.MonthShortToString(), year);
|
||||||
} else {
|
} else {
|
||||||
sprintf(dateStr, "%s %s %d %d", dateTimeController.DayOfWeekShortToString(), dateTimeController.MonthShortToString(), day, year);
|
lv_label_set_text_fmt(label_date, "%s %s %d %d", dateTimeController.DayOfWeekShortToString(), dateTimeController.MonthShortToString(), day, year);
|
||||||
}
|
}
|
||||||
lv_label_set_text(label_date, dateStr);
|
|
||||||
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
|
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
|
||||||
|
|
||||||
currentYear = year;
|
currentYear = year;
|
||||||
|
|
|
@ -35,10 +35,8 @@ namespace Pinetime {
|
||||||
|
|
||||||
bool Refresh() override;
|
bool Refresh() override;
|
||||||
|
|
||||||
void OnObjectEvent(lv_obj_t* pObj, lv_event_t i);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char displayedChar[5];
|
char displayedChar[5] {};
|
||||||
|
|
||||||
uint16_t currentYear = 1970;
|
uint16_t currentYear = 1970;
|
||||||
Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
|
Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
|
||||||
|
@ -63,7 +61,6 @@ namespace Pinetime {
|
||||||
lv_obj_t* batteryPlug;
|
lv_obj_t* batteryPlug;
|
||||||
lv_obj_t* heartbeatIcon;
|
lv_obj_t* heartbeatIcon;
|
||||||
lv_obj_t* heartbeatValue;
|
lv_obj_t* heartbeatValue;
|
||||||
lv_obj_t* heartbeatBpm;
|
|
||||||
lv_obj_t* stepIcon;
|
lv_obj_t* stepIcon;
|
||||||
lv_obj_t* stepValue;
|
lv_obj_t* stepValue;
|
||||||
lv_obj_t* notificationIcon;
|
lv_obj_t* notificationIcon;
|
||||||
|
|
Loading…
Reference in a new issue