Merge remote-tracking branch 'upstream/develop' into pinetimestyle-colorpicker

This commit is contained in:
Kieran Cawthray 2021-08-12 21:26:51 +02:00
commit 52ee25e551
28 changed files with 553 additions and 269 deletions

65
.devcontainer/Dockerfile Normal file
View 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
View 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
View 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!"

View File

@ -0,0 +1,2 @@
#!/bin/bash
cmake --build /workspaces/Pinetime/build --config Release -- -j6 pinetime-app

View 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

View 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"
}

View 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
View File

@ -4,7 +4,7 @@
# CMake
cmake-build-*
cmake-*
cmake-*/
CMakeFiles
**/CMakeCache.txt
cmake_install.cmake

20
.vscode/c_cpp_properties.json vendored Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
{
"recommendations": ["ms-vscode.cpptools","ms-vscode.cmake-tools","marus25.cortex-debug"]
}

46
.vscode/launch.json vendored Normal file
View 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"
]
}
]
}

66
.vscode/settings.json vendored
View File

@ -1,59 +1,9 @@
{
"files.associations": {
"chrono": "cpp",
"list": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"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"
}
}
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"cmake.configureArgs": [
"-DARM_NONE_EABI_TOOLCHAIN_PATH=${env:ARM_NONE_EABI_TOOLCHAIN_PATH}",
"-DNRF5_SDK_PATH=${env:NRF5_SDK_PATH}",
],
"cmake.generator": "Unix Makefiles",
"clang-tidy.buildPath": "build/compile_commands.json"
}

44
.vscode/tasks.json vendored Normal file
View 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": []
}
]
}

View File

@ -93,6 +93,7 @@ As of now, here is the list of achievements of this project:
- [Build the project](doc/buildAndProgram.md)
- [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md)
- [Build the project with Docker](doc/buildWithDocker.md)
- [Build the project with VSCode](doc/buildWithVScode.md)
- [Bootloader, OTA and DFU](./bootloader/README.md)
- [Stub using NRF52-DK](./doc/PinetimeStubWithNrf52DK.md)
- Logging with JLink RTT.

42
doc/buildWithVScode.md Normal file
View 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)

View File

@ -23,7 +23,6 @@ void Battery::Update() {
return;
}
// Non blocking read
samples = 0;
isReading = true;
SaadcInit();
@ -40,9 +39,9 @@ void Battery::SaadcInit() {
nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = 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,
.acq_time = NRF_SAADC_ACQTIME_3US,
.acq_time = NRF_SAADC_ACQTIME_40US,
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
.burst = NRF_SAADC_BURST_ENABLED,
.pin_p = batteryVoltageAdcInput,
@ -60,22 +59,21 @@ void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
// A hardware voltage divider divides the battery voltage by 2
// ADC gain is 1/5
// thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 10
// reference_voltage is 0.6V
// ADC gain is 1/4
// thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 8
// reference_voltage is 600mV
// p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
voltage = p_event->data.done.p_buffer[0] * 6000 / 1024;
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
percentRemaining = std::max(percentRemaining, 0);
percentRemaining = std::min(percentRemaining, 100);
percentRemainingBuffer.Insert(percentRemaining);
voltage = p_event->data.done.p_buffer[0] * (8 * 600) / 1024;
samples++;
if (samples > percentRemainingSamples) {
nrfx_saadc_uninit();
isReading = false;
if (voltage > battery_max) {
percentRemaining = 100;
} else if (voltage < battery_min) {
percentRemaining = 0;
} else {
nrfx_saadc_sample();
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
}
nrfx_saadc_uninit();
isReading = false;
}
}

View File

