Merge branch 'develop' into feat-doxygen
132
.clang-format
Normal file
@ -0,0 +1,132 @@
|
||||
---
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: true
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: true
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 140
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 2
|
||||
ContinuationIndentWidth: 2
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: true
|
||||
FixNamespaceComments: false
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentCaseLabels: true
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: BeforeHash
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatementsExceptForEachMacros
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
# Needs new Clang: SpaceAroundPointerQualifiers: After
|
||||
Standard: Latest
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
...
|
34
.clang-tidy
Normal file
@ -0,0 +1,34 @@
|
||||
Checks: '*,
|
||||
-altera-unroll-loops,
|
||||
-llvmlibc-callee-namespace,
|
||||
-llvm-header-guard,
|
||||
-llvm-namespace-comment,
|
||||
-google-build-using-namespace,
|
||||
-google-runtime-int,
|
||||
-google-readability-namespace-comments,
|
||||
-fuchsia-statically-constructed-objects,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-cstyle-cast,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-readability-magic-numbers,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-avoid-c-arrays,
|
||||
-hicpp-avoid-c-arrays,
|
||||
-hicpp-uppercase-literal-suffix,
|
||||
-hicpp-vararg,
|
||||
-hicpp-no-assembler,
|
||||
-hicpp-no-array-decay,
|
||||
-hicpp-signed-bitwise,
|
||||
-hicpp-special-member-functions,
|
||||
-cert-err58-cpp,
|
||||
-cert-err60-cpp'
|
||||
CheckOptions:
|
||||
- key: readability-function-cognitive-complexity.Threshold
|
||||
value: 100
|
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
@ -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
@ -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
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
cmake --build /workspaces/Pinetime/build --config Release -- -j6 pinetime-app
|
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
@ -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
@ -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}
|
15
.gitattributes
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.c text
|
||||
*.cpp text
|
||||
*.h text
|
||||
*.hpp text
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.bin binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
5
.gitconfig
Normal file
@ -0,0 +1,5 @@
|
||||
[core]
|
||||
whitespace = blank-at-eol,blank-at-eof,space-before-tab
|
||||
autocrlf = input
|
||||
[apply]
|
||||
whitespace = fix
|
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
liberapay: JF002
|
36
.github/workflows/main.yml
vendored
@ -1,7 +1,7 @@
|
||||
# GitHub Actions Workflow to build FreeRTOS Firmware for PineTime Smart Watch
|
||||
# See https://lupyuen.github.io/pinetime-rust-mynewt/articles/cloud
|
||||
# Based on https://github.com/JF002/Pinetime/blob/master/doc/buildAndProgram.md
|
||||
# and https://github.com/JF002/Pinetime/blob/master/bootloader/README.md
|
||||
# Based on https://github.com/JF002/InfiniTime/blob/master/doc/buildAndProgram.md
|
||||
# and https://github.com/JF002/InfiniTime/blob/master/bootloader/README.md
|
||||
|
||||
# Name of this Workflow
|
||||
name: Build PineTime Firmware
|
||||
@ -9,13 +9,13 @@ name: Build PineTime Firmware
|
||||
# When to run this Workflow...
|
||||
on:
|
||||
|
||||
# Run this Workflow when files are updated (Pushed) in the "master" Branch
|
||||
# Run this Workflow when files are updated (Pushed) in the "master" and "develop" Branch
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, develop ]
|
||||
|
||||
# Also run this Workflow when a Pull Request is created or updated in the "master" Branch
|
||||
# Also run this Workflow when a Pull Request is created or updated in the "master" and "develop" Branch
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [ master, develop ]
|
||||
|
||||
# Steps to run for the Workflow
|
||||
jobs:
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Install Embedded Arm Toolchain arm-none-eabi-gcc
|
||||
if: steps.cache-toolchain.outputs.cache-hit != 'true' # Install toolchain if not found in cache
|
||||
uses: fiam/arm-none-eabi-gcc@v1.0.2
|
||||
uses: fiam/arm-none-eabi-gcc@v1.0.4
|
||||
with:
|
||||
# GNU Embedded Toolchain for Arm release name, in the V-YYYY-qZ format (e.g. "9-2019-q4")
|
||||
release: 9-2020-q2
|
||||
@ -83,10 +83,11 @@ jobs:
|
||||
if: steps.cache-mcuboot.outputs.cache-hit != 'true' # Install MCUBoot if not found in cache
|
||||
run: |
|
||||
cd ${{ runner.temp }}
|
||||
git clone --branch v1.5.0 https://github.com/JuulLabs-OSS/mcuboot
|
||||
git clone --branch v1.7.2 https://github.com/mcu-tools/mcuboot
|
||||
|
||||
- name: Install imgtool dependencies
|
||||
run: pip3 install --user -r ${{ runner.temp }}/mcuboot/scripts/requirements.txt
|
||||
run: |
|
||||
pip3 install --user -r ${{ runner.temp }}/mcuboot/scripts/requirements.txt
|
||||
|
||||
- name: Install adafruit-nrfutil
|
||||
run: |
|
||||
@ -99,6 +100,8 @@ jobs:
|
||||
|
||||
- name: Checkout source files
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Show files
|
||||
run: set ; pwd ; ls -l
|
||||
@ -110,7 +113,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=${{ runner.temp }}/arm-none-eabi -DNRF5_SDK_PATH=${{ runner.temp }}/nrf5_sdk -DUSE_OPENOCD=1 ../
|
||||
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=${{ runner.temp }}/arm-none-eabi -DNRF5_SDK_PATH=${{ runner.temp }}/nrf5_sdk -DUSE_OPENOCD=1 -DBUILD_DFU=1 ../
|
||||
|
||||
#########################################################################################
|
||||
# Make and Upload DFU Package
|
||||
@ -125,19 +128,10 @@ jobs:
|
||||
cd build
|
||||
make pinetime-mcuboot-app
|
||||
|
||||
- name: Create firmware image
|
||||
- name: Unzip DFU package
|
||||
run: |
|
||||
# The generated firmware binary looks like "pinetime-mcuboot-app-0.8.2.bin"
|
||||
ls -l build/src/pinetime-mcuboot-app*.bin
|
||||
${{ runner.temp }}/mcuboot/scripts/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header build/src/pinetime-mcuboot-app*.bin build/src/pinetime-mcuboot-app-img.bin
|
||||
${{ runner.temp }}/mcuboot/scripts/imgtool.py verify build/src/pinetime-mcuboot-app-img.bin
|
||||
|
||||
- name: Create DFU package
|
||||
run: |
|
||||
~/.local/bin/adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/src/pinetime-mcuboot-app-img.bin build/src/pinetime-mcuboot-app-dfu.zip
|
||||
unzip -v build/src/pinetime-mcuboot-app-dfu.zip
|
||||
# Unzip the package because Upload Artifact will zip up the files
|
||||
unzip build/src/pinetime-mcuboot-app-dfu.zip -d build/src/pinetime-mcuboot-app-dfu
|
||||
unzip build/src/pinetime-mcuboot-app-dfu*.zip -d build/src/pinetime-mcuboot-app-dfu
|
||||
|
||||
- name: Upload DFU package
|
||||
uses: actions/upload-artifact@v2
|
||||
|
14
.gitignore
vendored
@ -1,10 +1,16 @@
|
||||
.idea/
|
||||
# Python virtual environment for DFU images
|
||||
.venv/
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
CMakeFiles/
|
||||
cmake-build-*
|
||||
cmake-*/
|
||||
CMakeFiles
|
||||
**/CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
Makefile
|
||||
build
|
||||
tools
|
||||
|
||||
# Resulting binary files
|
||||
*.a
|
||||
@ -37,3 +43,7 @@ Testing/Temporary/
|
||||
# Doxygen
|
||||
src/html/
|
||||
src/latex/
|
||||
|
||||
#VSCODE
|
||||
.vscode/.cortex-debug.registers.state.json
|
||||
.vscode/.cortex-debug.peripherals.state.json
|
||||
|
6
.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
[submodule "src/libs/lvgl"]
|
||||
path = src/libs/lvgl
|
||||
url = https://github.com/joaquimorg/lvgl.git
|
||||
[submodule "src/libs/littlefs"]
|
||||
path = src/libs/littlefs
|
||||
url = https://github.com/littlefs-project/littlefs.git
|
18
.gitpod.yml
Normal file
@ -0,0 +1,18 @@
|
||||
image:
|
||||
file: docker/.gitpod.Dockerfile
|
||||
github:
|
||||
prebuilds:
|
||||
# enable for the master/default branch (defaults to true)
|
||||
master: true
|
||||
# enable for all branches in this repo (defaults to false)
|
||||
branches: false
|
||||
# enable for pull requests coming from this repo (defaults to true)
|
||||
pullRequests: false
|
||||
# enable for pull requests coming from forks (defaults to false)
|
||||
pullRequestsFromForks: false
|
||||
# add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
|
||||
addComment: true
|
||||
# add a "Review in Gitpod" button to pull requests (defaults to false)
|
||||
addBadge: false
|
||||
# add a label once the prebuild is ready to pull requests (defaults to false)
|
||||
addLabel: false #prebuilt-in-gitpod
|
23
.idea/codeStyles/Project.xml
generated
@ -12,6 +12,29 @@
|
||||
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
|
||||
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
|
||||
</Objective-C>
|
||||
<Objective-C-extensions>
|
||||
<rules>
|
||||
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
|
||||
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
</rules>
|
||||
</Objective-C-extensions>
|
||||
<clangFormatSettings>
|
||||
<option name="ENABLED" value="true" />
|
||||
</clangFormatSettings>
|
||||
<codeStyleSettings language="ObjectiveC">
|
||||
<option name="RIGHT_MARGIN" value="140" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
|
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
@ -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
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["ms-vscode.cpptools","ms-vscode.cmake-tools","marus25.cortex-debug"]
|
||||
}
|
64
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cwd": "${workspaceRoot}",
|
||||
// TODO: find better way to get latest build filename
|
||||
"executable": "./build/src/pinetime-app-1.3.0.out",
|
||||
"name": "Debug OpenOCD ST-LINK pinetime-app-1.3.0.out",
|
||||
"request": "launch",
|
||||
"type": "cortex-debug",
|
||||
"showDevDebugOutput": false,
|
||||
"servertype": "openocd",
|
||||
"runToMain": true,
|
||||
// Only use armToolchainPath if your arm-none-eabi-gdb is not in your path (some GCC packages does not contain arm-none-eabi-gdb)
|
||||
"armToolchainPath": "${workspaceRoot}/../gcc-arm-none-eabi-9-2020-q2-update/bin",
|
||||
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||
"configFiles": [
|
||||
"interface/stlink.cfg",
|
||||
"target/nrf52.cfg"
|
||||
],
|
||||
}
|
||||
|
||||
]
|
||||
}
|
59
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"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",
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "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",
|
||||
"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",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
}
|
||||
}
|
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": []
|
||||
}
|
||||
]
|
||||
}
|
@ -1,5 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(pinetime VERSION 0.9.0 LANGUAGES C CXX ASM)
|
||||
project(pinetime VERSION 1.6.0 LANGUAGES C CXX ASM)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
# set(CMAKE_GENERATOR "Unix Makefiles")
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(NRF_TARGET "nrf52")
|
||||
|
||||
@ -11,10 +21,6 @@ if (NOT NRF5_SDK_PATH)
|
||||
message(FATAL_ERROR "The path to the NRF52 SDK must be specified on the command line (add -DNRF5_SDK_PATH=<path>")
|
||||
endif ()
|
||||
|
||||
if(NOT USE_JLINK AND NOT USE_GDB_CLIENT AND NOT USE_OPENOCD)
|
||||
set(USE_JLINK true)
|
||||
endif()
|
||||
|
||||
if(USE_JLINK)
|
||||
if (NOT NRFJPROG)
|
||||
message(FATAL_ERROR "the path to the tool nrfjprog must be specified on the command line (add -DNRFJPROG=<path>")
|
||||
@ -41,11 +47,36 @@ if(DEFINED USE_DEBUG_PINS AND USE_DEBUG_PINS)
|
||||
add_definitions(-DUSE_DEBUG_PINS)
|
||||
endif()
|
||||
|
||||
if(BUILD_DFU)
|
||||
set(BUILD_DFU true)
|
||||
endif()
|
||||
|
||||
option(WATCH_COLMI_P8 "Build for the Colmi P8" OFF)
|
||||
set(TARGET_DEVICE "PineTime")
|
||||
|
||||
if(WATCH_COLMI_P8)
|
||||
set(TARGET_DEVICE "Colmi P8")
|
||||
add_definitions(-DWATCH_P8)
|
||||
endif()
|
||||
|
||||
set(PROJECT_GIT_COMMIT_HASH "")
|
||||
|
||||
execute_process(COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE PROJECT_GIT_COMMIT_HASH
|
||||
RESULT_VARIABLE PROJECT_GIT_COMMIT_HASH_SUCCESS)
|
||||
|
||||
string(STRIP ${PROJECT_GIT_COMMIT_HASH} PROJECT_GIT_COMMIT_HASH)
|
||||
|
||||
message("PROJECT_GIT_COMMIT_HASH_SUCCESS? " ${PROJECT_GIT_COMMIT_HASH_SUCCESS})
|
||||
|
||||
message("BUILD CONFIGURATION")
|
||||
message("-------------------")
|
||||
message(" * Version : " ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
|
||||
message(" * Toolchain : " ${ARM_NONE_EABI_TOOLCHAIN_PATH})
|
||||
message(" * GitRef(S) : " ${PROJECT_GIT_COMMIT_HASH})
|
||||
message(" * NRF52 SDK : " ${NRF5_SDK_PATH})
|
||||
message(" * Target device : " ${TARGET_DEVICE})
|
||||
set(PROGRAMMER "???")
|
||||
if(USE_JLINK)
|
||||
message(" * Programmer/debugger : JLINK")
|
||||
@ -62,10 +93,15 @@ if(USE_DEBUG_PINS)
|
||||
else()
|
||||
message(" * Debug pins : Disabled")
|
||||
endif()
|
||||
if(BUILD_DFU)
|
||||
message(" * Build DFU (using adafruit-nrfutil) : Enabled")
|
||||
else()
|
||||
message(" * Build DFU (using adafruit-nrfutil) : Disabled")
|
||||
endif()
|
||||
|
||||
set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docker/post_build.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/docker/post_build.sh)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docker/post_build.sh.in ${CMAKE_CURRENT_BINARY_DIR}/post_build.sh)
|
||||
|
||||
|
||||
add_subdirectory(src)
|
||||
|
1
CONTRIBUTING.md
Symbolic link
@ -0,0 +1 @@
|
||||
doc/contribute.md
|
110
README.md
@ -1,30 +1,25 @@
|
||||
|
||||
# PineTime
|
||||
|
||||