@ -7,38 +7,6 @@
namespace Pinetime {
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 {
public:
Battery();
@ -47,10 +15,7 @@ namespace Pinetime {
void Update();
uint8_t PercentRemaining() const {
auto avg = percentRemainingBuffer.GetAverage();
avg = std::min(avg, static_cast<uint8_t>(100));
avg = std::max(avg, static_cast<uint8_t>(0));
return avg;
return percentRemaining;
}
uint16_t Voltage() const {
@ -69,14 +34,11 @@ namespace Pinetime {
static Battery* instance;
nrf_saadc_value_t saadc_value;
static constexpr uint8_t percentRemainingSamples = 5;
CircBuffer<percentRemainingSamples> percentRemainingBuffer {};
static constexpr uint32_t chargingPin = 12;
static constexpr uint32_t powerPresentPin = 19;
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
uint16_t voltage = 0;
int percentRemaining = -1;
uint8_t percentRemaining = 0;
bool isCharging = false;
bool isPowerPresent = false;
@ -87,7 +49,6 @@ namespace Pinetime {
static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
bool isReading = false;
uint8_t samples = 0;
};
}
}

View File

@ -55,9 +55,9 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
auto time = date::make_time(currentDateTime - dp);
auto yearMonthDay = date::year_month_day(dp);
year = (int) yearMonthDay.year();
month = static_cast<Months>((unsigned) yearMonthDay.month());
day = (unsigned) yearMonthDay.day();
year = static_cast<int>(yearMonthDay.year());
month = static_cast<Months>(static_cast<unsigned>(yearMonthDay.month()));
day = static_cast<unsigned>(yearMonthDay.day());
dayOfWeek = static_cast<Days>(date::weekday(yearMonthDay).iso_encoding());
hour = time.hours().count();
@ -75,31 +75,31 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
}
const char* DateTime::MonthShortToString() {
return DateTime::MonthsString[(uint8_t) month];
return DateTime::MonthsString[static_cast<uint8_t>(month)];
}
const char* DateTime::MonthShortToStringLow() {
return DateTime::MonthsStringLow[(uint8_t) month];
return DateTime::MonthsStringLow[static_cast<uint8_t>(month)];
}
const char* DateTime::MonthsToStringLow() {
return DateTime::MonthsLow[(uint8_t) month];
return DateTime::MonthsLow[static_cast<uint8_t>(month)];
}
const char* DateTime::DayOfWeekToString() {
return DateTime::DaysString[(uint8_t) dayOfWeek];
return DateTime::DaysString[static_cast<uint8_t>(dayOfWeek)];
}
const char* DateTime::DayOfWeekShortToString() {
return DateTime::DaysStringShort[(uint8_t) dayOfWeek];
return DateTime::DaysStringShort[static_cast<uint8_t>(dayOfWeek)];
}
const char* DateTime::DayOfWeekToStringLow() {
return DateTime::DaysStringLow[(uint8_t) dayOfWeek];
return DateTime::DaysStringLow[static_cast<uint8_t>(dayOfWeek)];
}
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) {

View File

@ -5,13 +5,13 @@
using namespace Pinetime::Applications::Screens;
const char* BatteryIcon::GetBatteryIcon(uint8_t batteryPercent) {
if (batteryPercent > 90)
if (batteryPercent > 87)
return Symbols::batteryFull;
if (batteryPercent > 75)
if (batteryPercent > 62)
return Symbols::batteryThreeQuarter;
if (batteryPercent > 50)
if (batteryPercent > 37)
return Symbols::batteryHalf;
if (batteryPercent > 25)
if (batteryPercent > 12)
return Symbols::batteryOneQuarter;
return Symbols::batteryEmpty;
}

View File

@ -2,11 +2,6 @@
#include <date/date.h>
#include <lvgl/lvgl.h>
#include <cstdio>
#include "BatteryIcon.h"
#include "BleIcon.h"
#include "NotificationIcon.h"
#include "Symbols.h"
#include "components/battery/BatteryController.h"
#include "components/motion/MotionController.h"
#include "components/ble/BleController.h"
@ -88,17 +83,4 @@ std::unique_ptr<Screen> Clock::PineTimeStyleScreen() {
notificatioManager,
settingsController,
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);
}
*/
}

View File

@ -9,9 +9,6 @@
#include "components/datetime/DateTimeController.h"
namespace Pinetime {
namespace Drivers {
class BMA421;
}
namespace Controllers {
class Settings;
class Battery;
@ -51,10 +48,6 @@ namespace Pinetime {
std::unique_ptr<Screen> WatchFaceDigitalScreen();
std::unique_ptr<Screen> WatchFaceAnalogScreen();
std::unique_ptr<Screen> PineTimeStyleScreen();
// Examples for more watch faces
// std::unique_ptr<Screen> WatchFaceMinimalScreen();
// std::unique_ptr<Screen> WatchFaceCustomScreen();
};
}
}

View File

@ -218,26 +218,17 @@ bool PineTimeStyle::Refresh() {
bleState = bleController.IsConnected();
if (bleState.IsUpdated()) {
if (bleState.Get() == true) {
lv_label_set_text(bleIcon, BleIcon::GetIcon(true));
lv_obj_realign(bleIcon);
} else {
lv_label_set_text(bleIcon, BleIcon::GetIcon(false));
}
lv_label_set_text(bleIcon, BleIcon::GetIcon(bleState.Get()));
lv_obj_realign(bleIcon);
}
notificationState = notificatioManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
if (notificationState.Get() == true) {
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
lv_obj_realign(notificationIcon);
} else {
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
}
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
lv_obj_realign(notificationIcon);
}
currentDateTime = dateTimeController.CurrentDateTime();
if (currentDateTime.IsUpdated()) {
auto newDateTime = currentDateTime.Get();
@ -245,9 +236,9 @@ bool PineTimeStyle::Refresh() {
auto time = date::make_time(newDateTime - dp);
auto yearMonthDay = date::year_month_day(dp);
auto year = (int) yearMonthDay.year();
auto month = static_cast<Pinetime::Controllers::DateTime::Months>((unsigned) yearMonthDay.month());
auto day = (unsigned) yearMonthDay.day();
auto year = static_cast<int>(yearMonthDay.year());
auto month = static_cast<Pinetime::Controllers::DateTime::Months>(static_cast<unsigned>(yearMonthDay.month()));
auto day = static_cast<unsigned>(yearMonthDay.day());
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
int hour = time.hours().count();
@ -258,9 +249,8 @@ bool PineTimeStyle::Refresh() {
char hoursChar[3];
char ampmChar[5];
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
sprintf(hoursChar, "%02d", hour);
sprintf(hoursChar, "%02d", hour);
} else {
if (hour == 0 && hour != 12) {
hour = 12;
@ -277,41 +267,26 @@ bool PineTimeStyle::Refresh() {
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]) {
displayedChar[0] = hoursChar[0];
displayedChar[1] = hoursChar[1];
displayedChar[2] = minutesChar[0];
displayedChar[3] = minutesChar[1];
char hourStr[3];
char minStr[3];
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
lv_label_set_text(timeAMPM, ampmChar);
}
// Display the time as 2 pairs of digits
sprintf(hourStr, "%c%c", hoursChar[0], hoursChar[1]);
lv_label_set_text(timeDD1, hourStr);
sprintf(minStr, "%c%c", minutesChar[0], minutesChar[1]);
lv_label_set_text(timeDD2, minStr);
lv_label_set_text_fmt(timeDD1, "%s", hoursChar);
lv_label_set_text_fmt(timeDD2, "%s", minutesChar);
}
if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
char dayOfWeekStr[4];
char dayStr[3];
char monthStr[4];
sprintf(dayOfWeekStr, "%s", dateTimeController.DayOfWeekShortToString());
sprintf(dayStr, "%d", day);
sprintf(monthStr, "%s", dateTimeController.MonthShortToString());
lv_label_set_text(dateDayOfWeek, dayOfWeekStr);
lv_label_set_text(dateDay, dayStr);
lv_label_set_text_fmt(dateDayOfWeek, "%s", dateTimeController.DayOfWeekShortToString());
lv_label_set_text_fmt(dateDay, "%d", day);
lv_obj_realign(dateDay);
lv_label_set_text(dateMonth, monthStr);
lv_label_set_text_fmt(dateMonth, "%s", dateTimeController.MonthShortToString());
currentYear = year;
currentMonth = month;

View File

@ -32,8 +32,6 @@ namespace Pinetime {
bool Refresh() override;
void OnObjectEvent(lv_obj_t* pObj, lv_event_t i);
private:
char displayedChar[5];
@ -67,9 +65,6 @@ namespace Pinetime {
lv_obj_t* calendarBar2;
lv_obj_t* calendarCrossBar1;
lv_obj_t* calendarCrossBar2;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* heartbeatBpm;
lv_obj_t* notificationIcon;
lv_obj_t* stepGauge;
lv_color_t needle_colors[1];

View File

@ -10,38 +10,37 @@ LV_IMG_DECLARE(bg_clock);
using namespace Pinetime::Applications::Screens;
namespace {
constexpr auto HOUR_LENGTH = 70;
constexpr auto MINUTE_LENGTH = 90;
constexpr auto SECOND_LENGTH = 110;
constexpr int16_t HourLength = 70;
constexpr int16_t MinuteLength = 90;
constexpr int16_t SecondLength = 110;
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
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);
}
int16_t sine(int16_t angle) {
int16_t Sine(int16_t 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);
}
int16_t coordinate_y_relocate(int16_t y) {
int16_t CoordinateYRelocate(int16_t y) {
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{
.x = coordinate_x_relocate(radius * static_cast<int32_t>(sine(angle)) / LV_TRIG_SCALE),
.y = coordinate_y_relocate(radius * static_cast<int32_t>(cosine(angle)) / LV_TRIG_SCALE)
.x = CoordinateXRelocate(radius * static_cast<int32_t>(Sine(angle)) / LV_TRIG_SCALE),
.y = CoordinateYRelocate(radius * static_cast<int32_t>(Cosine(angle)) / LV_TRIG_SCALE)
};
}
} // namespace
}
WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
Controllers::DateTime& dateTimeController,
@ -123,7 +122,6 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
}
WatchFaceAnalog::~WatchFaceAnalog() {
lv_style_reset(&hour_line_style);
lv_style_reset(&hour_line_style_trace);
lv_style_reset(&minute_line_style);
@ -134,18 +132,17 @@ WatchFaceAnalog::~WatchFaceAnalog() {
}
void WatchFaceAnalog::UpdateClock() {
hour = dateTimeController.Hours();
minute = dateTimeController.Minutes();
second = dateTimeController.Seconds();
if (sMinute != minute) {
auto const angle = minute * 6;
minute_point[0] = coordinate_relocate(30, angle);
minute_point[1] = coordinate_relocate(MINUTE_LENGTH, angle);
minute_point[0] = CoordinateRelocate(30, angle);
minute_point[1] = CoordinateRelocate(MinuteLength, angle);
minute_point_trace[0] = coordinate_relocate(5, angle);
minute_point_trace[1] = coordinate_relocate(31, angle);
minute_point_trace[0] = CoordinateRelocate(5, angle);
minute_point_trace[1] = CoordinateRelocate(31, angle);
lv_line_set_points(minute_body, minute_point, 2);
lv_line_set_points(minute_body_trace, minute_point_trace, 2);
@ -156,11 +153,11 @@ void WatchFaceAnalog::UpdateClock() {
sMinute = minute;
auto const angle = (hour * 30 + minute / 2);
hour_point[0] = coordinate_relocate(30, angle);
hour_point[1] = coordinate_relocate(HOUR_LENGTH, angle);
hour_point[0] = CoordinateRelocate(30, angle);
hour_point[1] = CoordinateRelocate(HourLength, angle);
hour_point_trace[0] = coordinate_relocate(5, angle);
hour_point_trace[1] = coordinate_relocate(31, angle);
hour_point_trace[0] = CoordinateRelocate(5, angle);
hour_point_trace[1] = CoordinateRelocate(31, angle);
lv_line_set_points(hour_body, hour_point, 2);
lv_line_set_points(hour_body_trace, hour_point_trace, 2);
@ -170,8 +167,8 @@ void WatchFaceAnalog::UpdateClock() {
sSecond = second;
auto const angle = second * 6;
second_point[0] = coordinate_relocate(-20, angle);
second_point[1] = coordinate_relocate(SECOND_LENGTH, angle);
second_point[0] = CoordinateRelocate(-20, angle);
second_point[1] = CoordinateRelocate(SecondLength, angle);
lv_line_set_points(second_body, second_point, 2);
}
}
@ -186,16 +183,12 @@ bool WatchFaceAnalog::Refresh() {
notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
if (notificationState.Get() == true)
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
else
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
currentDateTime = dateTimeController.CurrentDateTime();
if (currentDateTime.IsUpdated()) {
month = dateTimeController.Month();
day = dateTimeController.Day();
dayOfWeek = dateTimeController.DayOfWeek();
@ -203,7 +196,6 @@ bool WatchFaceAnalog::Refresh() {
UpdateClock();
if ((month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), day);
currentMonth = month;

View File

@ -58,14 +58,12 @@ namespace Pinetime {
lv_obj_t* minute_body_trace;
lv_obj_t* second_body;
// ##
lv_point_t hour_point[2];
lv_point_t hour_point_trace[2];
lv_point_t minute_point[2];
lv_point_t minute_point_trace[2];
lv_point_t second_point[2];
// ##
lv_style_t hour_line_style;
lv_style_t hour_line_style_trace;
lv_style_t minute_line_style;

View File

@ -12,9 +12,6 @@
#include "components/ble/NotificationManager.h"
#include "components/heartrate/HeartRateController.h"
#include "components/motion/MotionController.h"
#include "components/settings/Settings.h"
#include "../DisplayApp.h"
using namespace Pinetime::Applications::Screens;
WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
@ -36,12 +33,6 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
motionController {motionController} {
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);
lv_label_set_text(batteryIcon, Symbols::batteryFull);
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_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_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0);
@ -111,17 +102,13 @@ bool WatchFaceDigital::Refresh() {
if (batteryPercentRemaining.IsUpdated()) {
auto batteryPercent = batteryPercentRemaining.Get();
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));
}
bleState = bleController.IsConnected();
if (bleState.IsUpdated()) {
if (bleState.Get() == true) {
lv_label_set_text(bleIcon, BleIcon::GetIcon(true));
} else {
lv_label_set_text(bleIcon, BleIcon::GetIcon(false));
}
lv_label_set_text(bleIcon, BleIcon::GetIcon(bleState.Get()));
}
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);
@ -129,10 +116,7 @@ bool WatchFaceDigital::Refresh() {
notificationState = notificatioManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
if (notificationState.Get() == true)
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
else
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
currentDateTime = dateTimeController.CurrentDateTime();
@ -144,9 +128,9 @@ bool WatchFaceDigital::Refresh() {
auto time = date::make_time(newDateTime - dp);
auto yearMonthDay = date::year_month_day(dp);
auto year = (int) yearMonthDay.year();
auto month = static_cast<Pinetime::Controllers::DateTime::Months>((unsigned) yearMonthDay.month());
auto day = (unsigned) yearMonthDay.day();
auto year = static_cast<int>(yearMonthDay.year());
auto month = static_cast<Pinetime::Controllers::DateTime::Months>(static_cast<unsigned>(yearMonthDay.month()));
auto day = static_cast<unsigned>(yearMonthDay.day());
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
int hour = time.hours().count();
@ -175,15 +159,13 @@ bool WatchFaceDigital::Refresh() {
sprintf(hoursChar, "%02d", hour);
}
if (hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] ||
minutesChar[1] != displayedChar[3]) {
if ((hoursChar[0] != displayedChar[0]) or (hoursChar[1] != displayedChar[1]) or (minutesChar[0] != displayedChar[2]) or
(minutesChar[1] != displayedChar[3])) {
displayedChar[0] = hoursChar[0];
displayedChar[1] = hoursChar[1];
displayedChar[2] = minutesChar[0];
displayedChar[3] = minutesChar[1];
char timeStr[6];
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
lv_label_set_text(label_time_ampm, ampmChar);
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(label_time, timeStr);
lv_label_set_text_fmt(label_time, "%s:%s", hoursChar, minutesChar);
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
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)) {
char dateStr[22];
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 {
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);
currentYear = year;

View File

@ -35,10 +35,8 @@ namespace Pinetime {
bool Refresh() override;
void OnObjectEvent(lv_obj_t* pObj, lv_event_t i);
private:
char displayedChar[5];
char displayedChar[5] {};
uint16_t currentYear = 1970;
Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
@ -63,7 +61,6 @@ namespace Pinetime {
lv_obj_t* batteryPlug;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* heartbeatBpm;
lv_obj_t* stepIcon;
lv_obj_t* stepValue;
lv_obj_t* notificationIcon;