|
||||
|
||||
> The PineTime is a free and open source smartwatch capable of running custom-built open operating systems. Some of the notable features include a heart rate monitor, a week-long battery as well as a capacitive touch IPS display that is legible in direct sunlight. It is a fully community driven side-project, which means that it will ultimately be up to the developers and end-users to determine when they deem the PineTime ready to ship.
|
||||
|
||||
> We envision the PineTime as a companion for not only your PinePhone but also for your favorite devices — any phone, tablet, or even PC.
|
||||
|
||||
*https://www.pine64.org/pinetime/*
|
||||
|
||||
The **Pinetime** smartwatch is built around the NRF52832 MCU (512KB Flash, 64KB RAM), a 240*240 LCD display driven by the ST7789 controller, an accelerometer, a heart rate sensor, and a vibration motor.
|
||||
|
||||
# InfiniTime
|
||||
|
||||
[](https://github.com/JF002/InfiniTime/actions)
|
||||
|
||||

|
||||
|
||||
The goal of this project is to design an open-source firmware for the Pinetime smartwatch :
|
||||
The goal of this project is to design an open-source firmware for the [Pinetime smartwatch](https://www.pine64.org/pinetime/) :
|
||||
|
||||
- Code written in **modern C++**;
|
||||
- Build system based on **CMake**;
|
||||
- Based on **[FreeRTOS 10.0.0](https://freertos.org)** real-time OS.
|
||||
- Using **[LittleVGL/LVGL 6.1.2](https://lvgl.io/)** as UI library...
|
||||
- Using **[LittleVGL/LVGL 7](https://lvgl.io/)** as UI library...
|
||||
- ... and **[NimBLE 1.3.0](https://github.com/apache/mynewt-nimble)** as BLE stack.
|
||||
|
||||
## New to InfiniTime?
|
||||
|
||||
- [Getting started with InfiniTime 1.0 (quick user guide, update bootloader and InfiniTime,...)](doc/gettingStarted/gettingStarted-1.0.md)
|
||||
- [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/ota-gadgetbridge-nrfconnect.md)
|
||||
|
||||
## Overview
|
||||
|
||||

|
||||

|
||||
|
||||
As of now, here is the list of achievements of this project:
|
||||
|
||||
@ -33,53 +28,86 @@ As of now, here is the list of achievements of this project:
|
||||
- Rich user interface via display, touchscreen and pushbutton
|
||||
- Time synchronization via BLE
|
||||
- Notification via BLE
|
||||
- Multiple 'apps' :
|
||||
* Clock (displays the date, time, battery level, BLE connection status, heart rate and step count)
|
||||
* Heart rate
|
||||
* Motion
|
||||
* System info (displays various info : BLE MAC, build date/time, uptime, version, ...)
|
||||
* Brightness (allows the user to configure the brightness of the display)
|
||||
- Supported by 2 companion apps (development is in progress):
|
||||
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android)
|
||||
* [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS)
|
||||
- **[Experimental]** OTA (Over-the-air) update via BLE
|
||||
- **[Experimental]** Bootloader based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/)
|
||||
|
||||
- Heart rate measurements
|
||||
- Step counting
|
||||
- Wake-up on wrist rotation
|
||||
- Quick actions
|
||||
* Disable vibration on notification
|
||||
* Brightness settings
|
||||
* Flashlight
|
||||
* Settings
|
||||
- 3 watch faces:
|
||||
* Digital
|
||||
* Analog
|
||||
* [PineTimeStyle](https://wiki.pine64.org/wiki/PineTimeStyle)
|
||||
- Multiple 'apps' :
|
||||
* Music (control the playback of music on your phone)
|
||||
* Heart rate (measure your heart rate)
|
||||
* Navigation (displays navigation instructions coming from the companion app)
|
||||
* Notification (displays the last notification received)
|
||||
* Paddle (single player pong-like game)
|
||||
* Twos (2048 clone game)
|
||||
* Stopwatch
|
||||
* Steps (displays the number of steps taken)
|
||||
* Timer (set a countdown timer that will notify you when it expires)
|
||||
* Metronome (vibrates to a given bpm with a customizable beats per bar)
|
||||
- User settings:
|
||||
* Display timeout
|
||||
* Wake-up condition
|
||||
* Time format (12/24h)
|
||||
* Default watch face
|
||||
* Daily step goal
|
||||
* Battery status
|
||||
* Firmware validation
|
||||
* System information
|
||||
- Supported by 3 companion apps (development is in progress):
|
||||
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android via F-Droid)
|
||||
* [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS and Linux)
|
||||
* [Siglo](https://github.com/alexr4535/siglo) (on Linux)
|
||||
* **[Experimental]** [WebBLEWatch](https://hubmartin.github.io/WebBLEWatch/) Synchronize time directly from your web browser. [video](https://youtu.be/IakiuhVDdrY)
|
||||
* **[Experimental]** [Infini-iOS](https://github.com/xan-m/Infini-iOS) (on iOS)
|
||||
- OTA (Over-the-air) update via BLE
|
||||
- [Bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader) based on [MCUBoot](https://www.mcuboot.com)
|
||||
|
||||
## Documentation
|
||||
|
||||
### Getting started
|
||||
- [Flash, upgrade (OTA), time synchronization,...](doc/gettingStarted/gettingStarted.md)
|
||||
|
||||
### Develop
|
||||
- [Generate the fonts and symbols](src/displayapp/fonts/Readme.md)
|
||||
|
||||
- [Generate the fonts and symbols](src/displayapp/fonts/README.md)
|
||||
- [Creating a stopwatch in Pinetime(article)](https://pankajraghav.com/2021/04/03/PINETIME-STOPCLOCK.html)
|
||||
|
||||
### Build, flash and debug
|
||||
|
||||
- [Project branches](doc/branches.md)
|
||||
- [Versioning](doc/versioning.md)
|
||||
- [Files included in the release notes](doc/filesInReleaseNotes.md)
|
||||
- [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.
|
||||
- Using files from the releases
|
||||
|
||||
### Contribute
|
||||
|
||||
- [How to contribute ?](doc/contribute.md)
|
||||
|
||||
### API
|
||||
|
||||
- [BLE implementation and API](./doc/ble.md)
|
||||
|
||||
|
||||
### Architecture and technical topics
|
||||
|
||||
- [Memory analysis](./doc/MemoryAnalysis.md)
|
||||
|
||||
|
||||
### Using the firmware
|
||||
|
||||
- [Integration with Gadgetbridge](doc/companionapps/Gadgetbridge.md)
|
||||
- [Integration with AmazFish](doc/companionapps/Amazfish.md)
|
||||
- [Firmware update, OTA](doc/companionapps/NrfconnectOTA.md)
|
||||
|
||||
|
||||
|
||||
## TODO - contribute
|
||||
|
||||
This project is far from being finished, and there are still a lot of things to do for this project to become a firmware usable by the general public.
|
||||
@ -93,12 +121,13 @@ Here a quick list out of my head of things to do for this project:
|
||||
- Measure power consumption and improve battery life
|
||||
- Improve documentation, take better pictures and video than mine
|
||||
- Improve the UI
|
||||
- Create companion app for multiple OSes (Linux, Android, iOS) and platforms (desktop, ARM, mobile). Do not forget the other devices from Pine64 like [the Pinephone](https://www.pine64.org/pinephone/) and the [Pinebook Pro](https://www.pine64.org/pinebook-pro/).
|
||||
- Create companion app for multiple OSes (Linux, Android, iOS) and platforms (desktop, ARM, mobile). Do not forget the other devices from Pine64 like [the Pinephone](https://www.pine64.org/pinephone/) and the [Pinebook Pro](https://www.pine64.org/pinebook-pro/).
|
||||
- Design a simple CI (preferably self-hosted and easy to reproduce).
|
||||
|
||||
|
||||
Do not hesitate to clone/fork the code, hack it and create pull-requests. I'll do my best to review and merge them :)
|
||||
|
||||
## Licenses
|
||||
|
||||
This project is released under the GNU General Public License version 3 or, at your option, any later version.
|
||||
|
||||
It integrates the following projects:
|
||||
@ -106,8 +135,9 @@ It integrates the following projects:
|
||||
- UI : **[LittleVGL/LVGL](https://lvgl.io/)** under the MIT license
|
||||
- BLE stack : **[NimBLE](https://github.com/apache/mynewt-nimble)** under the Apache 2.0 license
|
||||
- Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license
|
||||
|
||||
## Credits
|
||||
|
||||
## Credits
|
||||
|
||||
I’m not working alone on this project. First, many people create PR for this projects. Then, there is the whole #pinetime community : a lot of people all around the world who are hacking, searching, experimenting and programming the Pinetime. We exchange our ideas, experiments and code in the chat rooms and forums.
|
||||
|
||||
Here are some people I would like to highlight:
|
||||
|
@ -13,7 +13,7 @@ class Unpacker(object):
|
||||
#
|
||||
#--------------------------------------------------------------------------
|
||||
def entropy(self, length):
|
||||
return ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for i in range (length))
|
||||
return ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for i in range (length))
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
|
@ -36,9 +36,6 @@ macro(nRF5x_setup)
|
||||
set(CMAKE_OSX_SYSROOT "/")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
|
||||
|
||||
# language standard/version settings
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
# CPU specyfic settings
|
||||
if (NRF_TARGET MATCHES "nrf51")
|
||||
@ -77,21 +74,6 @@ macro(nRF5x_setup)
|
||||
)
|
||||
endif ()
|
||||
|
||||
set(COMMON_FLAGS "-MP -MD -mthumb -mabi=aapcs -Wall -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums ${CPU_FLAGS} -Wreturn-type -Werror=return-type")
|
||||
|
||||
# compiler/assambler/linker flags
|
||||
set(CMAKE_C_FLAGS "${COMMON_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g3")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_CXX_FLAGS "${COMMON_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g3")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_ASM_FLAGS "-MP -MD -std=c99 -x assembler-with-cpp")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} ${CPU_FLAGS} -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm")
|
||||
# note: we must override the default cmake linker flags so that CMAKE_C_FLAGS are not added implicitly
|
||||
set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_COMPILER} <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
|
||||
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_C_COMPILER} <LINK_FLAGS> <OBJECTS> -lstdc++ -o <TARGET> <LINK_LIBRARIES>")
|
||||
|
||||
# basic board definitions and drivers
|
||||
include_directories(
|
||||
"${NRF5_SDK_PATH}/components"
|
||||
|
@ -98,7 +98,7 @@ The script makes use of the following tools:
|
||||
|
||||
After setup you can use cmake as usual:
|
||||
|
||||
1. Generate the actual build files (out-of-source builds are strongly recomended):
|
||||
1. Generate the actual build files (out-of-source builds are strongly recommended):
|
||||
|
||||
```commandline
|
||||
cmake -H. -B"cmake-build" -G "Unix Makefiles"
|
||||
|
@ -1,7 +1,274 @@
|
||||
# Memory analysis
|
||||
The PineTime is equipped with the following memories:
|
||||
- The internal RAM : **64KB**
|
||||
- The internal Flash : **512KB**
|
||||
- The external (SPI) Flash : **4MB**
|
||||
|
||||
Note that the NRF52832 cannot execute code stored in the external flash : we need to store the whole firmware in the internal flash memory, and use the external one to store graphicals assets, fonts...
|
||||
|
||||
This document describes how the RAM and Flash memories are used in InfiniTime and how to analyze and monitor their usage. It was written in the context of [this memory analysis effort](https://github.com/JF002/InfiniTime/issues/313).
|
||||
|
||||
## Code sections
|
||||
A binary is composed of multiple sections. Most of the time, these sections are : .text, .rodata, .data and .bss but more sections can be defined in the linker script.
|
||||
|
||||
Here is a small description of these sections and where they end up in memory:
|
||||
|
||||
- **TEXT** = code (FLASH)
|
||||
- **RODATA** = constants (FLASH)
|
||||
- **DATA** = initialized variables (FLASH + RAM)
|
||||
- **BSS** = uninitialized variables (RAM)
|
||||
|
||||
|
||||
## Internal FLASH
|
||||
The internal flash memory stores the whole firmware: code, variable that are not default-initialized, constants...
|
||||
|
||||
The content of the flash memory can be easily analyzed thanks to the MAP file generated by the compiler. This file lists all the symbols from the program along with their size and location (section and addresses) in RAM and FLASH.
|
||||
|
||||

|
||||
|
||||
As you can see on the picture above, this file contains a lot of information and is not easily readable by a human being. Fortunately, you can easily find tools that parse and display the content of the MAP file in a more understandable way.
|
||||
|
||||
In this analysis, I used [Linkermapviz](https://github.com/PromyLOPh/linkermapviz).
|
||||
|
||||
### Linkermapviz
|
||||
|
||||
[Linkermapviz](https://github.com/PromyLOPh/linkermapviz) parses the MAP file and displays its content in a graphical way into an HTML page:
|
||||
|
||||

|
||||
|
||||
Using this tool, you can easily see the size of each symbol relative to the other one, and check what is using most of the space,...
|
||||
|
||||
Also, as Linkermapviz is written in Python, you can easily modify it to adapt it to your firmware, export data in another format,... For example, [I modified it to parse the contents of the MAP file and export it in a CSV file](https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620). I could later on open this file in LibreOffice Calc and use sort/filter functionality to search for specific symbols in specific files...
|
||||
|
||||
### Puncover
|
||||
[Puncover](https://github.com/HBehrens/puncover) is another useful tools that analyses the binary file generated by the compiler (the .out file that contains all debug information). It provides valuable information about the symbols (data and code): name, position, size, max stack of each functions, callers, callees...
|
||||

|
||||
|
||||
Puncover is really easy to install:
|
||||
|
||||
- clone the repo and cd into the cloned directory
|
||||
- setup a venv
|
||||
- `python -m virtualenv venv`
|
||||
- `source venv/bin/activate`
|
||||
- Install : `pip install .`
|
||||
- Run : `puncover --gcc_tools_base=/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi- --elf_file /path/to/build/directory/src/pinetime-app-1.1.0.out --src_root /path/to/sources --build_dir /path/to/build/directory`
|
||||
- Replace
|
||||
* `/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin` with the path to your gcc-arm-none-eabi toolchain
|
||||
* `/path/to/build/directory/src/pinetime-app-1.1.0.out` with the path to the binary generated by GCC (.out file)
|
||||
* `/path/to/sources` with the path to the root folder of the sources (checkout directory)
|
||||
* `/path/to/build/directory` with the path to the build directory
|
||||
- Launch a browser at http://localhost:5000/
|
||||
|
||||
### Analysis
|
||||
Using the MAP file and tools, we can easily see what symbols are using most of the FLASH memory space. In this case, with no surprise, fonts and graphics are the biggest flash space consumer.
|
||||
|
||||

|
||||
|
||||
This way, you can easily check what needs to be optimized : we should find a way to store big static data (like fonts and graphics) in the external flash memory, for example.
|
||||
|
||||
It's always a good idea to check the flash memory space when working on the project : this way, you can easily check that your developments are using a reasonable amount of space.
|
||||
|
||||
### Links
|
||||
- Analysis with linkermapviz : https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620
|
||||
- Analysis with Puncover : https://github.com/JF002/InfiniTime/issues/313#issuecomment-847311392
|
||||
|
||||
## RAM
|
||||
RAM memory contains all the data that can be modified at run-time: variables, stack, heap...
|
||||
|
||||
### Data
|
||||
RAM memory can be *statically* allocated, meaning that the size and position of the data are known at compile-time:
|
||||
|
||||
You can easily analyze the memory used by variables declared in the global scope using the MAP. You'll find them in the .BSS or .DATA sections. Linkermapviz and Puncover can be used to analyze their memory usage.
|
||||
|
||||
Variables declared in the scope of a function will be allocated on the stack. It means that the stack usage will vary according to the state of the program, and cannot be easily analyzed at compile time.
|
||||
```
|
||||
uint8_t buffer[1024]
|
||||
|
||||
int main() {
|
||||
int a;
|
||||
}
|
||||
```
|
||||
|
||||
#### Analysis
|
||||
In Infinitime 1.1, the biggest buffers are the buffers allocated for LVGL (14KB) and the one for FreeRTOS (16KB). Nimble also allocated 9KB of RAM.
|
||||
|
||||
### Stack
|
||||
The stack will be used for everything except tasks, which have their own stack allocated by FreeRTOS. The stack is 8192B and is allocated in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L148).
|
||||
An easy way to monitor its usage is by filling the section with a known pattern at boot time, then use the firmware and dump the memory. You can then check the maximum stack usage by checking the address from the beginning of the stack that were overwritten.
|
||||
|
||||
#### Fill the stack section by a known pattern:
|
||||
Edit <NRFSDK>/modules/nrfx/mdk/gcc_startup_nrf52.S and add the following code after the copy of the data from read only memory to RAM at around line 243:
|
||||
|
||||
```
|
||||
/* Loop to copy data from read only memory to RAM.
|
||||
* The ranges of copy from/to are specified by following symbols:
|
||||
* __etext: LMA of start of the section to copy from. Usually end of text
|
||||
* __data_start__: VMA of start of the section to copy to.
|
||||
* __bss_start__: VMA of end of the section to copy to. Normally __data_end__ is used, but by using __bss_start__
|
||||
* the user can add their own initialized data section before BSS section with the INTERT AFTER command.
|
||||
*
|
||||
* All addresses must be aligned to 4 bytes boundary.
|
||||
*/
|
||||
ldr r1, =__etext
|
||||
ldr r2, =__data_start__
|
||||
ldr r3, =__bss_start__
|
||||
|
||||
subs r3, r3, r2
|
||||
ble .L_loop1_done
|
||||
|
||||
.L_loop1:
|
||||
subs r3, r3, #4
|
||||
ldr r0, [r1,r3]
|
||||
str r0, [r2,r3]
|
||||
bgt .L_loop1
|
||||
|
||||
.L_loop1_done:
|
||||
|
||||
|
||||
/* Add this code to fill the stack section with 0xFFEEDDBB */
|
||||
ldr r0, =__StackLimit
|
||||
ldr r1, =8192
|
||||
ldr r2, =0xFFEEDDBB
|
||||
.L_fill:
|
||||
str r2, [r0]
|
||||
adds r0, 4
|
||||
subs r1, 4
|
||||
bne .L_fill
|
||||
/* -- */
|
||||
```
|
||||
|
||||
#### Dump RAM memory and check usage
|
||||
Dumping the content of the ram is easy using JLink debugger and `nrfjprog`:
|
||||
|
||||
```
|
||||
nrfjprog --readram ram.bin
|
||||
```
|
||||
|
||||
You can then display the file using objdump:
|
||||
|
||||
```
|
||||
hexdump ram.bin -v | less
|
||||
```
|
||||
|
||||
The stack is positionned at the end of the RAM -> 0xFFFF. Its size is 8192 bytes, so the end of the stack is at 0xE000.
|
||||
On the following dump, the maximum stack usage is 520 bytes (0xFFFF - 0xFDF8):
|
||||
|
||||
```
|
||||
000fdb0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
|
||||
000fdc0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
|
||||
000fdd0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
|
||||
000fde0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
|
||||
000fdf0 ddbb ffee ddbb ffee ffff ffff c24b 0003
|
||||
000fe00 ffff ffff ffff ffff ffff ffff 0000 0000
|
||||
000fe10 0018 0000 0000 0000 0000 0000 fe58 2000
|
||||
000fe20 0000 0000 0000 00ff ddbb 00ff 0018 0000
|
||||
000fe30 929c 2000 0000 0000 0018 0000 0000 0000
|
||||
000fe40 92c4 2000 0458 2000 0000 0000 80e7 0003
|
||||
000fe50 0000 0000 8cd9 0003 ddbb ffee ddbb ffee
|
||||
000fe60 00dc 2000 92c4 2000 0005 0000 929c 2000
|
||||
000fe70 007f 0000 feb0 2000 92c4 2000 feb8 2000
|
||||
000fe80 ddbb ffee 0005 0000 929c 2000 0000 0000
|
||||
000fe90 aca0 2000 0000 0000 0028 0000 418b 0005
|
||||
000fea0 02f4 2000 001f 0000 0000 0000 0013 0000
|
||||
000feb0 b5a8 2000 2199 0005 b5a8 2000 2201 0005
|
||||
000fec0 b5a8 2000 001e 0000 0000 0000 0013 0000
|
||||
000fed0 b5b0 2000 0fe0 0006 b5a8 2000 0000 0000
|
||||
000fee0 0013 0000 2319 0005 0013 0000 0000 0000
|
||||
000fef0 0000 0000 3b1c 2000 3b1c 2000 d0e3 0000
|
||||
000ff00 4b70 2000 54ac 2000 4b70 2000 ffff ffff
|
||||
000ff10 0000 0000 1379 0003 6578 2000 0d75 0003
|
||||
000ff20 6578 2000 ffff ffff 0000 0000 1379 0003
|
||||
000ff30 000c 0000 cfeb 0002 39a1 2000 a824 2000
|
||||
000ff40 0015 0000 cfeb 0002 39a1 2000 a824 2000
|
||||
000ff50 39a1 2000 0015 0000 001b 0000 b4b9 0002
|
||||
000ff60 0000 0000 a9f4 2000 4b70 2000 0d75 0003
|
||||
000ff70 4b70 2000 ffff ffff 0000 0000 1379 0003
|
||||
000ff80 ed00 e000 a820 2000 1000 4001 7fc0 2000
|
||||
000ff90 7f64 2000 75a7 0001 a884 2000 7b04 2000
|
||||
000ffa0 a8c0 2000 0000 0000 0000 0000 0000 0000
|
||||
000ffb0 7fc0 2000 7f64 2000 8024 2000 a5a5 a5a5
|
||||
000ffc0 ed00 e000 3fd5 0001 0000 0000 72c0 2000
|
||||
000ffd0 0000 0000 72e4 2000 3f65 0001 7f64 2000
|
||||
000ffe0 0000 2001 0000 0000 ef30 e000 0010 0000
|
||||
000fff0 7fc0 2000 4217 0001 3f0a 0001 0000 6100
|
||||
```
|
||||
|
||||
#### Analysis
|
||||
According to my experimentations, we don't use the stack that much, and 8192 bytes is probably way too big for InfiniTime!
|
||||
|
||||
#### Links
|
||||
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-851035070
|
||||
|
||||
### Heap
|
||||
The heap is declared in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L136) and its current size is 8192 bytes. The heap is used for dynamic memory allocation(`malloc()`, `new`...).
|
||||
|
||||
Heap monitoring is not easy, but it seems that we can use the following code to know the current usage of the heap:
|
||||
|
||||
```
|
||||
auto m = mallinfo();
|
||||
NRF_LOG_INFO("heap : %d", m.uordblks);
|
||||
```
|
||||
|
||||
#### Analysis
|
||||
According to my experimentation, InfiniTime uses ~6000bytes of heap most of the time. Except when the Navigation app is launched, where the heap usage increases to... more than 9500 bytes (meaning that the heap overflows and could potentially corrupt the stack!!!). This is a bug that should be fixed in #362.
|
||||
|
||||
To know exactly what's consuming heap memory, you can `wrap` functions like `malloc()` into your own functions. In this wrapper, you can add logging code or put breakpoints:
|
||||
|
||||
- Add ` -Wl,-wrap,malloc` to the cmake variable `LINK_FLAGS` of the target you want to debug (pinetime-app, most probably)
|
||||
- Add the following code in `main.cpp`
|
||||
|
||||
```
|
||||
extern "C" {
|
||||
void *__real_malloc (size_t);
|
||||
void* __wrap_malloc(size_t size) {
|
||||
return __real_malloc(size);
|
||||
}
|
||||
}
|
||||
```
|
||||
Now, your function `__wrap_malloc()` will be called instead of `malloc()`. You can call the actual malloc from the stdlib by calling `__real_malloc()`.
|
||||
|
||||
Using this technique, I was able to trace all malloc calls at boot (boot -> digital watchface):
|
||||
|
||||
- system task = 3464 bytes (SystemTask could potentially be declared as a global variable to avoid heap allocation here)
|
||||
- string music = 31 (maybe we should not use std::string when not needed, as it does heap allocation)
|
||||
- ble_att_svr_start = 1720
|
||||
- ble gatts start = 40 + 88
|
||||
- ble ll task = 24
|
||||
- display app = 104
|
||||
- digital clock = 96 + 152
|
||||
- hr task = 304
|
||||
|
||||
#### Links
|
||||
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-851035625
|
||||
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-1-calculating-stack-size/
|
||||
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-2-properly-allocating-stacks/
|
||||
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-3-avoiding-heap-errors/
|
||||
|
||||
## LVGL
|
||||
I did a deep analysis of the usage of the buffer dedicated for lvgl (managed by lv_mem).
|
||||
This buffer is used by lvgl to allocated memory for drivers (display/touch), screens, themes, and all widgets created by the apps.
|
||||
|
||||
The usage of this buffer can be monitored using this code :
|
||||
|
||||
```
|
||||
lv_mem_monitor_t mon;
|
||||
lv_mem_monitor(&mon);
|
||||
NRF_LOG_INFO("\t Free %d / %d -- max %d", mon.free_size, mon.total_size, mon.max_used);
|
||||
```
|
||||
|
||||
The most interesting metric is `mon.max_used` which specifies the maximum number of bytes that were used from this buffer since the initialization of lvgl.
|
||||
According to my measurements, initializing the theme, display/touch driver and screens cost **4752** bytes!
|
||||
Then, initializing the digital clock face costs **1541 bytes**.
|
||||
For example a simple lv_label needs **~140 bytes** of memory.
|
||||
|
||||
I tried to monitor this max value while going through all the apps of InfiniTime 1.1 : the max value I've seen is **5660 bytes**. It means that we could probably **reduce the size of the buffer from 14KB to 6 - 10 KB** (we have to take the fragmentation of the memory into account).
|
||||
### Links
|
||||
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-850890064
|
||||
|
||||
|
||||
## FreeRTOS heap and task stack
|
||||
FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h*
|
||||
FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes,...).
|
||||
FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes...).
|
||||
|
||||
The function `xPortGetFreeHeapSize()` returns the amount of memory available in this *ucHeap* buffer. If this value reaches 0, FreeRTOS runs out of memory.
|
||||
|
||||
@ -20,59 +287,3 @@ for (int i = 0; i < nb; i++) {
|
||||
```
|
||||
|
||||
|
||||
## Global heap
|
||||
Heap is used for **dynamic memory allocation (malloc() / new)**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__HEAP_SIZE=8192` in *src/CMakeLists.txt*:
|
||||
|
||||
```
|
||||
add_definitions(-D__HEAP_SIZE=8192)
|
||||
```
|
||||
|
||||
You can trace the dynamic memory allocation by using the flag `--wrap` of the linker. When this flag is enabled, the linker will replace the calls to a specific function by a call to __wrap_the_function(). For example, if you specify `-Wl,-wrap,malloc` in the linker flags, the linker will replace all calls to `void* malloc(size_t)` by calls to `void* __wrap_malloc(size_t)`. This is a function you'll have to define in your code. In this function, you can call `__real_malloc()` to call the actual `malloc()' function.
|
||||
|
||||
This technic allows you to wrap all calls to malloc() with you own code.
|
||||
|
||||
In *src/CMakeLists.txt*:
|
||||
|
||||
```
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
|
||||
...
|
||||
LINK_FLAGS "-Wl,-wrap,malloc ..."
|
||||
...
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
In *main.cpp*:
|
||||
|
||||
```
|
||||
uint32_t totalMalloc = 0;
|
||||
extern "C" {
|
||||
extern void* __real_malloc(size_t s);
|
||||
void *__wrap_malloc(size_t s) {
|
||||
totalMalloc += s;
|
||||
return __real_malloc(s);
|
||||
}
|
||||
}
|
||||
```
|
||||
This function sums all the memory that is allocated during the runtime. You can monitor or log this value. You can also place breakpoints in this function to determine where the dynamic memory allocation occurs in your code.
|
||||
|
||||
|
||||
# Global stack
|
||||
The stack is used to allocate memory used by functions : **parameters and local variables**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__STACK_SIZE=8192` in *src/CMakeLists.txt*:
|
||||
|
||||
```
|
||||
add_definitions(-D__STACK_SIZE=8192)
|
||||
```
|
||||
|
||||
*NOTE*: FreeRTOS uses its own stack buffer. Thus, the global stack is only used for main() and IRQ handlers. It should be possible to reduce its size to a much lower value.
|
||||
|
||||
**NOTE**: [?] How to track the global stack usage?
|
||||
|
||||
#LittleVGL buffer
|
||||
*TODO*
|
||||
|
||||
#NimBLE buffers
|
||||
*TODO*
|
||||
|
||||
#Tools
|
||||
- https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC.
|
||||
|
118
doc/NavigationService.md
Normal file
@ -0,0 +1,118 @@
|
||||
# Navigation Service
|
||||
## Introduction
|
||||
The navigation ble service provides 4 characteristics to allow the the watch to display navigation instructions from a companion application. The intended purpose is when performing some outdoor activities, for example running or cycling.
|
||||
|
||||
The 4 characteristics are:
|
||||
flag (string) - Upcoming icon name
|
||||
narrative (string) - Textual description of instruction
|
||||
manDist (string) - Manouvre Distance, the distance to the upcoming change
|
||||
progress (uint8) - Percent complete of total route, value 0-100
|
||||
|
||||
## Service
|
||||
The service UUID is c7e60001-78fc-48fe-8e23-433b3a1942d0
|
||||
|
||||
## Characteristics
|
||||
## Flags (UUID c7e60002-78fc-48fe-8e23-433b3a1942d0)
|
||||
All included icons are from pure-maps, which provides the actual routing from the client. The icon names ultimately come from the mapbox project "direction-icons", See https://github.com/rinigus/pure-maps/tree/master/qml/icons/navigation See the end of this document for the full lsit of supported icon names.
|
||||
|
||||
## Narrative (UUID c7e60003-78fc-48fe-8e23-433b3a1942d0)
|
||||
This is a client supplied string describing the upcoming instruction such as "At the roundabout take the first exit".
|
||||
|
||||
## Man Dist (UUID c7e60004-78fc-48fe-8e23-433b3a1942d0)
|
||||
This is a short string describing the distance to the upcoming instruction such as "50 m".
|
||||
|
||||
## Progress (UUID c7e60001=5-78fc-48fe-8e23-433b3a1942d0)
|
||||
The percent complete in a uint8. The watch displays this as an overall progress in a progress bar.
|
||||
|
||||
## Full icon list
|
||||
* arrive
|
||||
* arrive-left
|
||||
* arrive-right
|
||||
* arrive-straight
|
||||
* close
|
||||
* continue
|
||||
* continue-left
|
||||
* continue-right
|
||||
* continue-slight-left
|
||||
* continue-slight-right
|
||||
* continue-straight
|
||||
* continue-uturn
|
||||
* depart
|
||||
* depart-left
|
||||
* depart-right
|
||||
* depart-straight
|
||||
* end-of-road-left
|
||||
* end-of-road-right
|
||||
* ferry
|
||||
* flag
|
||||
* fork
|
||||
* fork-left
|
||||
* fork-right
|
||||
* fork-slight-left
|
||||
* fork-slight-right
|
||||
* fork-straight
|
||||
* invalid
|
||||
* invalid-left
|
||||
* invalid-right
|
||||
* invalid-slight-left
|
||||
* invalid-slight-right
|
||||
* invalid-straight
|
||||
* invalid-uturn
|
||||
* merge-left
|
||||
* merge-right
|
||||
* merge-slight-left
|
||||
* merge-slight-right
|
||||
* merge-straight
|
||||
* new-name-left
|
||||
* new-name-right
|
||||
* new-name-sharp-left
|
||||
* new-name-sharp-right
|
||||
* new-name-slight-left
|
||||
* new-name-slight-right
|
||||
* new-name-straight
|
||||
* notification-left
|
||||
* notification-right
|
||||
* notification-sharp-left
|
||||
* notification-sharp-right
|
||||
* notification-slight-left
|
||||
* notification-slight-right
|
||||
* notification-straight
|
||||
* off-ramp-left
|
||||
* off-ramp-right
|
||||
* off-ramp-sharp-left
|
||||
* off-ramp-sharp-right
|
||||
* off-ramp-slight-left
|
||||
* off-ramp-slight-right
|
||||
* off-ramp-straight
|
||||
* on-ramp-left
|
||||
* on-ramp-right
|
||||
* on-ramp-sharp-left
|
||||
* on-ramp-sharp-right
|
||||
* on-ramp-slight-left
|
||||
* on-ramp-slight-right
|
||||
* on-ramp-straight
|
||||
* rotary
|
||||
* rotary-left
|
||||
* rotary-right
|
||||
* rotary-sharp-left
|
||||
* rotary-sharp-right
|
||||
* rotary-slight-left
|
||||
* rotary-slight-right
|
||||
* rotary-straight
|
||||
* roundabout
|
||||
* roundabout-left
|
||||
* roundabout-right
|
||||
* roundabout-sharp-left
|
||||
* roundabout-sharp-right
|
||||
* roundabout-slight-left
|
||||
* roundabout-slight-right
|
||||
* roundabout-straight
|
||||
* turn-left
|
||||
* turn-right
|
||||
* turn-sharp-left
|
||||
* turn-sharp-right
|
||||
* turn-slight-left
|
||||
* turn-slight-right
|
||||
* turn-stright
|
||||
* updown
|
||||
* uturn
|
@ -1,7 +1,7 @@
|
||||
# Build a stub for PineTime using NRF52-DK
|
||||
[NRF52-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52-DK) is the official developpment kit for NRF52832 SoC from Nordic Semiconductor.
|
||||
|
||||
It can be very useful for PineTime developpment:
|
||||
It can be very useful for PineTime development:
|
||||
* You can use it embedded JLink SWD programmer/debugger to program and debug you code on the PineTime
|
||||
* As it's based on the same SoC than the PineTime, you can program it to actually run the same code than the PineTime.
|
||||
|
||||
@ -47,4 +47,4 @@ You also need to enable the I/O expander to disconnect pins from buttons and led
|
||||
| --------- | --------- |
|
||||
| DETECT | GND |
|
||||
|
||||
Now, you should be able to program the SoC on the NRF52-DK board, and use it as if it was running on the pintime.
|
||||
Now, you should be able to program the SoC on the NRF52-DK board, and use it as if it was running on the PineTime.
|
27
doc/ble.md
@ -5,9 +5,9 @@ This page describes the BLE implementation and API built in this firmware.
|
||||
**Note** : I'm a beginner in BLE related technologies and the information of this document reflect my current knowledge and understanding of the BLE stack. These informations might be erroneous or incomplete. Feel free to submit a PR if you think you can improve these.
|
||||
|
||||
## BLE Connection
|
||||
When starting the firmware start a BLE advertising : it send small messages that can be received by any *central* device in range. This allows the device to announce its presence to other devices.
|
||||
When starting the firmware start a BLE advertising : it sends small messages that can be received by any *central* device in range. This allows the device to announce its presence to other devices.
|
||||
|
||||
A companion application (running on a PC, RasberryPi, smartphone) which received this avertising packet can request a connection to the device. This connection procedure allows the 2 devices to negociate communication parameters, security keys,...
|
||||
A companion application (running on a PC, RaspberryPi, smartphone) which received this avertising packet can request a connection to the device. This connection procedure allows the 2 devices to negotiate communication parameters, security keys,...
|
||||
|
||||
When the connection is established, the pinetime will try to discover services running on the companion application. For now **CTS** (**C**urrent **T**ime **S**ervice) and **ANS** (**A**lert **N**otification **S**ervice) are supported.
|
||||
|
||||
@ -15,6 +15,29 @@ If **CTS** is detected, it'll request the current time to the companion applicat
|
||||
|
||||

|
||||
|
||||
## BLE UUIDs
|
||||
When possible, InfiniTime tries to implement BLE services defined by the BLE specification.
|
||||
|
||||
When the service does not exist in the BLE specification, InfiniTime implement custom services. As all BLE services, custom services are identified by a UUID. Here is how to define the UUID of custom services in InfiniTime:
|
||||
|
||||
```
|
||||
- Base UUID : xxxxxxxx-78fc-48fe-8e23-433b3a1942d0
|
||||
- Service UUID : SSSS0000-78fc-48fe-8e23-433b3a1942d0 where SSSS is the service ID
|
||||
- Characteristic UUID : SSSSCCCC-78fc-48fe-8e23-433b3a1942d0 where CCCC is the characteristic ID for the service SSSS and is different than 0
|
||||
```
|
||||
|
||||
The following custom services are implemented in InfiniTime:
|
||||
|
||||
- Since InfiniTime 0.8:
|
||||
```
|
||||
* Music Service : 00000000-78fc-48fe-8e23-433b3a1942d0
|
||||
```
|
||||
|
||||
- Since InfiniTime 0.11:
|
||||
```
|
||||
* Navigation Service : 00010000-78fc-48fe-8e23-433b3a1942d0
|
||||
```
|
||||
|
||||
## BLE services
|
||||
[List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/)
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
The branching model of this project is based on the workflow named [Git flow](https://nvie.com/posts/a-successful-git-branching-model/).
|
||||
|
||||
It is based on 2 main branches:
|
||||
- **master** : this branch is always ready to be reployed. It means that at any time, we should be able to build the branch and release a new version of the application.
|
||||
- **master** : this branch is always ready to be deployed. It means that at any time, we should be able to build the branch and release a new version of the application.
|
||||
- **develop** : this branch contains the latest development that will be integrated in the next release once it's considered as stable.
|
||||
|
||||
New features should be implemented in **feature branches** created from **develop**. When the feature is ready, a pull-request is created and it'll be merge into **develop** when it is succesfully reviewed and accepted.
|
||||
|
@ -3,13 +3,15 @@
|
||||
To build this project, you'll need:
|
||||
- A cross-compiler : [ARM-GCC (9-2020-q2-update)](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads/9-2020-q2-update)
|
||||
- The NRF52 SDK 15.3.0 : [nRF-SDK v15.3.0](https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip)
|
||||
- The `cbor` and `intelhex` modules for Python 3
|
||||
- A reasonably recent version of CMake (I use 3.16.5)
|
||||
|
||||
## Build steps
|
||||
### Clone the repo
|
||||
```
|
||||
git clone https://github.com/JF002/Pinetime.git
|
||||
cd Pinetime
|
||||
git clone https://github.com/JF002/InfiniTime.git
|
||||
cd InfiniTime
|
||||
git submodule update --init
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
@ -21,15 +23,24 @@ CMake configures the project according to variables you specify the command line
|
||||
**ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`-DARM_NONE_EABI_TOOLCHAIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2020-q2-update/`|
|
||||
**NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`|
|
||||
**USE_JLINK, USE_GDB_CLIENT and USE_OPENOCD**|Enable *JLink* mode, *GDB Client* (Black Magic Probe) mode or *OpenOCD* mode (set the one you want to use to `1`)|`-DUSE_JLINK=1`
|
||||
**CMAKE_BUILD_TYPE**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
|
||||
**CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
|
||||
**NRFJPROG**|Path to the NRFJProg executable. Used only if `USE_JLINK` is 1.|`-DNRFJPROG=/opt/nrfjprog/nrfjprog`
|
||||
**GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb`
|
||||
**GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0`
|
||||
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
|
||||
**WATCH_COLMI_P8**|Use pin configuration for Colmi P8 watch|`-DWATCH_COLMI_P8=1`
|
||||
|
||||
####(**) Note about **CMAKE_BUILD_TYPE**:
|
||||
By default, this variable is set to *Release*. It compiles the code with size and speed optimizations. We use this value for all the binaries we publish when we [release](https://github.com/JF002/InfiniTime/releases) new versions of InfiniTime.
|
||||
|
||||
The *Debug* mode disables all optimizations, which makes the code easier to debug. However, the binary size will likely be too big to fit in the internal flash memory. If you want to build and debug a *Debug* binary, you'll need to disable some parts of the code. For example, the icons for the **Navigation** app use a lot of memory space. You can comment the content of `m_iconMap` in the [Navigation](https://github.com/JF002/InfiniTime/blob/develop/src/displayapp/screens/Navigation.h#L148) application to free some memory.
|
||||
|
||||
####(**) Note about **BUILD_DFU**:
|
||||
DFU files are the files you'll need to install your build of InfiniTime using OTA (over-the-air) mecanism. To generate the DFU file, the Python tool [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil) is needed on your system. Check that this tool is properly installed before enabling this option.
|
||||
|
||||
#### CMake command line for JLink
|
||||
```
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../
|
||||
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../
|
||||
```
|
||||
|
||||
#### CMake command line for GDB Client (Black Magic Probe)
|
||||
@ -44,13 +55,16 @@ cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DG
|
||||
|
||||
### Build the project
|
||||
During the project generation, CMake created the following targets:
|
||||
- FLASH_ERASE : mass erase the flash memory of the NRF52.
|
||||
- FLASH_pinetime-app : flash the firmware into the NRF52.
|
||||
- pinetime-app : build the standalone (without bootloader support) version of the firmware.
|
||||
- pinetime-mcuboot-app : build the firmware with the support of the bootloader (based on MCUBoot).
|
||||
- pinetime-graphics : small firmware that writes the boot graphics into the SPI flash.
|
||||
- **FLASH_ERASE** : mass erase the flash memory of the NRF52.
|
||||
- **FLASH_pinetime-app** : flash the firmware into the NRF52.
|
||||
- **pinetime-app** : build the standalone (without bootloader support) version of the firmware.
|
||||
- **pinetime-recovery** : build the standalone recovery version of infinitime (light firmware that only supports OTA and basic UI)
|
||||
- **pinetime-recovery-loader** : build the standalone tool that flashes the recovery firmware into the external SPI flash
|
||||
- **pinetime-mcuboot-app** : build the firmware with the support of the bootloader (based on MCUBoot).
|
||||
- **pinetime-mcuboot-recovery** : build pinetime-recovery with bootloader support
|
||||
- **pinetime-mcuboot-recovery-loader** : build pinetime-recovery-loader with bootloader support
|
||||
|
||||
If you just want to build the project and run it on the Pinetime, using *pinetime-app* is recommanded. See [this page](../bootloader/README.md) for more info about bootloader support.
|
||||
If you just want to build the project and run it on the Pinetime, using *pinetime-app* is recommended. See [this page](../bootloader/README.md) for more info about bootloader support.
|
||||
|
||||
Build:
|
||||
```
|
||||
@ -63,8 +77,11 @@ Binary files are generated into the folder `src`:
|
||||
- **pinetime-app.map** : map file
|
||||
- **pinetime-mcuboot-app.bin, .hex and .out** : firmware with bootloader support in bin, hex and out formats.
|
||||
- **pinetime-mcuboot-app.map** : map file
|
||||
- **pinetime-graphics.bin, .hex and .out** : firmware for the boot graphic in bin, hex and out formats.
|
||||
- **pinetime-graphics.map** : map file
|
||||
- **pinetime-mcuboot-app-image** : MCUBoot image of the firmware
|
||||
- **pinetime-mcuboot-ap-dfu** : DFU file of the firmware
|
||||
|
||||
The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader**
|
||||
|
||||
|
||||
### Program and run
|
||||
#### Using CMake targets
|
||||
@ -147,7 +164,7 @@ J-Link>g
|
||||
```
|
||||
|
||||
#### JLink RTT
|
||||
RTT is a feature from Segger's JLink devices that allows bidirectionnal communication between the debugger and the target. This feature can be used to get the logs from the embedded software on the development computer.
|
||||
RTT is a feature from Segger's JLink devices that allows bidirectional communication between the debugger and the target. This feature can be used to get the logs from the embedded software on the development computer.
|
||||
|
||||
- Program the MCU with the code (see above)
|
||||
- Start JLinkExe
|
||||
|
@ -1,33 +1,73 @@
|
||||
# Build the project using Docker
|
||||
A [Docker image (Dockerfile)](../docker) containing all the build environment is available for X86_64 and AMD64 architectures. This image makes the build of the firmware and the generation of the DFU file for OTA.
|
||||
|
||||
A [Docker image (Dockerfile)](../docker) containing all the build environment is available for X86_64 and AMD64 architectures. These images make the build of the firmware and the generation of the DFU file for OTA quite easy, as well as preventing clashes with any other toolchains or development environments you may have installed.
|
||||
|
||||
Based on Ubuntu 18.04 with the following build dependencies:
|
||||
|
||||
* ARM GCC Toolchain
|
||||
* nRF SDK
|
||||
* MCUBoot
|
||||
* adafruit-nrfutil
|
||||
|
||||
## Run a container to build the project
|
||||
|
||||
The `infinitime-build` image contains all the dependencies you need. The default `CMD` will compile sources found in `/sources`, so you need only mount your code.
|
||||
|
||||
This example will build the firmware, generate the MCUBoot image and generate the DFU file. For cloning the repo, see [these instructions](../doc/buildAndProgram.md#clone-the-repo). Outputs will be written to **<project_root>/build/output**:
|
||||
|
||||
```bash
|
||||
cd <project_root> # e.g. cd ./work/Pinetime
|
||||
docker run --rm -it -v $(pwd):/sources infinitime-build
|
||||
```
|
||||
|
||||
If you only want to build a single CMake target, you can pass it in as the first parameter to the build script. This means calling the script explicitly as it will override the `CMD`. Here's an example For `pinetime-app`:
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v $(pwd):/sources infinitime-build /opt/build.sh pinetime-app
|
||||
```
|
||||
|
||||
The image is built using 1000:1000 for the user id and group id. If this is different to your user or group ids (run `id -u` and `id -g` to find out what your id values are if you are unsure), you will need to override them via the `--user` parameter in order to prevent permission errors with the output files (and the cmake build cache).
|
||||
|
||||
Running with this image is the same as above, you just specify the ids to `docker run`
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v $(pwd):/sources --user $(id -u):$(id -g) pfeerick/infinitime-build
|
||||
```
|
||||
|
||||
Or you can specify your user id and group id (by number, not by name) directly:
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v $(pwd):/sources --user 1234:1234 infinitime-build
|
||||
```
|
||||
|
||||
## Using the image from Docker Hub
|
||||
|
||||
The image is avaiable via Docker Hub for both the amd64 and arm64v8 architectures at [pfeerick/infinitime-build](https://hub.docker.com/repository/docker/pfeerick/infinitime-build).
|
||||
|
||||
It can be pulled (downloaded) using the following command:
|
||||
|
||||
```bash
|
||||
docker pull pfeerick/infinitime-build
|
||||
```
|
||||
|
||||
The default `latest` tag *should* automatically identify the correct image architecture, but if for some reason Docker does not, you can specify it manually:
|
||||
|
||||
* For AMD64 (x86_64) systems: `docker pull pfeerick/infinitime-build:amd64`
|
||||
|
||||
* For ARM64v8 (ARM64/aarch64) systems: `docker pull pfeerick/infinitime-build:arm64v8`
|
||||
|
||||
## Build the image
|
||||
The image is not (yet) available on DockerHub, you need to build it yourself, which is quite easy. The following commands must be run from the root of the project.
|
||||
|
||||
If you are running on a x86_64 computer :
|
||||
```
|
||||
docker image build -t infinitime-build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) docker/x86_64/
|
||||
You can build the image yourself if you like!
|
||||
|
||||
The following commands must be run from the root of the project. This operation will take some time but, when done, a new image named *infinitime-build* is available.
|
||||
|
||||
```bash
|
||||
docker image build -t infinitime-build ./docker
|
||||
```
|
||||
|
||||
And if your are running on an ARM64 device (tested on RaspberryPi4 and Pine64 PineBookPro):
|
||||
The `PUID` and `PGID` build arguments are used to set the user and group ids used in the container, meaning you will not need to specify it later unless they change for some reason. Specifying them is not mandatory, as this can be over-ridden at build time via the `--user` flag, but doing so will make the command you need to run later a bit shorter. In the below examples, they are set to your current user id and group id automatically. You can specify them manually, but they must be specified by number, not by name.
|
||||
|
||||
```bash
|
||||
docker image build -t infinitime-build --build-arg PUID=$(id -u) --build-arg PGID=$(id -g) ./docker
|
||||
```
|
||||
docker image build -t infinitime-build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) docker/arm64/
|
||||
```
|
||||
|
||||
This operation will take some time. It builds a Docker image based on Ubuntu, install some packages, download the ARM toolchain, the NRF SDK, MCUBoot and adafruit-nrfutil.
|
||||
|
||||
When this is done, a new image named *infinitime-build* is available.
|
||||
|
||||
## Run a container to build the project:
|
||||
|
||||
```
|
||||
docker run --rm -v <project_root>:/sources infinitime-build
|
||||
```
|
||||
|
||||
Replace *<project_root>* by the path of the root of the project on your computer. For example:
|
||||
|
||||
```
|
||||
docker run --rm -v /home/jf/git/PineTime:/sources infinitime-build
|
||||
```
|
||||
|
||||
This will start a container, build the firmware and generate the MCUBoot image and the DFU file. The output of the build is stored in **<project_root>/built/output**.
|
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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -8,8 +8,9 @@ The following features are implemented:
|
||||
- Time synchronization
|
||||
- Notifications
|
||||
- Music control
|
||||
- Navigation with Puremaps
|
||||
|
||||
## Demo
|
||||
[This video](https://seafile.codingfield.com/f/21c5d023452740279e36/) shows how to connect to the Pinetime and control the playback of the music on the phone.
|
||||
Amazfish and Sailfish OS are running on the [Pinephone](https://www.pine64.org/pinephone/), another awesome device from Pine64.
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Integration with Gadgetbridge
|
||||
[Gadgetbridge](https://gadgetbridge.org/) is an Android application that supports many smartwatches and fitness trackers.
|
||||
|
||||
The integration of InfiniTime (previously Pinetime-JF) is now merged into the master branch (https://codeberg.org/Freeyourgadget/Gadgetbridge/).
|
||||
The integration of InfiniTime (previously Pinetime-JF) is now merged into the master branch (https://codeberg.org/Freeyourgadget/Gadgetbridge/) and initial support is available [starting with version 0.47](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/branch/master/CHANGELOG.md). Note that the official version is only available on F-Droid (as of May 2021), and the unofficial fork available on the Play Store is outdated and does not support Infinitime.
|
||||
|
||||
## Features
|
||||
The following features are implemented:
|
||||
|
@ -1,38 +1,90 @@
|
||||
# How to contribute?
|
||||
|
||||
## Report bugs
|
||||
You use your Pinetime and find a bug in the firmware? [Create an issue on Github](https://github.com/JF002/Pinetime/issues) explaining the bug, how to reproduce it, the version of the firmware you use...
|
||||
|
||||
Have you found a bug in the firmware? [Create an issue on Github](https://github.com/JF002/InfiniTime/issues) explaining the bug, how to reproduce it, the version of the firmware you use...
|
||||
|
||||
## Write and improve documentation
|
||||
Documentation might be incomplete, or not clear enough, and it is always possible to improve it with better wording, pictures, photo, video,...
|
||||
|
||||
Documentation might be incomplete, or not clear enough, and it is always possible to improve it with better wording, pictures, photo, video,...
|
||||
|
||||
As the documentation is part of the source code, you can submit your improvements to the documentation by submitting a pull request (see below).
|
||||
|
||||
## Fix bugs, add functionalities and improve the code
|
||||
|
||||
You want to fix a bug, add a cool new functionality or improve the code? See *How to submit a pull request below*.
|
||||
|
||||
## Spread the word
|
||||
Pinetime is a cool open source project that deserves to be know. Talk about it around you, on social networks, on your blog,... and let people know that we are working on an open-source firmware for a smartwatch!
|
||||
|
||||
The Pinetime is a cool open source project that deserves to be known. Talk about it around you, on social networks, on your blog,... and let people know that we are working on an open source firmware for a smartwatch!
|
||||
|
||||
# How to submit a pull request ?
|
||||
Your contribution is more than welcome!
|
||||
|
||||
If you want to fix a bug, add a functionality or improve the code, you'll first need to create a branch from the **develop** branch (see [this page about the branching model](./branches.md)). This branch is called a feature branch, and you should choose a name that explains what you are working on (ex: "add-doc-about-contributions"). In this branch, try to focus on only one topic, bug or feature. For example, if you created this branch to work on the UI of a specific application, do not commit modifications about the SPI driver. If you want to work on multiple topics, create one branch per topic.
|
||||
## TL;DR
|
||||
|
||||
When your feature branch is ready, make sure it actually works and do not forget to write documentation about it if necessary.
|
||||
- Create a branch from develop;
|
||||
- Work on a single subject in this branch. Create multiple branches/pulls-requests if you want to work on multiple subjects (bugs, features,...);
|
||||
- Test your modifications on the actual hardware;
|
||||
- Check the code formatting against our coding conventions and [clang-format](../.clang-format) and [clang-tidy](../.clang-tidy);
|
||||
- Clean your code and remove files that are not needed;
|
||||
- Write documentation related to your new feature if applicable;
|
||||
- Create a pull request and write a great description about it : what does your PR do, why, how,... Add pictures and video if possible;
|
||||
- Wait for someone to review your PR and take part in the review process;
|
||||
- Your PR will eventually be merged :)
|
||||
|
||||
Then, you can submit a pull-request for review. Try to describe your pull request as much as possible: what did you do in this branch, how does it work, how is it designed, are there any limitations,... This will help the contributors to understand and review your code easily.
|
||||
Your contributions are more than welcome!
|
||||
|
||||
Other contributors can post comments about the pull request, maybe ask for more info or adjustements in the code.
|
||||
If you want to fix a bug, add functionality or improve the code, you'll first need to create a branch from the **develop** branch (see [this page about the branching model](./branches.md)). This branch is called a feature branch, and you should choose a name that explains what you are working on (ex: "add-doc-about-contributions"). In this branch, **focus on only one topic, bug or feature**. For example, if you created this branch to work on the UI of a specific application, do not commit modifications about the SPI driver. If you want to work on multiple topics, create one branch for each topic.
|
||||
|
||||
Once the pull request is reviewed an accepted, it'll be merge in **develop** and will be released in the next release version of the firmware.
|
||||
When your feature branch is ready, **make sure it actually works** and **do not forget to write documentation** about it if it's relevant.
|
||||
|
||||
**Creating a pull request containing modifications that haven't been tested is strongly discouraged.** If, for any reason, you cannot test your modifications but want to publish them anyway, **please mention it in the description**. This way, other contributors might be willing to test it and provide feedback about your code.
|
||||
|
||||
Also, before submitting your PR, check the coding style of your code against the **coding conventions** detailed below. This project also provides [clang-format](../.clang-format) and [clang-tidy](../.clang-tidy) configuration files. You can use them to ensure correct formatting of your code.
|
||||
|
||||
Don't forget to check the files you are going to commit and remove those which aren't necessary (config files from your IDE, for example). Remove old comments, commented code,...
|
||||
|
||||
Then, you can submit a pull request for review. Try to **describe your pull request as much as possible**: what did you do in this branch, how does it work, how it is designed, are there any limitations,... This will help the contributors to understand and review your code easily. You can add pictures and video to the description so that contributors will have a quick overview of your work.
|
||||
|
||||
Other contributors can post comments about the pull request, maybe ask for more info or adjustments in the code.
|
||||
|
||||
Once the pull request is reviewed and accepted, it'll be merged into **develop** and will be released in the next version of the firmware.
|
||||
|
||||
## Why all these rules?
|
||||
|
||||
Reviewing pull requests is a **very time consuming task** for the creator of this project ([JF002](https://github.com/JF002)) and for other contributors who take the time to review them. Everything you do to make reviewing easier will **get your PR merged faster**.
|
||||
|
||||
When reviewing PRs, the author and contributors will first look at the **description**. If it's easy to understand what the PR does, why the modification is needed or interesting and how it's done, a good part of the work is already done : we understand the PR and its context.
|
||||
|
||||
Then, reviewing **a few files that were modified for a single purpose** is a lot more easier than to review 30 files modified for many reasons (bug fix, UI improvements, typos in doc,...), even if all these changes make sense. Also, it's possible that we agree on some modification but not on some other, so we won't be able to merge the PR because of the changes that are not accepted.
|
||||
|
||||
We do our best to keep the code as consistent as possible. If the formatting of the code in your PR is not consistent with our code base, we'll ask you to review it, which will take more time.
|
||||
|
||||
The last step of the review consists of **testing** the modification. If it doesn't work out of the box, we'll ask your to review your code and to ensure that it works as expected.
|
||||
|
||||
It's totally normal for a PR to need some more work even after it was created, that's why we review them. But every round trip takes time, so it's good practice to try to reduce them as much as possible by following those simple rules.
|
||||
|
||||
# Coding convention
|
||||
## Language
|
||||
The language of this project is **C++**, and all new code must be written in C++. (Modern) C++ provides a lot of useful tools and functionalities that are beneficial for embedded software development like `constexpr`, `template` and anything that provides zero-cost abstraction.
|
||||
|
||||
It's OK to include C code if this code comes from another library like FreeRTOS, NimBLE, LVGL or the NRF-SDK.
|
||||
## Language
|
||||
|
||||
The language of this project is **C++**, and all new code must be written in C++. (Modern) C++ provides a lot of useful tools and functionalities that are beneficial for embedded software development like `constexpr`, `template` and anything that provides zero-cost abstraction.
|
||||
|
||||
C code is accepted if it comes from another library like FreeRTOS, NimBLE, LVGL or the NRF-SDK.
|
||||
|
||||
## Coding style
|
||||
The most important rule to follow is to try to keep the code as easy to read and maintain as possible.
|
||||
|
||||
- **Identation** : 2 spaces, no tabulation
|
||||
The most important rule to follow is to try to keep the code as easy to read and maintain as possible.
|
||||
|
||||
Using an autoformatter is highly recommended, but make sure it's configured properly.
|
||||
|
||||
There are preconfigured autoformatter rules for:
|
||||
|
||||
* CLion (IntelliJ) in .idea/codeStyles/Project.xml
|
||||
|
||||
If there are no preconfigured rules for your IDE, you can use one of the existing ones to configure your IDE.
|
||||
|
||||
- **Indentation** : 2 spaces, no tabulation
|
||||
- **Opening brace** at the end of the line
|
||||
- **Naming** : Choose self-describing variable name
|
||||
- **class** : PascalCase
|
||||
@ -41,4 +93,4 @@ The most important rule to follow is to try to keep the code as easy to read and
|
||||
- **Include guard** : `#pragma once` (no `#ifdef __MODULE__ / #define __MODULE__ / #endif`)
|
||||
- **Includes** :
|
||||
- files from the project : `#include "relative/path/to/the/file.h"`
|
||||
- external files and std : `#include <file.h>`
|
||||
- external files and std : `#include <file.h>`
|
||||
|
@ -1,16 +1,16 @@
|
||||
# Using the releases
|
||||
For each new *stable* version of Pinetime, a [release note](https://github.com/JF002/Pinetime/releases) is created. It contains a description of the main changes in the release and some files you can use to flash the firmware in your Pinetime.
|
||||
For each new *stable* version of IniniTime, a [release note](https://github.com/JF002/InfiniTime/releases) is created. It contains a description of the main changes in the release and some files you can use to flash the firmware to your Pinetime.
|
||||
|
||||
This page describes the files from the release notes and how to use them.
|
||||
|
||||
**NOTE :** the files included in different could be different. This page describes the release note of [version 0.7.1](https://github.com/JF002/Pinetime/releases/tag/0.7.1), which is the version that'll probably be pre-programmed at the factory for the next batch of Pinetime devkits.
|
||||
**NOTE :** the files included in different Releases could be different. This page describes the release notes of [version 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1), which is the version that is pre-programmed for the last batches of pinetimes but will be replaced with [1.0.0](https://github.com/jF002/infiniTime/releases/tag/1.0.0) around june 2021.
|
||||
|
||||
## Files included in the release note
|
||||
## Files included in the release notes
|
||||
|
||||
### Standalone firmware
|
||||
This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flash at offset 0, meaning it will erase any bootloader that might be present in memory.
|
||||
This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flashed at offset 0, meaning it will erase any bootloader that might be present in memory.
|
||||
|
||||
- **pinetime-app.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB.
|
||||
- **pinetime-app.out** : Output file of GCC containing debug symbols, useful if you want to debug the firmware using GDB.
|
||||
- **pinetime-app.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it.
|
||||
- **pintime-app.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed.
|
||||
- **pinetime-app.map** : Map file containing all the symbols, addresses in memory,...
|
||||
@ -18,7 +18,7 @@ This firmware is standalone, meaning that it does not need a bootloader to actua
|
||||
**This firmware must be flashed at address 0x00 in the main flash memory**
|
||||
|
||||
### Bootloader
|
||||
The bootloader is maintained by [lupyuen](https://github.com/lupyuen) and is a binary version of [this release](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4).
|
||||
The bootloader is maintained by [lupyuen](https://github.com/lupyuen) and is a binary version of [this release](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4).
|
||||
|
||||
- **bootloader.hex** : Firmware in Intel HEX file format.
|
||||
|
||||
@ -38,7 +38,7 @@ This firmware is a small utility firmware that writes the boot graphic in the ex
|
||||
### Firmware with bootloader
|
||||
This firmware is intended to be used with our [MCUBoot-based bootloader](../bootloader/README.md).
|
||||
|
||||
- **pinetime-mcuboot-app-image.hex** : Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed **@ 0x8000** into flash memory. If the [bootloader](../bootloader/README.md) has been successfully programmed, it should run this firmware after the next reset.
|
||||
- **pinetime-mcuboot-app-image.hex**: Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed at **0x8000** into the flash memory. If the [bootloader](../bootloader/README.md) has been successfully programmed, it should run this firmware after the next reset.
|
||||
|
||||
The following files are not directly usable by the bootloader:
|
||||
|
||||
|
BIN
doc/gettingStarted/appmenu-071.jpg
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
doc/gettingStarted/appmenu.jpg
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
doc/gettingStarted/bootloader-1.0.jpg
Normal file
After Width: | Height: | Size: 156 KiB |
119
doc/gettingStarted/gettingStarted-1.0.md
Normal file
@ -0,0 +1,119 @@
|
||||
# Getting started with InfiniTime 1.0
|
||||
|
||||
On April 22 2021, InfiniTime and Pine64 [announced the release of InfiniTime 1.0](https://www.pine64.org/2021/04/22/its-time-infinitime-1-0/) and the availability of PineTime smartwatches as *enthusiast grade end-user product*. This page aims to guide you with your first step with your new PineTime.
|
||||
|
||||
## Firmware, InfiniTime, Bootloader, Recovery firmware, OTA, DFU... What is it?
|
||||
|
||||
You might have already seen these words by reading the announcement, release notes, or [the wiki guide](https://wiki.pine64.org/wiki/Upgrade_PineTime_to_InfiniTime_1.0.0) and, you may find them misleading if you're not familiar with the project.
|
||||
|
||||
Basically, a **firmware** is just a software running on the embedded hardware of a device, the PineTime in this case.
|
||||
**InfiniTime** is based on 3 distinct **firmwares**:
|
||||
- **[InfiniTime](https://github.com/JF002/InfiniTime)** itself, this is the *application firmware* running on the PineTime. This is the main firmware which provides most of the functionalities you'll use on a daily basis : bluetooth low-energy (BLE) connectivity, applications, watchfaces,...
|
||||
- **[The bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader)** is responsible for safely applying **updates** of the *application firmware*, reverting them in case of issues and load the recovery firmware when requested.
|
||||
- **The recovery firmware** is a specific *application firmware* than can be loaded by the bootloader on user request. This firmware can be useful in case of serious issue, when the main application firmware cannot perform an OTA update correctly. Currently, this recovery firmware is based on [InfiniTime 0.14.1](https://github.com/JF002/InfiniTime/releases/tag/0.14.1).
|
||||
|
||||
**OTA** and **DFU** refer to the update of the firmware over BLE (**B**luetooth **L**ow **E**nergy). **OTA** means **O**ver **T**he **A**ir, this is a functionality that allows the user to update the firmware how their device using a wireless communication like BLE. When we talk about **DFU** (**D**igital **F**irmware **U**pdate), we refer to the file format and protocol used to send the update of the firmware to the watch over-the-air. InfiniTime implement the (legacy) DFU protocol from Nordic Semiconductor (NRF).
|
||||
|
||||
## How to check the version of InfiniTime and the bootloader?
|
||||
|
||||
Since September 2020, all PineTimes (devkits or sealed) are flashed using the **[first iteration of the bootloader](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7)** and **[InfiniTime 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1)**. There was no recovery firmware at that time.
|
||||
|
||||
The bootloader only runs when the watch starts (from an empty battery, for example) or after a reset (after a successful OTA or a manual reset - long push on the button).
|
||||
|
||||
You can recognize this first iteration of the bootloader with it greenish **PINETIME** logo.
|
||||
|
||||

|
||||
|
||||
You can check the version of InfiniTime by opening the app *SystemInfo*. For version < 1.0:
|
||||
|
||||

|
||||

|
||||
|
||||
And for version >= 1.0 :
|
||||
|
||||

|
||||
|
||||
PineTime shipped from June 2021 (to be confirmed) will be flashed with the [new version of the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/tag/1.0.0), the [recovery firmware](https://github.com/JF002/InfiniTime/releases/tag/0.14.1) and [InfiniTime 1.0](https://github.com/JF002/InfiniTime/releases/tag/1.0.0).
|
||||
|
||||
The bootloader is easily recognizable with it white pine cone that is progressively drawn in green. It also displays its own version on the bottom (1.0.0 as of now).
|
||||
|
||||

|
||||
|
||||
## How to update your PineTime?
|
||||
|
||||
To update your PineTime, you can use one of the compatible companion applications. Here are the main ones:
|
||||
|
||||
- **[Amazfish](https://github.com/piggz/harbour-amazfish)** (Desktop Linux, mobile Linux, SailfishOS, runs on the PinebookPro and the Pinephone)
|
||||
- **[Gadgetbridge](https://www.gadgetbridge.org/)** (Android)
|
||||
- **[Siglo](https://github.com/alexr4535/siglo)** (Linux, GTK based)
|
||||
- **NRFConnect** (closed source, Android & iOS).
|
||||
|
||||
See [this page](ota-gadgetbridge-nrfconnect.md) for more info about the OTA procedure using Gadgetbridge and NRFConnect.
|
||||
|
||||
### From InfiniTime 0.7.1 / old bootloader
|
||||
|
||||
If your PineTime is currently running InfiniTime 0.7.1 and the old bootloader, we strongly recommend you update them to more recent version (Bootloader 1.0.0 and InfiniTime 1.0.0 as of now). We also recommend you install the recovery firmware once the bootloader is up-do-date.
|
||||
|
||||
Using the companion app of your choice, you'll need to apply the OTA procedure for these 3 firmwares in this sequence (failing to follow this specific order might temporarily or permanently brick your device):
|
||||
|
||||
1. Flash the latest version of InfiniTime. The file to upload is named **pinetime-mcuboot-app-dfu-x.y.z.zip**. Here is the link to [InfiniTime 1.0](https://github.com/JF002/InfiniTime/releases/download/1.0.0/pinetime-mcuboot-app-dfu-1.0.0.zip).
|
||||
2. Update the bootloader by applying the OTA procedure with the file named [**reloader-mcuboot.zip** from the repo of the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/download/1.0.0/reloader-mcuboot.zip).
|
||||
3. Install the recovery firmware by applying the OTA procedure with the file named [**pinetime-mcuboot-recovery-loader-dfu-0.14.1.zip** from the version 0.14.1 of InfiniTime](https://github.com/JF002/InfiniTime/releases/download/0.14.1/pinetime-mcuboot-recovery-loader-dfu-0.14.1.zip).
|
||||
|
||||
You'll find more info about this process in [this wiki page](https://wiki.pine64.org/wiki/Upgrade_PineTime_to_InfiniTime_1.0.0). You can also see the procedure in video [here](https://video.codingfield.com/videos/watch/831077c5-16f3-47b4-9b2b-c4bbfecc6529) and [here (from Amazfish)](https://video.codingfield.com/videos/watch/f7bffb3d-a6a1-43c4-8f01-f4aeff4adf9e)
|
||||
|
||||
### From version > 1.0
|
||||
|
||||
If you are already running the new "1.0.0" bootloader, all you have to do is update your version of InfiniTime when it'll be available. We'll write specific instructions when (if) we release a new version of the bootloader.
|
||||
|
||||
### Firmware validation
|
||||
|
||||
The bootloader requires a (manual) validation of the firmware. If the watch reset with an updated firmware that was not validated, the bootloader will consider it as non-functioning and will revert to the previous version of the firmware. This is a safety feature to prevent bricking your device with a faulty firmware.
|
||||
|
||||
You can validate your updated firmware on InfiniTime >= 1.0 by following this simple procedure:
|
||||
|
||||
- From the watchface, swipe **right** to display the *Quick Actions menu*
|
||||
- Open the **Settings** app by tapping the *gear* icon on the bottom right
|
||||
- Swipe down and tap on the entry named **Firmware**
|
||||
- This app shows the version that is currently running. If it's not validated yet, it displays 2 buttons:
|
||||
- **Validate** to validate your firmware
|
||||
- **Reset** to reset the watch and revert to the previously running version of the firmware
|
||||
|
||||
## InfiniTime 1.0 quick user guide
|
||||
|
||||
### Setting the time
|
||||
|
||||
By default, InfiniTime starts on the digital watchface. It'll probably display the epoch time (1 Jan 1970, 00:00). The time will be automatically synchronized once you connect on of the companion app to your PineTime using BLE connectivity. InfiniTime does not provide any way to manually set the time for now.
|
||||
|
||||
### Navigation in the menu
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
- Swipe **down** to display the notification panel. Notification sent by your companion app will be displayed in this panel.
|
||||
- Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu.
|
||||
- Swipe **right** to display the Quick Actions menu. This menu allows you to
|
||||
- Set the brightness of the display
|
||||
- Start the **flashlight** app
|
||||
- Enable/disable vibrations on notifications (Do Not Disturb mode)
|
||||
- Enter the **settings** menu
|
||||
- Settings
|
||||
- Display timeout
|
||||
- Wake up event (Tap, wrist rotation)
|
||||
- Time format (12/24H)
|
||||
- Default watchface (digital / analog)
|
||||
- Battery info
|
||||
- Firmware validation
|
||||
- About (system info, firmware version,...)
|
||||
|
||||
### Bootloader
|
||||
|
||||
Most of the time, the bootloader just runs without your intervention (update and load the firmware).
|
||||
|
||||
However, you can enable 2 functionalities using the push button:
|
||||
|
||||
- Push the button until the pine cone is drawn in **blue** to force the rollback of the previous version of the firmware, even if you've already validated the updated one
|
||||
- Push the button until the pine cone is drawn in **red** to load the recovery firmware. This recovery firmware only provides BLE connectivity and OTA functionality.
|
||||
|
||||
More info about the bootloader in [its project page](https://github.com/JF002/pinetime-mcuboot-bootloader/blob/master/README.md).
|
BIN
doc/gettingStarted/oldbootloaderlogo.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
@ -8,7 +8,7 @@ If you just want to flash or upgrade InfiniTime on your PineTime, this page is f
|
||||
- [How to flash InfiniTime using the SWD interface](#how-to-flash-infinitime-using-the-swd-interface)
|
||||
|
||||
## InfiniTime releases and versions
|
||||
All releases of InfiniTime are available on the [release page of the GitHub repo](https://github.com/JF002/Pinetime/releases).
|
||||
All releases of InfiniTime are available on the [release page of the GitHub repo](https://github.com/JF002/InfiniTime/releases).
|
||||
|
||||
Versions that are tagged as **RELEASE CANDIDATE** are pre-release versions, that are available for testing before actually releasing a new stable version. If you want to help us debug the project and provide stable versions to other user, you can use them. If you want stable and tested version, you should not flash these release candidate version.
|
||||
|
||||
@ -47,6 +47,8 @@ Read carefully the warning and tap **Install**:
|
||||
|
||||
Wait for the transfer to finish. Your PineTime should reset and reboot with the new version of InfiniTime!
|
||||
|
||||
Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and Firmware option and click **validate**. Otherwise after reboot the previous firmware will be used.
|
||||
|
||||

|
||||
|
||||
### Using NRFConnect
|
||||
@ -64,6 +66,8 @@ Select **Distribution packet (ZIP)**:
|
||||
|
||||
Browse to the DFU file you downloaded previously, the DFU transfer will start automatically. When the transfer is finished, your PineTime will reset and restart on the new version of InfiniTime!
|
||||
|
||||
Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and Firmware option and click **validate**. Otherwise after reboot the previous firmware will be used.
|
||||
|
||||

|
||||
|
||||
## How to flash InfiniTime using the SWD interface
|
||||
@ -88,14 +92,18 @@ If you are using OpenOCD with a STLinkV2, you can find more info [on this page](
|
||||
### Using Gadgetbridge
|
||||
Good news! Gadgetbridge **automatically** synchronizes the time when connecting to your PineTime!
|
||||
|
||||
### Using any Chromium-based web browser
|
||||
You can use it from your PC, Mac, Android. Browsers now have BLE support.
|
||||
https://hubmartin.github.io/WebBLEWatch/
|
||||
|
||||
### Using NRFConnect
|
||||
You must enable the **CTS** *GATT server* into NRFConnect so that InfiniTime can synchronize the time with your smartphone.
|
||||
|
||||
Launch NRFConnect, tap the sandwish button on the top left and select *Configure GATT server*:
|
||||
Launch NRFConnect, tap the sandwich button on the top left and select *Configure GATT server*:
|
||||
|
||||

|
||||
|
||||
|
||||
Tap *Add service* and select the server configuration *Current Time service*. Tap OK and connect to your PineTime, it should automcatically sync the time once the connection is established!
|
||||
|
||||

|
||||

|
BIN
doc/gettingStarted/quickactions.jpg
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
doc/gettingStarted/settings.jpg
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
doc/gettingStarted/version-071.jpg
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
doc/gettingStarted/version-1.0.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
doc/memoryAnalysis/linkermapviz.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
doc/memoryAnalysis/mapfile.png
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
doc/memoryAnalysis/puncover-all-symbols.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
doc/memoryAnalysis/puncover.png
Normal file
After Width: | Height: | Size: 212 KiB |
@ -89,12 +89,12 @@ reset
|
||||
## Examples
|
||||
### Flash bootloader and application
|
||||
```
|
||||
openocd -f ./openocd-stlink.cfg -f ./flash_bootloader_app.ocd
|
||||
openocd -f ./openocd-stlink.ocd -f ./flash_bootloader_app.ocd
|
||||
```
|
||||
|
||||
### Flash graphics flasher
|
||||
```
|
||||
openocd -f ./openocd-stlink.cfg -f ./flash_graphics.ocd
|
||||
openocd -f ./openocd-stlink.ocd -f ./flash_graphics.ocd
|
||||
```
|
||||
|
||||
## Connect the STLinkV2 to the PineTime
|
||||
@ -102,4 +102,4 @@ Here is an example using the pogo pins:
|
||||

|
||||

|
||||
|
||||
You can find more information about the SWD wiring [on the wiki](https://wiki.pine64.org/index.php?title=PineTime_devkit_wiring).
|
||||
You can find more information about the SWD wiring [on the wiki](https://wiki.pine64.org/index.php?title=PineTime_devkit_wiring).
|
||||
|
BIN
doc/ui/example.png
Normal file
After Width: | Height: | Size: 10 KiB |
16
doc/ui_guidelines.md
Normal file
@ -0,0 +1,16 @@
|
||||
# UI design guidelines
|
||||
|
||||
- Align objects all the way to the edge or corner
|
||||
- Buttons should generally be at least 50px high
|
||||
- Buttons should generally be on the bottom edge
|
||||
- Make interactable objects **big**
|
||||
- Recommendations for inner padding, aka distance between buttons:
|
||||
- When aligning 4 objects: 4px, e.g. Settings
|
||||
- When aligning 3 objects: 6px, e.g. App list
|
||||
- When aligning 2 objects: 10px, e.g. Quick settings
|
||||
- When using a page indicator, leave 8px for it on the right side
|
||||
- It is acceptable to leave 8px on the left side as well to center the content
|
||||
- Top bar takes at least 20px + padding
|
||||
- Top bar right icons move 8px to the left when using a page indicator
|
||||
|
||||

|
@ -3,4 +3,4 @@ The versioning of this project is based on [Semantic versionning](https://semver
|
||||
|
||||
- The **patch** is incremented when we fix a bug on a **released** version (most of the time using a **hotfix** branch).
|
||||
- The **minor** is incremented when we release a new version with new features. It corresponds to a merge of **develop** into **master**.
|
||||
- The **major** should be incremented when a breaking change is made to the application. We still have to define what is a breaking change in the context of this project. For now, I suggest that it stays **0** until we have a fully functionning firmware suited for the final user.
|
||||
- The **major** should be incremented when a breaking change is made to the application. We still have to define what is a breaking change in the context of this project. For now, I suggest that it stays **0** until we have a fully functioning firmware suited for the final user.
|
43
docker/.gitpod.Dockerfile
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
USER root
|
||||
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 \
|
||||
# aarch64 packages
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
python3-dev \
|
||||
git \
|
||||
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
|
||||
|
||||
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
|
||||
|
||||
# Needs to be installed as root
|
||||
RUN pip3 install adafruit-nrfutil
|
||||
RUN pip3 install -Iv cryptography==3.3
|
||||
|
||||
COPY docker/build.sh /opt/
|
||||
# Lets get each in a separate docker layer for better downloads
|
||||
# GCC
|
||||
RUN bash -c "source /opt/build.sh; GetGcc;"
|
||||
# NrfSdk
|
||||
RUN bash -c "source /opt/build.sh; GetNrfSdk;"
|
||||
# McuBoot
|
||||
RUN bash -c "source /opt/build.sh; GetMcuBoot;"
|
||||
|
||||
# Link the default checkout workspace in to the default $SOURCES_DIR
|
||||
RUN ln -s /workspace/InfiniTime /sources
|
||||
|
||||
USER gitpod
|
47
docker/Dockerfile
Normal file
@ -0,0 +1,47 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
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 \
|
||||
# aarch64 packages
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
python3-dev \
|
||||
python \
|
||||
git \
|
||||
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
|
||||
|
||||
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
|
||||
|
||||
RUN pip3 install adafruit-nrfutil
|
||||
RUN pip3 install -Iv cryptography==3.3
|
||||
|
||||
# build.sh knows how to compile
|
||||
COPY build.sh /opt/
|
||||
|
||||
# Lets get each in a separate docker layer for better downloads
|
||||
# GCC
|
||||
RUN bash -c "source /opt/build.sh; GetGcc;"
|
||||
# NrfSdk
|
||||
RUN bash -c "source /opt/build.sh; GetNrfSdk;"
|
||||
# McuBoot
|
||||
RUN bash -c "source /opt/build.sh; GetMcuBoot;"
|
||||
|
||||
ARG PUID=1000
|
||||
ARG PGID=1000
|
||||
RUN groupadd --system --gid $PGID infinitime && useradd --system --uid $PUID --gid $PGID infinitime
|
||||
|
||||
USER infinitime:infinitime
|
||||
ENV SOURCES_DIR /sources
|
||||
CMD ["/opt/build.sh"]
|
@ -1,17 +0,0 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
ARG USER_ID
|
||||
ARG GROUP_ID
|
||||
|
||||
RUN addgroup --gid $GROUP_ID user
|
||||
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
|
||||
|
||||
RUN apt-get update -qq && apt-get install -y wget unzip cmake make build-essential git python3 python3-pip libffi-dev libssl-dev python3-dev
|
||||
RUN wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-aarch64-linux.tar.bz2 -O - | tar -xj -C /opt/
|
||||
RUN wget -q https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip && unzip -q nRF5_SDK_15.3.0_59ac345.zip -d /opt/ && rm nRF5_SDK_15.3.0_59ac345.zip
|
||||
|
||||
RUN git clone https://github.com/JuulLabs-OSS/mcuboot.git /opt/mcuboot && pip3 install -r /opt/mcuboot/scripts/requirements.txt
|
||||
RUN pip3 install adafruit-nrfutil
|
||||
|
||||
USER user
|
||||
CMD ["/sources/docker/build.sh"]
|
@ -1,12 +1,79 @@
|
||||
#!/bin/sh
|
||||
#!/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
|
||||
|
||||
mkdir /sources/build
|
||||
cd /sources/build
|
||||
# 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}"
|
||||
|
||||
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update -DNRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345 -DUSE_OPENOCD=1 ../
|
||||
make -j$(nproc)
|
||||
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"}
|
||||
|
||||
sh /sources/docker/post_build.sh
|
||||
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" \
|
||||
-DBUILD_DFU=1 \
|
||||
"$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!"
|
@ -1,16 +1,26 @@
|
||||
#!/bin/sh
|
||||
export LC_ALL=C.UTF-8
|
||||
export LANG=C.UTF-8
|
||||
set -x
|
||||
set -e
|
||||
set +x
|
||||
|
||||
mkdir -p /sources/build/output
|
||||
/opt/mcuboot/scripts/imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header /sources/build/src/pinetime-mcuboot-app-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.bin /sources/build/output/image-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.bin
|
||||
adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application /sources/build/output/image-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.bin /sources/build/output/dfu-@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.zip
|
||||
export PROJECT_VERSION="@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@"
|
||||
|
||||
cp /sources/build/src/*.bin /sources/build/output/
|
||||
cp /sources/build/src/*.hex /sources/build/output/
|
||||
cp /sources/build/src/*.out /sources/build/output/
|
||||
cp /sources/build/src/*.map /sources/build/output/
|
||||
cp /sources/bootloader/bootloader-5.0.4.bin /sources/build/output/bootloader.bin
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
cp "$SOURCES_DIR"/bootloader/bootloader-5.0.4.bin $OUTPUT_DIR/bootloader.bin
|
||||
cp "$BUILD_DIR/src/pinetime-mcuboot-app-image-$PROJECT_VERSION.bin" "$OUTPUT_DIR/pinetime-mcuboot-app-image-$PROJECT_VERSION.bin"
|
||||
cp "$BUILD_DIR/src/pinetime-mcuboot-app-dfu-$PROJECT_VERSION.zip" "$OUTPUT_DIR/pinetime-mcuboot-app-dfu-$PROJECT_VERSION.zip"
|
||||
|
||||
cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin"
|
||||
cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip"
|
||||
|
||||
|
||||
mkdir -p "$OUTPUT_DIR/src"
|
||||
cd "$BUILD_DIR"
|
||||
cp src/*.bin "$OUTPUT_DIR/src"
|
||||
cp src/*.hex "$OUTPUT_DIR/src"
|
||||
cp src/*.out "$OUTPUT_DIR/src"
|
||||
cp src/*.map "$OUTPUT_DIR/src"
|
||||
|
||||
ls -RUv1 "$OUTPUT_DIR" | sed 's;^\([^/]\); \1;g'
|
@ -1,17 +0,0 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
ARG USER_ID
|
||||
ARG GROUP_ID
|
||||
|
||||
RUN addgroup --gid $GROUP_ID user
|
||||
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
|
||||
|
||||
RUN apt-get update -qq && apt-get install -y wget unzip cmake make build-essential git python3 python3-pip
|
||||
RUN wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz -O - | tar -xj -C /opt/
|
||||
RUN wget -q https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip && unzip -q nRF5_SDK_15.3.0_59ac345.zip -d /opt/ && rm nRF5_SDK_15.3.0_59ac345.zip
|
||||
|
||||
RUN git clone https://github.com/JuulLabs-OSS/mcuboot.git /opt/mcuboot && pip3 install -r /opt/mcuboot/scripts/requirements.txt
|
||||
RUN pip3 install adafruit-nrfutil
|
||||
|
||||
USER user
|
||||
CMD ["/sources/docker/build.sh"]
|
@ -6,12 +6,18 @@ GROUP(-lgcc -lc -lnosys)
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x08020, LENGTH = 0x78000
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
}
|
||||
.noinit(NOLOAD):
|
||||
{
|
||||
PROVIDE(__start_noinit_data = .);
|
||||
*(.noinit)
|
||||
PROVIDE(__stop_noinit_data = .);
|
||||
} > RAM
|
||||
} INSERT AFTER .bss
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
11
gcc_nrf52.ld
@ -6,12 +6,18 @@ GROUP(-lgcc -lc -lnosys)
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x00000, LENGTH = 0x78000
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
}
|
||||
.noinit(NOLOAD):
|
||||
{
|
||||
PROVIDE(__start_noinit_data = .);
|
||||
*(.noinit)
|
||||
PROVIDE(__stop_noinit_data = .);
|
||||
} > RAM
|
||||
} INSERT AFTER .bss
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
@ -44,6 +50,7 @@ SECTIONS
|
||||
PROVIDE(__stop_log_filter_data = .);
|
||||
} > RAM
|
||||
|
||||
|
||||
} INSERT AFTER .data;
|
||||
|
||||
SECTIONS
|
||||
|
5
hooks/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Git hooks
|
||||
|
||||
This directory contains Git hooks that simplify contributing to this repository.
|
||||
|
||||
You can install them by copying the files into `.git/hooks` in the repository folder or by running `git config --local core.hooksPath hooks`
|
25
hooks/pre-commit
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
if clang-format --version | grep -q 'version 11\.'; then
|
||||
CLANG_FORMAT_EXECUTABLE="clang-format"
|
||||
else
|
||||
CLANG_FORMAT_EXECUTABLE="clang-format-11"
|
||||
fi
|
||||
|
||||
if ! command -v $CLANG_FORMAT_EXECUTABLE &> /dev/null
|
||||
then
|
||||
echo $CLANG_FORMAT_EXECUTABLE does not exist, make sure to install it
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for FILE in $(git diff --cached --name-only)
|
||||
do
|
||||
if [[ "$FILE" =~ src/[A-Za-z0-9\ \-]+*\.(c|h|cpp|cc)$ ]]; then
|
||||
echo Autoformatting $FILE with $CLANG_FORMAT_EXECUTABLE
|
||||
$CLANG_FORMAT_EXECUTABLE -style=file -i -- $FILE
|
||||
git add -- $FILE
|
||||
elif [[ "$FILE" =~ src/(components|displayapp|drivers|heartratetask|logging|systemtask)/.*\.(c|h|cpp|cc)$ ]]; then
|
||||
echo Autoformatting $FILE with $CLANG_FORMAT_EXECUTABLE
|
||||
$CLANG_FORMAT_EXECUTABLE -style=file -i -- $FILE
|
||||
git add -- $FILE
|
||||
fi
|
||||
done
|
BIN
images/0.14.0/collage1.png
Normal file
After Width: | Height: | Size: 607 KiB |
BIN
images/0.14.0/collage2.png
Normal file
After Width: | Height: | Size: 625 KiB |
BIN
images/1.0.0/collage.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
images/infinitime-logo-github.jpg
Normal file
After Width: | Height: | Size: 37 KiB |
@ -1,26 +1,36 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include "BootloaderVersion.h"
|
||||
|
||||
using namespace Pinetime;
|
||||
|
||||
// NOTE : current bootloader does not export its version to the application firmware.
|
||||
// NOTE : version < 1.0.0 of bootloader does not export its version to the application firmware.
|
||||
|
||||
uint32_t BootloaderVersion::Major() {
|
||||
return 0;
|
||||
uint32_t BootloaderVersion::version = 0;
|
||||
char BootloaderVersion::versionString[BootloaderVersion::VERSION_STR_LEN] = "0.0.0";
|
||||
|
||||
const uint32_t BootloaderVersion::Major() {
|
||||
return (BootloaderVersion::version >> 16u) & 0xff;
|
||||
}
|
||||
|
||||
uint32_t BootloaderVersion::Minor() {
|
||||
return 0;
|
||||
const uint32_t BootloaderVersion::Minor() {
|
||||
return (BootloaderVersion::version >> 8u) & 0xff;
|
||||
}
|
||||
|
||||
uint32_t BootloaderVersion::Patch() {
|
||||
return 0;
|
||||
const uint32_t BootloaderVersion::Patch() {
|
||||
return BootloaderVersion::version & 0xff;
|
||||
}
|
||||
|
||||
const char *BootloaderVersion::VersionString() {
|
||||
return "0.0.0";
|
||||
const char* BootloaderVersion::VersionString() {
|
||||
return BootloaderVersion::versionString;
|
||||
}
|
||||
|
||||
bool BootloaderVersion::IsValid() {
|
||||
return false;
|
||||
const bool BootloaderVersion::IsValid() {
|
||||
return BootloaderVersion::version >= 0x00010000;
|
||||
}
|
||||
|
||||
void BootloaderVersion::SetVersion(uint32_t v) {
|
||||
BootloaderVersion::version = v;
|
||||
snprintf(BootloaderVersion::versionString, BootloaderVersion::VERSION_STR_LEN, "%ld.%ld.%ld",
|
||||
BootloaderVersion::Major(), BootloaderVersion::Minor(), BootloaderVersion::Patch());
|
||||
}
|
||||
|
@ -2,11 +2,16 @@
|
||||
|
||||
namespace Pinetime {
|
||||
class BootloaderVersion {
|
||||
public:
|
||||
static uint32_t Major();
|
||||
static uint32_t Minor();
|
||||
static uint32_t Patch();
|
||||
static const char* VersionString();
|
||||
static bool IsValid();
|
||||
public:
|
||||
static const uint32_t Major();
|
||||
static const uint32_t Minor();
|
||||
static const uint32_t Patch();
|
||||
static const char* VersionString();
|
||||
static const bool IsValid();
|
||||
static void SetVersion(uint32_t v);
|
||||
private:
|
||||
static uint32_t version;
|
||||
static constexpr size_t VERSION_STR_LEN = 12;
|
||||
static char versionString[VERSION_STR_LEN];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -294,6 +294,25 @@ static void vPortEnableVFP( void )
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
uint32_t ulSetInterruptMaskFromISR( void )
|
||||
{
|
||||
__asm volatile (
|
||||
" mrs r0, PRIMASK \n"
|
||||
" cpsid i \n"
|
||||
" bx lr "
|
||||
::: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
void vClearInterruptMaskFromISR( __attribute__( ( unused ) ) uint32_t ulMask )
|
||||
{
|
||||
__asm volatile (
|
||||
" msr PRIMASK, r0 \n"
|
||||
" bx lr "
|
||||
::: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
#if ( configASSERT_DEFINED == 1 )
|
||||
|
||||
void vPortValidateInterruptPriority( void )
|
||||
|
@ -104,8 +104,10 @@ typedef unsigned long UBaseType_t;
|
||||
/* Critical section management. */
|
||||
extern void vPortEnterCritical( void );
|
||||
extern void vPortExitCritical( void );
|
||||
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
|
||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)
|
||||
extern uint32_t ulSetInterruptMaskFromISR( void ) __attribute__( ( naked ) );
|
||||
extern void vClearInterruptMaskFromISR( uint32_t ulMask ) __attribute__( ( naked ) );
|
||||
#define portSET_INTERRUPT_MASK_FROM_ISR() ulSetInterruptMaskFromISR()
|
||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vClearInterruptMaskFromISR( x )
|
||||
#define portDISABLE_INTERRUPTS() __asm volatile ( " cpsid i " ::: "memory" )
|
||||
#define portENABLE_INTERRUPTS() __asm volatile ( " cpsie i " ::: "memory" )
|
||||
#define portENTER_CRITICAL() vPortEnterCritical()
|
||||
|
@ -26,20 +26,19 @@
|
||||
* 1 tab == 4 spaces!
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FREERTOS_CONFIG_H
|
||||
#define FREERTOS_CONFIG_H
|
||||
|
||||
#ifdef SOFTDEVICE_PRESENT
|
||||
#include "nrf_soc.h"
|
||||
#include "nrf_soc.h"
|
||||
#endif
|
||||
#include "app_util_platform.h"
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Possible configurations for system timer
|
||||
*/
|
||||
#define FREERTOS_USE_RTC 0 /**< Use real time clock for the system */
|
||||
#define FREERTOS_USE_SYSTICK 1 /**< Use SysTick timer for system */
|
||||
#define FREERTOS_USE_RTC 0 /**< Use real time clock for the system */
|
||||
#define FREERTOS_USE_SYSTICK 1 /**< Use SysTick timer for system */
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Application specific definitions.
|
||||
@ -55,153 +54,150 @@
|
||||
|
||||
#define configTICK_SOURCE FREERTOS_USE_RTC
|
||||
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
|
||||
#define configUSE_TICKLESS_IDLE 1
|
||||
#define configUSE_TICKLESS_IDLE_SIMPLE_DEBUG 0 /* See into vPortSuppressTicksAndSleep source code for explanation */
|
||||
#define configCPU_CLOCK_HZ ( SystemCoreClock )
|
||||
#define configTICK_RATE_HZ 1024
|
||||
#define configMAX_PRIORITIES ( 3 )
|
||||
#define configMINIMAL_STACK_SIZE ( 120 )
|
||||
#define configTOTAL_HEAP_SIZE ( 1024*11 )
|
||||
#define configMAX_TASK_NAME_LEN ( 4 )
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configIDLE_SHOULD_YIELD 1
|
||||
#define configUSE_MUTEXES 1
|
||||
#define configUSE_RECURSIVE_MUTEXES 1
|
||||
#define configUSE_COUNTING_SEMAPHORES 1
|
||||
#define configUSE_ALTERNATIVE_API 0 /* Deprecated! */
|
||||
#define configQUEUE_REGISTRY_SIZE 2
|
||||
#define configUSE_QUEUE_SETS 0
|
||||
#define configUSE_TIME_SLICING 0
|
||||
#define configUSE_NEWLIB_REENTRANT 0
|
||||
#define configENABLE_BACKWARD_COMPATIBILITY 1
|
||||
#define configUSE_TICKLESS_IDLE 1
|
||||
#define configUSE_TICKLESS_IDLE_SIMPLE_DEBUG 0 /* See into vPortSuppressTicksAndSleep source code for explanation */
|
||||
#define configCPU_CLOCK_HZ (SystemCoreClock)
|
||||
#define configTICK_RATE_HZ 1024
|
||||
#define configMAX_PRIORITIES (3)
|
||||
#define configMINIMAL_STACK_SIZE (120)
|
||||
#define configTOTAL_HEAP_SIZE (1024 * 17)
|
||||
#define configMAX_TASK_NAME_LEN (4)
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configIDLE_SHOULD_YIELD 1
|
||||
#define configUSE_MUTEXES 1
|
||||
#define configUSE_RECURSIVE_MUTEXES 1
|
||||
#define configUSE_COUNTING_SEMAPHORES 1
|
||||
#define configUSE_ALTERNATIVE_API 0 /* Deprecated! */
|
||||
#define configQUEUE_REGISTRY_SIZE 2
|
||||
#define configUSE_QUEUE_SETS 0
|
||||
#define configUSE_TIME_SLICING 0
|
||||
#define configUSE_NEWLIB_REENTRANT 0
|
||||
#define configENABLE_BACKWARD_COMPATIBILITY 1
|
||||
|
||||
/* Hook function related definitions. */
|
||||
#define configUSE_IDLE_HOOK 1
|
||||
#define configUSE_TICK_HOOK 0
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 0
|
||||
#define configUSE_MALLOC_FAILED_HOOK 0
|
||||
#define configUSE_IDLE_HOOK 0
|
||||
#define configUSE_TICK_HOOK 0
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 0
|
||||
#define configUSE_MALLOC_FAILED_HOOK 0
|
||||
|
||||
/* Run time and task stats gathering related definitions. */
|
||||
#define configGENERATE_RUN_TIME_STATS 0
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
|
||||
#define configGENERATE_RUN_TIME_STATS 0
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
|
||||
|
||||
/* Co-routine definitions. */
|
||||
#define configUSE_CO_ROUTINES 0
|
||||
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
|
||||
#define configUSE_CO_ROUTINES 0
|
||||
#define configMAX_CO_ROUTINE_PRIORITIES (2)
|
||||
|
||||
/* Software timer definitions. */
|
||||
#define configUSE_TIMERS 1
|
||||
#define configTIMER_TASK_PRIORITY ( 0 )
|
||||
#define configTIMER_QUEUE_LENGTH 32
|
||||
#define configTIMER_TASK_STACK_DEPTH ( 200 )
|
||||
#define configUSE_TIMERS 1
|
||||
#define configTIMER_TASK_PRIORITY (0)
|
||||
#define configTIMER_QUEUE_LENGTH 32
|
||||
#define configTIMER_TASK_STACK_DEPTH (300)
|
||||
|
||||
/* Tickless Idle configuration. */
|
||||
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
|
||||
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
|
||||
|
||||
/* Tickless idle/low power functionality. */
|
||||
|
||||
|
||||
/* Define to trap errors during development. */
|
||||
#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)
|
||||
#define configASSERT( x ) ASSERT(x)
|
||||
#define configASSERT(x) ASSERT(x)
|
||||
#endif
|
||||
|
||||
/* FreeRTOS MPU specific definitions. */
|
||||
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 1
|
||||
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 1
|
||||
|
||||
/* Optional functions - most linkers will remove unused functions anyway. */
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
#define INCLUDE_uxTaskPriorityGet 1
|
||||
#define INCLUDE_vTaskDelete 1
|
||||
#define INCLUDE_vTaskSuspend 1
|
||||
#define INCLUDE_xResumeFromISR 1
|
||||
#define INCLUDE_vTaskDelayUntil 1
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_xTaskGetSchedulerState 1
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||
#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1
|
||||
#define INCLUDE_pcTaskGetTaskName 1
|
||||
#define INCLUDE_eTaskGetState 1
|
||||
#define INCLUDE_xEventGroupSetBitFromISR 1
|
||||
#define INCLUDE_xTimerPendFunctionCall 1
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
#define INCLUDE_uxTaskPriorityGet 1
|
||||
#define INCLUDE_vTaskDelete 1
|
||||
#define INCLUDE_vTaskSuspend 1
|
||||
#define INCLUDE_xResumeFromISR 1
|
||||
#define INCLUDE_vTaskDelayUntil 1
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_xTaskGetSchedulerState 1
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||
#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1
|
||||
#define INCLUDE_pcTaskGetTaskName 1
|
||||
#define INCLUDE_eTaskGetState 1
|
||||
#define INCLUDE_xEventGroupSetBitFromISR 1
|
||||
#define INCLUDE_xTimerPendFunctionCall 1
|
||||
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
|
||||
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY _PRIO_APP_HIGH
|
||||
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY _PRIO_APP_HIGH
|
||||
|
||||
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||
#define configKERNEL_INTERRUPT_PRIORITY configLIBRARY_LOWEST_INTERRUPT_PRIORITY
|
||||
#define configKERNEL_INTERRUPT_PRIORITY configLIBRARY_LOWEST_INTERRUPT_PRIORITY
|
||||
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
|
||||
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
|
||||
|
||||
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||
standard names - or at least those used in the unmodified vector table. */
|
||||
|
||||
#define vPortSVCHandler SVC_Handler
|
||||
#define xPortPendSVHandler PendSV_Handler
|
||||
|
||||
#define vPortSVCHandler SVC_Handler
|
||||
#define xPortPendSVHandler PendSV_Handler
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Settings that are generated automatically
|
||||
* basing on the settings above
|
||||
*/
|
||||
#if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
|
||||
// do not define configSYSTICK_CLOCK_HZ for SysTick to be configured automatically
|
||||
// to CPU clock source
|
||||
#define xPortSysTickHandler SysTick_Handler
|
||||
// do not define configSYSTICK_CLOCK_HZ for SysTick to be configured automatically
|
||||
// to CPU clock source
|
||||
#define xPortSysTickHandler SysTick_Handler
|
||||
#elif (configTICK_SOURCE == FREERTOS_USE_RTC)
|
||||
#define configSYSTICK_CLOCK_HZ ( 32768UL )
|
||||
#define xPortSysTickHandler RTC1_IRQHandler
|
||||
#define configSYSTICK_CLOCK_HZ (32768UL)
|
||||
#define xPortSysTickHandler RTC1_IRQHandler
|
||||
#else
|
||||
#error Unsupported configTICK_SOURCE value
|
||||
#error Unsupported configTICK_SOURCE value
|
||||
#endif
|
||||
|
||||
/* Code below should be only used by the compiler, and not the assembler. */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__ASSEMBLER__))
|
||||
#include "nrf.h"
|
||||
#include "nrf_assert.h"
|
||||
#include "nrf.h"
|
||||
#include "nrf_assert.h"
|
||||
|
||||
/* This part of definitions may be problematic in assembly - it uses definitions from files that are not assembly compatible. */
|
||||
/* Cortex-M specific definitions. */
|
||||
#ifdef __NVIC_PRIO_BITS
|
||||
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
#else
|
||||
#error "This port requires __NVIC_PRIO_BITS to be defined"
|
||||
#endif
|
||||
/* This part of definitions may be problematic in assembly - it uses definitions from files that are not assembly compatible. */
|
||||
/* Cortex-M specific definitions. */
|
||||
#ifdef __NVIC_PRIO_BITS
|
||||
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
#else
|
||||
#error "This port requires __NVIC_PRIO_BITS to be defined"
|
||||
#endif
|
||||
|
||||
/* Access to current system core clock is required only if we are ticking the system by systimer */
|
||||
#if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
|
||||
#include <stdint.h>
|
||||
extern uint32_t SystemCoreClock;
|
||||
#endif
|
||||
/* Access to current system core clock is required only if we are ticking the system by systimer */
|
||||
#if (configTICK_SOURCE == FREERTOS_USE_SYSTICK)
|
||||
#include <stdint.h>
|
||||
extern uint32_t SystemCoreClock;
|
||||
#endif
|
||||
#endif /* !assembler */
|
||||
|
||||
/** Implementation note: Use this with caution and set this to 1 ONLY for debugging
|
||||
* ----------------------------------------------------------
|
||||
* Set the value of configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to below for enabling or disabling RTOS tick auto correction:
|
||||
* 0. This is default. If the RTC tick interrupt is masked for more than 1 tick by higher priority interrupts, then most likely
|
||||
* one or more RTC ticks are lost. The tick interrupt inside RTOS will detect this and make a correction needed. This is needed
|
||||
* for the RTOS internal timers to be more accurate.
|
||||
* 1. The auto correction for RTOS tick is disabled even though few RTC tick interrupts were lost. This feature is desirable when debugging
|
||||
* the RTOS application and stepping though the code. After stepping when the application is continued in debug mode, the auto-corrections of
|
||||
* RTOS tick might cause asserts. Setting configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to 1 will make RTC and RTOS go out of sync but could be
|
||||
* convenient for debugging.
|
||||
*/
|
||||
#define configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG 0
|
||||
* Set the value of configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to below for enabling or disabling RTOS tick auto correction:
|
||||
* 0. This is default. If the RTC tick interrupt is masked for more than 1 tick by higher priority interrupts, then most likely
|
||||
* one or more RTC ticks are lost. The tick interrupt inside RTOS will detect this and make a correction needed. This is needed
|
||||
* for the RTOS internal timers to be more accurate.
|
||||
* 1. The auto correction for RTOS tick is disabled even though few RTC tick interrupts were lost. This feature is desirable when debugging
|
||||
* the RTOS application and stepping though the code. After stepping when the application is continued in debug mode, the
|
||||
* auto-corrections of RTOS tick might cause asserts. Setting configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG to 1 will make RTC and RTOS go
|
||||
* out of sync but could be convenient for debugging.
|
||||
*/
|
||||
#define configUSE_DISABLE_TICK_AUTO_CORRECTION_DEBUG 0
|
||||
|
||||
#endif /* FREERTOS_CONFIG_H */
|
||||
|
@ -8,11 +8,13 @@ namespace Pinetime {
|
||||
static constexpr uint32_t Major() {return major;}
|
||||
static constexpr uint32_t Minor() {return minor;}
|
||||
static constexpr uint32_t Patch() {return patch;}
|
||||
static constexpr const char* GitCommitHash() {return commitHash;}
|
||||
static constexpr const char* VersionString() {return versionString;}
|
||||
private:
|
||||
static constexpr uint32_t major = @PROJECT_VERSION_MAJOR@;
|
||||
static constexpr uint32_t minor = @PROJECT_VERSION_MINOR@;
|
||||
static constexpr uint32_t patch = @PROJECT_VERSION_PATCH@;
|
||||
static constexpr const char* commitHash = "@PROJECT_GIT_COMMIT_HASH@";
|
||||
static constexpr const char* versionString = "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@";
|
||||
};
|
||||
}
|
114
src/components/alarm/AlarmController.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
/* Copyright (C) 2021 mruss77, Florian
|
||||
|
||||
This file is part of InfiniTime.
|
||||
|
||||
InfiniTime is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
InfiniTime is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "AlarmController.h"
|
||||
#include "systemtask/SystemTask.h"
|
||||
#include "app_timer.h"
|
||||
#include "task.h"
|
||||
#include <chrono>
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
AlarmController::AlarmController(Controllers::DateTime& dateTimeController) : dateTimeController {dateTimeController} {
|
||||
}
|
||||
|
||||
APP_TIMER_DEF(alarmAppTimer);
|
||||
|
||||
namespace {
|
||||
void SetOffAlarm(void* p_context) {
|
||||
auto* controller = static_cast<Pinetime::Controllers::AlarmController*>(p_context);
|
||||
if (controller != nullptr) {
|
||||
controller->SetOffAlarmNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AlarmController::Init(System::SystemTask* systemTask) {
|
||||
app_timer_create(&alarmAppTimer, APP_TIMER_MODE_SINGLE_SHOT, SetOffAlarm);
|
||||
this->systemTask = systemTask;
|
||||
}
|
||||
|
||||
void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) {
|
||||
hours = alarmHr;
|
||||
minutes = alarmMin;
|
||||
}
|
||||
|
||||
void AlarmController::ScheduleAlarm() {
|
||||
// Determine the next time the alarm needs to go off and set the app_timer
|
||||
app_timer_stop(alarmAppTimer);
|
||||
|
||||
auto now = dateTimeController.CurrentDateTime();
|
||||
alarmTime = now;
|
||||
time_t ttAlarmTime = std::chrono::system_clock::to_time_t(alarmTime);
|
||||
tm* tmAlarmTime = std::localtime(&ttAlarmTime);
|
||||
|
||||
// If the time being set has already passed today,the alarm should be set for tomorrow
|
||||
if (hours < dateTimeController.Hours() || (hours == dateTimeController.Hours() && minutes <= dateTimeController.Minutes())) {
|
||||
tmAlarmTime->tm_mday += 1;
|
||||
// tm_wday doesn't update automatically
|
||||
tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
|
||||
}
|
||||
|
||||
tmAlarmTime->tm_hour = hours;
|
||||
tmAlarmTime->tm_min = minutes;
|
||||
tmAlarmTime->tm_sec = 0;
|
||||
|
||||
// if alarm is in weekday-only mode, make sure it shifts to the next weekday
|
||||
if (recurrence == RecurType::Weekdays) {
|
||||
if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day
|
||||
tmAlarmTime->tm_mday += 1;
|
||||
} else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
|
||||
tmAlarmTime->tm_mday += 2;
|
||||
}
|
||||
}
|
||||
tmAlarmTime->tm_isdst = -1; // use system timezone setting to determine DST
|
||||
|
||||
// now can convert back to a time_point
|
||||
alarmTime = std::chrono::system_clock::from_time_t(std::mktime(tmAlarmTime));
|
||||
auto mSecToAlarm = std::chrono::duration_cast<std::chrono::milliseconds>(alarmTime - now).count();
|
||||
app_timer_start(alarmAppTimer, APP_TIMER_TICKS(mSecToAlarm), this);
|
||||
|
||||
state = AlarmState::Set;
|
||||
}
|
||||
|
||||
uint32_t AlarmController::SecondsToAlarm() {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(alarmTime - dateTimeController.CurrentDateTime()).count();
|
||||
}
|
||||
|
||||
void AlarmController::DisableAlarm() {
|
||||
app_timer_stop(alarmAppTimer);
|
||||
state = AlarmState::Not_Set;
|
||||
}
|
||||
|
||||
void AlarmController::SetOffAlarmNow() {
|
||||
state = AlarmState::Alerting;
|
||||
systemTask->PushMessage(System::Messages::SetOffAlarm);
|
||||
}
|
||||
|
||||
void AlarmController::StopAlerting() {
|
||||
systemTask->PushMessage(System::Messages::StopRinging);
|
||||
|
||||
// Alarm state is off unless this is a recurring alarm
|
||||
if (recurrence == RecurType::None) {
|
||||
state = AlarmState::Not_Set;
|
||||
} else {
|
||||
state = AlarmState::Set;
|
||||
// set next instance
|
||||
ScheduleAlarm();
|
||||
}
|
||||
}
|
68
src/components/alarm/AlarmController.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* Copyright (C) 2021 mruss77, Florian
|
||||
|
||||
This file is part of InfiniTime.
|
||||
|
||||
InfiniTime is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
InfiniTime is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "app_timer.h"
|
||||
#include "components/datetime/DateTimeController.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace System {
|
||||
class SystemTask;
|
||||
}
|
||||
namespace Controllers {
|
||||
class AlarmController {
|
||||
public:
|
||||
AlarmController(Controllers::DateTime& dateTimeController);
|
||||
|
||||
void Init(System::SystemTask* systemTask);
|
||||
void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
|
||||
void ScheduleAlarm();
|
||||
void DisableAlarm();
|
||||
void SetOffAlarmNow();
|
||||
uint32_t SecondsToAlarm();
|
||||
void StopAlerting();
|
||||
enum class AlarmState { Not_Set, Set, Alerting };
|
||||
enum class RecurType { None, Daily, Weekdays };
|
||||
uint8_t Hours() const {
|
||||
return hours;
|
||||
}
|
||||
uint8_t Minutes() const {
|
||||
return minutes;
|
||||
}
|
||||
AlarmState State() const {
|
||||
return state;
|
||||
}
|
||||
RecurType Recurrence() const {
|
||||
return recurrence;
|
||||
}
|
||||
void SetRecurrence(RecurType recurType) {
|
||||
recurrence = recurType;
|
||||
}
|
||||
|
||||
private:
|
||||
Controllers::DateTime& dateTimeController;
|
||||
System::SystemTask* systemTask = nullptr;
|
||||
uint8_t hours = 7;
|
||||
uint8_t minutes = 0;
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> alarmTime;
|
||||
AlarmState state = AlarmState::Not_Set;
|
||||
RecurType recurrence = RecurType::None;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,48 +1,89 @@
|
||||
#include <drivers/include/nrfx_saadc.h>
|
||||
#include <hal/nrf_gpio.h>
|
||||
#include <libraries/log/nrf_log.h>
|
||||
#include <algorithm>
|
||||
#include "BatteryController.h"
|
||||
#include "drivers/PinMap.h"
|
||||
#include <hal/nrf_gpio.h>
|
||||
#include <nrfx_saadc.h>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
void Battery::Init() {
|
||||
nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup);
|
||||
nrf_gpio_cfg_input(powerPresentPin, (nrf_gpio_pin_pull_t)GPIO_PIN_CNF_PULL_Pullup);
|
||||
Battery* Battery::instance = nullptr;
|
||||
|
||||
nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
|
||||
nrfx_saadc_init(&adcConfig, SaadcEventHandler);
|
||||
nrf_saadc_channel_config_t adcChannelConfig = {
|
||||
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
|
||||
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
|
||||
.gain = NRF_SAADC_GAIN1_5,
|
||||
.reference = NRF_SAADC_REFERENCE_INTERNAL,
|
||||
.acq_time = NRF_SAADC_ACQTIME_3US,
|
||||
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
|
||||
.burst = NRF_SAADC_BURST_DISABLED,
|
||||
.pin_p = batteryVoltageAdcInput,
|
||||
.pin_n = NRF_SAADC_INPUT_DISABLED
|
||||
};
|
||||
nrfx_saadc_channel_init(0, &adcChannelConfig);
|
||||
Battery::Battery() {
|
||||
instance = this;
|
||||
nrf_gpio_cfg_input(PinMap::Charging, static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Disabled);
|
||||
}
|
||||
|
||||
void Battery::Update() {
|
||||
isCharging = !nrf_gpio_pin_read(chargingPin);
|
||||
isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
|
||||
isCharging = !nrf_gpio_pin_read(PinMap::Charging);
|
||||
isPowerPresent = !nrf_gpio_pin_read(PinMap::PowerPresent);
|
||||
|
||||
nrf_saadc_value_t value = 0;
|
||||
nrfx_saadc_sample_convert(0, &value);
|
||||
if (isPowerPresent && !isCharging) {
|
||||
isFull = true;
|
||||
} else if (!isPowerPresent) {
|
||||
isFull = false;
|
||||
}
|
||||
|
||||
// see https://forum.pine64.org/showthread.php?tid=8147
|
||||
voltage = (value * 2.0f) / (1024/3.0f);
|
||||
percentRemaining = ((voltage - 3.55f)*100.0f)*3.9f;
|
||||
percentRemaining = std::max(percentRemaining, 0.0f);
|
||||
percentRemaining = std::min(percentRemaining, 100.0f);
|
||||
if (isReading) {
|
||||
return;
|
||||
}
|
||||
// Non blocking read
|
||||
isReading = true;
|
||||
SaadcInit();
|
||||
|
||||
// NRF_LOG_INFO("BATTERY " NRF_LOG_FLOAT_MARKER " %% - " NRF_LOG_FLOAT_MARKER " v", NRF_LOG_FLOAT(percentRemaining), NRF_LOG_FLOAT(voltage));
|
||||
// NRF_LOG_INFO("POWER Charging : %d - Power : %d", isCharging, isPowerPresent);
|
||||
nrfx_saadc_sample();
|
||||
}
|
||||
|
||||
void Battery::SaadcEventHandler(nrfx_saadc_evt_t const * event) {
|
||||
void Battery::AdcCallbackStatic(nrfx_saadc_evt_t const* event) {
|
||||
instance->SaadcEventHandler(event);
|
||||
}
|
||||
|
||||
}
|
||||
void Battery::SaadcInit() {
|
||||
nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
|
||||
APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, AdcCallbackStatic));
|
||||
|
||||
nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
|
||||
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
|
||||
.gain = NRF_SAADC_GAIN1_4,
|
||||
.reference = NRF_SAADC_REFERENCE_INTERNAL,
|
||||
.acq_time = NRF_SAADC_ACQTIME_40US,
|
||||
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
|
||||
.burst = NRF_SAADC_BURST_ENABLED,
|
||||
.pin_p = batteryVoltageAdcInput,
|
||||
.pin_n = NRF_SAADC_INPUT_DISABLED};
|
||||
APP_ERROR_CHECK(nrfx_saadc_channel_init(0, &adcChannelConfig));
|
||||
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
|
||||
}
|
||||
|
||||
void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
|
||||
const uint16_t battery_max = 4180; // maximum voltage of battery ( max charging voltage is 4.21 )
|
||||
const uint16_t battery_min = 3200; // minimum voltage of battery before shutdown ( depends on the battery )
|
||||
|
||||
if (p_event->type == NRFX_SAADC_EVT_DONE) {
|
||||
|
||||
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
|
||||
|
||||
// A hardware voltage divider divides the battery voltage by 2
|
||||
// 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] * (8 * 600) / 1024;
|
||||
|
||||
if (isFull) {
|
||||
percentRemaining = 100;
|
||||
} else if (voltage < battery_min) {
|
||||
percentRemaining = 0;
|
||||
} else {
|
||||
percentRemaining = std::min((voltage - battery_min) * 100 / (battery_max - battery_min), isCharging ? 99 : 100);
|
||||
}
|
||||
|
||||
nrfx_saadc_uninit();
|
||||
isReading = false;
|
||||
|
||||
systemTask->PushMessage(System::Messages::BatteryMeasurementDone);
|
||||
}
|
||||
}
|
||||
|
||||
void Battery::Register(Pinetime::System::SystemTask* systemTask) {
|
||||
this->systemTask = systemTask;
|
||||
}
|
||||
|
@ -1,27 +1,56 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <drivers/include/nrfx_saadc.h>
|
||||
|
||||
#include <systemtask/SystemTask.h>
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
class Battery {
|
||||
public:
|
||||
void Init();
|
||||
void Update();
|
||||
float PercentRemaining() const { return percentRemaining; }
|
||||
float Voltage() const { return voltage; }
|
||||
bool IsCharging() const { return isCharging; }
|
||||
bool IsPowerPresent() const { return isPowerPresent; }
|
||||
|
||||
private:
|
||||
static constexpr uint32_t chargingPin = 12;
|
||||
static constexpr uint32_t powerPresentPin = 19;
|
||||
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
|
||||
static void SaadcEventHandler(nrfx_saadc_evt_t const * p_event);
|
||||
float percentRemaining = 0.0f;
|
||||
float voltage = 0.0f;
|
||||
bool isCharging = false;
|
||||
bool isPowerPresent = false;
|
||||
class Battery {
|
||||
public:
|
||||
Battery();
|
||||
|
||||
void Update();
|
||||
void Register(System::SystemTask* systemTask);
|
||||
|
||||
uint8_t PercentRemaining() const {
|
||||
return percentRemaining;
|
||||
}
|
||||
|
||||
uint16_t Voltage() const {
|
||||
return voltage;
|
||||
}
|
||||
|
||||
bool IsCharging() const {
|
||||
// isCharging will go up and down when fully charged
|
||||
// isFull makes sure this returns false while fully charged.
|
||||
return isCharging && !isFull;
|
||||
}
|
||||
|
||||
bool IsPowerPresent() const {
|
||||
return isPowerPresent;
|
||||
}
|
||||
|
||||
private:
|
||||
static Battery* instance;
|
||||
nrf_saadc_value_t saadc_value;
|
||||
|
||||
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
|
||||
uint16_t voltage = 0;
|
||||
uint8_t percentRemaining = 0;
|
||||
|
||||
bool isFull = false;
|
||||
bool isCharging = false;
|
||||
bool isPowerPresent = false;
|
||||
|
||||
void SaadcInit();
|
||||
|
||||
void SaadcEventHandler(nrfx_saadc_evt_t const* p_event);
|
||||
static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
|
||||
|
||||
bool isReading = false;
|
||||
|
||||
Pinetime::System::SystemTask* systemTask = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <systemtask/SystemTask.h>
|
||||
#include "NotificationManager.h"
|
||||
|
||||
#include "AlertNotificationClient.h"
|
||||
#include <algorithm>
|
||||
#include "NotificationManager.h"
|
||||
#include "systemtask/SystemTask.h"
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
constexpr ble_uuid16_t AlertNotificationClient::ansServiceUuid;
|
||||
@ -12,50 +12,42 @@ constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid;
|
||||
constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid;
|
||||
|
||||
namespace {
|
||||
int
|
||||
OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service,
|
||||
void *arg) {
|
||||
auto client = static_cast<AlertNotificationClient *>(arg);
|
||||
int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg) {
|
||||
auto client = static_cast<AlertNotificationClient*>(arg);
|
||||
return client->OnDiscoveryEvent(conn_handle, error, service);
|
||||
}
|
||||
|
||||
int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
|
||||
const struct ble_gatt_chr *chr, void *arg) {
|
||||
auto client = static_cast<AlertNotificationClient *>(arg);
|
||||
int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle,
|
||||
const struct ble_gatt_error* error,
|
||||
const struct ble_gatt_chr* chr,
|
||||
void* arg) {
|
||||
auto client = static_cast<AlertNotificationClient*>(arg);
|
||||
return client->OnCharacteristicsDiscoveryEvent(conn_handle, error, chr);
|
||||
}
|
||||
|
||||
int OnAlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle,
|
||||
const struct ble_gatt_error *error,
|
||||
uint16_t chr_val_handle,
|
||||
const struct ble_gatt_dsc *dsc,
|
||||
void *arg) {
|
||||
auto client = static_cast<AlertNotificationClient *>(arg);
|
||||
int OnAlertNotificationDescriptorDiscoveryEventCallback(
|
||||
uint16_t conn_handle, const struct ble_gatt_error* error, uint16_t chr_val_handle, const struct ble_gatt_dsc* dsc, void* arg) {
|
||||
auto client = static_cast<AlertNotificationClient*>(arg);
|
||||
return client->OnDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
|
||||
}
|
||||
|
||||
int NewAlertSubcribeCallback(uint16_t conn_handle,
|
||||
const struct ble_gatt_error *error,
|
||||
struct ble_gatt_attr *attr,
|
||||
void *arg) {
|
||||
auto client = static_cast<AlertNotificationClient *>(arg);
|
||||
int NewAlertSubcribeCallback(uint16_t conn_handle, const struct ble_gatt_error* error, struct ble_gatt_attr* attr, void* arg) {
|
||||
auto client = static_cast<AlertNotificationClient*>(arg);
|
||||
return client->OnNewAlertSubcribe(conn_handle, error, attr);
|
||||
}
|
||||
}
|
||||
|
||||
AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
|
||||
Pinetime::Controllers::NotificationManager ¬ificationManager) :
|
||||
systemTask{systemTask}, notificationManager{notificationManager} {
|
||||
AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
|
||||
Pinetime::Controllers::NotificationManager& notificationManager)
|
||||
: systemTask {systemTask}, notificationManager {notificationManager} {
|
||||
}
|
||||
|
||||
bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
|
||||
const ble_gatt_svc *service) {
|
||||
bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
|
||||
if (service == nullptr && error->status == BLE_HS_EDONE) {
|
||||
if (isDiscovered) {
|
||||
NRF_LOG_INFO("ANS Discovery found, starting characteristics discovery");
|
||||
|
||||
ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle,
|
||||
OnAlertNotificationCharacteristicDiscoveredCallback, this);
|
||||
ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle, OnAlertNotificationCharacteristicDiscoveredCallback, this);
|
||||
} else {
|
||||
NRF_LOG_INFO("ANS not found");
|
||||
onServiceDiscovered(connectionHandle);
|
||||
@ -63,7 +55,7 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const
|
||||
return true;
|
||||
}
|
||||
|
||||
if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ansServiceUuid), &service->uuid.u) == 0) {
|
||||
if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ansServiceUuid), &service->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("ANS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
|
||||
ansStartHandle = service->start_handle;
|
||||
ansEndHandle = service->end_handle;
|
||||
@ -72,8 +64,9 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const
|
||||
return false;
|
||||
}
|
||||
|
||||
int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
|
||||
const ble_gatt_chr *characteristic) {
|
||||
int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle,
|
||||
const ble_gatt_error* error,
|
||||
const ble_gatt_chr* characteristic) {
|
||||
if (error->status != 0 && error->status != BLE_HS_EDONE) {
|
||||
NRF_LOG_INFO("ANS Characteristic discovery ERROR");
|
||||
onServiceDiscovered(connectionHandle);
|
||||
@ -83,41 +76,34 @@ int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connection
|
||||
if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
|
||||
NRF_LOG_INFO("ANS Characteristic discovery complete");
|
||||
if (isCharacteristicDiscovered) {
|
||||
ble_gattc_disc_all_dscs(connectionHandle,
|
||||
newAlertHandle, ansEndHandle,
|
||||
OnAlertNotificationDescriptorDiscoveryEventCallback, this);
|
||||
ble_gattc_disc_all_dscs(connectionHandle, newAlertHandle, ansEndHandle, OnAlertNotificationDescriptorDiscoveryEventCallback, this);
|
||||
} else
|
||||
onServiceDiscovered(connectionHandle);
|
||||
} else {
|
||||
if (characteristic != nullptr &&
|
||||
ble_uuid_cmp(((ble_uuid_t *) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
|
||||
if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid");
|
||||
supportedNewAlertCategoryHandle = characteristic->val_handle;
|
||||
} else if (characteristic != nullptr &&
|
||||
ble_uuid_cmp(((ble_uuid_t *) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
|
||||
} else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid");
|
||||
supportedUnreadAlertCategoryHandle = characteristic->val_handle;
|
||||
} else if (characteristic != nullptr &&
|
||||
ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &characteristic->uuid.u) == 0) {
|
||||
} else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &characteristic->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid");
|
||||
newAlertHandle = characteristic->val_handle;
|
||||
newAlertDefHandle = characteristic->def_handle;
|
||||
isCharacteristicDiscovered = true;
|
||||
} else if (characteristic != nullptr &&
|
||||
ble_uuid_cmp(((ble_uuid_t *) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
|
||||
} else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid");
|
||||
unreadAlertStatusHandle = characteristic->val_handle;
|
||||
} else if (characteristic != nullptr &&
|
||||
ble_uuid_cmp(((ble_uuid_t *) &controlPointUuid), &characteristic->uuid.u) == 0) {
|
||||
} else if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) &controlPointUuid), &characteristic->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid");
|
||||
controlPointHandle = characteristic->val_handle;
|
||||
} else NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle);
|
||||
} else
|
||||
NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error,
|
||||
ble_gatt_attr *attribute) {
|
||||
int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute) {
|
||||
if (error->status == 0) {
|
||||
NRF_LOG_INFO("ANS New alert subscribe OK");
|
||||
} else {
|
||||
@ -128,12 +114,12 @@ int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
|
||||
int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
|
||||
const ble_gatt_error* error,
|
||||
uint16_t characteristicValueHandle,
|
||||
const ble_gatt_dsc *descriptor) {
|
||||
const ble_gatt_dsc* descriptor) {
|
||||
if (error->status == 0) {
|
||||
if (characteristicValueHandle == newAlertHandle &&
|
||||
ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &descriptor->uuid.u)) {
|
||||
if (characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*) &newAlertUuid), &descriptor->uuid.u)) {
|
||||
if (newAlertDescriptorHandle == 0) {
|
||||
NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle);
|
||||
newAlertDescriptorHandle = descriptor->handle;
|
||||
@ -151,24 +137,29 @@ int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connect
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AlertNotificationClient::OnNotification(ble_gap_event *event) {
|
||||
void AlertNotificationClient::OnNotification(ble_gap_event* event) {
|
||||
if (event->notify_rx.attr_handle == newAlertHandle) {
|
||||
constexpr size_t stringTerminatorSize = 1; // end of string '\0'
|
||||
constexpr size_t headerSize = 3;
|
||||
const auto maxMessageSize{NotificationManager::MaximumMessageSize()};
|
||||
const auto maxBufferSize{maxMessageSize + headerSize};
|
||||
const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
|
||||
const auto maxBufferSize {maxMessageSize + headerSize};
|
||||
|
||||
const auto dbgPacketLen = OS_MBUF_PKTLEN(event->notify_rx.om);
|
||||
size_t bufferSize = min(dbgPacketLen + stringTerminatorSize, maxBufferSize);
|
||||
auto messageSize = min(maxMessageSize, (bufferSize - headerSize));
|
||||
// Ignore notifications with empty message
|
||||
const auto packetLen = OS_MBUF_PKTLEN(event->notify_rx.om);
|
||||
if (packetLen <= headerSize)
|
||||
return;
|
||||
|
||||
size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize);
|
||||
auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));
|
||||
|
||||
NotificationManager::Notification notif;
|
||||
os_mbuf_copydata(event->notify_rx.om, headerSize, messageSize - 1, notif.message.data());
|
||||
notif.message[messageSize - 1] = '\0';
|
||||
notif.size = messageSize;
|
||||
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
|
||||
notificationManager.Push(std::move(notif));
|
||||
|
||||
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
|
||||
systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,76 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#undef max
|
||||
#undef min
|
||||
#include "BleClient.h"
|
||||
|
||||
|
||||
namespace Pinetime {
|
||||
|
||||
namespace System {
|
||||
class SystemTask;
|
||||
}
|
||||
|
||||
namespace Controllers {
|
||||
class NotificationManager;
|
||||
|
||||
class AlertNotificationClient : public BleClient {
|
||||
public:
|
||||
explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
|
||||
Pinetime::Controllers::NotificationManager ¬ificationManager);
|
||||
public:
|
||||
explicit AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
|
||||
Pinetime::Controllers::NotificationManager& notificationManager);
|
||||
|
||||
bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service);
|
||||
int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
|
||||
const ble_gatt_chr *characteristic);
|
||||
int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
|
||||
int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
|
||||
uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
|
||||
void OnNotification(ble_gap_event *event);
|
||||
void Reset();
|
||||
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
|
||||
bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service);
|
||||
int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
|
||||
int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
|
||||
int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
|
||||
const ble_gatt_error* error,
|
||||
uint16_t characteristicValueHandle,
|
||||
const ble_gatt_dsc* descriptor);
|
||||
void OnNotification(ble_gap_event* event);
|
||||
void Reset();
|
||||
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
|
||||
|
||||
private:
|
||||
static constexpr uint16_t ansServiceId{0x1811};
|
||||
static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
|
||||
static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48;
|
||||
static constexpr uint16_t newAlertId = 0x2a46;
|
||||
static constexpr uint16_t unreadAlertStatusId = 0x2a45;
|
||||
static constexpr uint16_t controlPointId = 0x2a44;
|
||||
private:
|
||||
static constexpr uint16_t ansServiceId {0x1811};
|
||||
static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
|
||||
static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48;
|
||||
static constexpr uint16_t newAlertId = 0x2a46;
|
||||
static constexpr uint16_t unreadAlertStatusId = 0x2a45;
|
||||
static constexpr uint16_t controlPointId = 0x2a44;
|
||||
|
||||
static constexpr ble_uuid16_t ansServiceUuid{
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = ansServiceId
|
||||
};
|
||||
static constexpr ble_uuid16_t supportedNewAlertCategoryUuid{
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = supportedNewAlertCategoryId
|
||||
};
|
||||
static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid{
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = supportedUnreadAlertCategoryId
|
||||
};
|
||||
static constexpr ble_uuid16_t newAlertUuid{
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = newAlertId
|
||||
};
|
||||
static constexpr ble_uuid16_t unreadAlertStatusUuid{
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = unreadAlertStatusId
|
||||
};
|
||||
static constexpr ble_uuid16_t controlPointUuid{
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = controlPointId
|
||||
};
|
||||
static constexpr ble_uuid16_t ansServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansServiceId};
|
||||
static constexpr ble_uuid16_t supportedNewAlertCategoryUuid {.u {.type = BLE_UUID_TYPE_16}, .value = supportedNewAlertCategoryId};
|
||||
static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid {.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = supportedUnreadAlertCategoryId};
|
||||
static constexpr ble_uuid16_t newAlertUuid {.u {.type = BLE_UUID_TYPE_16}, .value = newAlertId};
|
||||
static constexpr ble_uuid16_t unreadAlertStatusUuid {.u {.type = BLE_UUID_TYPE_16}, .value = unreadAlertStatusId};
|
||||
static constexpr ble_uuid16_t controlPointUuid {.u {.type = BLE_UUID_TYPE_16}, .value = controlPointId};
|
||||
|
||||
uint16_t ansStartHandle = 0;
|
||||
uint16_t ansEndHandle = 0;
|
||||
uint16_t supportedNewAlertCategoryHandle = 0;
|
||||
uint16_t supportedUnreadAlertCategoryHandle = 0;
|
||||
uint16_t newAlertHandle = 0;
|
||||
uint16_t newAlertDescriptorHandle = 0;
|
||||
uint16_t newAlertDefHandle = 0;
|
||||
uint16_t unreadAlertStatusHandle = 0;
|
||||
uint16_t controlPointHandle = 0;
|
||||
bool isDiscovered = false;
|
||||
Pinetime::System::SystemTask &systemTask;
|
||||
Pinetime::Controllers::NotificationManager ¬ificationManager;
|
||||
std::function<void(uint16_t)> onServiceDiscovered;
|
||||
bool isCharacteristicDiscovered = false;
|
||||
bool isDescriptorFound = false;
|
||||
uint16_t ansStartHandle = 0;
|
||||
uint16_t ansEndHandle = 0;
|
||||
uint16_t supportedNewAlertCategoryHandle = 0;
|
||||
uint16_t supportedUnreadAlertCategoryHandle = 0;
|
||||
uint16_t newAlertHandle = 0;
|
||||
uint16_t newAlertDescriptorHandle = 0;
|
||||
uint16_t newAlertDefHandle = 0;
|
||||
uint16_t unreadAlertStatusHandle = 0;
|
||||
uint16_t controlPointHandle = 0;
|
||||
bool isDiscovered = false;
|
||||
Pinetime::System::SystemTask& systemTask;
|
||||
Pinetime::Controllers::NotificationManager& notificationManager;
|
||||
std::function<void(uint16_t)> onServiceDiscovered;
|
||||
bool isCharacteristicDiscovered = false;
|
||||
bool isDescriptorFound = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,17 @@
|
||||
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include "NotificationManager.h"
|
||||
#include <systemtask/SystemTask.h>
|
||||
|
||||
#include "AlertNotificationService.h"
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include "NotificationManager.h"
|
||||
#include "systemtask/SystemTask.h"
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
constexpr ble_uuid16_t AlertNotificationService::ansUuid;
|
||||
constexpr ble_uuid16_t AlertNotificationService::ansCharUuid;
|
||||
constexpr ble_uuid128_t AlertNotificationService::notificationEventUuid;
|
||||
|
||||
|
||||
int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||
int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
|
||||
auto anService = static_cast<AlertNotificationService*>(arg);
|
||||
return anService->OnAlert(conn_handle, attr_handle, ctxt);
|
||||
}
|
||||
@ -26,50 +25,102 @@ void AlertNotificationService::Init() {
|
||||
ASSERT(res == 0);
|
||||
}
|
||||
|
||||
AlertNotificationService::AlertNotificationService ( System::SystemTask& systemTask, NotificationManager& notificationManager )
|
||||
: characteristicDefinition{
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &ansCharUuid,
|
||||
.access_cb = AlertNotificationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE
|
||||
},
|
||||
{
|
||||
0
|
||||
}
|
||||
},
|
||||
serviceDefinition{
|
||||
{
|
||||
/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t *) &ansUuid,
|
||||
.characteristics = characteristicDefinition
|
||||
},
|
||||
{
|
||||
0
|
||||
},
|
||||
}, systemTask{systemTask}, notificationManager{notificationManager} {
|
||||
AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager)
|
||||
: characteristicDefinition {{.uuid = (ble_uuid_t*) &ansCharUuid,
|
||||
.access_cb = AlertNotificationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE},
|
||||
{.uuid = (ble_uuid_t*) ¬ificationEventUuid,
|
||||
.access_cb = AlertNotificationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_NOTIFY,
|
||||
.val_handle = &eventHandle},
|
||||
{0}},
|
||||
serviceDefinition {
|
||||
{/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t*) &ansUuid,
|
||||
.characteristics = characteristicDefinition},
|
||||
{0},
|
||||
},
|
||||
systemTask {systemTask},
|
||||
notificationManager {notificationManager} {
|
||||
}
|
||||
|
||||
int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt) {
|
||||
int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
|
||||
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
|
||||
constexpr size_t stringTerminatorSize = 1; // end of string '\0'
|
||||
constexpr size_t headerSize = 3;
|
||||
const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
|
||||
const auto maxBufferSize{maxMessageSize + headerSize};
|
||||
const auto maxBufferSize {maxMessageSize + headerSize};
|
||||
|
||||
const auto dbgPacketLen = OS_MBUF_PKTLEN(ctxt->om);
|
||||
size_t bufferSize = min(dbgPacketLen + stringTerminatorSize, maxBufferSize);
|
||||
auto messageSize = min(maxMessageSize, (bufferSize-headerSize));
|
||||
// Ignore notifications with empty message
|
||||
const auto packetLen = OS_MBUF_PKTLEN(ctxt->om);
|
||||
if (packetLen <= headerSize)
|
||||
return 0;
|
||||
|
||||
size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize);
|
||||
auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));
|
||||
Categories category;
|
||||
|
||||
NotificationManager::Notification notif;
|
||||
os_mbuf_copydata(ctxt->om, headerSize, messageSize-1, notif.message.data());
|
||||
notif.message[messageSize-1] = '\0';
|
||||
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
|
||||
notificationManager.Push(std::move(notif));
|
||||
os_mbuf_copydata(ctxt->om, headerSize, messageSize - 1, notif.message.data());
|
||||
os_mbuf_copydata(ctxt->om, 0, 1, &category);
|
||||
notif.message[messageSize - 1] = '\0';
|
||||
notif.size = messageSize;
|
||||
|
||||
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
|
||||
// TODO convert all ANS categories to NotificationController categories
|
||||
switch (category) {
|
||||
case Categories::Call:
|
||||
notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall;
|
||||
break;
|
||||
default:
|
||||
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
|
||||
break;
|
||||
}
|
||||
|
||||
auto event = Pinetime::System::Messages::OnNewNotification;
|
||||
notificationManager.Push(std::move(notif));
|
||||
systemTask.PushMessage(event);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AlertNotificationService::AcceptIncomingCall() {
|
||||
auto response = IncomingCallResponses::Answer;
|
||||
auto* om = ble_hs_mbuf_from_flat(&response, 1);
|
||||
|
||||
uint16_t connectionHandle = systemTask.nimble().connHandle();
|
||||
|
||||
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
|
||||
}
|
||||
|
||||
void AlertNotificationService::RejectIncomingCall() {
|
||||
auto response = IncomingCallResponses::Reject;
|
||||
auto* om = ble_hs_mbuf_from_flat(&response, 1);
|
||||
|
||||
uint16_t connectionHandle = systemTask.nimble().connHandle();
|
||||
|
||||
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
|
||||
}
|
||||
|
||||
void AlertNotificationService::MuteIncomingCall() {
|
||||
auto response = IncomingCallResponses::Mute;
|
||||
auto* om = ble_hs_mbuf_from_flat(&response, 1);
|
||||
|
||||
uint16_t connectionHandle = systemTask.nimble().connHandle();
|
||||
|
||||
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
|
||||
}
|
@ -1,39 +1,68 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#undef max
|
||||
#undef min
|
||||
|
||||
// 00020001-78fc-48fe-8e23-433b3a1942d0
|
||||
#define NOTIFICATION_EVENT_SERVICE_UUID_BASE \
|
||||
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x01, 0x00, 0x02, 0x00 }
|
||||
|
||||
namespace Pinetime {
|
||||
|
||||
namespace System {
|
||||
class SystemTask;
|
||||
}
|
||||
namespace Controllers {
|
||||
class NotificationManager;
|
||||
|
||||
class AlertNotificationService {
|
||||
public:
|
||||
AlertNotificationService(Pinetime::System::SystemTask &systemTask,
|
||||
Pinetime::Controllers::NotificationManager ¬ificationManager);
|
||||
void Init();
|
||||
public:
|
||||
AlertNotificationService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager);
|
||||
void Init();
|
||||
|
||||
int OnAlert(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt);
|
||||
int OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
|
||||
|
||||
void AcceptIncomingCall();
|
||||
void RejectIncomingCall();
|
||||
void MuteIncomingCall();
|
||||
|
||||
private:
|
||||
static constexpr uint16_t ansId {0x1811};
|
||||
static constexpr uint16_t ansCharId {0x2a46};
|
||||
enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 };
|
||||
|
||||
static constexpr ble_uuid16_t ansUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = ansId
|
||||
};
|
||||
private:
|
||||
enum class Categories : uint8_t {
|
||||
SimpleAlert = 0x00,
|
||||
Email = 0x01,
|
||||
News = 0x02,
|
||||
Call = 0x03,
|
||||
MissedCall = 0x04,
|
||||
MmsSms = 0x05,
|
||||
VoiceMail = 0x06,
|
||||
Schedule = 0x07,
|
||||
HighPrioritizedAlert = 0x08,
|
||||
InstantMessage = 0x09,
|
||||
All = 0xff
|
||||
};
|
||||
|
||||
static constexpr ble_uuid16_t ansCharUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = ansCharId
|
||||
};
|
||||
static constexpr uint16_t ansId {0x1811};
|
||||
static constexpr uint16_t ansCharId {0x2a46};
|
||||
|
||||
struct ble_gatt_chr_def characteristicDefinition[2];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
static constexpr ble_uuid16_t ansUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansId};
|
||||
|
||||
Pinetime::System::SystemTask &systemTask;
|
||||
NotificationManager ¬ificationManager;
|
||||
static constexpr ble_uuid16_t ansCharUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansCharId};
|
||||
|
||||
static constexpr ble_uuid128_t notificationEventUuid {.u {.type = BLE_UUID_TYPE_128}, .value = NOTIFICATION_EVENT_SERVICE_UUID_BASE};
|
||||
|
||||
struct ble_gatt_chr_def characteristicDefinition[3];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
|
||||
Pinetime::System::SystemTask& systemTask;
|
||||
NotificationManager& notificationManager;
|
||||
|
||||
uint16_t eventHandle;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <nrf_log.h>
|
||||
#include "BatteryInformationService.h"
|
||||
#include "components/battery/BatteryController.h"
|
||||
|
||||
@ -6,39 +7,26 @@ using namespace Pinetime::Controllers;
|
||||
constexpr ble_uuid16_t BatteryInformationService::batteryInformationServiceUuid;
|
||||
constexpr ble_uuid16_t BatteryInformationService::batteryLevelUuid;
|
||||
|
||||
|
||||
|
||||
int BatteryInformationServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||
int BatteryInformationServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
|
||||
auto* batteryInformationService = static_cast<BatteryInformationService*>(arg);
|
||||
return batteryInformationService->OnBatteryServiceRequested(conn_handle, attr_handle, ctxt);
|
||||
}
|
||||
|
||||
BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController) :
|
||||
batteryController{batteryController},
|
||||
characteristicDefinition{
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &batteryLevelUuid,
|
||||
.access_cb = BatteryInformationServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
.val_handle = &batteryLevelHandle
|
||||
},
|
||||
{
|
||||
0
|
||||
}
|
||||
},
|
||||
serviceDefinition{
|
||||
{
|
||||
/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t *) &batteryInformationServiceUuid,
|
||||
.characteristics = characteristicDefinition
|
||||
},
|
||||
{
|
||||
0
|
||||
},
|
||||
}{
|
||||
|
||||
BatteryInformationService::BatteryInformationService(Controllers::Battery& batteryController)
|
||||
: batteryController {batteryController},
|
||||
characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid,
|
||||
.access_cb = BatteryInformationServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
|
||||
.val_handle = &batteryLevelHandle},
|
||||
{0}},
|
||||
serviceDefinition {
|
||||
{/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t*) &batteryInformationServiceUuid,
|
||||
.characteristics = characteristicDefinition},
|
||||
{0},
|
||||
} {
|
||||
}
|
||||
|
||||
void BatteryInformationService::Init() {
|
||||
@ -50,13 +38,18 @@ void BatteryInformationService::Init() {
|
||||
ASSERT(res == 0);
|
||||
}
|
||||
|
||||
int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle,
|
||||
ble_gatt_access_ctxt *context) {
|
||||
if(attributeHandle == batteryLevelHandle) {
|
||||
int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHandle,
|
||||
uint16_t attributeHandle,
|
||||
ble_gatt_access_ctxt* context) {
|
||||
if (attributeHandle == batteryLevelHandle) {
|
||||
NRF_LOG_INFO("BATTERY : handle = %d", batteryLevelHandle);
|
||||
static uint8_t batteryValue = batteryController.PercentRemaining();
|
||||
uint8_t batteryValue = batteryController.PercentRemaining();
|
||||
int res = os_mbuf_append(context->om, &batteryValue, 1);
|
||||
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
void BatteryInformationService::NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level) {
|
||||
auto* om = ble_hs_mbuf_from_flat(&level, 1);
|
||||
ble_gattc_notify_custom(connectionHandle, batteryLevelHandle, om);
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#undef max
|
||||
#undef min
|
||||
|
||||
namespace Pinetime {
|
||||
namespace System {
|
||||
@ -8,33 +12,25 @@ namespace Pinetime {
|
||||
namespace Controllers {
|
||||
class Battery;
|
||||
class BatteryInformationService {
|
||||
public:
|
||||
BatteryInformationService(Controllers::Battery& batteryController);
|
||||
void Init();
|
||||
public:
|
||||
BatteryInformationService(Controllers::Battery& batteryController);
|
||||
void Init();
|
||||
|
||||
int
|
||||
OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
|
||||
int OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
|
||||
void NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level);
|
||||
private:
|
||||
Controllers::Battery& batteryController;
|
||||
static constexpr uint16_t batteryInformationServiceId {0x180F};
|
||||
static constexpr uint16_t batteryLevelId {0x2A19};
|
||||
|
||||
private:
|
||||
Controllers::Battery& batteryController;
|
||||
static constexpr uint16_t batteryInformationServiceId {0x180F};
|
||||
static constexpr uint16_t batteryLevelId {0x2A19};
|
||||
static constexpr ble_uuid16_t batteryInformationServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = batteryInformationServiceId};
|
||||
|
||||
static constexpr ble_uuid16_t batteryInformationServiceUuid {
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = batteryInformationServiceId
|
||||
};
|
||||
static constexpr ble_uuid16_t batteryLevelUuid {.u {.type = BLE_UUID_TYPE_16}, .value = batteryLevelId};
|
||||
|
||||
static constexpr ble_uuid16_t batteryLevelUuid {
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = batteryLevelId
|
||||
};
|
||||
|
||||
struct ble_gatt_chr_def characteristicDefinition[3];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
|
||||
uint16_t batteryLevelHandle;
|
||||
struct ble_gatt_chr_def characteristicDefinition[3];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
|
||||
uint16_t batteryLevelHandle;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@
|
||||
#include <functional>
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers{
|
||||
namespace Controllers {
|
||||
class BleClient {
|
||||
public:
|
||||
virtual void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) = 0;
|
||||
public:
|
||||
virtual void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) = 0;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include "BleController.h"
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
@ -27,5 +25,3 @@ void Ble::FirmwareUpdateTotalBytes(uint32_t totalBytes) {
|
||||
void Ble::FirmwareUpdateCurrentBytes(uint32_t currentBytes) {
|
||||
firmwareUpdateCurrentBytes = currentBytes;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,45 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <queue.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
class Ble {
|
||||
public:
|
||||
using BleAddress = std::array<uint8_t, 6>;
|
||||
enum class FirmwareUpdateStates {Idle, Running, Validated, Error};
|
||||
enum class AddressTypes { Public, Random };
|
||||
public:
|
||||
using BleAddress = std::array<uint8_t, 6>;
|
||||
enum class FirmwareUpdateStates { Idle, Running, Validated, Error };
|
||||
enum class AddressTypes { Public, Random };
|
||||
|
||||
Ble() = default;
|
||||
bool IsConnected() const {return isConnected;}
|
||||
void Connect();
|
||||
void Disconnect();
|
||||
Ble() = default;
|
||||
bool IsConnected() const {
|
||||
return isConnected;
|
||||
}
|
||||
void Connect();
|
||||
void Disconnect();
|
||||
|
||||
void StartFirmwareUpdate();
|
||||
void StopFirmwareUpdate();
|
||||
void FirmwareUpdateTotalBytes(uint32_t totalBytes);
|
||||
void FirmwareUpdateCurrentBytes(uint32_t currentBytes);
|
||||
void State(FirmwareUpdateStates state) { firmwareUpdateState = state; }
|
||||
void StartFirmwareUpdate();
|
||||
void StopFirmwareUpdate();
|
||||
void FirmwareUpdateTotalBytes(uint32_t totalBytes);
|
||||
void FirmwareUpdateCurrentBytes(uint32_t currentBytes);
|
||||
void State(FirmwareUpdateStates state) {
|
||||
firmwareUpdateState = state;
|
||||
}
|
||||
|
||||
bool IsFirmwareUpdating() const { return isFirmwareUpdating; }
|
||||
uint32_t FirmwareUpdateTotalBytes() const { return firmwareUpdateTotalBytes; }
|
||||
uint32_t FirmwareUpdateCurrentBytes() const { return firmwareUpdateCurrentBytes; }
|
||||
FirmwareUpdateStates State() const { return firmwareUpdateState; }
|
||||
bool IsFirmwareUpdating() const {
|
||||
return isFirmwareUpdating;
|
||||
}
|
||||
uint32_t FirmwareUpdateTotalBytes() const {
|
||||
return firmwareUpdateTotalBytes;
|
||||
}
|
||||
uint32_t FirmwareUpdateCurrentBytes() const {
|
||||
return firmwareUpdateCurrentBytes;
|
||||
}
|
||||
FirmwareUpdateStates State() const {
|
||||
return firmwareUpdateState;
|
||||
}
|
||||
|
||||
void Address(BleAddress&& addr) { address = addr; }
|
||||
const BleAddress& Address() const { return address; }
|
||||
void AddressType(AddressTypes t) { addressType = t;}
|
||||
private:
|
||||
bool isConnected = false;
|
||||
bool isFirmwareUpdating = false;
|
||||
uint32_t firmwareUpdateTotalBytes = 0;
|
||||
uint32_t firmwareUpdateCurrentBytes = 0;
|
||||
FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle;
|
||||
BleAddress address;
|
||||
AddressTypes addressType;
|
||||
void Address(BleAddress&& addr) {
|
||||
address = addr;
|
||||
}
|
||||
const BleAddress& Address() const {
|
||||
return address;
|
||||
}
|
||||
void AddressType(AddressTypes t) {
|
||||
addressType = t;
|
||||
}
|
||||
|
||||
private:
|
||||
bool isConnected = false;
|
||||
bool isFirmwareUpdating = false;
|
||||
uint32_t firmwareUpdateTotalBytes = 0;
|
||||
uint32_t firmwareUpdateCurrentBytes = 0;
|
||||
FirmwareUpdateStates firmwareUpdateState = FirmwareUpdateStates::Idle;
|
||||
BleAddress address;
|
||||
AddressTypes addressType;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include "CurrentTimeClient.h"
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include <nrf_log.h>
|
||||
#include "components/datetime/DateTimeController.h"
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
@ -7,39 +9,37 @@ constexpr ble_uuid16_t CurrentTimeClient::ctsServiceUuid;
|
||||
constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid;
|
||||
|
||||
namespace {
|
||||
int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) {
|
||||
auto client = static_cast<CurrentTimeClient *>(arg);
|
||||
int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg) {
|
||||
auto client = static_cast<CurrentTimeClient*>(arg);
|
||||
return client->OnDiscoveryEvent(conn_handle, error, service);
|
||||
}
|
||||
|
||||
int OnCurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
|
||||
const struct ble_gatt_chr *chr, void *arg) {
|
||||
auto client = static_cast<CurrentTimeClient *>(arg);
|
||||
int OnCurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle,
|
||||
const struct ble_gatt_error* error,
|
||||
const struct ble_gatt_chr* chr,
|
||||
void* arg) {
|
||||
auto client = static_cast<CurrentTimeClient*>(arg);
|
||||
return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr);
|
||||
}
|
||||
|
||||
int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
|
||||
auto client = static_cast<CurrentTimeClient *>(arg);
|
||||
int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error* error, struct ble_gatt_attr* attr, void* arg) {
|
||||
auto client = static_cast<CurrentTimeClient*>(arg);
|
||||
return client->OnCurrentTimeReadResult(conn_handle, error, attr);
|
||||
}
|
||||
}
|
||||
|
||||
CurrentTimeClient::CurrentTimeClient(DateTime &dateTimeController) : dateTimeController{dateTimeController} {
|
||||
|
||||
CurrentTimeClient::CurrentTimeClient(DateTime& dateTimeController) : dateTimeController {dateTimeController} {
|
||||
}
|
||||
|
||||
void CurrentTimeClient::Init() {
|
||||
|
||||
}
|
||||
|
||||
bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
|
||||
const ble_gatt_svc *service) {
|
||||
bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
|
||||
if (service == nullptr && error->status == BLE_HS_EDONE) {
|
||||
if (isDiscovered) {
|
||||
NRF_LOG_INFO("CTS found, starting characteristics discovery");
|
||||
|
||||
ble_gattc_disc_all_chrs(connectionHandle, ctsStartHandle, ctsEndHandle,
|
||||
OnCurrentTimeCharacteristicDiscoveredCallback, this);
|
||||
ble_gattc_disc_all_chrs(connectionHandle, ctsStartHandle, ctsEndHandle, OnCurrentTimeCharacteristicDiscoveredCallback, this);
|
||||
} else {
|
||||
NRF_LOG_INFO("CTS not found");
|
||||
onServiceDiscovered(connectionHandle);
|
||||
@ -47,7 +47,7 @@ bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_ga
|
||||
return true;
|
||||
}
|
||||
|
||||
if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ctsServiceUuid), &service->uuid.u) == 0) {
|
||||
if (service != nullptr && ble_uuid_cmp(((ble_uuid_t*) &ctsServiceUuid), &service->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("CTS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
|
||||
isDiscovered = true;
|
||||
ctsStartHandle = service->start_handle;
|
||||
@ -57,8 +57,9 @@ bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_ga
|
||||
return false;
|
||||
}
|
||||
|
||||
int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
|
||||
const ble_gatt_chr *characteristic) {
|
||||
int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle,
|
||||
const ble_gatt_error* error,
|
||||
const ble_gatt_chr* characteristic) {
|
||||
if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
|
||||
if (isCharacteristicDiscovered) {
|
||||
NRF_LOG_INFO("CTS Characteristic discovery complete, fetching time");
|
||||
@ -71,8 +72,7 @@ int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, cons
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (characteristic != nullptr &&
|
||||
ble_uuid_cmp(((ble_uuid_t *) ¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
|
||||
if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*) ¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
|
||||
NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle);
|
||||
isCharacteristicDiscovered = true;
|
||||
currentTimeHandle = characteristic->val_handle;
|
||||
@ -80,17 +80,15 @@ int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, cons
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error,
|
||||
const ble_gatt_attr *attribute) {
|
||||
int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_attr* attribute) {
|
||||
if (error->status == 0) {
|
||||
// TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
|
||||
CtsData result;
|
||||
os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
|
||||
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
|
||||
result.month, result.dayofmonth,
|
||||
result.hour, result.minute, result.second);
|
||||
dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
|
||||
0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||
NRF_LOG_INFO(
|
||||
"Received data: %d-%d-%d %d:%d:%d", result.year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
|
||||
dateTimeController.SetTime(
|
||||
result.year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||
} else {
|
||||
NRF_LOG_INFO("Error retrieving current time: %d", error->status);
|
||||
}
|
||||
|
@ -1,59 +1,58 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "components/datetime/DateTimeController.h"
|
||||
#include "BleClient.h"
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#undef max
|
||||
#undef min
|
||||
#include <cstdint>
|
||||
#include "BleClient.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
namespace Controllers {
|
||||
class DateTime;
|
||||
|
||||
class CurrentTimeClient : public BleClient {
|
||||
public:
|
||||
explicit CurrentTimeClient(DateTime& dateTimeController);
|
||||
void Init();
|
||||
void Reset();
|
||||
bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service);
|
||||
int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
|
||||
const ble_gatt_chr *characteristic);
|
||||
int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute);
|
||||
static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; }
|
||||
static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; }
|
||||
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
|
||||
class CurrentTimeClient : public BleClient {
|
||||
public:
|
||||
explicit CurrentTimeClient(DateTime& dateTimeController);
|
||||
void Init();
|
||||
void Reset();
|
||||
bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service);
|
||||
int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
|
||||
int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_attr* attribute);
|
||||
static constexpr const ble_uuid16_t* Uuid() {
|
||||
return &CurrentTimeClient::ctsServiceUuid;
|
||||
}
|
||||
static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() {
|
||||
return &CurrentTimeClient::currentTimeCharacteristicUuid;
|
||||
}
|
||||
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
|
||||
|
||||
private:
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t dayofmonth;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t millis;
|
||||
uint8_t reason;
|
||||
} CtsData;
|
||||
private:
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t dayofmonth;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t millis;
|
||||
uint8_t reason;
|
||||
} CtsData;
|
||||
|
||||
static constexpr uint16_t ctsServiceId {0x1805};
|
||||
static constexpr uint16_t currentTimeCharacteristicId {0x2a2b};
|
||||
static constexpr uint16_t ctsServiceId {0x1805};
|
||||
static constexpr uint16_t currentTimeCharacteristicId {0x2a2b};
|
||||
|
||||
static constexpr ble_uuid16_t ctsServiceUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = ctsServiceId
|
||||
};
|
||||
static constexpr ble_uuid16_t currentTimeCharacteristicUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = currentTimeCharacteristicId
|
||||
};
|
||||
static constexpr ble_uuid16_t ctsServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsServiceId};
|
||||
static constexpr ble_uuid16_t currentTimeCharacteristicUuid {.u {.type = BLE_UUID_TYPE_16}, .value = currentTimeCharacteristicId};
|
||||
|
||||
DateTime& dateTimeController;
|
||||
bool isDiscovered = false;
|
||||
uint16_t ctsStartHandle;
|
||||
uint16_t ctsEndHandle;
|
||||
DateTime& dateTimeController;
|
||||
bool isDiscovered = false;
|
||||
uint16_t ctsStartHandle;
|
||||
uint16_t ctsEndHandle;
|
||||
|
||||
bool isCharacteristicDiscovered = false;
|
||||
uint16_t currentTimeHandle;
|
||||
std::function<void(uint16_t)> onServiceDiscovered;
|
||||
};
|
||||
}
|
||||
bool isCharacteristicDiscovered = false;
|
||||
uint16_t currentTimeHandle;
|
||||
std::function<void(uint16_t)> onServiceDiscovered;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
#include "CurrentTimeService.h"
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include <nrf_log.h>
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
constexpr ble_uuid16_t CurrentTimeService::ctsUuid;
|
||||
constexpr ble_uuid16_t CurrentTimeService::ctChrUuid;
|
||||
|
||||
|
||||
int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||
int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
|
||||
auto cts = static_cast<CurrentTimeService*>(arg);
|
||||
return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt);
|
||||
}
|
||||
@ -21,22 +21,19 @@ void CurrentTimeService::Init() {
|
||||
ASSERT(res == 0);
|
||||
}
|
||||
|
||||
int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
|
||||
|
||||
int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt) {
|
||||
|
||||
NRF_LOG_INFO("Setting time...");
|
||||
NRF_LOG_INFO("Setting time...");
|
||||
|
||||
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
|
||||
CtsData result;
|
||||
os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result);
|
||||
|
||||
NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
|
||||
result.month, result.dayofmonth,
|
||||
result.hour, result.minute, result.second);
|
||||
NRF_LOG_INFO(
|
||||
"Received data: %d-%d-%d %d:%d:%d", result.year, result.month, result.dayofmonth, result.hour, result.minute, result.second);
|
||||
|
||||
m_dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
|
||||
0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||
m_dateTimeController.SetTime(
|
||||
result.year, result.month, result.dayofmonth, 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||
|
||||
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
|
||||
CtsData currentDateTime;
|
||||
@ -48,39 +45,26 @@ int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handl
|
||||
currentDateTime.second = m_dateTimeController.Seconds();
|
||||
currentDateTime.millis = 0;
|
||||
|
||||
|
||||
int res = os_mbuf_append(ctxt->om, ¤tDateTime, sizeof(CtsData));
|
||||
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) :
|
||||
characteristicDefinition{
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &ctChrUuid,
|
||||
.access_cb = CTSCallback,
|
||||
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ
|
||||
},
|
||||
{
|
||||
0
|
||||
}
|
||||
},
|
||||
serviceDefinition{
|
||||
{
|
||||
/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t *) &ctsUuid,
|
||||
.characteristics = characteristicDefinition
|
||||
},
|
||||
{
|
||||
0
|
||||
},
|
||||
}, m_dateTimeController{dateTimeController} {
|
||||
CurrentTimeService::CurrentTimeService(DateTime& dateTimeController)
|
||||
: characteristicDefinition {{.uuid = (ble_uuid_t*) &ctChrUuid,
|
||||
.access_cb = CTSCallback,
|
||||
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
|
||||
{0}},
|
||||
serviceDefinition {
|
||||
{/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t*) &ctsUuid,
|
||||
.characteristics = characteristicDefinition},
|
||||
{0},
|
||||
},
|
||||
m_dateTimeController {dateTimeController} {
|
||||
}
|
||||
|
||||
|
@ -3,47 +3,44 @@
|
||||
#include <array>
|
||||
|
||||
#include "components/datetime/DateTimeController.h"
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#undef max
|
||||
#undef min
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
class CurrentTimeService {
|
||||
public:
|
||||
CurrentTimeService(DateTime &dateTimeController);
|
||||
void Init();
|
||||
public:
|
||||
CurrentTimeService(DateTime& dateTimeController);
|
||||
void Init();
|
||||
|
||||
int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt);
|
||||
int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
|
||||
|
||||
private:
|
||||
static constexpr uint16_t ctsId {0x1805};
|
||||
static constexpr uint16_t ctsCharId {0x2a2b};
|
||||
private:
|
||||
static constexpr uint16_t ctsId {0x1805};
|
||||
static constexpr uint16_t ctsCharId {0x2a2b};
|
||||
|
||||
static constexpr ble_uuid16_t ctsUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = ctsId
|
||||
};
|
||||
static constexpr ble_uuid16_t ctsUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsId};
|
||||
|
||||
static constexpr ble_uuid16_t ctChrUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = ctsCharId
|
||||
};
|
||||
static constexpr ble_uuid16_t ctChrUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ctsCharId};
|
||||
|
||||
struct ble_gatt_chr_def characteristicDefinition[2];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
struct ble_gatt_chr_def characteristicDefinition[2];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t dayofmonth;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t millis;
|
||||
uint8_t reason;
|
||||
} CtsData;
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t dayofmonth;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t millis;
|
||||
uint8_t reason;
|
||||
} CtsData;
|
||||
|
||||
DateTime &m_dateTimeController;
|
||||
DateTime& m_dateTimeController;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,7 @@ constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid;
|
||||
constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid;
|
||||
constexpr ble_uuid16_t DeviceInformationService::swRevisionUuid;
|
||||
|
||||
|
||||
int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||
int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
|
||||
auto deviceInformationService = static_cast<DeviceInformationService*>(arg);
|
||||
return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt);
|
||||
}
|
||||
@ -25,10 +24,8 @@ void DeviceInformationService::Init() {
|
||||
ASSERT(res == 0);
|
||||
}
|
||||
|
||||
|
||||
int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt) {
|
||||
const char *str;
|
||||
int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
|
||||
const char* str;
|
||||
|
||||
switch (ble_uuid_u16(ctxt->chr->uuid)) {
|
||||
case manufacturerNameId:
|
||||
@ -57,60 +54,49 @@ int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16
|
||||
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
|
||||
DeviceInformationService::DeviceInformationService() :
|
||||
characteristicDefinition{
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &manufacturerNameUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &modelNumberUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &serialNumberUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &fwRevisionUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &hwRevisionUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &swRevisionUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
0
|
||||
}
|
||||
},
|
||||
serviceDefinition{
|
||||
{
|
||||
/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t *) &deviceInfoUuid,
|
||||
.characteristics = characteristicDefinition
|
||||
},
|
||||
{
|
||||
0
|
||||
},
|
||||
}
|
||||
{
|
||||
|
||||
DeviceInformationService::DeviceInformationService()
|
||||
: characteristicDefinition {{
|
||||
.uuid = (ble_uuid_t*) &manufacturerNameUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t*) &modelNumberUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t*) &serialNumberUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t*) &fwRevisionUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t*) &hwRevisionUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t*) &swRevisionUuid,
|
||||
.access_cb = DeviceInformationCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
},
|
||||
{0}},
|
||||
serviceDefinition {
|
||||
{/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t*) &deviceInfoUuid,
|
||||
.characteristics = characteristicDefinition},
|
||||
{0},
|
||||
} {
|
||||
}
|
||||
|
||||
|
@ -1,76 +1,52 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#include <Version.h>
|
||||
#undef max
|
||||
#undef min
|
||||
#include "Version.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
class DeviceInformationService {
|
||||
public:
|
||||
DeviceInformationService();
|
||||
void Init();
|
||||
public:
|
||||
DeviceInformationService();
|
||||
void Init();
|
||||
|
||||
int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt);
|
||||
int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
|
||||
|
||||
private:
|
||||
static constexpr uint16_t deviceInfoId {0x180a};
|
||||
static constexpr uint16_t manufacturerNameId {0x2a29};
|
||||
static constexpr uint16_t modelNumberId {0x2a24};
|
||||
static constexpr uint16_t serialNumberId {0x2a25};
|
||||
static constexpr uint16_t fwRevisionId {0x2a26};
|
||||
static constexpr uint16_t hwRevisionId {0x2a27};
|
||||
static constexpr uint16_t swRevisionId {0x2a28};
|
||||
private:
|
||||
static constexpr uint16_t deviceInfoId {0x180a};
|
||||
static constexpr uint16_t manufacturerNameId {0x2a29};
|
||||
static constexpr uint16_t modelNumberId {0x2a24};
|
||||
static constexpr uint16_t serialNumberId {0x2a25};
|
||||
static constexpr uint16_t fwRevisionId {0x2a26};
|
||||
static constexpr uint16_t hwRevisionId {0x2a27};
|
||||
static constexpr uint16_t swRevisionId {0x2a28};
|
||||
|
||||
static constexpr const char* manufacturerName = "PINE64";
|
||||
static constexpr const char* modelNumber = "PineTime";
|
||||
static constexpr const char* hwRevision = "1.0.0";
|
||||
static constexpr const char* serialNumber = "0";
|
||||
static constexpr const char* fwRevision = Version::VersionString();
|
||||
static constexpr const char* swRevision = "InfiniTime";
|
||||
static constexpr const char* manufacturerName = "PINE64";
|
||||
static constexpr const char* modelNumber = "PineTime";
|
||||
static constexpr const char* hwRevision = "1.0.0";
|
||||
static constexpr const char* serialNumber = "0";
|
||||
static constexpr const char* fwRevision = Version::VersionString();
|
||||
static constexpr const char* swRevision = "InfiniTime";
|
||||
|
||||
static constexpr ble_uuid16_t deviceInfoUuid {.u {.type = BLE_UUID_TYPE_16}, .value = deviceInfoId};
|
||||
|
||||
static constexpr ble_uuid16_t deviceInfoUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = deviceInfoId
|
||||
};
|
||||
static constexpr ble_uuid16_t manufacturerNameUuid {.u {.type = BLE_UUID_TYPE_16}, .value = manufacturerNameId};
|
||||
|
||||
static constexpr ble_uuid16_t manufacturerNameUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = manufacturerNameId
|
||||
};
|
||||
static constexpr ble_uuid16_t modelNumberUuid {.u {.type = BLE_UUID_TYPE_16}, .value = modelNumberId};
|
||||
|
||||
static constexpr ble_uuid16_t modelNumberUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = modelNumberId
|
||||
};
|
||||
static constexpr ble_uuid16_t serialNumberUuid {.u {.type = BLE_UUID_TYPE_16}, .value = serialNumberId};
|
||||
|
||||
static constexpr ble_uuid16_t serialNumberUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = serialNumberId
|
||||
};
|
||||
static constexpr ble_uuid16_t fwRevisionUuid {.u {.type = BLE_UUID_TYPE_16}, .value = fwRevisionId};
|
||||
|
||||
static constexpr ble_uuid16_t fwRevisionUuid {
|
||||
.u { .type = BLE_UUID_TYPE_16 },
|
||||
.value = fwRevisionId
|
||||
};
|
||||
|
||||
static constexpr ble_uuid16_t hwRevisionUuid {
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = hwRevisionId
|
||||
};
|
||||
|
||||
static constexpr ble_uuid16_t swRevisionUuid {
|
||||
.u {.type = BLE_UUID_TYPE_16},
|
||||
.value = swRevisionId
|
||||
};
|
||||
|
||||
struct ble_gatt_chr_def characteristicDefinition[7];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
static constexpr ble_uuid16_t hwRevisionUuid {.u {.type = BLE_UUID_TYPE_16}, .value = hwRevisionId};
|
||||
|
||||
static constexpr ble_uuid16_t swRevisionUuid {.u {.type = BLE_UUID_TYPE_16}, .value = swRevisionId};
|
||||
|
||||
struct ble_gatt_chr_def characteristicDefinition[7];
|
||||
struct ble_gatt_svc_def serviceDefinition[2];
|
||||
};
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "components/ble/BleController.h"
|
||||
#include "systemtask/SystemTask.h"
|
||||
#include "DfuService.h"
|
||||
#include <cstring>
|
||||
#include "components/ble/BleController.h"
|
||||
#include "drivers/SpiNorFlash.h"
|
||||
#include "systemtask/SystemTask.h"
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
@ -11,67 +11,60 @@ constexpr ble_uuid128_t DfuService::controlPointCharacteristicUuid;
|
||||
constexpr ble_uuid128_t DfuService::revisionCharacteristicUuid;
|
||||
constexpr ble_uuid128_t DfuService::packetCharacteristicUuid;
|
||||
|
||||
int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||
auto dfuService = static_cast<DfuService *>(arg);
|
||||
int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
|
||||
auto dfuService = static_cast<DfuService*>(arg);
|
||||
return dfuService->OnServiceData(conn_handle, attr_handle, ctxt);
|
||||
}
|
||||
|
||||
void NotificationTimerCallback(TimerHandle_t xTimer) {
|
||||
auto notificationManager = static_cast<DfuService::NotificationManager *>(pvTimerGetTimerID(xTimer));
|
||||
auto notificationManager = static_cast<DfuService::NotificationManager*>(pvTimerGetTimerID(xTimer));
|
||||
notificationManager->OnNotificationTimer();
|
||||
}
|
||||
|
||||
void TimeoutTimerCallback(TimerHandle_t xTimer) {
|
||||
auto dfuService = static_cast<DfuService *>(pvTimerGetTimerID(xTimer));
|
||||
auto dfuService = static_cast<DfuService*>(pvTimerGetTimerID(xTimer));
|
||||
dfuService->OnTimeout();
|
||||
}
|
||||
|
||||
DfuService::DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController,
|
||||
Pinetime::Drivers::SpiNorFlash &spiNorFlash) :
|
||||
systemTask{systemTask},
|
||||
bleController{bleController},
|
||||
dfuImage{spiNorFlash},
|
||||
characteristicDefinition{
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &packetCharacteristicUuid,
|
||||
.access_cb = DfuServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
|
||||
.val_handle = nullptr,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &controlPointCharacteristicUuid,
|
||||
.access_cb = DfuServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
|
||||
.val_handle = nullptr,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t *) &revisionCharacteristicUuid,
|
||||
.access_cb = DfuServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
.val_handle = &revision,
|
||||
DfuService::DfuService(Pinetime::System::SystemTask& systemTask,
|
||||
Pinetime::Controllers::Ble& bleController,
|
||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash)
|
||||
: systemTask {systemTask},
|
||||
bleController {bleController},
|
||||
dfuImage {spiNorFlash},
|
||||
characteristicDefinition {{
|
||||
.uuid = (ble_uuid_t*) &packetCharacteristicUuid,
|
||||
.access_cb = DfuServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
|
||||
.val_handle = nullptr,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t*) &controlPointCharacteristicUuid,
|
||||
.access_cb = DfuServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
|
||||
.val_handle = nullptr,
|
||||
},
|
||||
{
|
||||
.uuid = (ble_uuid_t*) &revisionCharacteristicUuid,
|
||||
.access_cb = DfuServiceCallback,
|
||||
.arg = this,
|
||||
.flags = BLE_GATT_CHR_F_READ,
|
||||
.val_handle = &revision,
|
||||
|
||||
},
|
||||
{
|
||||
0
|
||||
}
|
||||
},
|
||||
{0}
|
||||
|
||||
},
|
||||
serviceDefinition{
|
||||
{
|
||||
/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t *) &serviceUuid,
|
||||
.characteristics = characteristicDefinition
|
||||
},
|
||||
{
|
||||
0
|
||||
},
|
||||
} {
|
||||
timeoutTimer = xTimerCreate ("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
|
||||
},
|
||||
serviceDefinition {
|
||||
{/* Device Information Service */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = (ble_uuid_t*) &serviceUuid,
|
||||
.characteristics = characteristicDefinition},
|
||||
{0},
|
||||
} {
|
||||
timeoutTimer = xTimerCreate("notificationTimer", 10000, pdFALSE, this, TimeoutTimerCallback);
|
||||
}
|
||||
|
||||
void DfuService::Init() {
|
||||
@ -83,55 +76,59 @@ void DfuService::Init() {
|
||||
ASSERT(res == 0);
|
||||
}
|
||||
|
||||
int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) {
|
||||
if(bleController.IsFirmwareUpdating()){
|
||||
int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
|
||||
if (bleController.IsFirmwareUpdating()) {
|
||||
xTimerStart(timeoutTimer, 0);
|
||||
}
|
||||
|
||||
|
||||
ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &packetCharacteristicUuid, nullptr,
|
||||
&packetCharacteristicHandle);
|
||||
ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &controlPointCharacteristicUuid, nullptr,
|
||||
&controlPointCharacteristicHandle);
|
||||
ble_gatts_find_chr((ble_uuid_t *) &serviceUuid, (ble_uuid_t *) &revisionCharacteristicUuid, nullptr,
|
||||
&revisionCharacteristicHandle);
|
||||
ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &packetCharacteristicUuid, nullptr, &packetCharacteristicHandle);
|
||||
ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &controlPointCharacteristicUuid, nullptr, &controlPointCharacteristicHandle);
|
||||
ble_gatts_find_chr((ble_uuid_t*) &serviceUuid, (ble_uuid_t*) &revisionCharacteristicUuid, nullptr, &revisionCharacteristicHandle);
|
||||
|
||||
if (attributeHandle == packetCharacteristicHandle) {
|
||||
if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
|
||||
return WritePacketHandler(connectionHandle, context->om);
|
||||
else return 0;
|
||||
else
|
||||
return 0;
|
||||
} else if (attributeHandle == controlPointCharacteristicHandle) {
|
||||
if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR)
|
||||
return ControlPointHandler(connectionHandle, context->om);
|
||||
else return 0;
|
||||
else
|
||||
return 0;
|
||||
} else if (attributeHandle == revisionCharacteristicHandle) {
|
||||
if (context->op == BLE_GATT_ACCESS_OP_READ_CHR)
|
||||
return SendDfuRevision(context->om);
|
||||
else return 0;
|
||||
else
|
||||
return 0;
|
||||
} else {
|
||||
NRF_LOG_INFO("[DFU] Unknown Characteristic : %d", attributeHandle);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int DfuService::SendDfuRevision(os_mbuf *om) const {
|
||||
int DfuService::SendDfuRevision(os_mbuf* om) const {
|
||||
int res = os_mbuf_append(om, &revision, sizeof(revision));
|
||||
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
|
||||
int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
|
||||
switch (state) {
|
||||
case States::Start: {
|
||||
softdeviceSize = om->om_data[0] + (om->om_data[1] << 8) + (om->om_data[2] << 16) + (om->om_data[3] << 24);
|
||||
bootloaderSize = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
|
||||
applicationSize = om->om_data[8] + (om->om_data[9] << 8) + (om->om_data[10] << 16) + (om->om_data[11] << 24);
|
||||
bleController.FirmwareUpdateTotalBytes(applicationSize);
|
||||
NRF_LOG_INFO("[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize,
|
||||
bootloaderSize, applicationSize);
|
||||
NRF_LOG_INFO(
|
||||
"[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, bootloaderSize, applicationSize);
|
||||
|
||||
// wait until SystemTask has finished waking up all devices
|
||||
while (systemTask.IsSleeping()) {
|
||||
vTaskDelay(50); // 50ms
|
||||
}
|
||||
|
||||
dfuImage.Erase();
|
||||
|
||||
uint8_t data[]{16, 1, 1};
|
||||
uint8_t data[] {16, 1, 1};
|
||||
notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
|
||||
state = States::Init;
|
||||
}
|
||||
@ -139,19 +136,22 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
case States::Init: {
|
||||
uint16_t deviceType = om->om_data[0] + (om->om_data[1] << 8);
|
||||
uint16_t deviceRevision = om->om_data[2] + (om->om_data[3] << 8);
|
||||
uint32_t applicationVersion =
|
||||
om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
|
||||
uint32_t applicationVersion = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24);
|
||||
uint16_t softdeviceArrayLength = om->om_data[8] + (om->om_data[9] << 8);
|
||||
uint16_t sd[softdeviceArrayLength];
|
||||
for (int i = 0; i < softdeviceArrayLength; i++) {
|
||||
sd[i] = om->om_data[10 + (i * 2)] + (om->om_data[10 + (i * 2) + 1] << 8);
|
||||
}
|
||||
expectedCrc =
|
||||
om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
|
||||
expectedCrc = om->om_data[10 + (softdeviceArrayLength * 2)] + (om->om_data[10 + (softdeviceArrayLength * 2) + 1] << 8);
|
||||
|
||||
NRF_LOG_INFO(
|
||||
"[DFU] -> Init data received : deviceType = %d, deviceRevision = %d, applicationVersion = %d, nb SD = %d, First SD = %d, CRC = %u",
|
||||
deviceType, deviceRevision, applicationVersion, softdeviceArrayLength, sd[0], expectedCrc);
|
||||
"[DFU] -> Init data received : deviceType = %d, deviceRevision = %d, applicationVersion = %d, nb SD = %d, First SD = %d, CRC = %u",
|
||||
deviceType,
|
||||
deviceRevision,
|
||||
applicationVersion,
|
||||
softdeviceArrayLength,
|
||||
sd[0],
|
||||
expectedCrc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -163,16 +163,18 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
bleController.FirmwareUpdateCurrentBytes(bytesReceived);
|
||||
|
||||
if ((nbPacketReceived % nbPacketsToNotify) == 0 && bytesReceived != applicationSize) {
|
||||
uint8_t data[5]{static_cast<uint8_t>(Opcodes::PacketReceiptNotification),
|
||||
(uint8_t) (bytesReceived & 0x000000FFu), (uint8_t) (bytesReceived >> 8u),
|
||||
(uint8_t) (bytesReceived >> 16u), (uint8_t) (bytesReceived >> 24u)};
|
||||
uint8_t data[5] {static_cast<uint8_t>(Opcodes::PacketReceiptNotification),
|
||||
(uint8_t) (bytesReceived & 0x000000FFu),
|
||||
(uint8_t) (bytesReceived >> 8u),
|
||||
(uint8_t) (bytesReceived >> 16u),
|
||||
(uint8_t) (bytesReceived >> 24u)};
|
||||
NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received", bytesReceived);
|
||||
notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 5);
|
||||
}
|
||||
if (dfuImage.IsComplete()) {
|
||||
uint8_t data[3]{static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage),
|
||||
static_cast<uint8_t>(ErrorCodes::NoError)};
|
||||
uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::ReceiveFirmwareImage),
|
||||
static_cast<uint8_t>(ErrorCodes::NoError)};
|
||||
NRF_LOG_INFO("[DFU] -> Send packet notification : all bytes received!");
|
||||
notificationManager.Send(connectionHandle, controlPointCharacteristicHandle, data, 3);
|
||||
state = States::Validate;
|
||||
@ -186,7 +188,7 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
|
||||
auto opcode = static_cast<Opcodes>(om->om_data[0]);
|
||||
NRF_LOG_INFO("[DFU] -> ControlPointHandler");
|
||||
|
||||
@ -208,14 +210,13 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Running);
|
||||
bleController.FirmwareUpdateTotalBytes(0xffffffffu);
|
||||
bleController.FirmwareUpdateCurrentBytes(0);
|
||||
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateStarted);
|
||||
systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateStarted);
|
||||
return 0;
|
||||
} else {
|
||||
NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case Opcodes::InitDFUParameters: {
|
||||
if (state != States::Init) {
|
||||
NRF_LOG_INFO("[DFU] -> Init DFU requested, but we are not in Init state");
|
||||
@ -225,11 +226,9 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
NRF_LOG_INFO("[DFU] -> Init DFU parameters %s", isInitComplete ? " complete" : " not complete");
|
||||
|
||||
if (isInitComplete) {
|
||||
uint8_t data[3] {
|
||||
static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::InitDFUParameters),
|
||||
(isInitComplete ? uint8_t{1} : uint8_t{0})
|
||||
};
|
||||
uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::InitDFUParameters),
|
||||
(isInitComplete ? uint8_t {1} : uint8_t {0})};
|
||||
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
|
||||
return 0;
|
||||
}
|
||||
@ -257,27 +256,24 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
|
||||
NRF_LOG_INFO("[DFU] -> Validate firmware image requested -- %d", connectionHandle);
|
||||
|
||||
if(dfuImage.Validate()){
|
||||
if (dfuImage.Validate()) {
|
||||
state = States::Validated;
|
||||
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
|
||||
NRF_LOG_INFO("Image OK");
|
||||
|
||||
uint8_t data[3] {
|
||||
static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::ValidateFirmware),
|
||||
static_cast<uint8_t>(ErrorCodes::NoError)
|
||||
};
|
||||
uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::ValidateFirmware),
|
||||
static_cast<uint8_t>(ErrorCodes::NoError)};
|
||||
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
|
||||
} else {
|
||||
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
|
||||
NRF_LOG_INFO("Image Error : bad CRC");
|
||||
|
||||
uint8_t data[3] {
|
||||
static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::ValidateFirmware),
|
||||
static_cast<uint8_t>(ErrorCodes::CrcError)
|
||||
};
|
||||
uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
|
||||
static_cast<uint8_t>(Opcodes::ValidateFirmware),
|
||||
static_cast<uint8_t>(ErrorCodes::CrcError)};
|
||||
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
|
||||
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
|
||||
Reset();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -288,10 +284,8 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
return 0;
|
||||
}
|
||||
NRF_LOG_INFO("[DFU] -> Activate image and reset!");
|
||||
bleController.StopFirmwareUpdate();
|
||||
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
|
||||
Reset();
|
||||
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
|
||||
Reset();
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
@ -299,6 +293,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) {
|
||||
}
|
||||
|
||||
void DfuService::OnTimeout() {
|
||||
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
|
||||
Reset();
|
||||
}
|
||||
|
||||
@ -312,17 +307,16 @@ void DfuService::Reset() {
|
||||
applicationSize = 0;
|
||||
expectedCrc = 0;
|
||||
notificationManager.Reset();
|
||||
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
|
||||
bleController.StopFirmwareUpdate();
|
||||
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
|
||||
systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateFinished);
|
||||
}
|
||||
|
||||
DfuService::NotificationManager::NotificationManager() {
|
||||
timer = xTimerCreate ("notificationTimer", 1000, pdFALSE, this, NotificationTimerCallback);
|
||||
timer = xTimerCreate("notificationTimer", 1000, pdFALSE, this, NotificationTimerCallback);
|
||||
}
|
||||
|
||||
bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t s) {
|
||||
if(size != 0 || s > 10)
|
||||
bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t* data, size_t s) {
|
||||
if (size != 0 || s > 10)
|
||||
return false;
|
||||
|
||||
connectionHandle = connection;
|
||||
@ -334,14 +328,14 @@ bool DfuService::NotificationManager::AsyncSend(uint16_t connection, uint16_t ch
|
||||
}
|
||||
|
||||
void DfuService::NotificationManager::OnNotificationTimer() {
|
||||
if(size > 0) {
|
||||
if (size > 0) {
|
||||
Send(connectionHandle, characteristicHandle, buffer, size);
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DfuService::NotificationManager::Send(uint16_t connection, uint16_t charactHandle, const uint8_t *data, const size_t s) {
|
||||
auto *om = ble_hs_mbuf_from_flat(data, s);
|
||||
void DfuService::NotificationManager::Send(uint16_t connection, uint16_t charactHandle, const uint8_t* data, const size_t s) {
|
||||
auto* om = ble_hs_mbuf_from_flat(data, s);
|
||||
auto ret = ble_gattc_notify_custom(connection, charactHandle, om);
|
||||
ASSERT(ret == 0);
|
||||
}
|
||||
@ -354,27 +348,29 @@ void DfuService::NotificationManager::Reset() {
|
||||
}
|
||||
|
||||
void DfuService::DfuImage::Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc) {
|
||||
if(chunkSize != 20) return;
|
||||
if (chunkSize != 20)
|
||||
return;
|
||||
this->chunkSize = chunkSize;
|
||||
this->totalSize = totalSize;
|
||||
this->expectedCrc = expectedCrc;
|
||||
this->ready = true;
|
||||
}
|
||||
|
||||
void DfuService::DfuImage::Append(uint8_t *data, size_t size) {
|
||||
if(!ready) return;
|
||||
void DfuService::DfuImage::Append(uint8_t* data, size_t size) {
|
||||
if (!ready)
|
||||
return;
|
||||
ASSERT(size <= 20);
|
||||
|
||||
std::memcpy(tempBuffer + bufferWriteIndex, data, size);
|
||||
bufferWriteIndex += size;
|
||||
|
||||
if(bufferWriteIndex == bufferSize) {
|
||||
if (bufferWriteIndex == bufferSize) {
|
||||
spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
|
||||
totalWriteIndex += bufferWriteIndex;
|
||||
bufferWriteIndex = 0;
|
||||
}
|
||||
|
||||
if(bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) {
|
||||
if (bufferWriteIndex > 0 && totalWriteIndex + bufferWriteIndex == totalSize) {
|
||||
spiNorFlash.Write(writeOffset + totalWriteIndex, tempBuffer, bufferWriteIndex);
|
||||
totalWriteIndex += bufferWriteIndex;
|
||||
if (totalSize < maxSize)
|
||||
@ -383,15 +379,16 @@ void DfuService::DfuImage::Append(uint8_t *data, size_t size) {
|
||||
}
|
||||
|
||||
void DfuService::DfuImage::WriteMagicNumber() {
|
||||
uint32_t magic[4] = { // TODO When this variable is a static constexpr, the values written to the memory are not correct. Why?
|
||||
0xf395c277,
|
||||
0x7fefd260,
|
||||
0x0f505235,
|
||||
0x8079b62c,
|
||||
uint32_t magic[4] = {
|
||||
// TODO When this variable is a static constexpr, the values written to the memory are not correct. Why?
|
||||
0xf395c277,
|
||||
0x7fefd260,
|
||||
0x0f505235,
|
||||
0x8079b62c,
|
||||
};
|
||||
|
||||
uint32_t offset = writeOffset + (maxSize - (4 * sizeof(uint32_t)));
|
||||
spiNorFlash.Write(offset, reinterpret_cast<const uint8_t *>(magic), 4 * sizeof(uint32_t));
|
||||
spiNorFlash.Write(offset, reinterpret_cast<const uint8_t*>(magic), 4 * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void DfuService::DfuImage::Erase() {
|
||||
@ -421,7 +418,7 @@ bool DfuService::DfuImage::Validate() {
|
||||
return (crc == expectedCrc);
|
||||
}
|
||||
|
||||
uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc) {
|
||||
uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t const* p_crc) {
|
||||
uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
@ -436,6 +433,7 @@ uint16_t DfuService::DfuImage::ComputeCrc(uint8_t const *p_data, uint32_t size,
|
||||
}
|
||||
|
||||
bool DfuService::DfuImage::IsComplete() {
|
||||
if(!ready) return false;
|
||||
if (!ready)
|
||||
return false;
|
||||
return totalWriteIndex == totalSize;
|
||||
}
|
||||
|