Initial InfiniSim project

main
Reinhold Gschweicher 2022-02-16 21:42:29 +07:00
parent cea006b049
commit f19355949b
78 changed files with 9978 additions and 0 deletions

@ -0,0 +1,66 @@
# GitHub Actions Workflow to build Simulator for PineTime Smart Watch LVGL Interface
# Name of this Workflow
name: Build InfiniSim LVGL Simulator
# When to run this Workflow...
on:
# Run on all branches
push:
branches: []
# Also run this Workflow when a Pull Request is created or updated in the "main" and "develop" Branch
pull_request:
branches: [ main, develop ]
# Steps to run for the Workflow
jobs:
build:
# Run these steps on Ubuntu
runs-on: ubuntu-latest
steps:
#########################################################################################
# Download and Install Dependencies
- name: Install cmake
uses: lukka/get-cmake@v3.18.3
- name: Install SDL2 development package
run: |
sudo apt-get update
sudo apt-get -y install libsdl2-dev
#########################################################################################
# Checkout
- name: Checkout source files
uses: actions/checkout@v2
with:
submodules: recursive
#########################################################################################
# CMake
- name: CMake
run: |
cmake -G Ninja -S . -B build_lv_sim
#########################################################################################
# Build and Upload simulator
# For Debugging Builds: Remove "make" option "-j" for clearer output. Add "--trace" to see details.
# For Faster Builds: Add "make" option "-j"
- name: Build simulator executable
run: |
cmake --build build_lv_sim
- name: Upload simulator executable
uses: actions/upload-artifact@v2
with:
name: infinisim
path: build_lv_sim/infinisim

6
.gitmodules vendored

@ -0,0 +1,6 @@
[submodule "InfiniTime"]
path = InfiniTime
url = ../../NeroBurner/InfiniTime.git
[submodule "lv_drivers"]
path = lv_drivers
url = ../../lvgl/lv_drivers.git

@ -0,0 +1,208 @@
message(STATUS "Using CMake version ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 3.10...${CMAKE_VERSION})
SET(InfiniTime_DIR "InfiniTime" CACHE PATH "Path to InfiniTime source path to use for simulator")
if(NOT EXISTS ${InfiniTime_DIR})
message(FATAL_ERROR "Can't access folder '${InfiniTime_DIR}'. Try `git submodule update --init --recursive` to initialize the git submodule of InfiniTime")
endif()
if(NOT EXISTS ${InfiniTime_DIR}/src/libs/lvgl)
message(FATAL_ERROR "Can't access folder '${InfiniTime_DIR}/src/libs/lvgl'. Try `git submodule update --init --recursive` to initialize the git submodule of lvgl inside InfiniTime")
endif()
# try to extract the PineTime project version
file(READ "${InfiniTime_DIR}/CMakeLists.txt" main_cmake)
string(REGEX MATCH "project\\(pinetime VERSION ([0-9]*\.[0-9]*\.[0-9]*)" _ ${main_cmake})
set(PROJECT_VERSION ${CMAKE_MATCH_1})
message(STATUS "InfiniTime PROJECT_VERSION extracted: ${PROJECT_VERSION}")
project(InfiniSim VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
# https://cmake.org/cmake/help/latest/prop_tgt/CXX_STANDARD.html
string(COMPARE EQUAL "${CMAKE_CXX_STANDARD}" "" no_cmake_cxx_standard_set)
if(no_cmake_cxx_standard_set)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
message(STATUS "Using default C++ standard ${CMAKE_CXX_STANDARD}")
else()
message(STATUS "Using user specified C++ standard ${CMAKE_CXX_STANDARD}")
endif()
set(CMAKE_C_STANDARD 11)#C11
file(GLOB_RECURSE INCLUDES "lv_drivers/*.h" "${InfiniTime_DIR}/src/libs/lvgl/src/*.h" "./*.h" )
file(GLOB_RECURSE SOURCES "lv_drivers/*.c" "${InfiniTime_DIR}/src/libs/lvgl/src/*.c" )
add_executable(infinisim main.cpp ${SOURCES} ${INCLUDES})
# add simulator files
target_sources(infinisim PUBLIC
sim/displayapp/LittleVgl.h
sim/displayapp/LittleVgl.cpp
sim/displayapp/screens/Missing.h
sim/displayapp/screens/Missing.cpp
sim/components/battery/BatteryController.h
sim/components/battery/BatteryController.cpp
sim/components/ble/AlertNotificationService.h
sim/components/ble/AlertNotificationService.cpp
sim/components/ble/MusicService.h
sim/components/ble/MusicService.cpp
sim/components/ble/NavigationService.h
sim/components/ble/NavigationService.cpp
sim/components/ble/NimbleController.h
sim/components/ble/NimbleController.cpp
sim/components/ble/weather/WeatherService.h
sim/components/ble/weather/WeatherService.cpp
sim/components/brightness/BrightnessController.h
sim/components/brightness/BrightnessController.cpp
sim/components/firmwarevalidator/FirmwareValidator.h
sim/components/firmwarevalidator/FirmwareValidator.cpp
sim/components/fs/FS.h
sim/components/fs/FS.cpp
sim/components/heartrate/HeartRateController.h
sim/components/heartrate/HeartRateController.cpp
sim/components/motion/MotionController.h
sim/components/motion/MotionController.cpp
sim/components/motor/MotorController.h
sim/components/motor/MotorController.cpp
sim/drivers/Watchdog.h
sim/drivers/Watchdog.cpp
sim/drivers/Bma421.h
sim/drivers/Bma421.cpp
sim/drivers/Cst816s.h
sim/drivers/Cst816s.cpp
sim/drivers/Hrs3300.h
sim/drivers/Hrs3300.cpp
sim/drivers/SpiMaster.h
sim/drivers/SpiMaster.cpp
sim/drivers/TwiMaster.h
sim/drivers/TwiMaster.cpp
sim/drivers/SpiNorFlash.h
sim/drivers/SpiNorFlash.cpp
sim/heartratetask/HeartRateTask.h
sim/heartratetask/HeartRateTask.cpp
# FreeRTOS
sim/FreeRTOS.h
sim/FreeRTOS.cpp
sim/task.h
sim/task.cpp
sim/timers.h
sim/timers.cpp
sim/queue.h
sim/queue.cpp
# src/FreeRTOS
sim/portmacro_cmsis.h
sim/portmacro_cmsis.cpp
# nrf
sim/libraries/log/nrf_log.h
sim/libraries/delay/nrf_delay.h
sim/libraries/delay/nrf_delay.cpp
sim/nrfx/nrfx_log.h
sim/nrfx/drivers/include/nrfx_twi.h
sim/nrfx/hal/nrf_gpio.h
sim/nrfx/hal/nrf_gpio.cpp
sim/nrfx/hal/nrfx_gpiote.h # includes hal/nrf_gpio.h
sim/nrfx/hal/nrf_rtc.h
sim/nrfx/hal/nrf_rtc.cpp
# nrf/components/libraries/timer
sim/libraries/timer/app_timer.h
sim/libraries/timer/app_timer.cpp
sim/libraries/gpiote/app_gpiote.h # includes hal/nrf_gpio.h
)
target_include_directories(infinisim PRIVATE "sim")
target_include_directories(infinisim PRIVATE "sim/libraries/log") # for nrf_log.h
target_include_directories(infinisim PRIVATE "sim/libraries/timer") # for app_timer.h
target_include_directories(infinisim PRIVATE "sim/nrfx") # for nrfx_log.h and others
target_include_directories(infinisim PRIVATE "sim/nrfx/hal") # for nrfx_log.h
target_compile_definitions(infinisim PRIVATE LV_CONF_INCLUDE_SIMPLE)
target_sources(infinisim PUBLIC
# LVGL Fonts
${InfiniTime_DIR}/src/libs/lvgl/src/lv_font/lv_font_montserrat_14.c
${InfiniTime_DIR}/src/libs/lvgl/src/lv_font/lv_font_montserrat_18.c
${InfiniTime_DIR}/src/libs/lvgl/src/lv_font/lv_font_montserrat_22.c
${InfiniTime_DIR}/src/libs/lvgl/src/lv_font/lv_font_montserrat_28.c
${InfiniTime_DIR}/src/displayapp/fonts/lv_font_navi_80.c
${InfiniTime_DIR}/src/displayapp/fonts/jetbrains_mono_extrabold_compressed.c
${InfiniTime_DIR}/src/displayapp/fonts/jetbrains_mono_bold_20.c
${InfiniTime_DIR}/src/displayapp/fonts/jetbrains_mono_76.c
${InfiniTime_DIR}/src/displayapp/fonts/jetbrains_mono_42.c
${InfiniTime_DIR}/src/displayapp/fonts/lv_font_sys_48.c
${InfiniTime_DIR}/src/displayapp/fonts/open_sans_light.c
)
target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs")
target_include_directories(infinisim PRIVATE "lv_drivers")
# add dates library
target_include_directories(infinisim SYSTEM PRIVATE "${InfiniTime_DIR}/src/libs/date/includes")
# add Screens with a globbing expression, to enable easier CI test-runs for PRs adding new Screens
file(GLOB InfiniTime_SCREENS
"${InfiniTime_DIR}/src/displayapp/screens/*.h"
"${InfiniTime_DIR}/src/displayapp/screens/*.cpp"
"${InfiniTime_DIR}/src/displayapp/screens/settings/*.h"
"${InfiniTime_DIR}/src/displayapp/screens/settings/*.cpp"
)
target_sources(infinisim PUBLIC ${InfiniTime_SCREENS})
# add files directly from InfiniTime sources
target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src")
target_sources(infinisim PUBLIC
${InfiniTime_DIR}/src/BootloaderVersion.h
${InfiniTime_DIR}/src/BootloaderVersion.cpp
${InfiniTime_DIR}/src/displayapp/Colors.h
${InfiniTime_DIR}/src/displayapp/Colors.cpp
${InfiniTime_DIR}/src/displayapp/DisplayApp.h
${InfiniTime_DIR}/src/displayapp/DisplayApp.cpp
${InfiniTime_DIR}/src/displayapp/lv_pinetime_theme.h
${InfiniTime_DIR}/src/displayapp/lv_pinetime_theme.c
${InfiniTime_DIR}/src/displayapp/icons/bg_clock.c # used by WatchFaceAnalog.cpp
${InfiniTime_DIR}/src/buttonhandler/ButtonHandler.h
${InfiniTime_DIR}/src/buttonhandler/ButtonHandler.cpp
${InfiniTime_DIR}/src/components/alarm/AlarmController.h
${InfiniTime_DIR}/src/components/alarm/AlarmController.cpp
${InfiniTime_DIR}/src/components/ble/BleController.h
${InfiniTime_DIR}/src/components/ble/BleController.cpp
${InfiniTime_DIR}/src/components/datetime/DateTimeController.h
${InfiniTime_DIR}/src/components/datetime/DateTimeController.cpp
${InfiniTime_DIR}/src/components/settings/Settings.h
${InfiniTime_DIR}/src/components/settings/Settings.cpp
${InfiniTime_DIR}/src/components/ble/NotificationManager.h
${InfiniTime_DIR}/src/components/ble/NotificationManager.cpp
${InfiniTime_DIR}/src/components/timer/TimerController.h
${InfiniTime_DIR}/src/components/timer/TimerController.cpp
${InfiniTime_DIR}/src/drivers/PinMap.h
${InfiniTime_DIR}/src/drivers/Spi.h
${InfiniTime_DIR}/src/drivers/Spi.cpp
${InfiniTime_DIR}/src/drivers/St7789.h
${InfiniTime_DIR}/src/drivers/St7789.cpp
${InfiniTime_DIR}/src/touchhandler/TouchHandler.h
${InfiniTime_DIR}/src/touchhandler/TouchHandler.cpp
${InfiniTime_DIR}/src/systemtask/SystemTask.h
${InfiniTime_DIR}/src/systemtask/SystemTask.cpp
)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Special case for SDL2 dependency, goal is to find a config that exports SDL2::SDL2 target
# libsdl2-dev has a `sdl2-config.cmake` that doesn't export this, but vcpkg does..
find_package(SDL2 CONFIG QUIET)
if(NOT TARGET SDL2::SDL2)
find_package(SDL2 MODULE REQUIRED)
endif()
target_link_libraries(infinisim PRIVATE SDL2::SDL2)
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE PROJECT_GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!")
configure_file("${InfiniTime_DIR}/src/Version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/Version.h")
target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")

@ -0,0 +1 @@
Subproject commit 7142de2aa65bddbde7fda341bde2124952beda2c

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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.
This program 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/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

@ -0,0 +1,106 @@
# [InfiniSim](https://github.com/InfiniTimeOrg/InfiniSim)
[![Build InfiniSim LVGL Simulator](https://github.com/InfiniTimeOrg/InfiniSim/actions/workflows/lv_sim.yml/badge.svg)](https://github.com/InfiniTimeOrg/InfiniSim/actions/workflows/lv_sim.yml)
Simulator for [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) project.
Experience the `InfiniTime` user interface directly on your PC, to shorten the time until you get your hands on a real [PineTime smartwatch](https://www.pine64.org/pinetime/).
Or use it to develop new Watchfaces, new Screens, or quickly iterate on the user interface.
For a history on how this simulator started and the challenges on its way visit the [original PR](https://github.com/InfiniTimeOrg/InfiniTime/pull/743).
## Build dependencies
- CMake
- SDL2 (provides the simulator window, handles mouse and keyboard input)
- Compiler (g++ or clang++)
On Ubuntu/Debian install the following packages:
```sh
sudo apt get install -y cmake libsdl2-dev g++
```
On Arch Linux the following packages are needed:
```sh
sudo pacman -S cmake sdl2 gcc
```
## Get the Sources
Clone this repository and tell `git` to recursively download the submodules as well
```sh
git clone --recursive https://github.com/InfiniTimeOrg/InfiniSim.git
```
If you've already cloned the repository without the submodules (or you want to update them to the latest checked in version) run the following command:
```sh
git submodule update --init --recursive
```
## Configure and Build
In the most basic configuration tell cmake to configure the project and build it with the following two commands:
```sh
cmake -S . -B build
cmake --build build -j4
```
The following configuration settings can be added to the first `cmake -S . -B build` call
- `-DInfiniTime_DIR=InfiniTime`: a full path to an existing InfiniTime repository checked out.
Inside that directory the `src/libs/lvgl` submodule must be checked out as well.
The default value points to the InfiniTime submodule in this repository.
## Run Simulator
When the build was successful the simulator binary can be started with
```sh
./build/infinisim
```
![Running Simulator](https://user-images.githubusercontent.com/9076163/151057090-66fa6b10-eb4f-4b62-88e6-f9f307a57e40.gif)
To hide the second simulator-status-window start the binary with the `--hide-status` option
```sh
./build/infinisim --hide-status
```
- Left mouse button: simulates your finger, just click to tap, click and drag to swipe
- Right mouse button: simulates the hardware button (for example turn the screen off or on again)
Using the keyboard the following events can be triggered:
- `r` ... enable ringing
- `R` ... disable ringing
- `m` ... let motor run for 100 ms
- `M` ... let motor run for 255 ms
- `n` ... send notification
- `N` ... clear all notifications
- `b` ... connect Bluetooth
- `B` ... disconnect Bluetooth
- `v` ... increase battery voltage and percentage
- `V` ... decrease battery voltage and percentage
- `c` ... charging,
- `C` ... not charging
- `l` ... increase brightness level
- `L` ... lower brightness level
- `p` ... enable print lvgl memory usage to terminal
- `P` ... disable print memory usage
- `s` ... increase step count by 500 steps
- `S` ... decrease step count by 500 steps
- `h` ... set heartrate running, and on further presses increase by 10 bpm
- `H` ... stop heartrate
## Licenses
This project is released under the GNU General Public License version 3 or, at your option, any later version.
The same license as [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime).
The simulator is based on [lv_sim_eclipse_sdl](https://github.com/lvgl/lv_sim_eclipse_sdl) project under the MIT license.

@ -0,0 +1,144 @@
# Module: FindSDL2.cmake
# Based on: https://github.com/mosra/magnum/blob/master/modules/FindSDL2.cmake
#
# Exports:
# SDL2::SDL2 - Imported target
# SDL2_FOUND - True if SDL2 was found.
#
# Additionally these variables are defined for internal usage:
#
# SDL2_LIBRARY_DEBUG - SDL2 debug library, if found
# SDL2_LIBRARY_RELEASE - SDL2 release library, if found
# SDL2_DLL_DEBUG - SDL2 debug DLL on Windows, if found
# SDL2_DLL_RELEASE - SDL2 release DLL on Windows, if found
# SDL2_INCLUDE_DIR - Root include dir
set(_SDL2_PATH_SUFFIXES SDL2)
if(WIN32)
# Precompiled libraries for MSVC are in x86/x64 subdirectories
if(MSVC)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_SDL2_LIBRARY_PATH_SUFFIX lib/x64)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(_SDL2_LIBRARY_PATH_SUFFIX lib/x86)
endif()
# Both includes and libraries for MinGW are in some directory deep
# inside. There's also a CMake config file but it has HARDCODED path
# to /opt/local/i686-w64-mingw32, which doesn't make ANY SENSE,
# especially on Windows.
elseif(MINGW)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_SDL2_LIBRARY_PATH_SUFFIX x86_64-w64-mingw32/lib)
set(_SDL2_RUNTIME_PATH_SUFFIX x86_64-w64-mingw32/bin)
list(APPEND _SDL2_PATH_SUFFIXES x86_64-w64-mingw32/include/SDL2)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(_SDL2_LIBRARY_PATH_SUFFIX i686-w64-mingw32/lib)
set(_SDL2_RUNTIME_PATH_SUFFIX i686-w64-mingw32/lib)
list(APPEND _SDL2_PATH_SUFFIXES i686-w64-mingw32/include/SDL2)
endif()
else()
message(FATAL_ERROR "Unsupported compiler")
endif()
endif()
find_library(SDL2_LIBRARY_RELEASE
NAMES SDL2-2.0 SDL2
PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX})
find_library(SDL2_LIBRARY_DEBUG
NAMES SDL2d
PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX})
set(SDL2_LIBRARY_NEEDED SDL2_LIBRARY)
include(SelectLibraryConfigurations)
select_library_configurations(SDL2)
# Include dir
find_path(SDL2_INCLUDE_DIR
# We must search file which is present only in SDL2 and not in SDL1.
# Apparently when both SDL.h and SDL_scancode.h are specified, CMake is
# happy enough that it found SDL.h and doesn't bother about the other.
#
# On macOS, where the includes are not in SDL2/SDL.h form (which would
# solve this issue), but rather SDL2.framework/Headers/SDL.h, CMake might
# find SDL.framework/Headers/SDL.h if SDL1 is installed, which is wrong.
NAMES SDL_scancode.h
PATH_SUFFIXES ${_SDL2_PATH_SUFFIXES})
if(WIN32)
find_file(SDL2_DLL_RELEASE
NAMES SDL2.dll
PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
find_file(SDL2_DLL_DEBUG
NAMES SDL2d.dll
PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("SDL2" DEFAULT_MSG
${SDL2_LIBRARY_NEEDED}
${_SDL2_FRAMEWORK_LIBRARY_NAMES}
SDL2_INCLUDE_DIR)
if(NOT TARGET SDL2::SDL2)
if(SDL2_LIBRARY_NEEDED)
add_library(SDL2::SDL2 UNKNOWN IMPORTED)
# Work around BUGGY framework support on macOS
# https://cmake.org/Bug/view.php?id=14105
if(APPLE AND SDL2_LIBRARY_RELEASE MATCHES "\\.framework$")
set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE}/SDL2)
else()
if(SDL2_LIBRARY_RELEASE)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE})
endif()
if(SDL2_LIBRARY_DEBUG)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_DEBUG ${SDL2_LIBRARY_DEBUG})
endif()
endif()
# Link additional `dl` and `pthread` libraries required by a static
# build of SDL on Unixy platforms (except Apple, where it is most
# probably some frameworks instead)
if(UNIX AND NOT APPLE AND (SDL2_LIBRARY_DEBUG MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$" OR SDL2_LIBRARY_RELEASE MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$"))
find_package(Threads REQUIRED)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Threads::Threads ${CMAKE_DL_LIBS})
endif()
# Windows dependencies for a static library. Unfortunately there's no
# easy way to figure out if a *.lib is static or dynamic, so we're
# adding only if a DLL is not found.
if(WIN32 AND NOT SDL2_DLL_RELEASE AND NOT SDL2_DLL_DEBUG)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY INTERFACE_LINK_LIBRARIES
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1338
user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1384
dinput8)
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1422
# additionally has dxerr for MSVC if DirectX SDK is not used, but
# according to https://walbourn.github.io/wheres-dxerr-lib/ this
# thing is long deprecated.
if(MINGW)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY INTERFACE_LINK_LIBRARIES
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1386
dxerr8
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1388
mingw32)
endif()
endif()
else()
add_library(SDL2::SDL2 INTERFACE IMPORTED)
endif()
set_property(TARGET SDL2::SDL2 PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR})
endif()
mark_as_advanced(SDL2_INCLUDE_DIR)

@ -0,0 +1,766 @@
/**
* @file lv_conf.h
* Configuration file for v7.7.0-dev
*/
#ifndef LV_CONF_H
#define LV_CONF_H
/* clang-format off */
#include <stdint.h>
/*====================
Graphical settings
*====================*/
/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX (240)
#define LV_VER_RES_MAX (240)
/* Color depth:
* - 1: 1 byte per pixel
* - 8: RGB332
* - 16: RGB565
* - 32: ARGB8888
*/
#define LV_COLOR_DEPTH 16
/* Swap the 2 bytes of RGB565 color.
* Useful if the display has a 8 bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 1
/* 1: Enable screen transparency.
* Useful for OSD or other overlapping GUIs.
* Requires `LV_COLOR_DEPTH = 32` colors and the screen's style should be modified: `style.body.opa = ...`*/
#define LV_COLOR_SCREEN_TRANSP 0
/*Images pixels with this color will not be drawn (with chroma keying)*/
#define LV_COLOR_TRANSP LV_COLOR_MAKE(0x6c, 0xFc, 0x6a) /*LV_COLOR_LIME: pure green*/
/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
#define LV_ANTIALIAS 1
/* Default display refresh period.
* Can be changed in the display driver (`lv_disp_drv_t`).*/
#define LV_DISP_DEF_REFR_PERIOD 20 /*[ms]*/
/* Dot Per Inch: used to initialize default sizes.
* E.g. a button with width = LV_DPI / 2 -> half inch wide
* (Not so important, you can adjust it to modify default sizes and spaces)*/
#define LV_DPI 100 /*[px]*/
/* The the real width of the display changes some default values:
* default object sizes, layout of examples, etc.
* According to the width of the display (hor. res. / dpi)
* the displays fall in 4 categories.
* The 4th is extra large which has no upper limit so not listed here
* The upper limit of the categories are set below in 0.1 inch unit.
*/
#define LV_DISP_SMALL_LIMIT 30
#define LV_DISP_MEDIUM_LIMIT 50
#define LV_DISP_LARGE_LIMIT 70
/* Type of coordinates. Should be `int16_t` (or `int32_t` for extreme cases) */
typedef int16_t lv_coord_t;
/*=========================
Memory manager settings
*=========================*/
/* LittelvGL's internal memory manager's settings.
* The graphical objects and other related data are stored here. */
/* 1: use custom malloc/free, 0: use the built-in `lv_mem_alloc` and `lv_mem_free` */
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0
/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
#ifdef NDEBUG
#define LV_MEM_SIZE (14U * 1024U)
#else // debug mode -> allow more memory
#define LV_MEM_SIZE (28U * 1024U)
#endif
/* Complier prefix for a big array declaration */
#define LV_MEM_ATTR
/* Set an address for the memory pool instead of allocating it as an array.
* Can be in external SRAM too. */
#define LV_MEM_ADR 0
/* Automatically defrag. on free. Defrag. means joining the adjacent free cells. */
#define LV_MEM_AUTO_DEFRAG 1
#else /*LV_MEM_CUSTOM*/
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
#define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/
#define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/
#endif /*LV_MEM_CUSTOM*/
/* Use the standard memcpy and memset instead of LVGL's own functions.
* The standard functions might or might not be faster depending on their implementation. */
#define LV_MEMCPY_MEMSET_STD 1
/* Garbage Collector settings
* Used if lvgl is binded to higher level language and the memory is managed by that language */
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/
#define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/
#endif /* LV_ENABLE_GC */
/*=======================
Input device settings
*=======================*/
/* Input device default settings.
* Can be changed in the Input device driver (`lv_indev_drv_t`)*/
/* Input device read period in milliseconds */
#define LV_INDEV_DEF_READ_PERIOD 20
/* Drag threshold in pixels */
#define LV_INDEV_DEF_DRAG_LIMIT 10
/* Drag throw slow-down in [%]. Greater value -> faster slow-down */
#define LV_INDEV_DEF_DRAG_THROW 20
/* Long press time in milliseconds.
* Time to send `LV_EVENT_LONG_PRESSSED`) */
#define LV_INDEV_DEF_LONG_PRESS_TIME 400
/* Repeated trigger period in long press [ms]
* Time between `LV_EVENT_LONG_PRESSED_REPEAT */
#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100
/* Gesture threshold in pixels */
#define LV_INDEV_DEF_GESTURE_LIMIT 50
/* Gesture min velocity at release before swipe (pixels)*/
#define LV_INDEV_DEF_GESTURE_MIN_VELOCITY 3
/*==================
* Feature usage
*==================*/
/*1: Enable the Animations */
#define LV_USE_ANIMATION 1
#if LV_USE_ANIMATION
/*Declare the type of the user data of animations (can be e.g. `void *`, `int`, `struct`)*/
typedef void* lv_anim_user_data_t;
#endif
/* 1: Enable shadow drawing on rectangles*/
#define LV_USE_SHADOW 0
#if LV_USE_SHADOW
/* Allow buffering some shadow calculation
* LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer,
* where shadow size is `shadow_width + radius`
* Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
#define LV_SHADOW_CACHE_SIZE 0
#endif
/*1: enable outline drawing on rectangles*/
#define LV_USE_OUTLINE 0
/*1: enable pattern drawing on rectangles*/
#define LV_USE_PATTERN 0
/*1: enable value string drawing on rectangles*/
#define LV_USE_VALUE_STR 1
/* 1: Use other blend modes than normal (`LV_BLEND_MODE_...`)*/
#define LV_USE_BLEND_MODES 0
/* 1: Use the `opa_scale` style property to set the opacity of an object and its children at once*/
#define LV_USE_OPA_SCALE 0
/* 1: Use image zoom and rotation*/
#define LV_USE_IMG_TRANSFORM 0
/* 1: Enable object groups (for keyboard/encoder navigation) */
#define LV_USE_GROUP 0
#if LV_USE_GROUP
typedef void* lv_group_user_data_t;
#endif /*LV_USE_GROUP*/
/* 1: Enable GPU interface*/
#define LV_USE_GPU 0 /*Only enables `gpu_fill_cb` and `gpu_blend_cb` in the disp. drv- */
#define LV_USE_GPU_STM32_DMA2D 0
/*If enabling LV_USE_GPU_STM32_DMA2D, LV_GPU_DMA2D_CMSIS_INCLUDE must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h" */
#define LV_GPU_DMA2D_CMSIS_INCLUDE
/*1: Use PXP for CPU off-load on NXP RTxxx platforms */
#define LV_USE_GPU_NXP_PXP 0
/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol FSL_RTOS_FREE_RTOS
* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
* */
#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
/*1: Use VG-Lite for CPU offload on NXP RTxxx platforms */
#define LV_USE_GPU_NXP_VG_LITE 0
/* 1: Enable file system (might be required for images */
// TODO: Enable FS
#define LV_USE_FILESYSTEM 1
#if LV_USE_FILESYSTEM
/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/
typedef void * lv_fs_drv_user_data_t;
#endif
/*1: Add a `user_data` to drivers and objects*/
#define LV_USE_USER_DATA 1
/*1: Show CPU usage and FPS count in the right bottom corner*/
#define LV_USE_PERF_MONITOR 0
/*1: Use the functions and types from the older API if possible */
#define LV_USE_API_EXTENSION_V6 0
#define LV_USE_API_EXTENSION_V7 1
/*========================
* Image decoder and cache
*========================*/
/* 1: Enable indexed (palette) images */
#define LV_IMG_CF_INDEXED 1
/* 1: Enable alpha indexed images */
#define LV_IMG_CF_ALPHA 0
/* Default image cache size. Image caching keeps the images opened.
* If only the built-in image formats are used there is no real advantage of caching.
* (I.e. no new image decoder is added)
* With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
* However the opened images might consume additional RAM.
* LV_IMG_CACHE_DEF_SIZE must be >= 1 */
#define LV_IMG_CACHE_DEF_SIZE 6
/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/
typedef void* lv_img_decoder_user_data_t;
/*=====================
* Compiler settings
*====================*/
/* For big endian systems set to 1 */
#define LV_BIG_ENDIAN_SYSTEM 0
/* Define a custom attribute to `lv_tick_inc` function */
#define LV_ATTRIBUTE_TICK_INC
/* Define a custom attribute to `lv_task_handler` function */
#define LV_ATTRIBUTE_TASK_HANDLER
/* Define a custom attribute to `lv_disp_flush_ready` function */
#define LV_ATTRIBUTE_FLUSH_READY
/* Required alignment size for buffers */
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE
/* With size optimization (-Os) the compiler might not align data to
* 4 or 8 byte boundary. Some HW may need even 32 or 64 bytes.
* This alignment will be explicitly applied where needed.
* LV_ATTRIBUTE_MEM_ALIGN_SIZE should be used to specify required align size.
* E.g. __attribute__((aligned(LV_ATTRIBUTE_MEM_ALIGN_SIZE))) */
#define LV_ATTRIBUTE_MEM_ALIGN
/* Attribute to mark large constant arrays for example
* font's bitmaps */
#define LV_ATTRIBUTE_LARGE_CONST
/* Prefix performance critical functions to place them into a faster memory (e.g RAM)
* Uses 15-20 kB extra memory */
//#define LV_ATTRIBUTE_FAST_MEM
/* Export integer constant to binding.
* This macro is used with constants in the form of LV_<CONST> that
* should also appear on lvgl binding API such as Micropython
*
* The default value just prevents a GCC warning.
*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning
/* Prefix variables that are used in GPU accelerated operations, often these need to be
* placed in RAM sections that are DMA accessible */
// #define LV_ATTRIBUTE_DMA
/*===================
* HAL settings
*==================*/
/* 1: use a custom tick source.
* It removes the need to manually update the tick with `lv_tick_inc`) */
#define LV_TICK_CUSTOM 0
#if LV_TICK_CUSTOM == 1
#define LV_TICK_CUSTOM_INCLUDE "FreeRTOS.h" /*Header for the system time function*/
uint32_t xTaskGetTickCount(); /*Forward declare to avoid compiler warning*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (xTaskGetTickCount()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/
typedef void* lv_disp_drv_user_data_t; /*Type of user data in the display driver*/
typedef void* lv_indev_drv_user_data_t; /*Type of user data in the input device driver*/
/*================
* Log settings
*===============*/
/*1: Enable the log module*/
#define LV_USE_LOG 0
#if LV_USE_LOG
/* How important log should be added:
* LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
* LV_LOG_LEVEL_INFO Log important events
* LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
* LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
* LV_LOG_LEVEL_NONE Do not log anything
*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
/* 1: Print the log with 'printf';
* 0: user need to register a callback with `lv_log_register_print_cb`*/
#define LV_LOG_PRINTF 0
#endif /*LV_USE_LOG*/
/*=================
* Debug settings
*================*/
/* If Debug is enabled LittelvGL validates the parameters of the functions.
* If an invalid parameter is found an error log message is printed and
* the MCU halts at the error. (`LV_USE_LOG` should be enabled)
* If you are debugging the MCU you can pause
* the debugger to see exactly where the issue is.
*
* The behavior of asserts can be overwritten by redefining them here.
* E.g. #define LV_ASSERT_MEM(p) <my_assert_code>
*/
#define LV_USE_DEBUG 0
#if LV_USE_DEBUG
/*Check if the parameter is NULL. (Quite fast) */
#define LV_USE_ASSERT_NULL 1
/*Checks is the memory is successfully allocated or no. (Quite fast)*/
#define LV_USE_ASSERT_MEM 1
/*Check the integrity of `lv_mem` after critical operations. (Slow)*/
#define LV_USE_ASSERT_MEM_INTEGRITY 0
/* Check the strings.
* Search for NULL, very long strings, invalid characters, and unnatural repetitions. (Slow)
* If disabled `LV_USE_ASSERT_NULL` will be performed instead (if it's enabled) */
#define LV_USE_ASSERT_STR 0
/* Check NULL, the object's type and existence (e.g. not deleted). (Quite slow)
* If disabled `LV_USE_ASSERT_NULL` will be performed instead (if it's enabled) */
#define LV_USE_ASSERT_OBJ 0
/*Check if the styles are properly initialized. (Fast)*/
#define LV_USE_ASSERT_STYLE 0
#endif /*LV_USE_DEBUG*/
/*==================
* FONT USAGE
*===================*/
/* The built-in fonts contains the ASCII range and some Symbols with 4 bit-per-pixel.
* The symbols are available via `LV_SYMBOL_...` defines
* More info about fonts: https://docs.lvgl.io/v7/en/html/overview/font.html
* To create a new font go to: https://lvgl.com/ttf-font-to-c-array
*/
/* Montserrat fonts with bpp = 4
* https://fonts.google.com/specimen/Montserrat */
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_12 0
#define LV_FONT_MONTSERRAT_14 0
#define LV_FONT_MONTSERRAT_16 0
#define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 0
#define LV_FONT_MONTSERRAT_22 0
#define LV_FONT_MONTSERRAT_24 0
#define LV_FONT_MONTSERRAT_26 0
#define LV_FONT_MONTSERRAT_28 0
#define LV_FONT_MONTSERRAT_30 0
#define LV_FONT_MONTSERRAT_32 0
#define LV_FONT_MONTSERRAT_34 0
#define LV_FONT_MONTSERRAT_36 0
#define LV_FONT_MONTSERRAT_38 0
#define LV_FONT_MONTSERRAT_40 0
#define LV_FONT_MONTSERRAT_42 0
#define LV_FONT_MONTSERRAT_44 0
#define LV_FONT_MONTSERRAT_46 0
#define LV_FONT_MONTSERRAT_48 0
/* Demonstrate special features */
#define LV_FONT_MONTSERRAT_12_SUBPX 0
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, PErisan letters and all their forms*/
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
/*Pixel perfect monospace font
* http://pelulamu.net/unscii/ */
#define LV_FONT_UNSCII_8 0
/* Optionally declare your custom fonts here.
* You can use these fonts as default font too
* and they will be available globally. E.g.
* #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \
* LV_FONT_DECLARE(my_font_2)
*/
#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(jetbrains_mono_bold_20) \
LV_FONT_DECLARE(jetbrains_mono_extrabold_compressed) \
LV_FONT_DECLARE(jetbrains_mono_42) \
LV_FONT_DECLARE(jetbrains_mono_76) \
LV_FONT_DECLARE(open_sans_light) \
LV_FONT_DECLARE(lv_font_sys_48)
/* Enable it if you have fonts with a lot of characters.
* The limit depends on the font size, font face and bpp
* but with > 10,000 characters if you see issues probably you need to enable it.*/
#define LV_FONT_FMT_TXT_LARGE 0
/* Enables/disables support for compressed fonts. If it's disabled, compressed
* glyphs cannot be processed by the library and won't be rendered.
*/
#define LV_USE_FONT_COMPRESSED 1
/* Enable subpixel rendering */
#define LV_USE_FONT_SUBPX 0
#if LV_USE_FONT_SUBPX
/* Set the pixel order of the display.
* Important only if "subpx fonts" are used.
* With "normal" font it doesn't matter.
*/
#define LV_FONT_SUBPX_BGR 0
#endif
/*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/
typedef void* lv_font_user_data_t;
/*================
* THEME USAGE
*================*/
/*Always enable at least on theme*/
/* No theme, you can apply your styles as you need
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
#define LV_USE_THEME_EMPTY 1
/*Simple to the create your theme based on it
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
#define LV_USE_THEME_TEMPLATE 0
/* A fast and impressive theme.
* Flags:
* LV_THEME_MATERIAL_FLAG_LIGHT: light theme
* LV_THEME_MATERIAL_FLAG_DARK: dark theme
* LV_THEME_MATERIAL_FLAG_NO_TRANSITION: disable transitions (state change animations)
* LV_THEME_MATERIAL_FLAG_NO_FOCUS: disable indication of focused state)
* */
#define LV_USE_THEME_MATERIAL 0
/* Mono-color theme for monochrome displays.
* If LV_THEME_DEFAULT_COLOR_PRIMARY is LV_COLOR_BLACK the
* texts and borders will be black and the background will be
* white. Else the colors are inverted.
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
#define LV_USE_THEME_MONO 0
#define LV_THEME_DEFAULT_INCLUDE <stdint.h> /*Include a header for the init. function*/
#define LV_THEME_DEFAULT_INIT lv_theme_empty_init//lv_theme_material_init
#define LV_THEME_DEFAULT_COLOR_PRIMARY lv_color_hex(0xffffff)
#define LV_THEME_DEFAULT_COLOR_SECONDARY lv_color_hex(0xaaaaaa)
#define LV_THEME_DEFAULT_FLAG 0//LV_THEME_MATERIAL_FLAG_DARK
#define LV_THEME_DEFAULT_FONT_SMALL &jetbrains_mono_bold_20
#define LV_THEME_DEFAULT_FONT_NORMAL &jetbrains_mono_bold_20
#define LV_THEME_DEFAULT_FONT_SUBTITLE &jetbrains_mono_bold_20
#define LV_THEME_DEFAULT_FONT_TITLE &jetbrains_mono_bold_20
/*=================
* Text settings
*=================*/
/* Select a character encoding for strings.
* Your IDE or editor should have the same character encoding
* - LV_TXT_ENC_UTF8
* - LV_TXT_ENC_ASCII
* */
#define LV_TXT_ENC LV_TXT_ENC_UTF8
/*Can break (wrap) texts on these chars*/
#define LV_TXT_BREAK_CHARS " ,.;:-_"
/* If a word is at least this long, will break wherever "prettiest"
* To disable, set to a value <= 0 */
#define LV_TXT_LINE_BREAK_LONG_LEN 12
/* Minimum number of characters in a long word to put on a line before a break.
* Depends on LV_TXT_LINE_BREAK_LONG_LEN. */
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
/* Minimum number of characters in a long word to put on a line after a break.
* Depends on LV_TXT_LINE_BREAK_LONG_LEN. */
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
/* The control character to use for signalling text recoloring. */
#define LV_TXT_COLOR_CMD "#"
/* Support bidirectional texts.
* Allows mixing Left-to-Right and Right-to-Left texts.
* The direction will be processed according to the Unicode Bidirectioanl Algorithm:
* https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
#define LV_USE_BIDI 0
#if LV_USE_BIDI
/* Set the default direction. Supported values:
* `LV_BIDI_DIR_LTR` Left-to-Right
* `LV_BIDI_DIR_RTL` Right-to-Left
* `LV_BIDI_DIR_AUTO` detect texts base direction */
#define LV_BIDI_BASE_DIR_DEF LV_BIDI_DIR_AUTO
#endif
/* Enable Arabic/Persian processing
* In these languages characters should be replaced with
* an other form based on their position in the text */
#define LV_USE_ARABIC_PERSIAN_CHARS 0
/*Change the built in (v)snprintf functions*/
#define LV_SPRINTF_CUSTOM 0
#if LV_SPRINTF_CUSTOM
#define LV_SPRINTF_INCLUDE <stdio.h>
#define lv_snprintf snprintf
#define lv_vsnprintf vsnprintf
#else /*!LV_SPRINTF_CUSTOM*/
#define LV_SPRINTF_DISABLE_FLOAT 1
#endif /*LV_SPRINTF_CUSTOM*/
/*===================
* LV_OBJ SETTINGS
*==================*/
#if LV_USE_USER_DATA
/*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/
typedef void* lv_obj_user_data_t;
/*Provide a function to free user data*/
#define LV_USE_USER_DATA_FREE 0
#if LV_USE_USER_DATA_FREE
#define LV_USER_DATA_FREE_INCLUDE "something.h" /*Header for user data free function*/
/* Function prototype : void user_data_free(lv_obj_t * obj); */
#define LV_USER_DATA_FREE (user_data_free) /*Invoking for user data free function*/
#endif
#endif
/*1: enable `lv_obj_realign()` based on `lv_obj_align()` parameters*/
#define LV_USE_OBJ_REALIGN 1
/* Enable to make the object clickable on a larger area.
* LV_EXT_CLICK_AREA_OFF or 0: Disable this feature
* LV_EXT_CLICK_AREA_TINY: The extra area can be adjusted horizontally and vertically (0..255 px)
* LV_EXT_CLICK_AREA_FULL: The extra area can be adjusted in all 4 directions (-32k..+32k px)
*/
#define LV_USE_EXT_CLICK_AREA LV_EXT_CLICK_AREA_TINY
/*==================
* LV OBJ X USAGE
*================*/
/*
* Documentation of the object types: https://docs.lvgl.com/#Object-types
*/
/*Arc (dependencies: -)*/
#define LV_USE_ARC 1
/*Bar (dependencies: -)*/
#define LV_USE_BAR 1
/*Button (dependencies: lv_cont*/
#define LV_USE_BTN 1
/*Button matrix (dependencies: -)*/
#define LV_USE_BTNMATRIX 1
/*Calendar (dependencies: -)*/
#define LV_USE_CALENDAR 1
#if LV_USE_CALENDAR
#define LV_CALENDAR_WEEK_STARTS_MONDAY 0
#endif
/*Canvas (dependencies: lv_img)*/
#define LV_USE_CANVAS 0
/*Check box (dependencies: lv_btn, lv_label)*/
#define LV_USE_CHECKBOX 1
/*Chart (dependencies: -)*/
#define LV_USE_CHART 1
#if LV_USE_CHART
#define LV_CHART_AXIS_TICK_LABEL_MAX_LEN 256
#endif
/*Container (dependencies: -*/
#define LV_USE_CONT 1
/*Color picker (dependencies: -*/
#define LV_USE_CPICKER 0
/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
#define LV_USE_DROPDOWN 1
#if LV_USE_DROPDOWN != 0
/*Open and close default animation time [ms] (0: no animation)*/
#define LV_DROPDOWN_DEF_ANIM_TIME 200
#endif
/*Gauge (dependencies:lv_bar, lv_linemeter)*/
#define LV_USE_GAUGE 1
/*Image (dependencies: lv_label*/
#define LV_USE_IMG 1
/*Image Button (dependencies: lv_btn*/
#define LV_USE_IMGBTN 1
#if LV_USE_IMGBTN
/*1: The imgbtn requires left, mid and right parts and the width can be set freely*/
#define LV_IMGBTN_TILED 0
#endif
/*Keyboard (dependencies: lv_btnm)*/
#define LV_USE_KEYBOARD 0
/*Label (dependencies: -*/
#define LV_USE_LABEL 1
#if LV_USE_LABEL != 0
/*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_ROLL/ROLL_CIRC' mode*/
#define LV_LABEL_DEF_SCROLL_SPEED 25
/* Waiting period at beginning/end of animation cycle */
#define LV_LABEL_WAIT_CHAR_COUNT 3
/*Enable selecting text of the label */
#define LV_LABEL_TEXT_SEL 0
/*Store extra some info in labels (12 bytes) to speed up drawing of very long texts*/
#define LV_LABEL_LONG_TXT_HINT 0
#endif
/*LED (dependencies: -)*/
#define LV_USE_LED 0
#if LV_USE_LED
#define LV_LED_BRIGHT_MIN 120 /*Minimal brightness*/
#define LV_LED_BRIGHT_MAX 255 /*Maximal brightness*/
#endif
/*Line (dependencies: -*/
#define LV_USE_LINE 1
/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/
#define LV_USE_LIST 1
#if LV_USE_LIST != 0
/*Default animation time of focusing to a list element [ms] (0: no animation) */
#define LV_LIST_DEF_ANIM_TIME 100
#endif
/*Line meter (dependencies: *;)*/
#define LV_USE_LINEMETER 1
#if LV_USE_LINEMETER
/* Draw line more precisely at cost of performance.
* Useful if there are lot of lines any minor are visible
* 0: No extra precision
* 1: Some extra precision
* 2: Best precision
*/
#define LV_LINEMETER_PRECISE 0
#endif
/*Mask (dependencies: -)*/
#define LV_USE_OBJMASK 0
/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
#define LV_USE_MSGBOX 0
/*Page (dependencies: lv_cont)*/
#define LV_USE_PAGE 1
#if LV_USE_PAGE != 0
/*Focus default animation time [ms] (0: no animation)*/
#define LV_PAGE_DEF_ANIM_TIME 400
#endif
/*Preload (dependencies: lv_arc, lv_anim)*/
#define LV_USE_SPINNER 0
#if LV_USE_SPINNER != 0
#define LV_SPINNER_DEF_ARC_LENGTH 60 /*[deg]*/
#define LV_SPINNER_DEF_SPIN_TIME 1000 /*[ms]*/
#define LV_SPINNER_DEF_ANIM LV_SPINNER_TYPE_SPINNING_ARC
#endif
/*Roller (dependencies: lv_ddlist)*/
#define LV_USE_ROLLER 1
#if LV_USE_ROLLER != 0
/*Focus animation time [ms] (0: no animation)*/
#define LV_ROLLER_DEF_ANIM_TIME 200
/*Number of extra "pages" when the roller is infinite*/
#define LV_ROLLER_INF_PAGES 7
#endif
/*Slider (dependencies: lv_bar)*/
#define LV_USE_SLIDER 1
/*Spinbox (dependencies: lv_ta)*/
#define LV_USE_SPINBOX 0
/*Switch (dependencies: lv_slider)*/
#define LV_USE_SWITCH 1
/*Text area (dependencies: lv_label, lv_page)*/
#define LV_USE_TEXTAREA 1
#if LV_USE_TEXTAREA != 0
#define LV_TEXTAREA_DEF_CURSOR_BLINK_TIME 400 /*ms*/
#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
#endif
/*Table (dependencies: lv_label)*/
#define LV_USE_TABLE 1
#if LV_USE_TABLE
#define LV_TABLE_COL_MAX 12
#define LV_TABLE_CELL_STYLE_CNT 5
#endif
/*Tab (dependencies: lv_page, lv_btnm)*/
#define LV_USE_TABVIEW 0
# if LV_USE_TABVIEW != 0
/*Time of slide animation [ms] (0: no animation)*/
#define LV_TABVIEW_DEF_ANIM_TIME 300
#endif
/*Tileview (dependencies: lv_page) */
#define LV_USE_TILEVIEW 0
#if LV_USE_TILEVIEW
/*Time of slide animation [ms] (0: no animation)*/
#define LV_TILEVIEW_DEF_ANIM_TIME 300
#endif
/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/
#define LV_USE_WIN 0
/*==================
* Non-user section
*==================*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/
#define _CRT_SECURE_NO_WARNINGS
#endif
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/

@ -0,0 +1 @@
Subproject commit 8c96359f4199297809ab205870eb603b03d98b4e

@ -0,0 +1,410 @@
/**
* @file lv_drv_conf.h
*
*/
/*
* COPY THIS FILE AS lv_drv_conf.h
*/
#if 1 /*Set it to "1" to enable the content*/
#ifndef LV_DRV_CONF_H
#define LV_DRV_CONF_H
#include "lv_conf.h"
/*********************
* DELAY INTERFACE
*********************/
#define LV_DRV_DELAY_INCLUDE <stdint.h> /*Dummy include by default*/
#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/
#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/
/*********************
* DISPLAY INTERFACE
*********************/
/*------------
* Common
*------------*/
#define LV_DRV_DISP_INCLUDE <stdint.h> /*Dummy include by default*/
#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/
#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/
/*---------
* SPI
*---------*/
#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/
#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/
#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/
/*------------------
* Parallel port
*-----------------*/
#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/
#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/
#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/
#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/
#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/
/***************************
* INPUT DEVICE INTERFACE
***************************/
/*----------
* Common
*----------*/
#define LV_DRV_INDEV_INCLUDE <stdint.h> /*Dummy include by default*/
#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/
#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/
/*---------
* SPI
*---------*/
#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/
#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/
/*---------
* I2C
*---------*/
#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/
#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/
#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/
#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/
#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/
/*********************
* DISPLAY DRIVERS
*********************/
#ifndef USE_WAYLAND
# define USE_WAYLAND 0
#endif
#if USE_WAYLAND
# define WAYLAND_HOR_RES 480
# define WAYLAND_VER_RES 320
# define WAYLAND_SURF_TITLE "LVGL"
#endif
/*-------------------
* Monitor of PC
*-------------------*/
#ifndef USE_MONITOR
# define USE_MONITOR 1
#endif
#if USE_MONITOR
# define MONITOR_HOR_RES 240
# define MONITOR_VER_RES 240
/* Scale window by this factor (useful when simulating small screens) */
# define MONITOR_ZOOM 1
/* Used to test true double buffering with only address changing.
* Set LV_draw_buf_SIZE = (LV_HOR_RES * LV_VER_RES) and LV_draw_buf_DOUBLE = 1 and LV_COLOR_DEPTH = 32" */
# define MONITOR_DOUBLE_BUFFERED 0
/*Eclipse: <SDL2/SDL.h> Visual Studio: <SDL.h>*/
# define MONITOR_SDL_INCLUDE_PATH <SDL2/SDL.h>
/*Different rendering might be used if running in a Virtual machine*/
# define MONITOR_VIRTUAL_MACHINE 0
/*Open two windows to test multi display support*/
# define MONITOR_DUAL 0
#endif
/*-----------------------------------
* Native Windows (including mouse)
*----------------------------------*/
#ifndef USE_WINDOWS
# define USE_WINDOWS 0
#endif
#define USE_WINDOWS 0
#if USE_WINDOWS
# define WINDOW_HOR_RES 480
# define WINDOW_VER_RES 320
#endif
/*----------------------------------------
* GTK drivers (monitor, mouse, keyboard
*---------------------------------------*/
#ifndef USE_GTK
# define USE_GTK 0
#endif
/*----------------
* SSD1963
*--------------*/
#ifndef USE_SSD1963
# define USE_SSD1963 0
#endif
#if USE_SSD1963
# define SSD1963_HOR_RES LV_HOR_RES
# define SSD1963_VER_RES LV_VER_RES
# define SSD1963_HT 531
# define SSD1963_HPS 43
# define SSD1963_LPS 8
# define SSD1963_HPW 10
# define SSD1963_VT 288
# define SSD1963_VPS 12
# define SSD1963_FPS 4
# define SSD1963_VPW 10
# define SSD1963_HS_NEG 0 /*Negative hsync*/
# define SSD1963_VS_NEG 0 /*Negative vsync*/
# define SSD1963_ORI 0 /*0, 90, 180, 270*/
# define SSD1963_COLOR_DEPTH 16
#endif
/*----------------
* R61581
*--------------*/
#ifndef USE_R61581
# define USE_R61581 0
#endif
#if USE_R61581
# define R61581_HOR_RES LV_HOR_RES
# define R61581_VER_RES LV_VER_RES
# define R61581_HSPL 0 /*HSYNC signal polarity*/
# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/
# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/
# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */
# define R61581_VSPL 0 /*VSYNC signal polarity*/
# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/
# define R61581_VFP 8 /*Vertical Front poarch*/
# define R61581_VBP 8 /*Vertical Back poarch */
# define R61581_DPL 0 /*DCLK signal polarity*/
# define R61581_EPL 1 /*ENABLE signal polarity*/
# define R61581_ORI 0 /*0, 180*/
# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/
#endif
/*------------------------------
* ST7565 (Monochrome, low res.)
*-----------------------------*/
#ifndef USE_ST7565
# define USE_ST7565 0
#endif
#if USE_ST7565
/*No settings*/
#endif /*USE_ST7565*/
/*------------------------------
* GC9A01 (color, low res.)
*-----------------------------*/
#ifndef USE_GC9A01
# define USE_GC9A01 0
#endif
#if USE_GC9A01
/*No settings*/
#endif /*USE_GC9A01*/
/*------------------------------------------
* UC1610 (4 gray 160*[104|128])
* (EA DOGXL160 160x104 tested)
*-----------------------------------------*/
#ifndef USE_UC1610
# define USE_UC1610 0
#endif
#if USE_UC1610
# define UC1610_HOR_RES LV_HOR_RES
# define UC1610_VER_RES LV_VER_RES
# define UC1610_INIT_CONTRAST 33 /* init contrast, values in [%] */
# define UC1610_INIT_HARD_RST 0 /* 1 : hardware reset at init, 0 : software reset */
# define UC1610_TOP_VIEW 0 /* 0 : Bottom View, 1 : Top View */
#endif /*USE_UC1610*/
/*-------------------------------------------------
* SHARP memory in pixel monochrome display series
* LS012B7DD01 (184x38 pixels.)
* LS013B7DH03 (128x128 pixels.)
* LS013B7DH05 (144x168 pixels.)
* LS027B7DH01 (400x240 pixels.) (tested)
* LS032B7DD02 (336x536 pixels.)
* LS044Q7DH01 (320x240 pixels.)
*------------------------------------------------*/
#ifndef USE_SHARP_MIP
# define USE_SHARP_MIP 0
#endif
#if USE_SHARP_MIP
# define SHARP_MIP_HOR_RES LV_HOR_RES
# define SHARP_MIP_VER_RES LV_VER_RES
# define SHARP_MIP_SOFT_COM_INVERSION 0
# define SHARP_MIP_REV_BYTE(b) /*((uint8_t) __REV(__RBIT(b)))*/ /*Architecture / compiler dependent byte bits order reverse*/
#endif /*USE_SHARP_MIP*/
/*-------------------------------------------------
* ILI9341 240X320 TFT LCD
*------------------------------------------------*/
#ifndef USE_ILI9341
# define USE_ILI9341 0
#endif
#if USE_ILI9341
# define ILI9341_HOR_RES LV_HOR_RES
# define ILI9341_VER_RES LV_VER_RES
# define ILI9341_GAMMA 1
# define ILI9341_TEARING 0
#endif /*USE_ILI9341*/
/*-----------------------------------------
* Linux frame buffer device (/dev/fbx)
*-----------------------------------------*/
#ifndef USE_FBDEV
# define USE_FBDEV 1
#endif
#if USE_FBDEV
# define FBDEV_PATH "/dev/fb0"
#endif
/*-----------------------------------------
* FreeBSD frame buffer device (/dev/fbx)
*.........................................*/
#ifndef USE_BSD_FBDEV
# define USE_BSD_FBDEV 0
#endif
#if USE_BSD_FBDEV
# define FBDEV_PATH "/dev/fb0"
#endif
/*-----------------------------------------
* DRM/KMS device (/dev/dri/cardX)
*-----------------------------------------*/
#ifndef USE_DRM
# define USE_DRM 0
#endif
#if USE_DRM
# define DRM_CARD "/dev/dri/card0"
# define DRM_CONNECTOR_ID -1 /* -1 for the first connected one */
#endif
/*********************
* INPUT DEVICES
*********************/
/*--------------
* XPT2046
*--------------*/
#ifndef USE_XPT2046
# define USE_XPT2046 0
#endif
#if USE_XPT2046
# define XPT2046_HOR_RES 480
# define XPT2046_VER_RES 320
# define XPT2046_X_MIN 200
# define XPT2046_Y_MIN 200
# define XPT2046_X_MAX 3800
# define XPT2046_Y_MAX 3800
# define XPT2046_AVG 4
# define XPT2046_X_INV 0
# define XPT2046_Y_INV 0
# define XPT2046_XY_SWAP 0
#endif
/*-----------------
* FT5406EE8
*-----------------*/
#ifndef USE_FT5406EE8
# define USE_FT5406EE8 0
#endif
#if USE_FT5406EE8
# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/
#endif
/*---------------
* AD TOUCH
*--------------*/
#ifndef USE_AD_TOUCH
# define USE_AD_TOUCH 0
#endif
#if USE_AD_TOUCH
/*No settings*/
#endif
/*---------------------------------------
* Mouse or touchpad on PC (using SDL)
*-------------------------------------*/
#ifndef USE_MOUSE
# define USE_MOUSE 1
#endif
#if USE_MOUSE
/*No settings*/
#endif
/*-------------------------------------------
* Mousewheel as encoder on PC (using SDL)
*------------------------------------------*/
#ifndef USE_MOUSEWHEEL
# define USE_MOUSEWHEEL 1
#endif
#if USE_MOUSEWHEEL
/*No settings*/
#endif
/*-------------------------------------------------
* Touchscreen as libinput interface (for Linux based systems)
*------------------------------------------------*/
#ifndef USE_LIBINPUT
# define USE_LIBINPUT 0
#endif
#if USE_LIBINPUT
# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/
#endif /*USE_LIBINPUT*/
/*-------------------------------------------------
* Mouse or touchpad as evdev interface (for Linux based systems)
*------------------------------------------------*/
#ifndef USE_EVDEV
# define USE_EVDEV 0
#endif
#ifndef USE_BSD_EVDEV
# define USE_BSD_EVDEV 0
#endif
#if USE_EVDEV || USE_BSD_EVDEV
# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/
# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/
# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/
# if EVDEV_CALIBRATE
# define EVDEV_HOR_MIN 0 /*to invert axis swap EVDEV_XXX_MIN by EVDEV_XXX_MAX*/
# define EVDEV_HOR_MAX 4096 /*"evtest" Linux tool can help to get the correct calibraion values>*/
# define EVDEV_VER_MIN 0
# define EVDEV_VER_MAX 4096
# endif /*EVDEV_CALIBRATE*/
#endif /*USE_EVDEV*/
/*-------------------------------
* Keyboard of a PC (using SDL)
*------------------------------*/
#ifndef USE_KEYBOARD
# define USE_KEYBOARD 1
#endif
#if USE_KEYBOARD
/*No settings*/
#endif
#endif /*LV_DRV_CONF_H*/
#endif /*End of "Content enable"*/

@ -0,0 +1,783 @@
/**
* @file main
*
*/
/*********************
* INCLUDES
*********************/
#define _DEFAULT_SOURCE /* needed for usleep() */
#include <stdlib.h>
#include <unistd.h>
#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/
#include <SDL2/SDL.h>
#include "lvgl/lvgl.h"
//#include "lvgl/examples/lv_examples.h"
//#include "lv_demos/lv_demo.h"
#include "lv_drivers/display/monitor.h"
#include "lv_drivers/indev/mouse.h"
#include "lv_drivers/indev/keyboard.h"
#include "lv_drivers/indev/mousewheel.h"
// get PineTime header
#include "displayapp/lv_pinetime_theme.h"
#include <drivers/Hrs3300.h>
#include <drivers/Bma421.h>
#include "BootloaderVersion.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/brightness/BrightnessController.h"
#include "components/motor/MotorController.h"
#include "components/datetime/DateTimeController.h"
#include "components/heartrate/HeartRateController.h"
#include "components/fs/FS.h"
#include "drivers/Spi.h"
#include "drivers/SpiMaster.h"
#include "drivers/SpiNorFlash.h"
#include "drivers/St7789.h"
#include "drivers/TwiMaster.h"
#include "drivers/Cst816s.h"
#include "drivers/PinMap.h"
#include "systemtask/SystemTask.h"
#include "drivers/PinMap.h"
#include "touchhandler/TouchHandler.h"
#include "buttonhandler/ButtonHandler.h"
// get the simulator-headers
#include "displayapp/DisplayApp.h"
#include "displayapp/LittleVgl.h"
#include <nrfx_gpiote.h>
#include <iostream>
#include <typeinfo>
#include <algorithm>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void hal_init(void);
static int tick_thread(void *data);
/**********************
* STATIC VARIABLES
**********************/
lv_indev_t *kb_indev;
lv_indev_t *mouse_indev = nullptr;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {}
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* VARIABLES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
constexpr NRF_TWIM_Type *NRF_TWIM1 = nullptr;
static constexpr uint8_t touchPanelTwiAddress = 0x15;
static constexpr uint8_t motionSensorTwiAddress = 0x18;
static constexpr uint8_t heartRateSensorTwiAddress = 0x44;
Pinetime::Drivers::SpiMaster spi {Pinetime::Drivers::SpiMaster::SpiModule::SPI0,
{Pinetime::Drivers::SpiMaster::BitOrder::Msb_Lsb,
Pinetime::Drivers::SpiMaster::Modes::Mode3,
Pinetime::Drivers::SpiMaster::Frequencies::Freq8Mhz,
Pinetime::PinMap::SpiSck,
Pinetime::PinMap::SpiMosi,
Pinetime::PinMap::SpiMiso}};
Pinetime::Drivers::Spi lcdSpi {spi, Pinetime::PinMap::SpiLcdCsn};
Pinetime::Drivers::St7789 lcd {lcdSpi, Pinetime::PinMap::LcdDataCommand};
Pinetime::Drivers::Spi flashSpi {spi, Pinetime::PinMap::SpiFlashCsn};
Pinetime::Drivers::SpiNorFlash spiNorFlash {flashSpi};
// The TWI device should work @ up to 400Khz but there is a HW bug which prevent it from
// respecting correct timings. According to erratas heet, this magic value makes it run
// at ~390Khz with correct timings.
static constexpr uint32_t MaxTwiFrequencyWithoutHardwareBug {0x06200000};
Pinetime::Drivers::TwiMaster twiMaster {NRF_TWIM1, MaxTwiFrequencyWithoutHardwareBug, Pinetime::PinMap::TwiSda, Pinetime::PinMap::TwiScl};
Pinetime::Drivers::Cst816S touchPanel; // {twiMaster, touchPanelTwiAddress};
//#ifdef PINETIME_IS_RECOVERY
// #include "displayapp/DummyLittleVgl.h"
// #include "displayapp/DisplayAppRecovery.h"
//#else
// #include "displayapp/LittleVgl.h"
// #include "displayapp/DisplayApp.h"
//#endif
Pinetime::Components::LittleVgl lvgl {lcd, touchPanel};
Pinetime::Drivers::Bma421 motionSensor {twiMaster, motionSensorTwiAddress};
Pinetime::Drivers::Hrs3300 heartRateSensor {twiMaster, heartRateSensorTwiAddress};
TimerHandle_t debounceTimer;
TimerHandle_t debounceChargeTimer;
Pinetime::Controllers::Battery batteryController;
Pinetime::Controllers::Ble bleController;
Pinetime::Controllers::HeartRateController heartRateController;
Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController);
Pinetime::Controllers::FS fs; // {spiNorFlash};
Pinetime::Controllers::Settings settingsController {fs};
Pinetime::Controllers::MotorController motorController {};
Pinetime::Controllers::DateTime dateTimeController {settingsController};
Pinetime::Drivers::Watchdog watchdog;
Pinetime::Drivers::WatchdogView watchdogView(watchdog);
Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::TimerController timerController;
Pinetime::Controllers::AlarmController alarmController {dateTimeController};
Pinetime::Controllers::TouchHandler touchHandler(touchPanel, lvgl);
Pinetime::Controllers::ButtonHandler buttonHandler;
Pinetime::Controllers::BrightnessController brightnessController {};
Pinetime::Applications::DisplayApp displayApp(lcd,
lvgl,
touchPanel,
batteryController,
bleController,
dateTimeController,
watchdogView,
notificationManager,
heartRateController,
settingsController,
motorController,
motionController,
timerController,
alarmController,
brightnessController,
touchHandler);
Pinetime::System::SystemTask systemTask(spi,
lcd,
spiNorFlash,
twiMaster,
touchPanel,
lvgl,
batteryController,
bleController,
dateTimeController,
timerController,
alarmController,
watchdog,
notificationManager,
motorController,
heartRateSensor,
motionController,
motionSensor,
settingsController,
heartRateController,
displayApp,
heartRateApp,
fs,
touchHandler,
buttonHandler);
// variable used in SystemTask.cpp Work loop
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> NoInit_BackUpTime;
class Framework {
public:
// Contructor which initialize the parameters.
Framework(bool visible_, int height_, int width_) :
visible(visible_), height(height_), width(width_)
{
if (visible) {
//SDL_Init(SDL_INIT_VIDEO); // Initializing SDL as Video
SDL_CreateWindowAndRenderer(width, height, 0, &window, &renderer);
SDL_SetWindowTitle(window, "LV Simulator Status");
{ // move window a bit to the right, to not be over the PineTime Screen
int x,y;
SDL_GetWindowPosition(window, &x, &y);
SDL_SetWindowPosition(window, x+LV_HOR_RES_MAX, y);
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); // setting draw color
SDL_RenderClear(renderer); // Clear the newly created window
SDL_RenderPresent(renderer); // Reflects the changes done in the
// window.
}
motorController.Init();
settingsController.Init();
lvgl.Init();
lv_mem_monitor(&mem_mon);
printf("initial free_size = %u\n", mem_mon.free_size);
// update time to current system time once on startup
dateTimeController.SetCurrentTime(std::chrono::system_clock::now());
systemTask.Start();
// initialize the first LVGL screen
//const auto clockface = settingsController.GetClockFace();
//switch_to_screen(1+clockface);
}
// Destructor
~Framework(){
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
void draw_circle_red(int center_x, int center_y, int radius_){
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
draw_circle_(center_x, center_y, radius_);
}
void draw_circle_green(int center_x, int center_y, int radius_){
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
draw_circle_(center_x, center_y, radius_);
}
void draw_circle_blue(int center_x, int center_y, int radius_){
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
draw_circle_(center_x, center_y, radius_);
}
void draw_circle_yellow(int center_x, int center_y, int radius_){
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
draw_circle_(center_x, center_y, radius_);
}
void draw_circle_grey(int center_x, int center_y, int radius_){
SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
draw_circle_(center_x, center_y, radius_);
}
void draw_circle_white(int center_x, int center_y, int radius_){
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
draw_circle_(center_x, center_y, radius_);
}
void draw_circle_(int center_x, int center_y, int radius_){
// Drawing circle
for(int x=center_x-radius_; x<=center_x+radius_; x++){
for(int y=center_y-radius_; y<=center_y+radius_; y++){
if((std::pow(center_y-y,2)+std::pow(center_x-x,2)) <=
std::pow(radius_,2)){
SDL_RenderDrawPoint(renderer, x, y);
}
}
}
}
void refresh() {
// always refresh the LVGL screen
this->refresh_screen();
if (!visible) {
return;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
{ // motorController.is_ringing
constexpr const int center_x = 15;
constexpr const int center_y = 15;
if (motorController.is_ringing) {
draw_circle_red(center_x, center_y, 15);
} else {
draw_circle_grey(center_x, center_y, 15);
}
}
{ // motorController.motor_is_running
constexpr const int center_x = 45;
constexpr const int center_y = 15;
if (motorController.motor_is_running) {
draw_circle_red(center_x, center_y, 15);
} else {
draw_circle_grey(center_x, center_y, 15);
}
}
{ // ble.motor_is_running
constexpr const int center_x = 75;
constexpr const int center_y = 15;
if (bleController.IsConnected()) {
draw_circle_blue(center_x, center_y, 15);
} else {
draw_circle_grey(center_x, center_y, 15);
}
}
// batteryController.percentRemaining
for (uint8_t percent=0; percent<=10; percent++) {
const int center_x = 15+15*percent;
const int center_y = 60;
if (batteryController.percentRemaining < percent*10) {
draw_circle_grey(center_x, center_y, 15);
} else {
draw_circle_green(center_x, center_y, 15);
}
}
{ // batteryController.isCharging
constexpr const int center_x = 15;
constexpr const int center_y = 90;
if (batteryController.isCharging) {
draw_circle_yellow(center_x, center_y, 15);
} else
{
draw_circle_grey(center_x, center_y, 15);
}
}
{ // brightnessController.Level
constexpr const int center_y = 15;
const Pinetime::Controllers::BrightnessController::Levels level = brightnessController.Level();
uint8_t level_idx = 0;
if (level == Pinetime::Controllers::BrightnessController::Levels::Low)
{
level_idx = 1;
} else if (level == Pinetime::Controllers::BrightnessController::Levels::Medium)
{
level_idx = 2;
} else if (level == Pinetime::Controllers::BrightnessController::Levels::High)
{
level_idx = 3;
}
for (uint8_t i=0; i<4; i++) {
const int center_x = 115+15*i;
if (i <= level_idx) {
draw_circle_white(center_x, center_y, 15);
} else {
draw_circle_grey(center_x, center_y, 15);
}
}
}
// Show the change on the screen
SDL_RenderPresent(renderer);
}
void send_notification() {
Pinetime::Controllers::NotificationManager::Notification notif;
const std::string message("Lorem Ipsum");
std::copy(message.begin(), message.end(), notif.message.data());
notif.message[message.size() - 1] = '\0';
notif.size = message.size();
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif));
}
// can't use SDL_PollEvent, as those are fed to lvgl
// implement a non-descructive key-pressed handler (as not consuming SDL_Events)
void handle_keys() {
const Uint8 *state = SDL_GetKeyboardState(NULL);
const bool key_shift = state[SDL_SCANCODE_LSHIFT] || state[SDL_SCANCODE_RSHIFT];
auto debounce = [&] (const char key_low, const char key_capital, const bool scancode, bool &key_handled) {
if (scancode && !key_handled) {
if (key_shift) {
this->handle_key(key_capital);
} else {
this->handle_key(key_low);
}
key_handled = true;
} else if (scancode && key_handled) {
// ignore, already handled
} else {
key_handled = false;
}
};
debounce('r', 'R', state[SDL_SCANCODE_R], key_handled_r);
debounce('n', 'N', state[SDL_SCANCODE_N], key_handled_n);
debounce('m', 'M', state[SDL_SCANCODE_M], key_handled_m);
debounce('b', 'B', state[SDL_SCANCODE_B], key_handled_b);
debounce('v', 'V', state[SDL_SCANCODE_V], key_handled_v);
debounce('c', 'C', state[SDL_SCANCODE_C], key_handled_c);
debounce('l', 'L', state[SDL_SCANCODE_L], key_handled_l);
debounce('p', 'P', state[SDL_SCANCODE_P], key_handled_p);
debounce('s', 'S', state[SDL_SCANCODE_S], key_handled_s);
debounce('h', 'H', state[SDL_SCANCODE_H], key_handled_h);
// screen switcher buttons
debounce('1', '!'+1, state[SDL_SCANCODE_1], key_handled_1);
debounce('2', '!'+2, state[SDL_SCANCODE_2], key_handled_2);
debounce('3', '!'+3, state[SDL_SCANCODE_3], key_handled_3);
debounce('4', '!'+4, state[SDL_SCANCODE_4], key_handled_4);
debounce('5', '!'+5, state[SDL_SCANCODE_5], key_handled_5);
debounce('6', '!'+6, state[SDL_SCANCODE_6], key_handled_6);
debounce('7', '!'+7, state[SDL_SCANCODE_7], key_handled_7);
debounce('8', '!'+8, state[SDL_SCANCODE_8], key_handled_8);
debounce('9', '!'+9, state[SDL_SCANCODE_9], key_handled_9);
debounce('0', '!'+0, state[SDL_SCANCODE_0], key_handled_0);
}
// modify the simulated controller depending on the pressed key
void handle_key(SDL_Keycode key) {
if (key == 'r') {
motorController.StartRinging();
} else if (key == 'R') {
motorController.StopRinging();
} else if (key == 'm') {
motorController.RunForDuration(100);
} else if (key == 'M') {
motorController.RunForDuration(255);
} else if (key == 'n') {
send_notification();
} else if (key == 'N') {
notificationManager.ClearNewNotificationFlag();
} else if (key == 'b') {
bleController.Connect();
} else if (key == 'B') {
bleController.Disconnect();
} else if (key == 'v') {
if (batteryController.percentRemaining >= 90) {
batteryController.percentRemaining = 100;
} else {
batteryController.percentRemaining += 10;
}
} else if (key == 'V') {
if (batteryController.percentRemaining <= 10) {
batteryController.percentRemaining = 0;
} else {
batteryController.percentRemaining -= 10;
}
} else if (key == 'c') {
batteryController.isCharging = true;
batteryController.isPowerPresent = true;
} else if (key == 'C') {
batteryController.isCharging = false;
batteryController.isPowerPresent = false;
} else if (key == 'l') {
brightnessController.Higher();
} else if (key == 'L') {
brightnessController.Lower();
} else if (key == 'p') {
this->print_memory_usage = true;
} else if (key == 'P') {
this->print_memory_usage = false;
} else if (key == 's') {
motionSensor.steps += 500;
} else if (key == 'S') {
if (motionSensor.steps > 500) {
motionSensor.steps -= 500;
} else {
motionSensor.steps = 0;
}
} else if (key == 'h') {
if (heartRateController.State() == Pinetime::Controllers::HeartRateController::States::Stopped) {
heartRateController.Start();
} else if (heartRateController.State() == Pinetime::Controllers::HeartRateController::States::NotEnoughData) {
heartRateController.Update(Pinetime::Controllers::HeartRateController::States::Running, 10);
} else {
uint8_t heartrate = heartRateController.HeartRate();
heartRateController.Update(Pinetime::Controllers::HeartRateController::States::Running, heartrate + 10);
}
} else if (key == 'H') {
heartRateController.Stop();
} else if (key >= '0' && key <= '9') {
this->switch_to_screen(key-'0');
} else if (key >= '!'+0 && key <= '!'+9) {
this->switch_to_screen(key-'!'+10);
}
batteryController.voltage = batteryController.percentRemaining * 50;
}
void handle_touch_and_button() {
int x, y;
uint32_t buttons = SDL_GetMouseState(&x, &y);
const bool left_click = (buttons & SDL_BUTTON_LMASK) != 0;
const bool right_click = (buttons & SDL_BUTTON_RMASK) != 0;
if (left_click) {
left_release_sent = false;
systemTask.OnTouchEvent();
return;
} else {
if (!left_release_sent) {
left_release_sent = true;
systemTask.OnTouchEvent();
return;
}
}
if (right_click != right_last_state) {
right_last_state =right_click;
systemTask.PushMessage(Pinetime::System::Messages::HandleButtonEvent);
return;
}
}
// helper function to switch between screens
void switch_to_screen(uint8_t screen_idx)
{
if (screen_idx == 1) {
settingsController.SetClockFace(0);
displayApp.StartApp(Pinetime::Applications::Apps::Clock, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 2) {
settingsController.SetClockFace(1);
displayApp.StartApp(Pinetime::Applications::Apps::Clock, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 3) {
settingsController.SetClockFace(2);
displayApp.StartApp(Pinetime::Applications::Apps::Clock, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 4) {
displayApp.StartApp(Pinetime::Applications::Apps::Paddle, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 5) {
displayApp.StartApp(Pinetime::Applications::Apps::Twos, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 6) {
displayApp.StartApp(Pinetime::Applications::Apps::Metronome, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 7) {
displayApp.StartApp(Pinetime::Applications::Apps::FirmwareUpdate, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 8) {
displayApp.StartApp(Pinetime::Applications::Apps::BatteryInfo, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 9) {
displayApp.StartApp(Pinetime::Applications::Apps::FlashLight, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 0) {
displayApp.StartApp(Pinetime::Applications::Apps::QuickSettings, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 11) {
displayApp.StartApp(Pinetime::Applications::Apps::Music, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 12) {
displayApp.StartApp(Pinetime::Applications::Apps::Paint, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 13) {
displayApp.StartApp(Pinetime::Applications::Apps::SysInfo, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 14) {
displayApp.StartApp(Pinetime::Applications::Apps::Steps, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 15) {
displayApp.StartApp(Pinetime::Applications::Apps::Error, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 17) {
displayApp.StartApp(Pinetime::Applications::Apps::PassKey, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else if (screen_idx == 10) {
displayApp.StartApp(Pinetime::Applications::Apps::Missing, Pinetime::Applications::DisplayApp::FullRefreshDirections::None);
}
else {
std::cout << "unhandled screen_idx: " << int(screen_idx) << std::endl;
}
}
// render the current status of the simulated controller
void refresh_screen() {
const Pinetime::Controllers::BrightnessController::Levels level = brightnessController.Level();
if (level == Pinetime::Controllers::BrightnessController::Levels::Off) {
if (!screen_off_created) {
screen_off_created = true;
screen_off_bg = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_size(screen_off_bg, 240, 240);
lv_obj_set_pos(screen_off_bg, 0, 0);
lv_obj_set_style_local_bg_color(screen_off_bg, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
screen_off_label = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(screen_off_label, "Screen is OFF");
lv_obj_align(screen_off_label, nullptr, LV_ALIGN_CENTER, 0, -20);
}
} else {
if (screen_off_created) {
screen_off_created = false;
lv_obj_del(screen_off_bg);
lv_obj_del(screen_off_label);
}
}
if (print_memory_usage) {
// print free memory with the knowledge that 14KiB RAM is the actual PineTime-Memory
lv_mem_monitor(&mem_mon);
printf("actual free_size = %d\n", int64_t(mem_mon.free_size) - (LV_MEM_SIZE - 14U*1024U));
}
}
bool print_memory_usage = false;
lv_mem_monitor_t mem_mon;
// variables to create and destroy an lvgl overlay to indicate a turned off screen
bool screen_off_created = false;
lv_obj_t *screen_off_bg;
lv_obj_t *screen_off_label;
private:
bool key_handled_r = false; // r ... enable ringing, R ... disable ringing
bool key_handled_m = false; // m ... let motor run, M ... stop motor
bool key_handled_n = false; // n ... send notification, N ... clear all notifications
bool key_handled_b = false; // b ... connect Bluetooth, B ... disconnect Bluetooth
bool key_handled_v = false; // battery Voltage and percentage, v ... increase, V ... decrease
bool key_handled_c = false; // c ... charging, C ... not charging
bool key_handled_l = false; // l ... increase brightness level, L ... lower brightness level
bool key_handled_p = false; // p ... enable print memory usage, P ... disable print memory usage
bool key_handled_s = false; // s ... increase step count, S ... decrease step count
bool key_handled_h = false; // h ... set heartrate running, H ... stop heartrate
// numbers from 0 to 9 to switch between screens
bool key_handled_1 = false;
bool key_handled_2 = false;
bool key_handled_3 = false;
bool key_handled_4 = false;
bool key_handled_5 = false;
bool key_handled_6 = false;
bool key_handled_7 = false;
bool key_handled_8 = false;
bool key_handled_9 = false;
bool key_handled_0 = false;
bool visible; // show Simulator window
int height; // Height of the window
int width; // Width of the window
SDL_Renderer *renderer = NULL; // Pointer for the renderer
SDL_Window *window = NULL; // Pointer for the window
bool left_release_sent = true; // make sure to send one mouse button release event
bool right_last_state = false; // varable used to send message only on changing state
};
int main(int argc, char **argv)
{
// parse arguments
bool fw_status_window_visible = true;
bool arg_help = false;
for (int i=1; i<argc; i++)
{
const std::string arg(argv[i]);
if (arg == "--hide-status")
{
fw_status_window_visible = false;
} else if (arg == "-h" || arg == "--help")
{
arg_help = true;
} else
{
std::cout << "unknown argument '" << arg << "'" << std::endl;
return 1;
}
}
if (arg_help) {
std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h, --help show this help message and exit" << std::endl;
std::cout << " --hide-status don't show simulator status window, so only lvgl window is open" << std::endl;
return 0;
}
/*Initialize LVGL*/
lv_init();
/*Initialize the HAL (display, input devices, tick) for LVGL*/
hal_init();
// initialize the core of our Simulator
Framework fw(fw_status_window_visible, 240,240);
while(1) {
/* Periodically call the lv_task handler.
* It could be done in a timer interrupt or an OS task too.*/
lv_task_handler();
fw.handle_keys(); // key event polling
fw.handle_touch_and_button();
fw.refresh();
usleep(LV_DISP_DEF_REFR_PERIOD * 1000);
}
return 0;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Initialize the Hardware Abstraction Layer (HAL) for the LVGL graphics
* library
*/
static void hal_init(void)
{
/* Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/
monitor_init();
/* Tick init.
* You have to call 'lv_tick_inc()' in periodically to inform LittelvGL about
* how much time were elapsed Create an SDL thread to do this*/
SDL_CreateThread(tick_thread, "tick", NULL);
// use pinetime_theme
lv_theme_t* th = lv_pinetime_theme_init(
LV_COLOR_WHITE, LV_COLOR_SILVER, 0, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20);
lv_theme_set_act(th);
/*Create a display buffer*/
static lv_disp_buf_t disp_buf1;
static lv_color_t buf1_1[LV_HOR_RES_MAX * 120];
lv_disp_buf_init(&disp_buf1, buf1_1, NULL, LV_HOR_RES_MAX * 120);
/*Create a display*/
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.buffer = &disp_buf1;
disp_drv.flush_cb = monitor_flush;
lv_disp_drv_register(&disp_drv);
/* Add the mouse as input device
* Use the 'mouse' driver which reads the PC's mouse*/
//mouse_init();
//static lv_indev_drv_t indev_drv_1;
//lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
//indev_drv_1.type = LV_INDEV_TYPE_POINTER;
/*This function will be called periodically (by the library) to get the mouse position and state*/
//indev_drv_1.read_cb = mouse_read;
//mouse_indev = lv_indev_drv_register(&indev_drv_1);
/*Add the keyboard as input device.*/
//lv_indev_drv_t kb_drv;
//lv_indev_drv_init(&kb_drv);
//kb_drv.type = LV_INDEV_TYPE_KEYPAD;
//kb_drv.read_cb = keyboard_read;
//lv_indev_drv_register(&kb_drv);
/*Add the mousewheel as input device.*/
//lv_indev_drv_t enc_drv;
//lv_indev_drv_init(&enc_drv);
//enc_drv.type = LV_INDEV_TYPE_ENCODER;
//enc_drv.read_cb = mousewheel_read;
//lv_indev_drv_register(&enc_drv);
/*Set a cursor for the mouse*/
//LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/
//lv_obj_t * cursor_obj = lv_img_create(lv_scr_act(), NULL); /*Create an image object for the cursor */
//lv_img_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/
//lv_indev_set_cursor(mouse_indev, cursor_obj); /*Connect the image object to the driver*/
}
/**
* A task to measure the elapsed time for LVGL
* @param data unused
* @return never return
*/
static int tick_thread(void *data) {
(void)data;
while(1) {
SDL_Delay(LV_DISP_DEF_REFR_PERIOD);
lv_tick_inc(LV_DISP_DEF_REFR_PERIOD); /*Tell LittelvGL that 30 milliseconds were elapsed*/
}
return 0;
}

@ -0,0 +1,3 @@
#include "FreeRTOS.h"
void NVIC_SystemReset(void) {}

@ -0,0 +1,84 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef INC_FREERTOS_H
#define INC_FREERTOS_H
#include "portmacro_cmsis.h"
//#include "app_error.h"
// from nrf_error.h
/** @defgroup NRF_ERRORS_BASE Error Codes Base number definitions
* @{ */
#define NRF_ERROR_BASE_NUM (0x0) ///< Global error base
#define NRF_ERROR_SDM_BASE_NUM (0x1000) ///< SDM error base
#define NRF_ERROR_SOC_BASE_NUM (0x2000) ///< SoC error base
#define NRF_ERROR_STK_BASE_NUM (0x3000) ///< STK error base
/** @} */
#define NRF_SUCCESS (NRF_ERROR_BASE_NUM + 0) ///< Successful command
#define NRF_ERROR_SVC_HANDLER_MISSING (NRF_ERROR_BASE_NUM + 1) ///< SVC handler is missing
#define NRF_ERROR_SOFTDEVICE_NOT_ENABLED (NRF_ERROR_BASE_NUM + 2) ///< SoftDevice has not been enabled
#define NRF_ERROR_INTERNAL (NRF_ERROR_BASE_NUM + 3) ///< Internal Error
#define NRF_ERROR_NO_MEM (NRF_ERROR_BASE_NUM + 4) ///< No Memory for operation
#define NRF_ERROR_NOT_FOUND (NRF_ERROR_BASE_NUM + 5) ///< Not found
#define NRF_ERROR_NOT_SUPPORTED (NRF_ERROR_BASE_NUM + 6) ///< Not supported
#define NRF_ERROR_INVALID_PARAM (NRF_ERROR_BASE_NUM + 7) ///< Invalid Parameter
#define NRF_ERROR_INVALID_STATE (NRF_ERROR_BASE_NUM + 8) ///< Invalid state, operation disallowed in this state
#define NRF_ERROR_INVALID_LENGTH (NRF_ERROR_BASE_NUM + 9) ///< Invalid Length
#define NRF_ERROR_INVALID_FLAGS (NRF_ERROR_BASE_NUM + 10) ///< Invalid Flags
#define NRF_ERROR_INVALID_DATA (NRF_ERROR_BASE_NUM + 11) ///< Invalid Data
#define NRF_ERROR_DATA_SIZE (NRF_ERROR_BASE_NUM + 12) ///< Data size exceeds limit
#define NRF_ERROR_TIMEOUT (NRF_ERROR_BASE_NUM + 13) ///< Operation timed out
#define NRF_ERROR_NULL (NRF_ERROR_BASE_NUM + 14) ///< Null Pointer
#define NRF_ERROR_FORBIDDEN (NRF_ERROR_BASE_NUM + 15) ///< Forbidden Operation
#define NRF_ERROR_INVALID_ADDR (NRF_ERROR_BASE_NUM + 16) ///< Bad Memory Address
#define NRF_ERROR_BUSY (NRF_ERROR_BASE_NUM + 17) ///< Busy
#include <stdexcept>
template<typename T>
void APP_ERROR_HANDLER(T err) {
throw std::runtime_error("APP_ERROR_HANDLER: " + std::to_string(err));
}
struct SCB_t {
unsigned ICSR = 0;
};
static SCB_t SCB_member;
static SCB_t *SCB = &SCB_member;
//#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */
constexpr unsigned SCB_ICSR_VECTACTIVE_Msk = 0x01;
/**
\brief System Reset
\details Initiates a system reset request to reset the MCU.
*/
// copied from nRF5_SDK_15.3.0_59ac345/components/toolchain/cmsis/include/core_cm4.h
void NVIC_SystemReset(void);
#endif /* INC_FREERTOS_H */

@ -0,0 +1,95 @@
#include "components/battery/BatteryController.h"
//#include "drivers/PinMap.h"
//#include <hal/nrf_gpio.h>
//#include <nrfx_saadc.h>
#include <algorithm>
using namespace Pinetime::Controllers;
Battery* Battery::instance = nullptr;
Battery::Battery() {
instance = this;
//nrf_gpio_cfg_input(PinMap::Charging, static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Disabled);
}
void Battery::ReadPowerState() {
if (isPowerPresent && !isCharging) {
isFull = true;
} else if (!isPowerPresent) {
isFull = false;
}
}
void Battery::MeasureVoltage() {
ReadPowerState();
if (isReading) {
return;
}
// Non blocking read
isReading = true;
//SaadcInit();
//nrfx_saadc_sample();
}
//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;
//
// uint8_t newPercent;
// if (isFull) {
// newPercent = 100;
// } else if (voltage < battery_min) {
// newPercent = 0;
// } else {
// newPercent = std::min((voltage - battery_min) * 100 / (battery_max - battery_min), isCharging ? 99 : 100);
// }
//
// if ((isPowerPresent && newPercent > percentRemaining) || (!isPowerPresent && newPercent < percentRemaining) || firstMeasurement) {
// firstMeasurement = false;
// percentRemaining = newPercent;
// systemTask->PushMessage(System::Messages::BatteryPercentageUpdated);
// }
//
// nrfx_saadc_uninit();
// isReading = false;
// }
//}
void Battery::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}

@ -0,0 +1,57 @@
#pragma once
#include <cstdint>
#include "systemtask/SystemTask.h"
namespace Pinetime {
namespace Controllers {
class Battery {
public:
Battery();
void ReadPowerState();
void MeasureVoltage();
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;
//static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
public:
uint16_t voltage = 0;
uint8_t percentRemaining = 0;
bool isFull = false;
bool isCharging = false;
bool isPowerPresent = false;
bool firstMeasurement = true;
//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;
};
}
}

@ -0,0 +1,125 @@
#include "components/ble/AlertNotificationService.h"
#include <hal/nrf_rtc.h>
#include <cstring>
#include <algorithm>
#include "components/ble/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) {
auto anService = static_cast<AlertNotificationService*>(arg);
return anService->OnAlert(conn_handle, attr_handle, ctxt);
}
void AlertNotificationService::Init() {
// int res;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager)
:
// : characteristicDefinition {{.uuid = &ansCharUuid.u, .access_cb = AlertNotificationCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE},
// {.uuid = &notificationEventUuid.u,
// .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 = &ansUuid.u,
// .characteristics = characteristicDefinition},
// {0},
// },
systemTask {systemTask},
notificationManager {notificationManager} {
}
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};
//
// // 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());
// os_mbuf_copydata(ctxt->om, 0, 1, &category);
// notif.message[messageSize - 1] = '\0';
// notif.size = messageSize;
//
// // 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);
}

@ -0,0 +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& notificationManager);
void Init();
int OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
void AcceptIncomingCall();
void RejectIncomingCall();
void MuteIncomingCall();
enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 };
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 uint16_t ansId {0x1811};
static constexpr uint16_t ansCharId {0x2a46};
// static constexpr ble_uuid16_t ansUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansId};
// 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;
};
}
}

@ -0,0 +1,198 @@
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
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 "components/ble/MusicService.h"
#include "systemtask/SystemTask.h"
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
//constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t{
// .u = {.type = BLE_UUID_TYPE_128},
// .value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
// };
//}
// 00000000-78fc-48fe-8e23-433b3a1942d0
//constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
//}
//constexpr ble_uuid128_t msUuid {BaseUuid()};
//constexpr ble_uuid128_t msEventCharUuid {CharUuid(0x01, 0x00)};
//constexpr ble_uuid128_t msStatusCharUuid {CharUuid(0x02, 0x00)};
//constexpr ble_uuid128_t msArtistCharUuid {CharUuid(0x03, 0x00)};
//constexpr ble_uuid128_t msTrackCharUuid {CharUuid(0x04, 0x00)};
//constexpr ble_uuid128_t msAlbumCharUuid {CharUuid(0x05, 0x00)};
//constexpr ble_uuid128_t msPositionCharUuid {CharUuid(0x06, 0x00)};
//constexpr ble_uuid128_t msTotalLengthCharUuid {CharUuid(0x07, 0x00)};
//constexpr ble_uuid128_t msTrackNumberCharUuid {CharUuid(0x08, 0x00)};
//constexpr ble_uuid128_t msTrackTotalCharUuid {CharUuid(0x09, 0x00)};
//constexpr ble_uuid128_t msPlaybackSpeedCharUuid {CharUuid(0x0a, 0x00)};
//constexpr ble_uuid128_t msRepeatCharUuid {CharUuid(0x0b, 0x00)};
//constexpr ble_uuid128_t msShuffleCharUuid {CharUuid(0x0c, 0x00)};
//int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
// return static_cast<Pinetime::Controllers::MusicService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
//}
}
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask& system) : m_system(system) {
// characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_NOTIFY,
// .val_handle = &eventHandle};
// characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[12] = {.uuid = &msShuffleCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[13] = {0};
//
// serviceDefinition[0] = {
// .type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &msUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
}
void Pinetime::Controllers::MusicService::Init() {
//uint8_t res = 0;
//res = ble_gatts_count_cfg(serviceDefinition);
//ASSERT(res == 0);
//res = ble_gatts_add_svcs(serviceDefinition);
//ASSERT(res == 0);
}
//int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// char data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
// artistName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackCharUuid.u) == 0) {
// trackName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msAlbumCharUuid.u) == 0) {
// albumName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msStatusCharUuid.u) == 0) {
// playing = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msRepeatCharUuid.u) == 0) {
// repeat = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msShuffleCharUuid.u) == 0) {
// shuffle = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPositionCharUuid.u) == 0) {
// trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTotalLengthCharUuid.u) == 0) {
// trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackNumberCharUuid.u) == 0) {
// trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackTotalCharUuid.u) == 0) {
// tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPlaybackSpeedCharUuid.u) == 0) {
// playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f;
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::MusicService::getAlbum() const {
return albumName;
}
std::string Pinetime::Controllers::MusicService::getArtist() const {
return artistName;
}
std::string Pinetime::Controllers::MusicService::getTrack() const {
return trackName;
}
bool Pinetime::Controllers::MusicService::isPlaying() const {
return playing;
}
float Pinetime::Controllers::MusicService::getPlaybackSpeed() const {
return playbackSpeed;
}
int Pinetime::Controllers::MusicService::getProgress() const {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() const {
return trackLength;
}
void Pinetime::Controllers::MusicService::event(char event) {
std::ignore = event;
//auto* om = ble_hs_mbuf_from_flat(&event, 1);
//uint16_t connectionHandle = m_system.nimble().connHandle();
//if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
//}
//ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}

@ -0,0 +1,87 @@
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
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 <string>
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class MusicService {
public:
explicit MusicService(Pinetime::System::SystemTask& system);
void Init();
//int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
void event(char event);
std::string getArtist() const;
std::string getTrack() const;
std::string getAlbum() const;
int getProgress() const;
int getTrackLength() const;
float getPlaybackSpeed() const;
bool isPlaying() const;
static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00;
static const char EVENT_MUSIC_PAUSE = 0x01;
static const char EVENT_MUSIC_NEXT = 0x03;
static const char EVENT_MUSIC_PREV = 0x04;
static const char EVENT_MUSIC_VOLUP = 0x05;
static const char EVENT_MUSIC_VOLDOWN = 0x06;
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private:
//struct ble_gatt_chr_def characteristicDefinition[14];
//struct ble_gatt_svc_def serviceDefinition[2];
uint16_t eventHandle {};
std::string artistName {"Waiting for"};
std::string albumName {};
std::string trackName {"track information.."};
bool playing {false};
int trackProgress {0};
int trackLength {0};
int trackNumber {};
int tracksTotal {};
float playbackSpeed {1.0f};
bool repeat {false};
bool shuffle {false};
Pinetime::System::SystemTask& m_system;
};
}
}

@ -0,0 +1,111 @@
/* Copyright (C) 2021 Adam Pigg
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 "components/ble/NavigationService.h"
#include "systemtask/SystemTask.h"
namespace {
// // 0001yyxx-78fc-48fe-8e23-433b3a1942d0
// constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128},
// .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x01, 0x00}};
// }
//
// // 00010000-78fc-48fe-8e23-433b3a1942d0
// constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
// }
//
// constexpr ble_uuid128_t navUuid {BaseUuid()};
//
// constexpr ble_uuid128_t navFlagCharUuid {CharUuid(0x01, 0x00)};
// constexpr ble_uuid128_t navNarrativeCharUuid {CharUuid(0x02, 0x00)};
// constexpr ble_uuid128_t navManDistCharUuid {CharUuid(0x03, 0x00)};
// constexpr ble_uuid128_t navProgressCharUuid {CharUuid(0x04, 0x00)};
//
// int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
// auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg);
// return navService->OnCommand(conn_handle, attr_handle, ctxt);
// }
} // namespace
Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) {
// characteristicDefinition[0] = {
// .uuid = &navFlagCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[1] = {
// .uuid = &navNarrativeCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {
// .uuid = &navManDistCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {
// .uuid = &navProgressCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[4] = {0};
//
// serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &navUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
m_progress = 0;
}
void Pinetime::Controllers::NavigationService::Init() {
// int res = 0;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
//int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
//
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// uint8_t data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = (char*) &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &navFlagCharUuid.u) == 0) {
// m_flag = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navNarrativeCharUuid.u) == 0) {
// m_narrative = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navManDistCharUuid.u) == 0) {
// m_manDist = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navProgressCharUuid.u) == 0) {
// m_progress = data[0];
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::NavigationService::getFlag() {
return m_flag;
}
std::string Pinetime::Controllers::NavigationService::getNarrative() {
return m_narrative;
}
std::string Pinetime::Controllers::NavigationService::getManDist() {
return m_manDist;
}
int Pinetime::Controllers::NavigationService::getProgress() {
return m_progress;
}

@ -0,0 +1,63 @@
/* Copyright (C) 2021 Adam Pigg
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 <string>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_uuid.h>
//#undef max
//#undef min
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class NavigationService {
public:
explicit NavigationService(Pinetime::System::SystemTask& system);
void Init();
// int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
std::string getFlag();
std::string getNarrative();
std::string getManDist();
int getProgress();
private:
// struct ble_gatt_chr_def characteristicDefinition[5];
// struct ble_gatt_svc_def serviceDefinition[2];
std::string m_flag;
std::string m_narrative;
std::string m_manDist;
int m_progress = 0;
Pinetime::System::SystemTask& m_system;
};
}
}

@ -0,0 +1,456 @@
#include "components/ble/NimbleController.h"
#include <cstring>
//#include <hal/nrf_rtc.h>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_hs.h>
//#include <host/ble_hs_id.h>
//#include <host/util/util.h>
//#include <controller/ble_ll.h>
//#include <controller/ble_hw.h>
//#include <services/gap/ble_svc_gap.h>
//#include <services/gatt/ble_svc_gatt.h>
//#undef max
//#undef min
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/datetime/DateTimeController.h"
#include "components/fs/FS.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Controllers::FS& fs)
: systemTask {systemTask},
bleController {bleController},
dateTimeController {dateTimeController},
notificationManager {notificationManager},
spiNorFlash {spiNorFlash},
fs {fs},
// dfuService {systemTask, bleController, spiNorFlash},
// currentTimeClient {dateTimeController},
anService {systemTask, notificationManager},
// alertNotificationClient {systemTask, notificationManager},
// currentTimeService {dateTimeController},
musicService {systemTask},
weatherService {systemTask, dateTimeController},
navService {systemTask} {
// batteryInformationService {batteryController},
// immediateAlertService {systemTask, notificationManager},
// heartRateService {systemTask, heartRateController},
// motionService {systemTask, motionController},
// fsService {systemTask, fs},
// serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
}
//void nimble_on_reset(int reason) {
// NRF_LOG_INFO("Nimble lost sync, resetting state; reason=%d", reason);
//}
//
//void nimble_on_sync(void) {
// int rc;
//
// NRF_LOG_INFO("Nimble is synced");
//
// rc = ble_hs_util_ensure_addr(0);
// ASSERT(rc == 0);
//
// nptr->StartAdvertising();
//}
//
//int GAPEventCallback(struct ble_gap_event* event, void* arg) {
// auto nimbleController = static_cast<NimbleController*>(arg);
// return nimbleController->OnGAPEvent(event);
//}
void NimbleController::Init() {
// while (!ble_hs_synced()) {
// }
//
// nptr = this;
// ble_hs_cfg.reset_cb = nimble_on_reset;
// ble_hs_cfg.sync_cb = nimble_on_sync;
// ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
//
// ble_svc_gap_init();
// ble_svc_gatt_init();
//
// deviceInformationService.Init();
// currentTimeClient.Init();
// currentTimeService.Init();
musicService.Init();
weatherService.Init();
navService.Init();
// anService.Init();
// dfuService.Init();
// batteryInformationService.Init();
// immediateAlertService.Init();
// heartRateService.Init();
// motionService.Init();
// fsService.Init();
//
// int rc;
// rc = ble_hs_util_ensure_addr(0);
// ASSERT(rc == 0);
// rc = ble_hs_id_infer_auto(0, &addrType);
// ASSERT(rc == 0);
// rc = ble_svc_gap_device_name_set(deviceName);
// ASSERT(rc == 0);
// rc = ble_svc_gap_device_appearance_set(0xC2);
// ASSERT(rc == 0);
// Pinetime::Controllers::Ble::BleAddress address;
// rc = ble_hs_id_copy_addr(addrType, address.data(), nullptr);
// ASSERT(rc == 0);
//
// bleController.Address(std::move(address));
// switch (addrType) {
// case BLE_OWN_ADDR_PUBLIC:
// bleController.AddressType(Ble::AddressTypes::Public);
// break;
// case BLE_OWN_ADDR_RANDOM:
// bleController.AddressType(Ble::AddressTypes::Random);
// break;
// case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
// bleController.AddressType(Ble::AddressTypes::RPA_Public);
// break;
// case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
// bleController.AddressType(Ble::AddressTypes::RPA_Random);
// break;
// }
//
// rc = ble_gatts_start();
// ASSERT(rc == 0);
//
// RestoreBond();
//
// StartAdvertising();
}
//void NimbleController::StartAdvertising() {
// struct ble_gap_adv_params adv_params;
// struct ble_hs_adv_fields fields;
// struct ble_hs_adv_fields rsp_fields;
//
// memset(&adv_params, 0, sizeof(adv_params));
// memset(&fields, 0, sizeof(fields));
// memset(&rsp_fields, 0, sizeof(rsp_fields));
//
// adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
// adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
// /* fast advertise for 30 sec */
// if (fastAdvCount < 15) {
// adv_params.itvl_min = 32;
// adv_params.itvl_max = 47;
// fastAdvCount++;
// } else {
// adv_params.itvl_min = 1636;
// adv_params.itvl_max = 1651;
// }
//
// fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
// fields.uuids128 = &dfuServiceUuid;
// fields.num_uuids128 = 1;
// fields.uuids128_is_complete = 1;
// fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
//
// rsp_fields.name = reinterpret_cast<const uint8_t*>(deviceName);
// rsp_fields.name_len = strlen(deviceName);
// rsp_fields.name_is_complete = 1;
//
// int rc;
// rc = ble_gap_adv_set_fields(&fields);
// ASSERT(rc == 0);
//
// rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
// ASSERT(rc == 0);
//
// rc = ble_gap_adv_start(addrType, NULL, 2000, &adv_params, GAPEventCallback, this);
// ASSERT(rc == 0);
//}
//
//int NimbleController::OnGAPEvent(ble_gap_event* event) {
// switch (event->type) {
// case BLE_GAP_EVENT_ADV_COMPLETE:
// NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
// NRF_LOG_INFO("reason=%d; status=%0X", event->adv_complete.reason, event->connect.status);
// StartAdvertising();
// break;
//
// case BLE_GAP_EVENT_CONNECT:
// /* A new connection was established or a connection attempt failed. */
// NRF_LOG_INFO("Connect event : BLE_GAP_EVENT_CONNECT");
// NRF_LOG_INFO("connection %s; status=%0X ", event->connect.status == 0 ? "established" : "failed", event->connect.status);
//
// if (event->connect.status != 0) {
// /* Connection failed; resume advertising. */
// currentTimeClient.Reset();
// alertNotificationClient.Reset();
// connectionHandle = BLE_HS_CONN_HANDLE_NONE;
// bleController.Disconnect();
// fastAdvCount = 0;
// StartAdvertising();
// } else {
// connectionHandle = event->connect.conn_handle;
// bleController.Connect();
// systemTask.PushMessage(Pinetime::System::Messages::BleConnected);
// // Service discovery is deferred via systemtask
// }
// break;
//
// case BLE_GAP_EVENT_DISCONNECT:
// /* Connection terminated; resume advertising. */
// NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT");
// NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason);
//
// if (event->disconnect.conn.sec_state.bonded) {
// PersistBond(event->disconnect.conn);
// }
//
// currentTimeClient.Reset();
// alertNotificationClient.Reset();
// connectionHandle = BLE_HS_CONN_HANDLE_NONE;
// bleController.Disconnect();
// fastAdvCount = 0;
// StartAdvertising();
// break;
//
// case BLE_GAP_EVENT_CONN_UPDATE:
// /* The central has updated the connection parameters. */
// NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE");
// NRF_LOG_INFO("update status=%0X ", event->conn_update.status);
// break;
//
// case BLE_GAP_EVENT_CONN_UPDATE_REQ:
// /* The central has requested updated connection parameters */
// NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE_REQ");
// NRF_LOG_INFO("update request : itvl_min=%d itvl_max=%d latency=%d supervision=%d",
// event->conn_update_req.peer_params->itvl_min,
// event->conn_update_req.peer_params->itvl_max,
// event->conn_update_req.peer_params->latency,
// event->conn_update_req.peer_params->supervision_timeout);
// break;
//
// case BLE_GAP_EVENT_ENC_CHANGE:
// /* Encryption has been enabled or disabled for this connection. */
// NRF_LOG_INFO("Security event : BLE_GAP_EVENT_ENC_CHANGE");
// NRF_LOG_INFO("encryption change event; status=%0X ", event->enc_change.status);
//
// if (event->enc_change.status == 0) {
// struct ble_gap_conn_desc desc;
// ble_gap_conn_find(event->enc_change.conn_handle, &desc);
// if (desc.sec_state.bonded) {
// PersistBond(desc);
// }
//
// NRF_LOG_INFO("new state: encrypted=%d authenticated=%d bonded=%d key_size=%d",
// desc.sec_state.encrypted,
// desc.sec_state.authenticated,
// desc.sec_state.bonded,
// desc.sec_state.key_size);
// }
// break;
//
// case BLE_GAP_EVENT_PASSKEY_ACTION:
// /* Authentication has been requested for this connection.
// *
// * BLE authentication is determined by the combination of I/O capabilities
// * on the central and peripheral. When the peripheral is display only and
// * the central has a keyboard and display then passkey auth is selected.
// * When both the central and peripheral have displays and support yes/no
// * buttons then numeric comparison is selected. We currently advertise
// * display capability only so we only handle the "display" action here.
// *
// * Standards insist that the rand() PRNG be deterministic.
// * Use the tinycrypt prng here since rand() is predictable.
// */
// NRF_LOG_INFO("Security event : BLE_GAP_EVENT_PASSKEY_ACTION");
// if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
// struct ble_sm_io pkey = {0};
// pkey.action = event->passkey.params.action;
// pkey.passkey = ble_ll_rand() % 1000000;
// bleController.SetPairingKey(pkey.passkey);
// systemTask.PushMessage(Pinetime::System::Messages::OnPairing);
// ble_sm_inject_io(event->passkey.conn_handle, &pkey);
// }
// break;
//
// case BLE_GAP_EVENT_SUBSCRIBE:
// NRF_LOG_INFO("Subscribe event; conn_handle=%d attr_handle=%d "
// "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
// event->subscribe.conn_handle,
// event->subscribe.attr_handle,
// event->subscribe.reason,
// event->subscribe.prev_notify,
// event->subscribe.cur_notify,
// event->subscribe.prev_indicate);
//
// if (event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) {
// heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// } else if (event->subscribe.prev_notify == 0 && event->subscribe.cur_notify == 1) {
// heartRateService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// } else if (event->subscribe.prev_notify == 1 && event->subscribe.cur_notify == 0) {
// heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// }
// break;
//
// case BLE_GAP_EVENT_MTU:
// NRF_LOG_INFO("MTU Update event; conn_handle=%d cid=%d mtu=%d", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
// break;
//
// case BLE_GAP_EVENT_REPEAT_PAIRING: {
// NRF_LOG_INFO("Pairing event : BLE_GAP_EVENT_REPEAT_PAIRING");
// /* We already have a bond with the peer, but it is attempting to
// * establish a new secure link. This app sacrifices security for
// * convenience: just throw away the old bond and accept the new link.
// */
//
// /* Delete the old bond. */
// struct ble_gap_conn_desc desc;
// ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
// ble_store_util_delete_peer(&desc.peer_id_addr);
//
// /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
// * continue with the pairing operation.
// */
// }
// return BLE_GAP_REPEAT_PAIRING_RETRY;
//
// case BLE_GAP_EVENT_NOTIFY_RX: {
// /* Peer sent us a notification or indication. */
// /* Attribute data is contained in event->notify_rx.attr_data. */
// NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_RX");
// size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om);
//
// NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d "
// "attr_len=%d",
// event->notify_rx.indication ? "indication" : "notification",
// event->notify_rx.conn_handle,
// event->notify_rx.attr_handle,
// notifSize);
//
// alertNotificationClient.OnNotification(event);
// } break;
//
// case BLE_GAP_EVENT_NOTIFY_TX:
// NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_TX");
// break;
//
// case BLE_GAP_EVENT_IDENTITY_RESOLVED:
// NRF_LOG_INFO("Identity event : BLE_GAP_EVENT_IDENTITY_RESOLVED");
// break;
//
// default:
// NRF_LOG_INFO("UNHANDLED GAP event : %d", event->type);
// break;
// }
// return 0;
//}
void NimbleController::StartDiscovery() {
// if (connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
// serviceDiscovery.StartDiscovery(connectionHandle);
// }
}
//uint16_t NimbleController::connHandle() {
// return connectionHandle;
//}
void NimbleController::NotifyBatteryLevel(uint8_t level) {
// if (connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
// batteryInformationService.NotifyBatteryLevel(connectionHandle, level);
// }
}
//void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
// union ble_store_key key;
// union ble_store_value our_sec, peer_sec, peer_cccd_set[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)] = {0};
// int rc;
//
// memset(&key, 0, sizeof key);
// memset(&our_sec, 0, sizeof our_sec);
// key.sec.peer_addr = desc.peer_id_addr;
// rc = ble_store_read_our_sec(&key.sec, &our_sec.sec);
//
// if (memcmp(&our_sec.sec, &bondId, sizeof bondId) == 0) {
// return;
// }
//
// memcpy(&bondId, &our_sec.sec, sizeof bondId);
//
// memset(&key, 0, sizeof key);
// memset(&peer_sec, 0, sizeof peer_sec);
// key.sec.peer_addr = desc.peer_id_addr;
// rc += ble_store_read_peer_sec(&key.sec, &peer_sec.sec);
//
// if (rc == 0) {
// memset(&key, 0, sizeof key);
// key.cccd.peer_addr = desc.peer_id_addr;
// int peer_count = 0;
// ble_store_util_count(BLE_STORE_OBJ_TYPE_CCCD, &peer_count);
// for (int i = 0; i < peer_count; i++) {
// key.cccd.idx = peer_count;
// ble_store_read_cccd(&key.cccd, &peer_cccd_set[i].cccd);
// }
//
// /* Wakeup Spi and SpiNorFlash before accessing the file system
// * This should be fixed in the FS driver
// */
// systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
// systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
// vTaskDelay(10);
//
// lfs_file_t file_p;
//
// rc = fs.FileOpen(&file_p, "/bond.dat", LFS_O_WRONLY | LFS_O_CREAT);
// if (rc == 0) {
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&our_sec.sec), sizeof our_sec);
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_sec.sec), sizeof peer_sec);
// fs.FileWrite(&file_p, reinterpret_cast<const uint8_t*>(&peer_count), 1);
// for (int i = 0; i < peer_count; i++) {
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_cccd_set[i].cccd), sizeof(struct ble_store_value_cccd));
// }
// fs.FileClose(&file_p);
// }
// systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
// }
//}
//void NimbleController::RestoreBond() {
// lfs_file_t file_p;
// union ble_store_value sec, cccd;
// uint8_t peer_count = 0;
//
// if (fs.FileOpen(&file_p, "/bond.dat", LFS_O_RDONLY) == 0) {
// memset(&sec, 0, sizeof sec);
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec);
// ble_store_write_our_sec(&sec.sec);
//
// memset(&sec, 0, sizeof sec);
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec);
// ble_store_write_peer_sec(&sec.sec);
//
// fs.FileRead(&file_p, &peer_count, 1);
// for (int i = 0; i < peer_count; i++) {
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&cccd.cccd), sizeof(struct ble_store_value_cccd));
// ble_store_write_cccd(&cccd.cccd);
// }
//
// fs.FileClose(&file_p);
// fs.FileDelete("/bond.dat");
// }
//}

@ -0,0 +1,132 @@
#pragma once
#include <cstdint>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#undef max
//#undef min
//#include "components/ble/AlertNotificationClient.h"
#include "components/ble/AlertNotificationService.h"
//#include "components/ble/BatteryInformationService.h"
//#include "components/ble/CurrentTimeClient.h"
//#include "components/ble/CurrentTimeService.h"
//#include "components/ble/DeviceInformationService.h"
//#include "components/ble/DfuService.h"
//#include "components/ble/HeartRateService.h"
//#include "components/ble/ImmediateAlertService.h"
#include "components/ble/MusicService.h"
#include "components/ble/NavigationService.h"
//#include "components/ble/ServiceDiscovery.h"
//#include "components/ble/MotionService.h"
#include "components/ble/weather/WeatherService.h"
#include "components/fs/FS.h"
//#include "components/ble/FSService.h"
namespace Pinetime {
namespace Drivers {
class SpiNorFlash;
}
namespace System {
class SystemTask;
}
namespace Controllers {
class Battery;
class Ble;
class DateTime;
class FS;
class HeartRateController;
class MotionController;
class NotificationManager;
class NimbleController {
public:
NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Pinetime::Controllers::FS& fs);
void Init();
void StartAdvertising();
// int OnGAPEvent(ble_gap_event* event);
// int OnDiscoveryEvent(uint16_t i, const ble_gatt_error* pError, const ble_gatt_svc* pSvc);
// int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
// int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
// int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
// int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
// const ble_gatt_error* error,
// uint16_t characteristicValueHandle,
// const ble_gatt_dsc* descriptor);
void StartDiscovery();
Pinetime::Controllers::MusicService& music() {
return musicService;
};
Pinetime::Controllers::NavigationService& navigation() {
return navService;
};
Pinetime::Controllers::AlertNotificationService& alertService() {
return anService;
};
Pinetime::Controllers::WeatherService& weather() {
return weatherService;
};
uint16_t connHandle();
void NotifyBatteryLevel(uint8_t level);
void RestartFastAdv() {
fastAdvCount = 0;
}
private:
// void PersistBond(struct ble_gap_conn_desc& desc);
// void RestoreBond();
static constexpr const char* deviceName = "InfiniTime";
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::Ble& bleController;
DateTime& dateTimeController;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
Pinetime::Controllers::FS& fs;
// Pinetime::Controllers::DfuService dfuService;
// DeviceInformationService deviceInformationService;
// CurrentTimeClient currentTimeClient;
AlertNotificationService anService;
// AlertNotificationClient alertNotificationClient;
// CurrentTimeService currentTimeService;
MusicService musicService;
WeatherService weatherService;
NavigationService navService;
// BatteryInformationService batteryInformationService;
// ImmediateAlertService immediateAlertService;
// HeartRateService heartRateService;
// MotionService motionService;
// FSService fsService;
// ServiceDiscovery serviceDiscovery;
uint8_t addrType;
// uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
uint8_t fastAdvCount = 0;
uint8_t bondId[16] = {0};
// ble_uuid128_t dfuServiceUuid {
// .u {.type = BLE_UUID_TYPE_128},
// .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
};
// static NimbleController* nptr;
}
}

@ -0,0 +1,602 @@
/* Copyright (C) 2021 Avamander
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 <qcbor/qcbor_spiffy_decode.h>
#include "components/ble/weather/WeatherService.h"
//#include "libs/QCBOR/inc/qcbor/qcbor.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::WeatherService*>(arg)->OnCommand(connHandle, attrHandle, ctxt);
}
WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController)
: system(system), dateTimeController(dateTimeController) {
nullHeader = &nullTimelineheader;
nullTimelineheader->timestamp = 0;
}
void WeatherService::Init() {
// uint8_t res = 0;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
// if (packetLen <= 0) {
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// // Decode
// QCBORDecodeContext decodeContext;
// UsefulBufC encodedCbor = {ctxt->om->om_data, OS_MBUF_PKTLEN(ctxt->om)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
// QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL);
// // KINDLY provide us a fixed-length map
// QCBORDecode_EnterMap(&decodeContext, nullptr);
// // Always encodes to the smallest number of bytes based on the value
// int64_t tmpTimestamp = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// int64_t tmpExpires = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpExpires < 0 || tmpExpires > 4294967295) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// int64_t tmpEventType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpEventType < 0 ||
// tmpEventType >= static_cast<int64_t>(WeatherData::eventtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// switch (static_cast<WeatherData::eventtype>(tmpEventType)) {
// case WeatherData::eventtype::AirQuality: {
// std::unique_ptr<WeatherData::AirQuality> airquality = std::make_unique<WeatherData::AirQuality>();
// airquality->timestamp = tmpTimestamp;
// airquality->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// airquality->expires = tmpExpires;
// UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here?
// QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &stringBuf);
// if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// airquality->polluter = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 4294967295) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// airquality->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(airquality))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Obscuration: {
// std::unique_ptr<WeatherData::Obscuration> obscuration = std::make_unique<WeatherData::Obscuration>();
// obscuration->timestamp = tmpTimestamp;
// obscuration->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// obscuration->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::obscurationtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// obscuration->type = static_cast<WeatherData::obscurationtype>(tmpType);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 65535) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// obscuration->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(obscuration))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Precipitation: {
// std::unique_ptr<WeatherData::Precipitation> precipitation = std::make_unique<WeatherData::Precipitation>();
// precipitation->timestamp = tmpTimestamp;
// precipitation->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// precipitation->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::precipitationtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// precipitation->type = static_cast<WeatherData::precipitationtype>(tmpType);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// precipitation->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(precipitation))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Wind: {
// std::unique_ptr<WeatherData::Wind> wind = std::make_unique<WeatherData::Wind>();
// wind->timestamp = tmpTimestamp;
// wind->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// wind->expires = tmpExpires;
// int64_t tmpMin = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMin);
// if (tmpMin < 0 || tmpMin > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->speedMin = tmpMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpMax = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMax);
// if (tmpMax < 0 || tmpMax > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->speedMax = tmpMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDMin = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMin", &tmpDMin);
// if (tmpDMin < 0 || tmpDMin > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->directionMin = tmpDMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDMax = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMax", &tmpDMax);
// if (tmpDMax < 0 || tmpDMax > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->directionMax = tmpDMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(wind))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Temperature: {
// std::unique_ptr<WeatherData::Temperature> temperature = std::make_unique<WeatherData::Temperature>();
// temperature->timestamp = tmpTimestamp;
// temperature->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// temperature->expires = tmpExpires;
// int64_t tmpTemperature = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature);
// if (tmpTemperature < -32768 || tmpTemperature > 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// temperature->temperature =
// static_cast<int16_t>(tmpTemperature); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDewPoint = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint);
// if (tmpDewPoint < -32768 || tmpDewPoint > 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// temperature->dewPoint =
// static_cast<int16_t>(tmpDewPoint); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(temperature))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Special: {
// std::unique_ptr<WeatherData::Special> special = std::make_unique<WeatherData::Special>();
// special->timestamp = tmpTimestamp;
// special->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// special->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::specialtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// special->type = static_cast<WeatherData::specialtype>(tmpType);
// if (!AddEventToTimeline(std::move(special))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Pressure: {
// std::unique_ptr<WeatherData::Pressure> pressure = std::make_unique<WeatherData::Pressure>();
// pressure->timestamp = tmpTimestamp;
// pressure->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// pressure->expires = tmpExpires;
// int64_t tmpPressure = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Pressure", &tmpPressure);
// if (tmpPressure < 0 || tmpPressure >= 65535) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// pressure->pressure = tmpPressure; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(pressure))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Location: {
// std::unique_ptr<WeatherData::Location> location = std::make_unique<WeatherData::Location>();
// location->timestamp = tmpTimestamp;
// location->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// location->expires = tmpExpires;
// UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here?
// QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Location", &stringBuf);
// if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->location = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len);
// int64_t tmpAltitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Altitude", &tmpAltitude);
// if (tmpAltitude < -32768 || tmpAltitude >= 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->altitude = static_cast<int16_t>(tmpAltitude);
// int64_t tmpLatitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Latitude", &tmpLatitude);
// if (tmpLatitude < -2147483648 || tmpLatitude >= 2147483647) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->latitude = static_cast<int32_t>(tmpLatitude);
// int64_t tmpLongitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Longitude", &tmpLongitude);
// if (tmpLongitude < -2147483648 || tmpLongitude >= 2147483647) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->latitude = static_cast<int32_t>(tmpLongitude);
// if (!AddEventToTimeline(std::move(location))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Clouds: {
// std::unique_ptr<WeatherData::Clouds> clouds = std::make_unique<WeatherData::Clouds>();
// clouds->timestamp = tmpTimestamp;
// clouds->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// clouds->expires = tmpExpires;
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// clouds->amount = static_cast<uint8_t>(tmpAmount);
// if (!AddEventToTimeline(std::move(clouds))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Humidity: {
// std::unique_ptr<WeatherData::Humidity> humidity = std::make_unique<WeatherData::Humidity>();
// humidity->timestamp = tmpTimestamp;
// humidity->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// humidity->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Humidity", &tmpType);
// if (tmpType < 0 || tmpType >= 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// humidity->humidity = static_cast<uint8_t>(tmpType);
// if (!AddEventToTimeline(std::move(humidity))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// default: {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// }
// QCBORDecode_ExitMap(&decodeContext);
// GetTimelineLength();
// TidyTimeline();
// if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
// // Encode
// uint8_t buffer[64];
// QCBOREncodeContext encodeContext;
// /* TODO: This is very much still a test endpoint
// * it needs a characteristic UUID check
// * and actual implementations that show
// * what actually has to be read.
// * WARN: Consider commands not part of the API for now!
// */
// QCBOREncode_Init(&encodeContext, UsefulBuf_FROM_BYTE_ARRAY(buffer));
// QCBOREncode_OpenMap(&encodeContext);
// QCBOREncode_AddTextToMap(&encodeContext, "test", UsefulBuf_FROM_SZ_LITERAL("test"));
// QCBOREncode_AddInt64ToMap(&encodeContext, "test", 1ul);
// QCBOREncode_CloseMap(&encodeContext);
// UsefulBufC encodedEvent;
// auto uErr = QCBOREncode_Finish(&encodeContext, &encodedEvent);
// if (uErr != 0) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// auto res = os_mbuf_append(ctxt->om, &buffer, sizeof(buffer));
// if (res == 0) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// return 0;
// }
return 0;
}
std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(*this->nullHeader);
}
size_t WeatherService::GetTimelineLength() const {
return timeline.size();
}
bool WeatherService::AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event) {
if (timeline.size() == timeline.max_size()) {
return false;
}
timeline.push_back(std::move(event));
return true;
}
bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : timeline) {
if (header->eventType == type && IsEventStillValid(header, currentTimestamp)) {
return true;
}
}
return false;
}
void WeatherService::TidyTimeline() {
uint64_t timeCurrent = GetCurrentUnixTimestamp();
timeline.erase(std::remove_if(std::begin(timeline),
std::end(timeline),
[&](std::unique_ptr<WeatherData::TimelineHeader> const& header) {
return !IsEventStillValid(header, timeCurrent);
}),
std::end(timeline));
std::sort(std::begin(timeline), std::end(timeline), CompareTimelineEvents);
}
bool WeatherService::CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::TimelineHeader>& second) {
return first->timestamp > second->timestamp;
}
bool WeatherService::IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp) {
// Not getting timestamp in isEventStillValid for more speed
return uniquePtr->timestamp + uniquePtr->expires >= timestamp;
}
uint64_t WeatherService::GetCurrentUnixTimestamp() const {
return std::chrono::duration_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime().time_since_epoch()).count();
}
int16_t WeatherService::GetTodayMinTemp() const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) -
((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds());
int16_t result = -32768;
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) &&
header->timestamp < currentDayEnd &&
reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) {
int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature;
if (result == -32768) {
result = temperature;
} else if (result > temperature) {
result = temperature;
} else {
// The temperature in this item is higher than the lowest we've found
}
}
}
return result;
}
int16_t WeatherService::GetTodayMaxTemp() const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) -
((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds());
int16_t result = -32768;
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) &&
header->timestamp < currentDayEnd &&
reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) {
int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature;
if (result == -32768) {
result = temperature;
} else if (result < temperature) {
result = temperature;
} else {
// The temperature in this item is lower than the highest we've found
}
}
}
return result;
}
// void WeatherService::CleanUpQcbor(QCBORDecodeContext* decodeContext) {
// QCBORDecode_ExitMap(decodeContext);
// QCBORDecode_Finish(decodeContext);
// }

@ -0,0 +1,172 @@
/* Copyright (C) 2021 Avamander
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 <string>
#include <vector>
#include <memory>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_uuid.h>
//#undef max
//#undef min
#include "components/ble/weather/WeatherData.h"
//#include "libs/QCBOR/inc/qcbor/qcbor.h"
#include "components/datetime/DateTimeController.h"
//int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg);
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class WeatherService {
public:
explicit WeatherService(System::SystemTask& system, DateTime& dateTimeController);
void Init();
int OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt);
/*
* Helper functions for quick access to currently valid data
*/
std::unique_ptr<WeatherData::Location>& GetCurrentLocation();
std::unique_ptr<WeatherData::Clouds>& GetCurrentClouds();
std::unique_ptr<WeatherData::Obscuration>& GetCurrentObscuration();
std::unique_ptr<WeatherData::Precipitation>& GetCurrentPrecipitation();
std::unique_ptr<WeatherData::Wind>& GetCurrentWind();
std::unique_ptr<WeatherData::Temperature>& GetCurrentTemperature();
std::unique_ptr<WeatherData::Humidity>& GetCurrentHumidity();
std::unique_ptr<WeatherData::Pressure>& GetCurrentPressure();
std::unique_ptr<WeatherData::AirQuality>& GetCurrentQuality();
/**
* Searches for the current day's maximum temperature
* @return -32768 if there's no data, degrees Celsius times 100 otherwise
*/
int16_t GetTodayMaxTemp() const;
/**
* Searches for the current day's minimum temperature
* @return -32768 if there's no data, degrees Celsius times 100 otherwise
*/
int16_t GetTodayMinTemp() const;
/*
* Management functions
*/
/**
* Adds an event to the timeline
* @return
*/
bool AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event);
/**
* Gets the current timeline length
*/
size_t GetTimelineLength() const;
/**
* Checks if an event of a certain type exists in the timeline
*/
bool HasTimelineEventOfType(WeatherData::eventtype type) const;
private:
// 00040000-78fc-48fe-8e23-433b3a1942d0
// static constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
// }
// // 0004yyxx-78fc-48fe-8e23-433b3a1942d0
// static constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128},
// .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, y, x, 0x04, 0x00}};
// }
// ble_uuid128_t weatherUuid {BaseUuid()};
/**
* Just write timeline data here.
*
* See {@link WeatherData.h} for more information.
*/
// ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)};
/**
* This doesn't take timeline data, provides some control over it.
*
* NOTE: Currently not supported. Companion app implementer feedback required.
* There's very little point in solidifying an API before we know the needs.
*/
// ble_uuid128_t weatherControlCharUuid {CharUuid(0x00, 0x02)};
// const struct ble_gatt_chr_def characteristicDefinition[3] = {
// {.uuid = &weatherDataCharUuid.u,
// .access_cb = WeatherCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE,
// .val_handle = &eventHandle},
// {.uuid = &weatherControlCharUuid.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
// {nullptr}};
// const struct ble_gatt_svc_def serviceDefinition[2] = {
// {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, {0}};
uint16_t eventHandle {};
Pinetime::System::SystemTask& system;
Pinetime::Controllers::DateTime& dateTimeController;
std::vector<std::unique_ptr<WeatherData::TimelineHeader>> timeline;
std::unique_ptr<WeatherData::TimelineHeader> nullTimelineheader = std::make_unique<WeatherData::TimelineHeader>();
std::unique_ptr<WeatherData::TimelineHeader>* nullHeader;
/**
* Cleans up the timeline of expired events
*/
void TidyTimeline();
/**
* Compares two timeline events
*/
static bool CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::TimelineHeader>& second);
/**
* Returns current UNIX timestamp
*/
uint64_t GetCurrentUnixTimestamp() const;
/**
* Checks if the event hasn't gone past and expired
*
* @param header timeline event to check
* @param currentTimestamp what's the time right now
* @return if the event is valid
*/
static bool IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp);
/**
* This is a helper function that closes a QCBOR map and decoding context cleanly
*/
// void CleanUpQcbor(QCBORDecodeContext* decodeContext);
};
}
}

@ -0,0 +1,126 @@
#include "BrightnessController.h"
//#include <hal/nrf_gpio.h>
#include "displayapp/screens/Symbols.h"
#include "drivers/PinMap.h"
using namespace Pinetime::Controllers;
void BrightnessController::Init() {
//nrf_gpio_cfg_output(PinMap::LcdBacklightLow);
//nrf_gpio_cfg_output(PinMap::LcdBacklightMedium);
//nrf_gpio_cfg_output(PinMap::LcdBacklightHigh);
Set(level);
}
void BrightnessController::Set(BrightnessController::Levels level) {
this->level = level;
//switch (level) {
// default:
// case Levels::High:
// nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
// nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_clear(PinMap::LcdBacklightHigh);
// break;
// case Levels::Medium:
// nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
// nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
// break;
// case Levels::Low:
// nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
// nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
// break;
// case Levels::Off:
// nrf_gpio_pin_set(PinMap::LcdBacklightLow);
// nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
// nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
// break;
//}
}
void BrightnessController::Lower() {
switch (level) {
case Levels::High:
Set(Levels::Medium);
break;
case Levels::Medium:
Set(Levels::Low);
break;
case Levels::Low:
Set(Levels::Off);
break;
default:
break;
}
}
void BrightnessController::Higher() {
switch (level) {
case Levels::Off:
Set(Levels::Low);
break;
case Levels::Low:
Set(Levels::Medium);
break;
case Levels::Medium:
Set(Levels::High);
break;
default:
break;
}
}
BrightnessController::Levels BrightnessController::Level() const {
return level;
}
void BrightnessController::Backup() {
backupLevel = level;
}
void BrightnessController::Restore() {
Set(backupLevel);
}
void BrightnessController::Step() {
switch (level) {
case Levels::Low:
Set(Levels::Medium);
break;
case Levels::Medium:
Set(Levels::High);
break;
case Levels::High:
Set(Levels::Low);
break;
default:
break;
}
}
const char* BrightnessController::GetIcon() {
switch (level) {
case Levels::Medium:
return Applications::Screens::Symbols::brightnessMedium;
case Levels::High:
return Applications::Screens::Symbols::brightnessHigh;
default:
break;
}
return Applications::Screens::Symbols::brightnessLow;
}
const char* BrightnessController::ToString() {
switch (level) {
case Levels::Off:
return "Off";
case Levels::Low:
return "Low";
case Levels::Medium:
return "Medium";
case Levels::High:
return "High";
default:
return "???";
}
}

@ -0,0 +1,29 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class BrightnessController {
public:
enum class Levels { Off, Low, Medium, High };
void Init();
void Set(Levels level);
Levels Level() const;
void Lower();
void Higher();
void Step();
void Backup();
void Restore();
const char* GetIcon();
const char* ToString();
private:
Levels level = Levels::High;
Levels backupLevel = Levels::High;
};
}
}

@ -0,0 +1,21 @@
#include "components/firmwarevalidator/FirmwareValidator.h"
//#include <hal/nrf_rtc.h>
//#include "drivers/InternalFlash.h"
using namespace Pinetime::Controllers;
bool FirmwareValidator::IsValidated() const {
return true; // lv_sim
// auto* imageOkPtr = reinterpret_cast<uint32_t*>(validBitAdress);
// return (*imageOkPtr) == validBitValue;
}
void FirmwareValidator::Validate() {
// if (!IsValidated())
// Pinetime::Drivers::InternalFlash::WriteWord(validBitAdress, validBitValue);
}
void FirmwareValidator::Reset() {
// NVIC_SystemReset();
}

@ -0,0 +1,19 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class FirmwareValidator {
public:
void Validate();
bool IsValidated() const;
void Reset();
private:
static constexpr uint32_t validBitAdress {0x7BFE8};
static constexpr uint32_t validBitValue {1};
};
}
}

@ -0,0 +1,250 @@
#include "FS.h"
#include <cassert>
#include <cstring>
#include <filesystem>
//#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using namespace Pinetime::Controllers;
//FS::FS(Pinetime::Drivers::SpiNorFlash& driver)
// : flashDriver {driver},
// lfsConfig {
// .context = this,
// .read = SectorRead,
// .prog = SectorProg,
// .erase = SectorErase,
// .sync = SectorSync,
//
// .read_size = 16,
// .prog_size = 8,
// .block_size = blockSize,
// .block_count = size / blockSize,
// .block_cycles = 1000u,
//
// .cache_size = 16,
// .lookahead_size = 16,
//
// .name_max = 50,
// .attr_max = 50,
// } {
//}
void FS::Init() {
// // try mount
// int err = lfs_mount(&lfs, &lfsConfig);
//
// // reformat if we can't mount the filesystem
// // this should only happen on the first boot
// if (err != LFS_ERR_OK) {
// lfs_format(&lfs, &lfsConfig);
// err = lfs_mount(&lfs, &lfsConfig);
// if (err != LFS_ERR_OK) {
// return;
// }
// }
//
//#ifndef PINETIME_IS_RECOVERY
// VerifyResource();
// LVGLFileSystemInit();
//#endif
}
void FS::VerifyResource() {
// validate the resource metadata
resourcesValid = true;
}
int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
// create the file in the current directory
const char *local_filename = fileName[0]=='/' ? &fileName[1] : fileName;
const char *mode;
bool flag_read = flags & LFS_O_RDONLY;
bool flag_write = flags & LFS_O_WRONLY;
bool flag_create = flags & LFS_O_CREAT;
if (flag_create) {
if (std::filesystem::exists(local_filename)) {
if (flag_read && flag_write) {
mode = "rb+";
} else if (flag_read) {
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
} else {
if (flag_read && flag_write) {
mode = "wb+";
} else if (flag_read) {
assert(false); // read only file not existing
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
}
} else {
if (std::filesystem::exists(local_filename)) {
if (flag_read && flag_write) {
mode = "rb+";
} else if (flag_read) {
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
} else {
return LFS_ERR_IO;
}
}
FILE *fptr = fopen(local_filename, mode);
if (fptr == nullptr) {
return LFS_ERR_BADF;
} else {
*file_p = fptr;
return LFS_ERR_OK;
}
//return lfs_file_open(&lfs, file_p, fileName, flags);
}
int FS::FileClose(lfs_file_t* file_p) {
return fclose(*file_p);
//return lfs_file_close(&lfs, file_p);
}
int FS::FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size) {
return fread(buff, sizeof(uint8_t), size, *file_p);
//return lfs_file_read(&lfs, file_p, buff, size);
}
int FS::FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size) {
return fwrite((void*)buff, sizeof(uint8_t), size, *file_p);
//return lfs_file_write(&lfs, file_p, buff, size);
}
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
return fseek(*file_p, pos, SEEK_SET);
//return lfs_file_seek(&lfs, file_p, pos, whence);
}
int FS::FileDelete(const char* fileName) {
return std::filesystem::remove(fileName);
//return lfs_remove(&lfs, fileName);
}
int FS::DirCreate(const char* path) {
return std::filesystem::create_directory(path);
//return lfs_mkdir(&lfs, path);
}
// Delete directory and all files inside
int FS::DirDelete(const char* path) {
return std::filesystem::remove_all(path);
//lfs_dir_t lfs_dir;
//lfs_info entryInfo;
//int err;
//err = lfs_dir_open(&lfs, &lfs_dir, path);
//if (err) {
// return err;
//}
//while (lfs_dir_read(&lfs, &lfs_dir, &entryInfo)) {
// lfs_remove(&lfs, entryInfo.name);
//}
//lfs_dir_close(&lfs, &lfs_dir);
//return LFS_ERR_OK;
}
/*
----------- Interface between littlefs and SpiNorFlash -----------
*/
//int FS::SectorSync(const struct lfs_config* c) {
// return 0;
//}
//
//int FS::SectorErase(const struct lfs_config* c, lfs_block_t block) {
// Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
// const size_t address = startAddress + (block * blockSize);
// lfs.flashDriver.SectorErase(address);
// return lfs.flashDriver.EraseFailed() ? -1 : 0;
//}
//
//int FS::SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size) {
// Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
// const size_t address = startAddress + (block * blockSize) + off;
// lfs.flashDriver.Write(address, (uint8_t*) buffer, size);
// return lfs.flashDriver.ProgramFailed() ? -1 : 0;
//}
//
//int FS::SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size) {
// Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
// const size_t address = startAddress + (block * blockSize) + off;
// lfs.flashDriver.Read(address, static_cast<uint8_t*>(buffer), size);
// return 0;
//}
/*
----------- LVGL filesystem integration -----------
*/
namespace {
lv_fs_res_t lvglOpen(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode) {
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
FS* filesys = static_cast<FS*>(drv->user_data);
int ret = filesys->FileOpen(file, path, LFS_O_RDONLY);
if (ret != LFS_ERR_OK) {
return LV_FS_RES_FS_ERR;
}
return LV_FS_RES_OK;
}
lv_fs_res_t lvglClose(lv_fs_drv_t* drv, void* file_p) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileClose(file);
return LV_FS_RES_OK;
}
lv_fs_res_t lvglRead(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileRead(file, static_cast<uint8_t*>(buf), btr);
*br = btr;
return LV_FS_RES_OK;
}
lv_fs_res_t lvglSeek(lv_fs_drv_t* drv, void* file_p, uint32_t pos) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileSeek(file, pos);
return LV_FS_RES_OK;
}
}
void FS::LVGLFileSystemInit() {
lv_fs_drv_init(&fs_drv);
fs_drv.file_size = sizeof(lfs_file_t);
fs_drv.letter = 'F';
fs_drv.open_cb = lvglOpen;
fs_drv.close_cb = lvglClose;
fs_drv.read_cb = lvglRead;
fs_drv.seek_cb = lvglSeek;
fs_drv.user_data = this;
lv_fs_drv_register(&fs_drv);
}

@ -0,0 +1,134 @@
#pragma once
#include <cstdio>
#include <cstdint>
//#include "drivers/SpiNorFlash.h"
//#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using lfs_file_t = FILE*;
// copied from src/libs/littlefs/lfs.h
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -84, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_FBIG = -27, // File too large
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NOATTR = -61, // No data/attr available
LFS_ERR_NAMETOOLONG = -36, // File name too long
};
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
#ifndef LFS_READONLY
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
#endif
};
// File seek flags
enum lfs_whence_flags {
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
};
typedef int32_t lfs_ssize_t;
namespace Pinetime {
namespace Controllers {
class FS {
public:
//FS(Pinetime::Drivers::SpiNorFlash&);
void Init();
void LVGLFileSystemInit();
int FileOpen(lfs_file_t* file_p, const char* fileName, const int flags);
int FileClose(lfs_file_t* file_p);
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
int FileSeek(lfs_file_t* file_p, uint32_t pos);
int FileDelete(const char* fileName);
//int DirOpen(const char* path, lfs_dir_t* lfs_dir);
//int DirClose(lfs_dir_t* lfs_dir);
//int DirRead(lfs_dir_t* dir, lfs_info* info);
//int DirRewind(lfs_dir_t* dir);
int DirCreate(const char* path);
int DirDelete(const char* path);
lfs_ssize_t GetFSSize();
int Rename(const char* oldPath, const char* newPath);
//int Stat(const char* path, lfs_info* info);
void VerifyResource();
static size_t getSize() {
return size;
}
static size_t getBlockSize() {
return blockSize;
}
private:
//Pinetime::Drivers::SpiNorFlash& flashDriver;
/*
* External Flash MAP (4 MBytes)
*
* 0x000000 +---------------------------------------+
* | Bootloader Assets |
* | 256 KBytes |
* | |
* 0x040000 +---------------------------------------+
* | OTA |
* | 464 KBytes |
* | |
* | |
* | |
* 0x0B4000 +---------------------------------------+
* | File System |
* | |
* | |
* | |
* | |
* 0x400000 +---------------------------------------+
*
*/
static constexpr size_t startAddress = 0x0B4000;
static constexpr size_t size = 0x34C000;
static constexpr size_t blockSize = 4096;
lv_fs_drv_t fs_drv;
bool resourcesValid = false;
//const struct lfs_config lfsConfig;
//lfs_t lfs;
//static int SectorSync(const struct lfs_config* c);
//static int SectorErase(const struct lfs_config* c, lfs_block_t block);
//static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
//static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
};
}
}

@ -0,0 +1,35 @@
#include "components/heartrate/HeartRateController.h"
#include <heartratetask/HeartRateTask.h>
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
this->state = newState;
if (this->heartRate != heartRate) {
this->heartRate = heartRate;
//service->OnNewHeartRateValue(heartRate);
}
}
void HeartRateController::Start() {
if (task != nullptr) {
state = States::NotEnoughData;
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StartMeasurement);
}
}
void HeartRateController::Stop() {
if (task != nullptr) {
state = States::Stopped;
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StopMeasurement);
}
}
void HeartRateController::SetHeartRateTask(Pinetime::Applications::HeartRateTask* task) {
this->task = task;
}
//void HeartRateController::SetService(Pinetime::Controllers::HeartRateService* service) {
// this->service = service;
//}

@ -0,0 +1,40 @@
#pragma once
#include <cstdint>
//#include <components/ble/HeartRateService.h>
namespace Pinetime {
namespace Applications {
class HeartRateTask;
}
namespace System {
class SystemTask;
}
namespace Controllers {
class HeartRateController {
public:
enum class States { Stopped, NotEnoughData, NoTouch, Running };
HeartRateController() = default;
void Start();
void Stop();
void Update(States newState, uint8_t heartRate);
void SetHeartRateTask(Applications::HeartRateTask* task);
States State() const {
return state;
}
uint8_t HeartRate() const {
return heartRate;
}
// void SetService(Pinetime::Controllers::HeartRateService* service);
private:
Applications::HeartRateTask* task = nullptr;
States state = States::Stopped;
uint8_t heartRate = 0;
//Pinetime::Controllers::HeartRateService* service = nullptr;
};
}
}

@ -0,0 +1,88 @@
#include "components/motion/MotionController.h"
//#include "os/os_cputime.h"
using namespace Pinetime::Controllers;
void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) {
// if (this->nbSteps != nbSteps && service != nullptr) {
// service->OnNewStepCountValue(nbSteps);
// }
//
// if (service != nullptr && (this->x != x || this->y != y || this->z != z)) {
// service->OnNewMotionValues(x, y, z);
// }
this->x = x;
this->y = y;
this->z = z;
int32_t deltaSteps = nbSteps - this->nbSteps;
this->nbSteps = nbSteps;
if (deltaSteps > 0) {
currentTripSteps += deltaSteps;
}
}
bool MotionController::Should_RaiseWake(bool isSleeping) {
if ((x + 335) <= 670 && z < 0) {
if (not isSleeping) {
if (y <= 0) {
return false;
} else {
lastYForWakeUp = 0;
return false;
}
}
if (y >= 0) {
lastYForWakeUp = 0;
return false;
}
if (y + 230 < lastYForWakeUp) {
lastYForWakeUp = y;
return true;
}
}
return false;
}
bool MotionController::Should_ShakeWake(uint16_t thresh) {
return false;
// bool wake = false;
// auto diff = xTaskGetTickCount() - lastShakeTime;
// lastShakeTime = xTaskGetTickCount();
// /* Currently Polling at 10hz, If this ever goes faster scalar and EMA might need adjusting */
// int32_t speed = std::abs(z + (y / 2) + (x / 4) - lastYForShake - lastZForShake) / diff * 100;
// //(.2 * speed) + ((1 - .2) * accumulatedspeed);
// // implemented without floats as .25Alpha
// accumulatedspeed = (speed / 5) + ((accumulatedspeed / 5) * 4);
//
// if (accumulatedspeed > thresh) {
// wake = true;
// }
// lastXForShake = x / 4;
// lastYForShake = y / 2;
// lastZForShake = z;
// return wake;
}
int32_t MotionController::currentShakeSpeed() {
return accumulatedspeed;
}
void MotionController::IsSensorOk(bool isOk) {
isSensorOk = isOk;
}
void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) {
switch (types) {
case Drivers::Bma421::DeviceTypes::BMA421:
this->deviceType = DeviceTypes::BMA421;
break;
case Drivers::Bma421::DeviceTypes::BMA425:
this->deviceType = DeviceTypes::BMA425;
break;
default:
this->deviceType = DeviceTypes::Unknown;
break;
}
}
//void MotionController::SetService(Pinetime::Controllers::MotionService* service) {
// this->service = service;
//}

@ -0,0 +1,72 @@
#pragma once
#include <cstdint>
#include <drivers/Bma421.h>
//#include <components/ble/MotionService.h>
namespace Pinetime {
namespace Controllers {
class MotionController {
public:
enum class DeviceTypes{
Unknown,
BMA421,
BMA425,
};
void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
int16_t X() const {
return x;
}
int16_t Y() const {
return y;
}
int16_t Z() const {
return z;
}
uint32_t NbSteps() const {
return nbSteps;
}
void ResetTrip() {
currentTripSteps = 0;
}
uint32_t GetTripSteps() const {
return currentTripSteps;
}
bool Should_ShakeWake(uint16_t thresh);
bool Should_RaiseWake(bool isSleeping);
int32_t currentShakeSpeed();
void IsSensorOk(bool isOk);
bool IsSensorOk() const {
return isSensorOk;
}
DeviceTypes DeviceType() const {
return deviceType;
}
void Init(Pinetime::Drivers::Bma421::DeviceTypes types);
// void SetService(Pinetime::Controllers::MotionService* service);
private:
uint32_t nbSteps;
uint32_t currentTripSteps = 0;
int16_t x;
int16_t y;
int16_t z;
int16_t lastYForWakeUp = 0;
bool isSensorOk = false;
DeviceTypes deviceType = DeviceTypes::Unknown;
// Pinetime::Controllers::MotionService* service = nullptr;
int16_t lastXForShake = 0;
int16_t lastYForShake = 0;
int16_t lastZForShake = 0;
int32_t accumulatedspeed = 0;
uint32_t lastShakeTime = 0;
};
}
}

@ -0,0 +1,58 @@
#include "components/motor/MotorController.h"
#include <SDL2/SDL.h>
using namespace Pinetime::Controllers;
void MotorController::Init() {
//nrf_gpio_cfg_output(PinMap::Motor);
//nrf_gpio_pin_set(PinMap::Motor);
//app_timer_init();
//app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, StopMotor);
//app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, Ring);
}
void MotorController::Ring(void* p_context) {
auto* motorController = static_cast<MotorController*>(p_context);
motorController->RunForDuration(50);
}
Uint32 StopMotor_callback(Uint32 interval, void *param)
{
auto* motorController = static_cast<MotorController*>(param);
motorController->motor_is_running = false;
return 0; // cancel timer
}
Uint32 Ring_callback(Uint32 interval, void *param)
{
auto* motorController = static_cast<MotorController*>(param);
motorController->RunForDuration(50);
if (motorController->is_ringing) {
return interval;
}
return 0;
}
void MotorController::RunForDuration(uint8_t motorDuration) {
this->motor_is_running = true;
SDL_AddTimer(motorDuration, StopMotor_callback, this);
//nrf_gpio_pin_clear(PinMap::Motor);
//app_timer_start(shortVibTimer, APP_TIMER_TICKS(motorDuration), nullptr);
}
void MotorController::StartRinging() {
Ring(this);
is_ringing = true;
SDL_AddTimer(1000, Ring_callback, this);
//app_timer_start(longVibTimer, APP_TIMER_TICKS(1000), this);
}
void MotorController::StopRinging() {
is_ringing = false;
}
void MotorController::StopMotor(void* p_context) {
//nrf_gpio_pin_set(PinMap::Motor);
auto* motorController = static_cast<MotorController*>(p_context);
motorController->motor_is_running = false;
}

@ -0,0 +1,25 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class MotorController {
public:
MotorController() = default;
void Init();
void RunForDuration(uint8_t motorDuration);
void StartRinging();
void StopRinging();
bool motor_is_running = false;
bool is_ringing = false;
private:
static void Ring(void* p_context);
static void StopMotor(void* p_context);
};
}
}

@ -0,0 +1,327 @@
#include "displayapp/LittleVgl.h"
#include "displayapp/lv_pinetime_theme.h"
//#include <FreeRTOS.h>
//#include <task.h>
////#include <projdefs.h>
#include "drivers/Cst816s.h"
#include "drivers/St7789.h"
using namespace Pinetime::Components;
//lv_style_t* LabelBigStyle = nullptr;
//
//static void disp_flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) {
// auto* lvgl = static_cast<LittleVgl*>(disp_drv->user_data);
// lvgl->FlushDisplay(area, color_p);
//}
//
bool touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data) {
auto* lvgl = static_cast<LittleVgl*>(indev_drv->user_data);
return lvgl->GetTouchPadInfo(data);
}
LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel)
: lcd {lcd}, touchPanel {touchPanel}, previousClick {0, 0} {
}
void LittleVgl::Init() {
// lv_init();
// InitDisplay();
// InitTheme();
InitTouchpad();
}
//void LittleVgl::InitDisplay() {
// lv_disp_draw_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 4); /*Initialize the display buffer*/
// lv_disp_drv_init(&disp_drv); /*Basic initialization*/
//
// /*Set up the functions to access to your display*/
//
// /*Set the resolution of the display*/
// disp_drv.hor_res = 240;
// disp_drv.ver_res = 240;
//
// /*Used to copy the buffer's content to the display*/
// disp_drv.flush_cb = disp_flush;
// /*Set a display buffer*/
// disp_drv.draw_buf = &disp_buf_2;
// disp_drv.user_data = this;
//
// /*Finally register the driver*/
// lv_disp_drv_register(&disp_drv);
//}
void LittleVgl::InitTouchpad() {
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_drv.user_data = this;
lv_indev_drv_register(&indev_drv);
}
void LittleVgl::SetFullRefresh(FullRefreshDirections direction) {
if (scrollDirection == FullRefreshDirections::None) {
scrollDirection = direction;
if (scrollDirection == FullRefreshDirections::Down) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::Right) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::Left) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::RightAnim) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
} else if (scrollDirection == FullRefreshDirections::LeftAnim) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
}
}
}
//
//
//void LittleVgl::DisplayDownScroll(){
// // We are controlling the drawing process, disable lvgl timers
// lv_timer_enable(false);
//
// // For each segment, draw the full width, 4 lines at a time starting from the bottom
// // TODO: Should probably calculate this from the size of the draw buffer
// int16_t height = 4;
// int16_t width = 240;
// int16_t y2 = 240;
// int16_t y1 = 240 - height;
//
// lv_area_t area;
// area.x1 = 0;
// area.x2 = width;
//
// // Start from the bottom and create a 4 line high box
// for (y1 = 240 - height; y1 >= 0; y1 -= height) {
// y2 = y1 + height - 1;
//
// // If the box has reached the end of the visible line on the lcd controller...
// if (y2 == visibleNbLines - 1) {
// // move past the non visible lines
// writeOffset += (totalNbLines - visibleNbLines);
// // and wrap around to the start of address space
// writeOffset %= totalNbLines;
// }
// // Set new box
// area.y1 = y1;
// area.y2 = y2;
//
// // Scroll as we draw
// uint16_t toScroll = height;
// if (scrollOffset >= toScroll)
// scrollOffset -= toScroll;
// else { // now we need to wrap the scroll address
// toScroll -= scrollOffset;
// scrollOffset = totalNbLines - toScroll;
// }
// lcd.VerticalScrollStartAddress(scrollOffset);
//
// lv_disp_t* disp = lv_disp_get_default();
// // Clear invalid area list / tells lvgl that nothing on the screen needs to be updated
// _lv_inv_area(disp, nullptr);
// // invalidate only the segment we want to update in this portion of the animation
// _lv_inv_area(disp, &area);
// // cancel any current flushes in the display driver
// // Since we've stopped timers, it will be waiting forever if there is currently a flush
// lv_disp_flush_ready(disp->driver);
// lv_refr_now(disp);
// }
// // Done! clear flags and enable timers
// scrollDirection = FullRefreshDirections::None;
// animating = false;
// lv_timer_enable(true);
//}
//
//void LittleVgl::DisplayHorizAnim() {
// lv_timer_enable(false);
//
// int16_t height, width, x1, x2;
// lv_area_t area;
//
// height = 240;
// width = 4;
// int16_t (*NextStep)(int16_t, int16_t){};
// bool (*CheckEnd)(int16_t){};
//
// area.y1=0;
// area.y2=height;
//
// if (scrollDirection == FullRefreshDirections::RightAnim) {
// x1 = 0;
//
// CheckEnd = [](int16_t x) -> bool {
// return (x < LV_HOR_RES_MAX);
// };
// NextStep = [](int16_t x, int16_t width) -> int16_t {
// auto newx = x + width * 2;
// if (newx < 240) {return newx;};
// return (newx < 240 + width) ? (newx - 240 + width) : newx;
// };
//
// } else if (scrollDirection == FullRefreshDirections::LeftAnim) {
// x1 = 240 - width;
//
// CheckEnd = [](int16_t x) -> bool {
// return (x >= 0);
// };
// NextStep = [](int16_t x, int16_t width) -> int16_t {
// auto newx = x - width * 2;
// if (newx >= 0) {return newx;}
// return (newx >= 0 - width) ? (newx + 240 - width) : newx;
// };
//
// } else {
// // Not set for a horizontal animation!
// lv_timer_enable(true);
// return;
// }
//
// for (; CheckEnd(x1); x1 = NextStep(x1, width)) {
// x2 = x1 + width-1;
//
// if (area.y2 == visibleNbLines - 1) {
// writeOffset += (totalNbLines - visibleNbLines);
// writeOffset %= totalNbLines;
// }
// area.x1 = x1;
// area.x2 = x2;
//
// lv_disp_t* disp = lv_disp_get_default();
// _lv_inv_area(disp, nullptr);
// _lv_inv_area(disp, &area);
// lv_disp_flush_ready(disp->driver);
// lv_refr_now(disp);
// }
// scrollDirection = FullRefreshDirections::None;
// animating = false;
// lv_timer_enable(true);
//}
//
//void LittleVgl::FlushDisplayManually() {
// switch(scrollDirection){
// case FullRefreshDirections::Down:
// DisplayDownScroll();
// break;
// case FullRefreshDirections::RightAnim:
// case FullRefreshDirections::LeftAnim:
// DisplayHorizAnim();
// break;
// default:
// break;
// }
//}
//
void LittleVgl::FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
// uint16_t y1, y2, width, height = 0;
//
// ulTaskNotifyTake(pdTRUE, 200);
// // NOtification is still needed (even if there is a mutex on SPI) because of the DataCommand pin
// // which cannot be set/clear during a transfert.
//
// if (!animating && (scrollDirection == FullRefreshDirections::Down ||
// scrollDirection == FullRefreshDirections::RightAnim ||
// scrollDirection == FullRefreshDirections::LeftAnim)){
// animating = true;
// FlushDisplayManually();
// return;
// }
//
// if ((scrollDirection == FullRefreshDirections::Up) && (area->y1 == 0)) {
// writeOffset = (writeOffset + visibleNbLines) % totalNbLines;
// }
//
// y1 = (area->y1 + writeOffset) % totalNbLines;
// y2 = (area->y2 + writeOffset) % totalNbLines;
//
// width = (area->x2 - area->x1) + 1;
// height = (area->y2 - area->y1) + 1;
//
// if (scrollDirection == FullRefreshDirections::Up) {
//
// if (area->y1 > 0) {
// if (area->y2 == visibleNbLines - 1) {
// scrollOffset += (height * 2);
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// } else {
// scrollOffset += height;
// }
// scrollOffset = scrollOffset % totalNbLines;
// lcd.VerticalScrollStartAddress(scrollOffset);
// }
// } else if (scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) {
// if (area->x2 == visibleNbLines - 1) {
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// }
// } else if (scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) {
// if (area->x1 == 0) {
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// }
// }
//
// if (y2 < y1) {
// height = totalNbLines - y1;
//
// if (height > 0) {
// lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
// ulTaskNotifyTake(pdTRUE, 100);
// }
//
// uint16_t pixOffset = width * height;
// height = y2 + 1;
// lcd.DrawBuffer(area->x1, 0, width, height, reinterpret_cast<const uint8_t*>(color_p + pixOffset), width * height * 2);
//
// } else {
// lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
// }
//
// // IMPORTANT!!!
// // Inform the graphics library that you are ready with the flushing
// lv_disp_flush_ready(&disp_drv);
lv_disp_t *disp = lv_disp_get_default();
lv_disp_drv_t *disp_drv = &disp->driver;
lv_area_t area_trimmed = *area;
if (area->x1 < 0)
area_trimmed.x1 = 0;
if (area->x2 >= LV_HOR_RES)
area_trimmed.x2 = LV_HOR_RES-1;
if (area->y1 < 0)
area_trimmed.y1 = 0;
if (area->y2 >= LV_VER_RES)
area_trimmed.y2 = LV_VER_RES-1;
// tell flush_cb this is the last thing to flush to get the monitor refreshed
lv_disp_get_buf(disp)->flushing_last = true;
disp_drv->flush_cb(disp_drv, &area_trimmed, color_p);
}
void LittleVgl::SetNewTouchPoint(uint16_t x, uint16_t y, bool contact) {
tap_x = x;
tap_y = y;
tapped = contact;
}
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
ptr->point.x = tap_x;
ptr->point.y = tap_y;
if (tapped) {
ptr->state = LV_INDEV_STATE_PR;
} else {
ptr->state = LV_INDEV_STATE_REL;
}
return false;
}
//void LittleVgl::InitTheme() {
// if (!lv_pinetime_theme_is_inited()) {
// lv_theme_t* th = lv_pinetime_theme_init(lv_disp_get_default(), lv_color_white(), lv_color_hex(0xC0C0C0), &jetbrains_mono_bold_20);
// lv_disp_set_theme(lv_disp_get_default(), th);
// }
//}

@ -0,0 +1,64 @@
#pragma once
#include <lvgl/lvgl.h>
namespace Pinetime {
namespace Drivers {
class Cst816S;
class St7789;
}
namespace Components {
class LittleVgl {
public:
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel);
LittleVgl(const LittleVgl&) = delete;
LittleVgl& operator=(const LittleVgl&) = delete;
LittleVgl(LittleVgl&&) = delete;
LittleVgl& operator=(LittleVgl&&) = delete;
void Init();
void FlushDisplay(const lv_area_t* area, lv_color_t* color_p);
bool GetTouchPadInfo(lv_indev_data_t* ptr);
void SetFullRefresh(FullRefreshDirections direction);
void SetNewTouchPoint(uint16_t x, uint16_t y, bool contact);
//
// private:
// void InitDisplay();
void InitTouchpad();
// void InitTheme();
//
// void FlushDisplayManually();
// void DisplayDownScroll();
// void DisplayHorizAnim();
Pinetime::Drivers::St7789& lcd;
Pinetime::Drivers::Cst816S& touchPanel;
// lv_disp_draw_buf_t disp_buf_2;
// lv_color_t buf2_1[LV_HOR_RES_MAX * 4];
// lv_color_t buf2_2[LV_HOR_RES_MAX * 4];
//
lv_disp_drv_t disp_drv;
lv_point_t previousClick;
bool firstTouch = true;
static constexpr uint8_t nbWriteLines = 4;
static constexpr uint16_t totalNbLines = 320;
static constexpr uint16_t visibleNbLines = 240;
static constexpr uint8_t MaxScrollOffset() {
return LV_VER_RES_MAX - nbWriteLines;
}
FullRefreshDirections scrollDirection = FullRefreshDirections::None;
uint16_t writeOffset = 0;
uint16_t scrollOffset = 0;
uint16_t tap_x = 0;
uint16_t tap_y = 0;
bool tapped = false;
};
}
}

@ -0,0 +1,131 @@
#include "displayapp/screens/Missing.h"
#include "displayapp/DisplayApp.h"
using namespace Pinetime::Applications::Screens;
Missing::Missing(Pinetime::Applications::DisplayApp* app, Pinetime::Applications::Apps app_key) : Screen(app) {
const char *screen_lbl;
switch (app_key) {
case Pinetime::Applications::Apps::None:
screen_lbl = "None";
break;
case Pinetime::Applications::Apps::Launcher:
screen_lbl = "Launcher";
break;
case Pinetime::Applications::Apps::Clock:
screen_lbl = "Clock";
break;
case Pinetime::Applications::Apps::SysInfo:
screen_lbl = "SysInfo";
break;
case Pinetime::Applications::Apps::FirmwareUpdate:
screen_lbl = "FirmwareUpdate";
break;
case Pinetime::Applications::Apps::FirmwareValidation:
screen_lbl = "FirmwareValidation";
break;
case Pinetime::Applications::Apps::NotificationsPreview:
screen_lbl = "NotificationPreview";
break;
case Pinetime::Applications::Apps::Notifications:
screen_lbl = "Notifications";
break;
case Pinetime::Applications::Apps::Timer:
screen_lbl = "Timer";
break;
case Pinetime::Applications::Apps::Alarm:
screen_lbl = "Alarm";
break;
case Pinetime::Applications::Apps::FlashLight:
screen_lbl = "FlashLight";
break;
case Pinetime::Applications::Apps::BatteryInfo:
screen_lbl = "BatteryInfo";
break;
case Pinetime::Applications::Apps::Music:
screen_lbl = "Music";
break;
case Pinetime::Applications::Apps::Paint:
screen_lbl = "Paint";
break;
case Pinetime::Applications::Apps::Paddle:
screen_lbl = "Paddle";
break;
case Pinetime::Applications::Apps::Twos:
screen_lbl = "Twos";
break;
case Pinetime::Applications::Apps::HeartRate:
screen_lbl = "HeartRate";
break;
case Pinetime::Applications::Apps::Navigation:
screen_lbl = "Navigation";
break;
case Pinetime::Applications::Apps::StopWatch:
screen_lbl = "StopWatch";
break;
case Pinetime::Applications::Apps::Metronome:
screen_lbl = "Metronome";
break;
case Pinetime::Applications::Apps::Motion:
screen_lbl = "Motion";
break;
case Pinetime::Applications::Apps::Steps:
screen_lbl = "Steps";
break;
case Pinetime::Applications::Apps::Weather:
screen_lbl = "Weather";
break;
case Pinetime::Applications::Apps::PassKey:
screen_lbl = "PassKey";
break;
case Pinetime::Applications::Apps::QuickSettings:
screen_lbl = "QuickSettings";
break;
case Pinetime::Applications::Apps::Settings:
screen_lbl = "Settings";
break;
case Pinetime::Applications::Apps::SettingWatchFace:
screen_lbl = "SettingWatchFace";
break;
case Pinetime::Applications::Apps::SettingTimeFormat:
screen_lbl = "SettingTimeFormat";
break;
case Pinetime::Applications::Apps::SettingDisplay:
screen_lbl = "SettingDisplay";
break;
case Pinetime::Applications::Apps::SettingWakeUp:
screen_lbl = "SettingWakeUp";
break;
case Pinetime::Applications::Apps::SettingSteps:
screen_lbl = "SettingSteps";
break;
case Pinetime::Applications::Apps::SettingSetDate:
screen_lbl = "SettingSetDate";
break;
case Pinetime::Applications::Apps::SettingSetTime:
screen_lbl = "SettingSetTime";
break;
case Pinetime::Applications::Apps::SettingChimes:
screen_lbl = "SettingChimes";
break;
case Pinetime::Applications::Apps::SettingShakeThreshold:
screen_lbl = "SettingThreshold";
break;
case Pinetime::Applications::Apps::Error:
screen_lbl = "Error";
break;
//case Pinetime::Applications::Apps::Weather:
// screen_lbl = "Weather";
// break;
default:
screen_lbl = "unkown screen";
}
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(label, screen_lbl);
lv_obj_align(label, nullptr, LV_ALIGN_CENTER, 0, -20);
}
Missing::~Missing() {
lv_obj_clean(lv_scr_act());
}

@ -0,0 +1,18 @@
#pragma once
#include "displayapp/screens/Screen.h"
#include "displayapp/Apps.h"
#include <lvgl/lvgl.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class Missing : public Screen {
public:
Missing(DisplayApp* app, Pinetime::Applications::Apps app_key);
~Missing() override;
};
}
}
}

@ -0,0 +1,134 @@
#include "drivers/Bma421.h"
#include <libraries/delay/nrf_delay.h>
#include <libraries/log/nrf_log.h>
#include "drivers/TwiMaster.h"
#include <drivers/Bma421_C/bma423.h>
using namespace Pinetime::Drivers;
namespace {
// int8_t user_i2c_read(uint8_t reg_addr, uint8_t* reg_data, uint32_t length, void* intf_ptr) {
// auto bma421 = static_cast<Bma421*>(intf_ptr);
// bma421->Read(reg_addr, reg_data, length);
// return 0;
// }
//
// int8_t user_i2c_write(uint8_t reg_addr, const uint8_t* reg_data, uint32_t length, void* intf_ptr) {
// auto bma421 = static_cast<Bma421*>(intf_ptr);
// bma421->Write(reg_addr, reg_data, length);
// return 0;
// }
//
// void user_delay(uint32_t period_us, void* intf_ptr) {
// nrf_delay_us(period_us);
// }
}
Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, deviceAddress {twiAddress} {
// bma.intf = BMA4_I2C_INTF;
// bma.bus_read = user_i2c_read;
// bma.bus_write = user_i2c_write;
// bma.variant = BMA42X_VARIANT;
// bma.intf_ptr = this;
// bma.delay_us = user_delay;
// bma.read_write_len = 16;
}
void Bma421::Init() {
if (not isResetOk)
return; // Call SoftReset (and reset TWI device) first!
// auto ret = bma423_init(&bma);
// if (ret != BMA4_OK)
// return;
switch(bma.chip_id) {
case BMA423_CHIP_ID: deviceType = DeviceTypes::BMA421; break;
case BMA425_CHIP_ID: deviceType = DeviceTypes::BMA425; break;
default: deviceType = DeviceTypes::Unknown; break;
}
// ret = bma423_write_config_file(&bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma4_set_interrupt_mode(BMA4_LATCH_MODE, &bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma423_feature_enable(BMA423_STEP_CNTR, 1, &bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma423_step_detector_enable(0, &bma);
// if (ret != BMA4_OK)
// return;
//
// ret = bma4_set_accel_enable(1, &bma);
// if (ret != BMA4_OK)
// return;
//
// struct bma4_accel_config accel_conf;
// accel_conf.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
// accel_conf.range = BMA4_ACCEL_RANGE_2G;
// accel_conf.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
// accel_conf.perf_mode = BMA4_CIC_AVG_MODE;
// ret = bma4_set_accel_config(&accel_conf, &bma);
// if (ret != BMA4_OK)
// return;
//
isOk = true;
}
void Bma421::Reset() {
uint8_t data = 0xb6;
twiMaster.Write(deviceAddress, 0x7E, &data, 1);
}
void Bma421::Read(uint8_t registerAddress, uint8_t* buffer, size_t size) {
twiMaster.Read(deviceAddress, registerAddress, buffer, size);
}
void Bma421::Write(uint8_t registerAddress, const uint8_t* data, size_t size) {
twiMaster.Write(deviceAddress, registerAddress, data, size);
}
Bma421::Values Bma421::Process() {
if (not isOk)
return {};
// struct bma4_accel data;
// bma4_read_accel_xyz(&data, &bma);
//
// uint32_t steps = 0;
// bma423_step_counter_output(&steps, &bma);
//
// int32_t temperature = 0;
// bma4_get_temperature(&temperature, BMA4_DEG, &bma);
// temperature = temperature / 1000;
//
// uint8_t activity = 0;
// bma423_activity_output(&activity, &bma);
//
// // X and Y axis are swapped because of the way the sensor is mounted in the PineTime
// return {steps, data.y, data.x, data.z};
return {steps, 0, 0, 0};
}
bool Bma421::IsOk() const {
return isOk;
}
void Bma421::ResetStepCounter() {
// bma423_reset_step_counter(&bma);
steps = 0;
}
void Bma421::SoftReset() {
// auto ret = bma4_soft_reset(&bma);
// if (ret == BMA4_OK) {
// isResetOk = true;
// nrf_delay_ms(1);
// }
}
Bma421::DeviceTypes Bma421::DeviceType() const {
return deviceType;
}

@ -0,0 +1,56 @@
#pragma once
#include <drivers/Bma421_C/bma4_defs.h>
#include <cstdint>
#include <cstddef>
#include <memory>
namespace Pinetime {
namespace Drivers {
class TwiMaster;
class Bma421 {
public:
enum class DeviceTypes : uint8_t {
Unknown,
BMA421,
BMA425
};
struct Values {
uint32_t steps;
int16_t x;
int16_t y;
int16_t z;
};
Bma421(TwiMaster& twiMaster, uint8_t twiAddress);
Bma421(const Bma421&) = delete;
Bma421& operator=(const Bma421&) = delete;
Bma421(Bma421&&) = delete;
Bma421& operator=(Bma421&&) = delete;
/// The chip freezes the TWI bus after the softreset operation. Softreset is separated from the
/// Init() method to allow the caller to uninit and then reinit the TWI device after the softreset.
void SoftReset();
void Init();
Values Process();
void ResetStepCounter();
void Read(uint8_t registerAddress, uint8_t* buffer, size_t size);
void Write(uint8_t registerAddress, const uint8_t* data, size_t size);
bool IsOk() const;
DeviceTypes DeviceType() const;
// lv_sim: returned by Process(), public to be modified by main.cpp
uint32_t steps = 0;
private:
void Reset();
TwiMaster& twiMaster;
uint8_t deviceAddress = 0x18;
bma4_dev bma;
bool isOk = true;
bool isResetOk = false;
DeviceTypes deviceType = DeviceTypes::Unknown;
};
}
}

@ -0,0 +1,112 @@
#include "drivers/Cst816s.h"
#include <SDL2/SDL.h>
#include <libraries/log/nrf_log.h>
#include <cmath>
using namespace Pinetime::Drivers;
/* References :
* This implementation is based on this article :
* https://medium.com/@ly.lee/building-a-rust-driver-for-pinetimes-touch-controller-cbc1a5d5d3e9 Touch panel datasheet (weird chinese
* translation) : https://wiki.pine64.org/images/5/51/CST816S%E6%95%B0%E6%8D%AE%E6%89%8B%E5%86%8CV1.1.en.pdf
*
* TODO : we need a complete datasheet and protocol reference!
* */
//Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} {
//}
Cst816S::Cst816S() {
}
bool Cst816S::Init() {
return true;
}
Cst816S::TouchInfos Cst816S::GetTouchInfo() {
int x, y;
uint32_t buttons = SDL_GetMouseState(&x, &y);
Cst816S::TouchInfos info;
info.x = x;
info.y = y;
info.touching = (buttons & SDL_BUTTON_LMASK) != 0;
//info.gesture = gesture;
info.isValid = x > 0 && x <= maxX && y > 0 && y <= maxY;
if(info.isValid) {
if(!is_pressed && info.touching) {
// start klick
pressed_since = std::chrono::steady_clock::now();
is_pressed = true;
is_long_press = false;
is_swipe = false;
is_stationary = true;
x_start = info.x;
y_start = info.y;
} else if(is_pressed && info.touching) {
// is it long press?
if (is_stationary) { // check if while touching we moved away from the start coordinates
double x_diff = static_cast<double>(info.x) - x_start;
double y_diff = static_cast<double>(info.y) - y_start;
double norm = hypot(x_diff, y_diff);
if(norm > 20) { // we moved out of start area
is_stationary = false;
}
}
if (!is_long_press && !is_swipe) { // check for long-press only if it's not yet a long-press and didn't move
std::chrono::duration<double> press_duration = std::chrono::steady_clock::now() - pressed_since;
if(is_stationary && press_duration.count() > 1.0) {
// longer than 1 second pressed, then it is long-press
is_long_press = true;
info.gesture = Gestures::LongPress;
} else if(!is_stationary) {
// moved mouse fast enough to not be a long-press
is_swipe = true;
double x_diff = static_cast<double>(info.x) - x_start;
double y_diff = static_cast<double>(info.y) - y_start;
if (fabs(x_diff) > fabs(y_diff)) {
// x-swipe
if (x_diff < 0) {
info.gesture = Gestures::SlideLeft;
} else {
info.gesture = Gestures::SlideRight;
}
} else {
// y-swipe
if (y_diff < 0) {
info.gesture = Gestures::SlideUp;
} else {
info.gesture = Gestures::SlideDown;
}
}
}
}
} else if(is_pressed && !info.touching) {
// end klick
is_pressed = false;
double x_diff = static_cast<double>(info.x) - x_start;
double y_diff = static_cast<double>(info.y) - y_start;
double norm = hypot(x_diff, y_diff);
if(norm < 20) {
if(is_stationary && !is_long_press && !is_swipe) {
// no swipe with less than 5 pixel mouse movement
info.gesture = Gestures::SingleTap;
}
}
}
}
return info;
}
void Cst816S::Sleep() {
NRF_LOG_INFO("[TOUCHPANEL] Sleep");
}
void Cst816S::Wakeup() {
Init();
NRF_LOG_INFO("[TOUCHPANEL] Wakeup");
}
bool Cst816S::CheckDeviceIds() {
return chipId == 0xb4 && vendorId == 0 && fwVersion == 1;
}

@ -0,0 +1,88 @@
#pragma once
//#include "drivers/TwiMaster.h"
#include <chrono>
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class Cst816S {
public:
enum class Gestures : uint8_t {
None = 0x00,
SlideDown = 0x01,
SlideUp = 0x02,
SlideLeft = 0x03,
SlideRight = 0x04,
SingleTap = 0x05,
DoubleTap = 0x0B,
LongPress = 0x0C
};
struct TouchInfos {
uint16_t x = 0;
uint16_t y = 0;
Gestures gesture = Gestures::None;
bool touching = false;
bool isValid = false;
};
Cst816S();
// Cst816S(TwiMaster& twiMaster, uint8_t twiAddress);
Cst816S(const Cst816S&) = delete;
Cst816S& operator=(const Cst816S&) = delete;
Cst816S(Cst816S&&) = delete;
Cst816S& operator=(Cst816S&&) = delete;
bool Init();
TouchInfos GetTouchInfo();
void Sleep();
void Wakeup();
uint8_t GetChipId() const {
return chipId;
}
uint8_t GetVendorId() const {
return vendorId;
}
uint8_t GetFwVersion() const {
return fwVersion;
}
private:
bool CheckDeviceIds();
// Unused/Unavailable commented out
static constexpr uint8_t gestureIndex = 1;
static constexpr uint8_t touchPointNumIndex = 2;
//static constexpr uint8_t touchEventIndex = 3;
static constexpr uint8_t touchXHighIndex = 3;
static constexpr uint8_t touchXLowIndex = 4;
//static constexpr uint8_t touchIdIndex = 5;
static constexpr uint8_t touchYHighIndex = 5;
static constexpr uint8_t touchYLowIndex = 6;
//static constexpr uint8_t touchStep = 6;
//static constexpr uint8_t touchXYIndex = 7;
//static constexpr uint8_t touchMiscIndex = 8;
static constexpr uint8_t maxX = 240;
static constexpr uint8_t maxY = 240;
// TwiMaster& twiMaster;
// uint8_t twiAddress;
const uint8_t chipId = 0xb4;
const uint8_t vendorId = 0;
const uint8_t fwVersion = 1;
// simulation members for swipe detection from mouse
std::chrono::time_point<std::chrono::steady_clock> pressed_since;
bool is_pressed = false;
bool is_long_press = false;
bool is_stationary = true;
bool is_swipe = false;
uint8_t x_start;
uint8_t y_start;
};
}
}

@ -0,0 +1,105 @@
/*
SPDX-License-Identifier: LGPL-3.0-or-later
Original work Copyright (C) 2020 Daniel Thompson
C++ port Copyright (C) 2021 Jean-François Milants
*/
#include "drivers/Hrs3300.h"
#include <algorithm>
#include <nrf_gpio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <nrf_log.h>
using namespace Pinetime::Drivers;
/** Driver for the HRS3300 heart rate sensor.
* Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/drivers/hrs3300.py
*/
Hrs3300::Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} {
}
void Hrs3300::Init() {
nrf_gpio_cfg_input(30, NRF_GPIO_PIN_NOPULL);
Disable();
// vTaskDelay(100);
// HRS disabled, 12.5 ms wait time between cycles, (partly) 20mA drive
WriteRegister(static_cast<uint8_t>(Registers::Enable), 0x60);
// (partly) 20mA drive, power on, "magic" (datasheet says both
// "reserved" and "set low nibble to 8" but 0xe gives better results
// and is used by at least two other HRS3300 drivers
WriteRegister(static_cast<uint8_t>(Registers::PDriver), 0x6E);
// HRS and ALS both in 16-bit mode
WriteRegister(static_cast<uint8_t>(Registers::Res), 0x88);
// 64x gain
WriteRegister(static_cast<uint8_t>(Registers::Hgain), 0x10);
}
void Hrs3300::Enable() {
NRF_LOG_INFO("ENABLE");
auto value = ReadRegister(static_cast<uint8_t>(Registers::Enable));
value |= 0x80;
WriteRegister(static_cast<uint8_t>(Registers::Enable), value);
}
void Hrs3300::Disable() {
NRF_LOG_INFO("DISABLE");
auto value = ReadRegister(static_cast<uint8_t>(Registers::Enable));
value &= ~0x80;
WriteRegister(static_cast<uint8_t>(Registers::Enable), value);
}
uint16_t Hrs3300::ReadHrs() {
auto m = ReadRegister(static_cast<uint8_t>(Registers::C0DataM));
auto h = ReadRegister(static_cast<uint8_t>(Registers::C0DataH));
auto l = ReadRegister(static_cast<uint8_t>(Registers::C0dataL));
return (m << 8) | ((h & 0x0f) << 4) | (l & 0x0f) | ((l & 0x30) << 12);
}
uint16_t Hrs3300::ReadAls() {
auto m = ReadRegister(static_cast<uint8_t>(Registers::C1dataM));
auto h = ReadRegister(static_cast<uint8_t>(Registers::C1dataH));
auto l = ReadRegister(static_cast<uint8_t>(Registers::C1dataL));
return (m << 3) | ((h & 0x3f) << 11) | (l & 0x07);
}
void Hrs3300::SetGain(uint8_t gain) {
constexpr uint8_t maxGain = 64U;
gain = std::min(gain, maxGain);
uint8_t hgain = 0;
while ((1 << hgain) < gain) {
++hgain;
}
WriteRegister(static_cast<uint8_t>(Registers::Hgain), hgain << 2);
}
void Hrs3300::SetDrive(uint8_t drive) {
auto en = ReadRegister(static_cast<uint8_t>(Registers::Enable));
auto pd = ReadRegister(static_cast<uint8_t>(Registers::PDriver));
en = (en & 0xf7) | ((drive & 2) << 2);
pd = (pd & 0xbf) | ((drive & 1) << 6);
WriteRegister(static_cast<uint8_t>(Registers::Enable), en);
WriteRegister(static_cast<uint8_t>(Registers::PDriver), pd);
}
void Hrs3300::WriteRegister(uint8_t reg, uint8_t data) {
auto ret = twiMaster.Write(twiAddress, reg, &data, 1);
if (ret != TwiMaster::ErrorCodes::NoError)
NRF_LOG_INFO("WRITE ERROR");
}
uint8_t Hrs3300::ReadRegister(uint8_t reg) {
uint8_t value;
auto ret = twiMaster.Read(twiAddress, reg, &value, 1);
if (ret != TwiMaster::ErrorCodes::NoError)
NRF_LOG_INFO("READ ERROR");
return value;
}

@ -0,0 +1,46 @@
#pragma once
#include "drivers/TwiMaster.h"
namespace Pinetime {
namespace Drivers {
class Hrs3300 {
public:
enum class Registers : uint8_t {
Id = 0x00,
Enable = 0x01,
EnableHen = 0x80,
C1dataM = 0x08,
C0DataM = 0x09,
C0DataH = 0x0a,
PDriver = 0x0c,
C1dataH = 0x0d,
C1dataL = 0x0e,
C0dataL = 0x0f,
Res = 0x16,
Hgain = 0x17
};
Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress);
Hrs3300(const Hrs3300&) = delete;
Hrs3300& operator=(const Hrs3300&) = delete;
Hrs3300(Hrs3300&&) = delete;
Hrs3300& operator=(Hrs3300&&) = delete;
void Init();
void Enable();
void Disable();
uint16_t ReadHrs();
uint16_t ReadAls();
void SetGain(uint8_t gain);
void SetDrive(uint8_t drive);
private:
TwiMaster& twiMaster;
uint8_t twiAddress;
void WriteRegister(uint8_t reg, uint8_t data);
uint8_t ReadRegister(uint8_t reg);
};
}
}

@ -0,0 +1,296 @@
#include "drivers/SpiMaster.h"
//#include <hal/nrf_gpio.h>
//#include <hal/nrf_spim.h>
#include <nrfx_log.h>
#include <algorithm>
using namespace Pinetime::Drivers;
SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters& params) : spi {spi}, params {params} {
}
bool SpiMaster::Init() {
// if(mutex == nullptr) {
// mutex = xSemaphoreCreateBinary();
// ASSERT(mutex != nullptr);
// }
//
// /* Configure GPIO pins used for pselsck, pselmosi, pselmiso and pselss for SPI0 */
// nrf_gpio_pin_set(params.pinSCK);
// nrf_gpio_cfg_output(params.pinSCK);
// nrf_gpio_pin_clear(params.pinMOSI);
// nrf_gpio_cfg_output(params.pinMOSI);
// nrf_gpio_cfg_input(params.pinMISO, NRF_GPIO_PIN_NOPULL);
// // nrf_gpio_cfg_output(params.pinCSN);
// // pinCsn = params.pinCSN;
//
// switch (spi) {
// case SpiModule::SPI0:
// spiBaseAddress = NRF_SPIM0;
// break;
// case SpiModule::SPI1:
// spiBaseAddress = NRF_SPIM1;
// break;
// default:
// return false;
// }
//
// /* Configure pins, frequency and mode */
// spiBaseAddress->PSELSCK = params.pinSCK;
// spiBaseAddress->PSELMOSI = params.pinMOSI;
// spiBaseAddress->PSELMISO = params.pinMISO;
//
// uint32_t frequency;
// switch (params.Frequency) {
// case Frequencies::Freq8Mhz:
// frequency = 0x80000000;
// break;
// default:
// return false;
// }
// spiBaseAddress->FREQUENCY = frequency;
//
// uint32_t regConfig = 0;
// switch (params.bitOrder) {
// case BitOrder::Msb_Lsb:
// break;
// case BitOrder::Lsb_Msb:
// regConfig = 1;
// break;
// default:
// return false;
// }
// switch (params.mode) {
// case Modes::Mode0:
// break;
// case Modes::Mode1:
// regConfig |= (0x01 << 1);
// break;
// case Modes::Mode2:
// regConfig |= (0x02 << 1);
// break;
// case Modes::Mode3:
// regConfig |= (0x03 << 1);
// break;
// default:
// return false;
// }
//
// spiBaseAddress->CONFIG = regConfig;
// spiBaseAddress->EVENTS_ENDRX = 0;
// spiBaseAddress->EVENTS_ENDTX = 0;
// spiBaseAddress->EVENTS_END = 0;
//
// spiBaseAddress->INTENSET = ((unsigned) 1 << (unsigned) 6);
// spiBaseAddress->INTENSET = ((unsigned) 1 << (unsigned) 1);
// spiBaseAddress->INTENSET = ((unsigned) 1 << (unsigned) 19);
//
// spiBaseAddress->ENABLE = (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos);
//
// NRFX_IRQ_PRIORITY_SET(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn, 2);
// NRFX_IRQ_ENABLE(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn);
//
// xSemaphoreGive(mutex);
return true;
}
//void SpiMaster::SetupWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel) {
// // Create an event when SCK toggles.
// NRF_GPIOTE->CONFIG[gpiote_channel] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | (spim->PSEL.SCK << GPIOTE_CONFIG_PSEL_Pos) |
// (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos);
//
// // Stop the spim instance when SCK toggles.
// NRF_PPI->CH[ppi_channel].EEP = (uint32_t) &NRF_GPIOTE->EVENTS_IN[gpiote_channel];
// NRF_PPI->CH[ppi_channel].TEP = (uint32_t) &spim->TASKS_STOP;
// NRF_PPI->CHENSET = 1U << ppi_channel;
// spiBaseAddress->EVENTS_END = 0;
//
// // Disable IRQ
// spim->INTENCLR = (1 << 6);
// spim->INTENCLR = (1 << 1);
// spim->INTENCLR = (1 << 19);
//}
//void SpiMaster::DisableWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel) {
// NRF_GPIOTE->CONFIG[gpiote_channel] = 0;
// NRF_PPI->CH[ppi_channel].EEP = 0;
// NRF_PPI->CH[ppi_channel].TEP = 0;
// NRF_PPI->CHENSET = ppi_channel;
// spiBaseAddress->EVENTS_END = 0;
// spim->INTENSET = (1 << 6);
// spim->INTENSET = (1 << 1);
// spim->INTENSET = (1 << 19);
//}
void SpiMaster::OnEndEvent() {
if (currentBufferAddr == 0) {
return;
}
// auto s = currentBufferSize;
// if (s > 0) {
// auto currentSize = std::min((size_t) 255, s);
// PrepareTx(currentBufferAddr, currentSize);
// currentBufferAddr += currentSize;
// currentBufferSize -= currentSize;
//
// spiBaseAddress->TASKS_START = 1;
// } else {
// BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// if (taskToNotify != nullptr) {
// vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken);
// portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
// }
//
// nrf_gpio_pin_set(this->pinCsn);
// currentBufferAddr = 0;
// BaseType_t xHigherPriorityTaskWoken2 = pdFALSE;
// xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken2);
// portYIELD_FROM_ISR(xHigherPriorityTaskWoken | xHigherPriorityTaskWoken2);
// }
}
void SpiMaster::OnStartedEvent() {
}
//void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size) {
// spiBaseAddress->TXD.PTR = bufferAddress;
// spiBaseAddress->TXD.MAXCNT = size;
// spiBaseAddress->TXD.LIST = 0;
// spiBaseAddress->RXD.PTR = 0;
// spiBaseAddress->RXD.MAXCNT = 0;
// spiBaseAddress->RXD.LIST = 0;
// spiBaseAddress->EVENTS_END = 0;
//}
//void SpiMaster::PrepareRx(const volatile uint32_t cmdAddress,
// const volatile size_t cmdSize,
// const volatile uint32_t bufferAddress,
// const volatile size_t size) {
// spiBaseAddress->TXD.PTR = 0;
// spiBaseAddress->TXD.MAXCNT = 0;
// spiBaseAddress->TXD.LIST = 0;
// spiBaseAddress->RXD.PTR = bufferAddress;
// spiBaseAddress->RXD.MAXCNT = size;
// spiBaseAddress->RXD.LIST = 0;
// spiBaseAddress->EVENTS_END = 0;
//}
bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size) {
// if (data == nullptr)
// return false;
// auto ok = xSemaphoreTake(mutex, portMAX_DELAY);
// ASSERT(ok == true);
// taskToNotify = xTaskGetCurrentTaskHandle();
//
// this->pinCsn = pinCsn;
//
// if (size == 1) {
// SetupWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// } else {
// DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// }
//
// nrf_gpio_pin_clear(this->pinCsn);
//
// currentBufferAddr = (uint32_t) data;
// currentBufferSize = size;
//
// auto currentSize = std::min((size_t) 255, (size_t) currentBufferSize);
// PrepareTx(currentBufferAddr, currentSize);
// currentBufferSize -= currentSize;
// currentBufferAddr += currentSize;
// spiBaseAddress->TASKS_START = 1;
//
// if (size == 1) {
// while (spiBaseAddress->EVENTS_END == 0)
// ;
// nrf_gpio_pin_set(this->pinCsn);
// currentBufferAddr = 0;
// xSemaphoreGive(mutex);
// }
return true;
}
bool SpiMaster::Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize) {
// xSemaphoreTake(mutex, portMAX_DELAY);
//
// taskToNotify = nullptr;
//
// this->pinCsn = pinCsn;
// DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// spiBaseAddress->INTENCLR = (1 << 6);
// spiBaseAddress->INTENCLR = (1 << 1);
// spiBaseAddress->INTENCLR = (1 << 19);
//
// nrf_gpio_pin_clear(this->pinCsn);
//
// currentBufferAddr = 0;
// currentBufferSize = 0;
//
// PrepareTx((uint32_t) cmd, cmdSize);
// spiBaseAddress->TASKS_START = 1;
// while (spiBaseAddress->EVENTS_END == 0)
// ;
//
// PrepareRx((uint32_t) cmd, cmdSize, (uint32_t) data, dataSize);
// spiBaseAddress->TASKS_START = 1;
//
// while (spiBaseAddress->EVENTS_END == 0)
// ;
// nrf_gpio_pin_set(this->pinCsn);
//
// xSemaphoreGive(mutex);
return true;
}
void SpiMaster::Sleep() {
// while (spiBaseAddress->ENABLE != 0) {
// spiBaseAddress->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos);
// }
// nrf_gpio_cfg_default(params.pinSCK);
// nrf_gpio_cfg_default(params.pinMOSI);
// nrf_gpio_cfg_default(params.pinMISO);
NRF_LOG_INFO("[SPIMASTER] sleep");
}
void SpiMaster::Wakeup() {
Init();
NRF_LOG_INFO("[SPIMASTER] Wakeup");
}
bool SpiMaster::WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize) {
// xSemaphoreTake(mutex, portMAX_DELAY);
//
// taskToNotify = nullptr;
//
// this->pinCsn = pinCsn;
// DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
// spiBaseAddress->INTENCLR = (1 << 6);
// spiBaseAddress->INTENCLR = (1 << 1);
// spiBaseAddress->INTENCLR = (1 << 19);
//
// nrf_gpio_pin_clear(this->pinCsn);
//
// currentBufferAddr = 0;
// currentBufferSize = 0;
//
// PrepareTx((uint32_t) cmd, cmdSize);
// spiBaseAddress->TASKS_START = 1;
// while (spiBaseAddress->EVENTS_END == 0)
// ;
//
// PrepareTx((uint32_t) data, dataSize);
// spiBaseAddress->TASKS_START = 1;
//
// while (spiBaseAddress->EVENTS_END == 0)
// ;
// nrf_gpio_pin_set(this->pinCsn);
//
// xSemaphoreGive(mutex);
return true;
}

@ -0,0 +1,65 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <FreeRTOS.h>
//#include <semphr.h>
//#include <task.h>
namespace Pinetime {
namespace Drivers {
class SpiMaster {
public:
enum class SpiModule : uint8_t { SPI0, SPI1 };
enum class BitOrder : uint8_t { Msb_Lsb, Lsb_Msb };
enum class Modes : uint8_t { Mode0, Mode1, Mode2, Mode3 };
enum class Frequencies : uint8_t { Freq8Mhz };
struct Parameters {
BitOrder bitOrder;
Modes mode;
Frequencies Frequency;
uint8_t pinSCK;
uint8_t pinMOSI;
uint8_t pinMISO;
};
SpiMaster(const SpiModule spi, const Parameters& params);
SpiMaster(const SpiMaster&) = delete;
SpiMaster& operator=(const SpiMaster&) = delete;
SpiMaster(SpiMaster&&) = delete;
SpiMaster& operator=(SpiMaster&&) = delete;
bool Init();
bool Write(uint8_t pinCsn, const uint8_t* data, size_t size);
bool Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize);
bool WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize);
void OnStartedEvent();
void OnEndEvent();
void Sleep();
void Wakeup();
private:
// void SetupWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel);
// void DisableWorkaroundForFtpan58(NRF_SPIM_Type* spim, uint32_t ppi_channel, uint32_t gpiote_channel);
// void PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size);
// void PrepareRx(const volatile uint32_t cmdAddress,
// const volatile size_t cmdSize,
// const volatile uint32_t bufferAddress,
// const volatile size_t size);
// NRF_SPIM_Type* spiBaseAddress;
uint8_t pinCsn;
SpiMaster::SpiModule spi;
SpiMaster::Parameters params;
volatile uint32_t currentBufferAddr = 0;
volatile size_t currentBufferSize = 0;
// volatile TaskHandle_t taskToNotify;
// SemaphoreHandle_t mutex = nullptr;
};
}
}

@ -0,0 +1,144 @@
#include "drivers/SpiNorFlash.h"
#include <hal/nrf_gpio.h>
#include <libraries/delay/nrf_delay.h>
#include <libraries/log/nrf_log.h>
#include "drivers/Spi.h"
using namespace Pinetime::Drivers;
SpiNorFlash::SpiNorFlash(Spi& spi) : spi {spi} {
}
void SpiNorFlash::Init() {
device_id = ReadIdentificaion();
NRF_LOG_INFO(
"[SpiNorFlash] Manufacturer : %d, Memory type : %d, memory density : %d", device_id.manufacturer, device_id.type, device_id.density);
}
void SpiNorFlash::Uninit() {
}
void SpiNorFlash::Sleep() {
auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown);
spi.Write(&cmd, sizeof(uint8_t));
NRF_LOG_INFO("[SpiNorFlash] Sleep")
}
void SpiNorFlash::Wakeup() {
// send Commands::ReleaseFromDeepPowerDown then 3 dummy bytes before reading Device ID
// static constexpr uint8_t cmdSize = 4;
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::ReleaseFromDeepPowerDown), 0x01, 0x02, 0x03};
// uint8_t id = 0;
// spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, &id, 1);
// auto devId = device_id = ReadIdentificaion();
// if (devId.type != device_id.type) {
// NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: Failed");
// } else {
// NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: %d", id);
// }
NRF_LOG_INFO("[SpiNorFlash] Wakeup")
}
SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() {
// auto cmd = static_cast<uint8_t>(Commands::ReadIdentification);
// Identification identification;
// spi.Read(&cmd, 1, reinterpret_cast<uint8_t*>(&identification), sizeof(Identification));
// return identification;
return {};
}
uint8_t SpiNorFlash::ReadStatusRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadStatusRegister);
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
}
bool SpiNorFlash::WriteInProgress() {
// return (ReadStatusRegister() & 0x01u) == 0x01u;
return false;
}
bool SpiNorFlash::WriteEnabled() {
// return (ReadStatusRegister() & 0x02u) == 0x02u;
return false;
}
uint8_t SpiNorFlash::ReadConfigurationRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadConfigurationRegister);
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
}
void SpiNorFlash::Read(uint32_t address, uint8_t* buffer, size_t size) {
static constexpr uint8_t cmdSize = 4;
uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::Read), (uint8_t) (address >> 16U), (uint8_t) (address >> 8U), (uint8_t) address};
spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, buffer, size);
}
void SpiNorFlash::WriteEnable() {
auto cmd = static_cast<uint8_t>(Commands::WriteEnable);
spi.Read(&cmd, sizeof(cmd), nullptr, 0);
}
void SpiNorFlash::SectorErase(uint32_t sectorAddress) {
// static constexpr uint8_t cmdSize = 4;
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::SectorErase),
// (uint8_t) (sectorAddress >> 16U),
// (uint8_t) (sectorAddress >> 8U),
// (uint8_t) sectorAddress};
//
// WriteEnable();
// while (!WriteEnabled())
// vTaskDelay(1);
//
// spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, nullptr, 0);
//
// while (WriteInProgress())
// vTaskDelay(1);
}
uint8_t SpiNorFlash::ReadSecurityRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadSecurityRegister);
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
}
bool SpiNorFlash::ProgramFailed() {
// return (ReadSecurityRegister() & 0x20u) == 0x20u;
return false;
}
bool SpiNorFlash::EraseFailed() {
// return (ReadSecurityRegister() & 0x40u) == 0x40u;
return false;
}
void SpiNorFlash::Write(uint32_t address, const uint8_t* buffer, size_t size) {
// static constexpr uint8_t cmdSize = 4;
//
// size_t len = size;
// uint32_t addr = address;
// const uint8_t* b = buffer;
// while (len > 0) {
// uint32_t pageLimit = (addr & ~(pageSize - 1u)) + pageSize;
// uint32_t toWrite = pageLimit - addr > len ? len : pageLimit - addr;
//
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::PageProgram), (uint8_t) (addr >> 16U), (uint8_t) (addr >> 8U), (uint8_t) addr};
//
// WriteEnable();
// while (!WriteEnabled())
// vTaskDelay(1);
//
// spi.WriteCmdAndBuffer(cmd, cmdSize, b, toWrite);
//
// while (WriteInProgress())
// vTaskDelay(1);
//
// addr += toWrite;
// b += toWrite;
// len -= toWrite;
// }
}

@ -0,0 +1,60 @@
#pragma once
#include <cstddef>
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class Spi;
class SpiNorFlash {
public:
explicit SpiNorFlash(Spi& spi);
SpiNorFlash(const SpiNorFlash&) = delete;
SpiNorFlash& operator=(const SpiNorFlash&) = delete;
SpiNorFlash(SpiNorFlash&&) = delete;
SpiNorFlash& operator=(SpiNorFlash&&) = delete;
typedef struct __attribute__((packed)) {
uint8_t manufacturer = 0;
uint8_t type = 0;
uint8_t density = 0;
} Identification;
Identification ReadIdentificaion();
uint8_t ReadStatusRegister();
bool WriteInProgress();
bool WriteEnabled();
uint8_t ReadConfigurationRegister();
void Read(uint32_t address, uint8_t* buffer, size_t size);
void Write(uint32_t address, const uint8_t* buffer, size_t size);
void WriteEnable();
void SectorErase(uint32_t sectorAddress);
uint8_t ReadSecurityRegister();
bool ProgramFailed();
bool EraseFailed();
void Init();
void Uninit();
void Sleep();
void Wakeup();
private:
enum class Commands : uint8_t {
PageProgram = 0x02,
Read = 0x03,
ReadStatusRegister = 0x05,
WriteEnable = 0x06,
ReadConfigurationRegister = 0x15,
SectorErase = 0x20,
ReadSecurityRegister = 0x2B,
ReadIdentification = 0x9F,
ReleaseFromDeepPowerDown = 0xAB,
DeepPowerDown = 0xB9
};
static constexpr uint16_t pageSize = 256;
Spi& spi;
Identification device_id;
};
}
}

@ -0,0 +1,187 @@
#include "drivers/TwiMaster.h"
#include <cstring>
#include <hal/nrf_gpio.h>
#include <nrfx_log.h>
using namespace Pinetime::Drivers;
// TODO use shortcut to automatically send STOP when receive LastTX, for example
// TODO use DMA/IRQ
TwiMaster::TwiMaster(NRF_TWIM_Type* module, uint32_t frequency, uint8_t pinSda, uint8_t pinScl)
: module {module}, frequency {frequency}, pinSda {pinSda}, pinScl {pinScl} {
}
//void TwiMaster::ConfigurePins() const {
// NRF_GPIO->PIN_CNF[pinScl] =
// (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
// (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
// (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
// (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
// (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
//
// NRF_GPIO->PIN_CNF[pinSda] =
// (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
// (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
// (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
// (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
// (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
//}
void TwiMaster::Init() {
// if (mutex == nullptr) {
// mutex = xSemaphoreCreateBinary();
// }
//
// ConfigurePins();
//
// twiBaseAddress = module;
//
// twiBaseAddress->FREQUENCY = frequency;
//
// twiBaseAddress->PSEL.SCL = pinScl;
// twiBaseAddress->PSEL.SDA = pinSda;
// twiBaseAddress->EVENTS_LASTRX = 0;
// twiBaseAddress->EVENTS_STOPPED = 0;
// twiBaseAddress->EVENTS_LASTTX = 0;
// twiBaseAddress->EVENTS_ERROR = 0;
// twiBaseAddress->EVENTS_RXSTARTED = 0;
// twiBaseAddress->EVENTS_SUSPENDED = 0;
// twiBaseAddress->EVENTS_TXSTARTED = 0;
//
// twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
//
// xSemaphoreGive(mutex);
}
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* data, size_t size) {
// xSemaphoreTake(mutex, portMAX_DELAY);
// Wakeup();
// auto ret = Write(deviceAddress, &registerAddress, 1, false);
// ret = Read(deviceAddress, data, size, true);
// Sleep();
// xSemaphoreGive(mutex);
// return ret;
return TwiMaster::ErrorCodes::NoError;
}
TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size) {
// ASSERT(size <= maxDataSize);
// xSemaphoreTake(mutex, portMAX_DELAY);
// Wakeup();
// internalBuffer[0] = registerAddress;
// std::memcpy(internalBuffer + 1, data, size);
// auto ret = Write(deviceAddress, internalBuffer, size + 1, true);
// Sleep();
// xSemaphoreGive(mutex);
// return ret;
return TwiMaster::ErrorCodes::NoError;
}
//TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop) {
// twiBaseAddress->ADDRESS = deviceAddress;
// twiBaseAddress->TASKS_RESUME = 0x1UL;
// twiBaseAddress->RXD.PTR = (uint32_t) buffer;
// twiBaseAddress->RXD.MAXCNT = size;
//
// twiBaseAddress->TASKS_STARTRX = 1;
//
// while (!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR)
// ;
// twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
//
// txStartedCycleCount = DWT->CYCCNT;
// uint32_t currentCycleCount;
// while (!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR) {
// currentCycleCount = DWT->CYCCNT;
// if ((currentCycleCount - txStartedCycleCount) > HwFreezedDelay) {
// FixHwFreezed();
// return ErrorCodes::TransactionFailed;
// }
// }
// twiBaseAddress->EVENTS_LASTRX = 0x0UL;
//
// if (stop || twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->TASKS_STOP = 0x1UL;
// while (!twiBaseAddress->EVENTS_STOPPED)
// ;
// twiBaseAddress->EVENTS_STOPPED = 0x0UL;
// } else {
// twiBaseAddress->TASKS_SUSPEND = 0x1UL;
// while (!twiBaseAddress->EVENTS_SUSPENDED)
// ;
// twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
// }
//
// if (twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->EVENTS_ERROR = 0x0UL;
// }
// return ErrorCodes::NoError;
//}
//TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop) {
// twiBaseAddress->ADDRESS = deviceAddress;
// twiBaseAddress->TASKS_RESUME = 0x1UL;
// twiBaseAddress->TXD.PTR = (uint32_t) data;
// twiBaseAddress->TXD.MAXCNT = size;
//
// twiBaseAddress->TASKS_STARTTX = 1;
//
// while (!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR)
// ;
// twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
//
// txStartedCycleCount = DWT->CYCCNT;
// uint32_t currentCycleCount;
// while (!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR) {
// currentCycleCount = DWT->CYCCNT;
// if ((currentCycleCount - txStartedCycleCount) > HwFreezedDelay) {
// FixHwFreezed();
// return ErrorCodes::TransactionFailed;
// }
// }
// twiBaseAddress->EVENTS_LASTTX = 0x0UL;
//
// if (stop || twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->TASKS_STOP = 0x1UL;
// while (!twiBaseAddress->EVENTS_STOPPED)
// ;
// twiBaseAddress->EVENTS_STOPPED = 0x0UL;
// } else {
// twiBaseAddress->TASKS_SUSPEND = 0x1UL;
// while (!twiBaseAddress->EVENTS_SUSPENDED)
// ;
// twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
// }
//
// if (twiBaseAddress->EVENTS_ERROR) {
// twiBaseAddress->EVENTS_ERROR = 0x0UL;
// uint32_t error = twiBaseAddress->ERRORSRC;
// twiBaseAddress->ERRORSRC = error;
// }
//
// return ErrorCodes::NoError;
//}
void TwiMaster::Sleep() {
// twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos);
}
void TwiMaster::Wakeup() {
// twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
}
/* Sometimes, the TWIM device just freeze and never set the event EVENTS_LASTTX.
* This method disable and re-enable the peripheral so that it works again.
* This is just a workaround, and it would be better if we could find a way to prevent
* this issue from happening.
* */
//void TwiMaster::FixHwFreezed() {
// NRF_LOG_INFO("I2C device frozen, reinitializing it!");
//
// uint32_t twi_state = NRF_TWI1->ENABLE;
//
// Sleep();
//
// twiBaseAddress->ENABLE = twi_state;
//}

@ -0,0 +1,41 @@
#pragma once
#include <FreeRTOS.h>
//#include <semphr.h>
#include <drivers/include/nrfx_twi.h> // NRF_TWIM_Type
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class TwiMaster {
public:
enum class ErrorCodes { NoError, TransactionFailed };
TwiMaster(NRF_TWIM_Type* module, uint32_t frequency, uint8_t pinSda, uint8_t pinScl);
void Init();
ErrorCodes Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
ErrorCodes Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
void Sleep();
void Wakeup();
private:
// ErrorCodes Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
// ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
// void FixHwFreezed();
// void ConfigurePins() const;
NRF_TWIM_Type* twiBaseAddress;
// SemaphoreHandle_t mutex = nullptr;
NRF_TWIM_Type* module;
uint32_t frequency;
uint8_t pinSda;
uint8_t pinScl;
static constexpr uint8_t maxDataSize {16};
static constexpr uint8_t registerSize {1};
uint8_t internalBuffer[maxDataSize + registerSize];
uint32_t txStartedCycleCount = 0;
static constexpr uint32_t HwFreezedDelay {161000};
};
}
}

@ -0,0 +1,41 @@
#include "drivers/Watchdog.h"
using namespace Pinetime::Drivers;
void Watchdog::Setup(uint8_t timeoutSeconds) {
resetReason = ActualResetReason();
}
void Watchdog::Start() {
}
void Watchdog::Kick() {
}
Watchdog::ResetReasons Watchdog::ActualResetReason() const {
return ResetReasons::ResetPin;
}
const char* Watchdog::ResetReasonToString(Watchdog::ResetReasons reason) {
switch (reason) {
case ResetReasons::ResetPin:
return "Reset pin";
case ResetReasons::Watchdog:
return "Watchdog";
case ResetReasons::DebugInterface:
return "Debug interface";
case ResetReasons::LpComp:
return "LPCOMP";
case ResetReasons::SystemOff:
return "System OFF";
case ResetReasons::CpuLockup:
return "CPU Lock-up";
case ResetReasons::SoftReset:
return "Soft reset";
case ResetReasons::NFC:
return "NFC";
case ResetReasons::HardReset:
return "Hard reset";
default:
return "Unknown";
}
}

@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Drivers {
class Watchdog {
public:
enum class ResetReasons { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset };
void Setup(uint8_t timeoutSeconds);
void Start();
void Kick();
ResetReasons ResetReason() const {
return resetReason;
}
static const char* ResetReasonToString(ResetReasons reason);
private:
ResetReasons resetReason;
ResetReasons ActualResetReason() const;
};
class WatchdogView {
public:
WatchdogView(const Watchdog& watchdog) : watchdog {watchdog} {
}
Watchdog::ResetReasons ResetReason() const {
return watchdog.ResetReason();
}
private:
const Watchdog& watchdog;
};
}
}

@ -0,0 +1,101 @@
#include "heartratetask/HeartRateTask.h"
#include <drivers/Hrs3300.h>
#include <components/heartrate/HeartRateController.h>
#include <nrf_log.h>
using namespace Pinetime::Applications;
HeartRateTask::HeartRateTask(Drivers::Hrs3300& heartRateSensor, Controllers::HeartRateController& controller)
: heartRateSensor {heartRateSensor}, controller {controller} { //, ppg{} {
}
void HeartRateTask::Start() {
messageQueue = xQueueCreate(10, 1);
controller.SetHeartRateTask(this);
// if (pdPASS != xTaskCreate(HeartRateTask::Process, "Heartrate", 500, this, 0, &taskHandle))
// APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
//void HeartRateTask::Process(void* instance) {
// auto* app = static_cast<HeartRateTask*>(instance);
// app->Work();
//}
void HeartRateTask::Work() {
// int lastBpm = 0;
// while (true) {
// Messages msg;
// uint32_t delay;
// if (state == States::Running) {
// if (measurementStarted)
// delay = 40;
// else
// delay = 100;
// } else
// delay = portMAX_DELAY;
//
// if (xQueueReceive(messageQueue, &msg, delay)) {
// switch (msg) {
// case Messages::GoToSleep:
// StopMeasurement();
// state = States::Idle;
// break;
// case Messages::WakeUp:
// state = States::Running;
// if (measurementStarted) {
// lastBpm = 0;
// StartMeasurement();
// }
// break;
// case Messages::StartMeasurement:
// if (measurementStarted)
// break;
// lastBpm = 0;
// StartMeasurement();
// measurementStarted = true;
// break;
// case Messages::StopMeasurement:
// if (!measurementStarted)
// break;
// StopMeasurement();
// measurementStarted = false;
// break;
// }
// }
//
// if (measurementStarted) {
// auto hrs = heartRateSensor.ReadHrs();
// ppg.Preprocess(hrs);
// auto bpm = ppg.HeartRate();
//
// if (lastBpm == 0 && bpm == 0)
// controller.Update(Controllers::HeartRateController::States::NotEnoughData, 0);
// if (bpm != 0) {
// lastBpm = bpm;
// controller.Update(Controllers::HeartRateController::States::Running, lastBpm);
// }
// }
// }
}
void HeartRateTask::PushMessage(HeartRateTask::Messages msg) {
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(messageQueue, &msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
/* Actual macro used here is port specific. */
// TODO : should I do something here?
}
}
//void HeartRateTask::StartMeasurement() {
// heartRateSensor.Enable();
// vTaskDelay(100);
// ppg.SetOffset(static_cast<float>(heartRateSensor.ReadHrs()));
//}
//
//void HeartRateTask::StopMeasurement() {
// heartRateSensor.Disable();
// vTaskDelay(100);
//}

@ -0,0 +1,40 @@
#pragma once
#include <FreeRTOS.h>
//#include <task.h>
#include <queue.h>
//#include <components/heartrate/Ppg.h>
namespace Pinetime {
namespace Drivers {
class Hrs3300;
}
namespace Controllers {
class HeartRateController;
}
namespace Applications {
class HeartRateTask {
public:
enum class Messages : uint8_t { GoToSleep, WakeUp, StartMeasurement, StopMeasurement };
enum class States { Idle, Running };
explicit HeartRateTask(Drivers::Hrs3300& heartRateSensor, Controllers::HeartRateController& controller);
void Start();
void Work();
void PushMessage(Messages msg);
private:
//static void Process(void* instance);
//void StartMeasurement();
//void StopMeasurement();
// TaskHandle_t taskHandle;
QueueHandle_t messageQueue;
States state = States::Running;
Drivers::Hrs3300& heartRateSensor;
Controllers::HeartRateController& controller;
// Controllers::Ppg ppg;
bool measurementStarted = false;
};
}
}

@ -0,0 +1,7 @@
#include "libraries/delay/nrf_delay.h"
#include <SDL2/SDL.h>
void nrf_delay_ms(uint32_t ms_time)
{
SDL_Delay(ms_time);
}

@ -0,0 +1,54 @@
/**
* Copyright (c) 2011 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _NRF_DELAY_H
#define _NRF_DELAY_H
#include <cstdint>
/**
* @brief Function for delaying execution for a number of milliseconds.
*
* @param ms_time Number of milliseconds to wait.
*/
void nrf_delay_ms(uint32_t ms_time);
#endif

@ -0,0 +1,2 @@
#pragma once
#include "hal/nrf_gpio.h"

@ -0,0 +1,60 @@
/**
* Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup nrf_log Logger module
* @{
* @ingroup app_common
*
* @brief The nrf_log module interface.
*/
#ifndef NRF_LOG_H_
#define NRF_LOG_H_
#include <cstdio>
#define NRF_LOG_ERROR(...) do {printf("error: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#define NRF_LOG_WARNING(...) do {printf("warn: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#define NRF_LOG_INFO(...) do {printf("info: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#define NRF_LOG_DEBUG(...) do {printf("debug: "); printf( __VA_ARGS__ ); printf("\n"); } while(false);
#endif // NRF_LOG_H_

@ -0,0 +1,79 @@
// original files copied from nRF5_SDK_15.3.0_59ac345/components/libraries/timer/app_timer.c
// modified to be simulated using SDL2 Timers by NeroBurner
/**
* Copyright (c) 2012 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "app_timer.h"
#include <stdexcept>
#include <cstdlib>
#include <cstdint>
//uint32_t constexpr APP_TIMER_TICKS(uint32_t ms) {
// return static_cast<uint32_t>(
// static_cast<uint64_t>(ms) * configTICK_RATE_HZ / 1000
// );
//}
ret_code_t app_timer_init(void) {
return 0;
}
ret_code_t app_timer_create(app_timer_t *p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler) {
if (mode != APP_TIMER_MODE_SINGLE_SHOT) {
throw std::runtime_error("only mode 'APP_TIMER_MODE_SINGLE_SHOT' implemented");
}
p_timer_id->handler = timeout_handler;
return 0;
}
Uint32 timeout_callback_wrapper(Uint32 interval, void *param)
{
auto* timer_id = static_cast<app_timer_t*>(param);
timer_id->handler(timer_id->p_context);
return 0; // cancel timer
}
ret_code_t app_timer_start(app_timer_t &timer_id, uint32_t timeout_ticks, void * p_context) {
timer_id.p_context = p_context;
timer_id.sdl_timer_id = SDL_AddTimer(timeout_ticks, timeout_callback_wrapper, (void*)(&timer_id));
return 0;
}
ret_code_t app_timer_stop(app_timer_t &timer_id) {
return (SDL_RemoveTimer(timer_id.sdl_timer_id) == SDL_TRUE);
}

@ -0,0 +1,253 @@
// original files copied from nRF5_SDK_15.3.0_59ac345/components/libraries/timer/app_timer.h
// modified to be simulated using SDL2 Timers by NeroBurner
/**
* Copyright (c) 2012 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup app_timer Application Timer
* @{
* @ingroup app_common
*
* @brief Application timer functionality.
*
* @details This module enables the application to create multiple timer instances based on the RTC1
* peripheral. Checking for time-outs and invocation of user time-out handlers is performed
* in the RTC1 interrupt handler. List handling is done using a software interrupt (SWI0).
* Both interrupt handlers are running in APP_LOW priority level.
*
* @details When calling app_timer_start() or app_timer_stop(), the timer operation is just queued,
* and the software interrupt is triggered. The actual timer start/stop operation is
* executed by the SWI0 interrupt handler. Since the SWI0 interrupt is running in APP_LOW,
* if the application code calling the timer function is running in APP_LOW or APP_HIGH,
* the timer operation will not be performed until the application handler has returned.
* This will be the case, for example, when stopping a timer from a time-out handler when not using
* the scheduler.
*
* @details Use the USE_SCHEDULER parameter of the APP_TIMER_INIT() macro to select if the
* @ref app_scheduler should be used or not. Even if the scheduler is
* not used, app_timer.h will include app_scheduler.h, so when
* compiling, app_scheduler.h must be available in one of the compiler include paths.
*/
#ifndef APP_TIMER_H__
#define APP_TIMER_H__
#include <SDL2/SDL.h>
#include "task.h" // configTICK_RATE_HZ
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
// copied from sdk_errors.h
typedef uint32_t ret_code_t;
/** @brief Name of the module used for logger messaging.
*/
#define APP_TIMER_LOG_NAME app_timer
//#define APP_TIMER_CLOCK_FREQ 32768 /**< Clock frequency of the RTC timer used to implement the app timer module. */
//#define APP_TIMER_MIN_TIMEOUT_TICKS 5 /**< Minimum value of the timeout_ticks parameter of app_timer_start(). */
/**@brief Application time-out handler type. */
typedef void (*app_timer_timeout_handler_t)(void * p_context);
struct app_timer_t
{
//nrf_sortlist_item_t list_item; /**< Token used by sortlist. */
//volatile uint32_t end_val; /**< RTC counter value when timer expires. */
SDL_TimerID sdl_timer_id = 0;
uint32_t repeat_period; /**< Repeat period (0 if single shot mode). */
app_timer_timeout_handler_t handler; /**< User handler. */
void * p_context; /**< User context. */
//NRF_LOG_INSTANCE_PTR_DECLARE(p_log) /**< Pointer to instance of the logger object (Conditionally compiled). */
//volatile bool active; /**< Flag indicating that timer is active. */
};
/**
* @brief Create a timer identifier and statically allocate memory for the timer.
*
* @param timer_id Name of the timer identifier variable that will be used to control the timer.
*/
#define APP_TIMER_DEF(timer_id) static app_timer_t timer_id;
/**@brief Convert milliseconds to timer ticks.
*
* This macro uses 64-bit integer arithmetic, but as long as the macro parameters are
* constants (i.e. defines), the computation will be done by the preprocessor.
*
* @param[in] MS Milliseconds.
*
* @return Number of timer ticks.
*/
//#define APP_TIMER_TICKS(MS) \
// ((uint32_t)ROUNDED_DIV( \
// (MS) * (uint64_t)APP_TIMER_CLOCK_FREQ, \
// 1000 * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1)))
uint32_t constexpr APP_TIMER_TICKS(uint32_t ms) {
return static_cast<uint32_t>(
static_cast<uint64_t>(ms) * configTICK_RATE_HZ / 1000
);
}
/**@brief Timer modes. */
typedef enum
{
APP_TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */
APP_TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */
} app_timer_mode_t;
/**@brief Function for initializing the timer module.
*
* @retval NRF_SUCCESS If the module was initialized successfully.
*/
ret_code_t app_timer_init(void);
/**@brief Function for creating a timer instance.
*
* @param[in] p_timer_id Pointer to timer identifier.
* @param[in] mode Timer mode.
* @param[in] timeout_handler Function to be executed when the timer expires.
*
* @retval NRF_SUCCESS If the timer was successfully created.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or
* the timer is running.
*
* @note This function does the timer allocation in the caller's context. It is also not protected
* by a critical region. Therefore care must be taken not to call it from several interrupt
* levels simultaneously.
* @note The function can be called again on the timer instance and will re-initialize the instance if
* the timer is not running.
* @attention The FreeRTOS and RTX app_timer implementation does not allow app_timer_create to
* be called on the previously initialized instance.
*/
ret_code_t app_timer_create(app_timer_t *p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler);
/**@brief Function for starting a timer.
*
* @param[in] timer_id Timer identifier.
* @param[in] timeout_ticks Number of ticks (of RTC1, including prescaling) to time-out event
* (minimum 5 ticks).
* @param[in] p_context General purpose pointer. Will be passed to the time-out handler when
* the timer expires.
*
* @retval NRF_SUCCESS If the timer was successfully started.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer
* has not been created.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*
* @note The minimum timeout_ticks value is 5.
* @note For multiple active timers, time-outs occurring in close proximity to each other (in the
* range of 1 to 3 ticks) will have a positive jitter of maximum 3 ticks.
* @note When calling this method on a timer that is already running, the second start operation
* is ignored.
*/
ret_code_t app_timer_start(app_timer_t &timer_id, uint32_t timeout_ticks, void * p_context);
/**@brief Function for stopping the specified timer.
*
* @param[in] timer_id Timer identifier.
*
* @retval NRF_SUCCESS If the timer was successfully stopped.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer
* has not been created.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*/
ret_code_t app_timer_stop(app_timer_t &timer_id);
/**@brief Function for stopping all running timers.
*
* @retval NRF_SUCCESS If all timers were successfully stopped.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*/
//ret_code_t app_timer_stop_all(void);
/**@brief Function for returning the current value of the RTC1 counter.
*
* @return Current value of the RTC1 counter.
*/
//uint32_t app_timer_cnt_get(void);
/**@brief Function for computing the difference between two RTC1 counter values.
*
* @param[in] ticks_to Value returned by app_timer_cnt_get().
* @param[in] ticks_from Value returned by app_timer_cnt_get().
*
* @return Number of ticks from ticks_from to ticks_to.
*/
//uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to,
// uint32_t ticks_from);
/**@brief Function for getting the maximum observed operation queue utilization.
*
* Function for tuning the module and determining OP_QUEUE_SIZE value and thus module RAM usage.
*
* @note APP_TIMER_WITH_PROFILER must be enabled to use this functionality.
*
* @return Maximum number of events in queue observed so far.
*/
//uint8_t app_timer_op_queue_utilization_get(void);
/**
* @brief Function for pausing RTC activity which drives app_timer.
*
* @note This function can be used for debugging purposes to ensure
* that application is halted when entering a breakpoint.
*/
//void app_timer_pause(void);
/**
* @brief Function for resuming RTC activity which drives app_timer.
*
* @note This function can be used for debugging purposes to resume
* application activity.
*/
//void app_timer_resume(void);
#endif // APP_TIMER_H__
/** @} */

@ -0,0 +1,95 @@
/**
* Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRFX_TWI_H__
#define NRFX_TWI_H__
#include <cstdint>
/**
* @brief I2C compatible Two-Wire Master Interface with EasyDMA 0 (TWIM0)
*/
struct NRF_TWIM_Type { /*!< (@ 0x40003000) TWIM0 Structure */
uint32_t TASKS_STARTRX; /*!< (@ 0x00000000) Start TWI receive sequence */
uint32_t RESERVED;
uint32_t TASKS_STARTTX; /*!< (@ 0x00000008) Start TWI transmit sequence */
uint32_t RESERVED1[2];
uint32_t TASKS_STOP; /*!< (@ 0x00000014) Stop TWI transaction. Must be issued while the
TWI master is not suspended. */
uint32_t RESERVED2;
uint32_t TASKS_SUSPEND; /*!< (@ 0x0000001C) Suspend TWI transaction */
uint32_t TASKS_RESUME; /*!< (@ 0x00000020) Resume TWI transaction */
uint32_t RESERVED3[56];
uint32_t EVENTS_STOPPED; /*!< (@ 0x00000104) TWI stopped */
uint32_t RESERVED4[7];
uint32_t EVENTS_ERROR; /*!< (@ 0x00000124) TWI error */
uint32_t RESERVED5[8];
uint32_t EVENTS_SUSPENDED; /*!< (@ 0x00000148) Last byte has been sent out after the SUSPEND
task has been issued, TWI traffic is now
suspended. */
uint32_t EVENTS_RXSTARTED; /*!< (@ 0x0000014C) Receive sequence started */
uint32_t EVENTS_TXSTARTED; /*!< (@ 0x00000150) Transmit sequence started */
uint32_t RESERVED6[2];
uint32_t EVENTS_LASTRX; /*!< (@ 0x0000015C) Byte boundary, starting to receive the last byte */
uint32_t EVENTS_LASTTX; /*!< (@ 0x00000160) Byte boundary, starting to transmit the last
byte */
uint32_t RESERVED7[39];
uint32_t SHORTS; /*!< (@ 0x00000200) Shortcut register */
uint32_t RESERVED8[63];
uint32_t INTEN; /*!< (@ 0x00000300) Enable or disable interrupt */
uint32_t INTENSET; /*!< (@ 0x00000304) Enable interrupt */
uint32_t INTENCLR; /*!< (@ 0x00000308) Disable interrupt */
uint32_t RESERVED9[110];
uint32_t ERRORSRC; /*!< (@ 0x000004C4) Error source */
uint32_t RESERVED10[14];
uint32_t ENABLE; /*!< (@ 0x00000500) Enable TWIM */
uint32_t RESERVED11;
/// TWIM_PSEL_Type PSEL; /*!< (@ 0x00000508) Unspecified */
uint32_t RESERVED12[5];
uint32_t FREQUENCY; /*!< (@ 0x00000524) TWI frequency */
uint32_t RESERVED13[3];
// TWIM_RXD_Type RXD; /*!< (@ 0x00000534) RXD EasyDMA channel */
// TWIM_TXD_Type TXD; /*!< (@ 0x00000544) TXD EasyDMA channel */
uint32_t RESERVED14[13];
uint32_t ADDRESS; /*!< (@ 0x00000588) Address used in the TWI transfer */
}; /*!< Size = 1420 (0x58c) */
#endif // NRFX_TWI_H__

@ -0,0 +1,33 @@
#include "hal/nrf_gpio.h"
#include "drivers/PinMap.h"
#include <SDL2/SDL.h>
#include <stdexcept>
void nrf_gpio_cfg_default(uint32_t pin_number) {}
void nrf_gpio_pin_set(uint32_t pin_number) {}
uint32_t nrf_gpio_pin_read(uint32_t pin_number)
{
if (pin_number == Pinetime::PinMap::Button) {
int x, y;
uint32_t buttons = SDL_GetMouseState(&x, &y);
bool right_click = (buttons & SDL_BUTTON_RMASK) != 0;
return right_click;
}
throw std::runtime_error("nrf_gpio_pin_read: unhandled pin_number: " + std::to_string(pin_number));
return 0;
}
void nrf_gpio_cfg_output(uint32_t pin_number) {}
void nrf_gpio_pin_clear(uint32_t pin_number) {}
void nrf_gpio_range_cfg_input(uint32_t pin_range_start,
uint32_t pin_range_end,
nrf_gpio_pin_pull_t pull_config) {}
void nrf_gpio_cfg_input(uint32_t pin_number, nrf_gpio_pin_pull_t pull_config) {}
void nrfx_gpiote_in_init(uint32_t pin_number, nrfx_gpiote_in_config_t *config, nrfx_gpiote_event_handler_t) {}
void nrfx_gpiote_in_event_enable(uint32_t pin_number, bool enable) {}
void nrf_gpio_cfg_sense_input(uint32_t pin_number, nrf_gpio_pin_pull_t pin_pull, nrf_gpio_pin_sense_t sense) {}
void APP_GPIOTE_INIT(uint32_t max_users) {}

@ -0,0 +1,150 @@
/**
* Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_GPIO_H__
#define NRF_GPIO_H__
#include <cstdint>
/**
* @brief Enumerator used for selecting the pin to be pulled down or up at the time of pin configuration.
*/
// lv_sim: values copied from nrf52810_bifiellds.h
enum nrf_gpio_pin_pull_t
{
NRF_GPIO_PIN_NOPULL = 0, // GPIO_PIN_CNF_PULL_Disabled, ///< Pin pull-up resistor disabled.
NRF_GPIO_PIN_PULLDOWN = 1, // GPIO_PIN_CNF_PULL_Pulldown, ///< Pin pull-down resistor enabled.
NRF_GPIO_PIN_PULLUP = 3, // GPIO_PIN_CNF_PULL_Pullup, ///< Pin pull-up resistor enabled.
};
constexpr uint16_t GPIO_PIN_CNF_PULL_Pulldown = 1;
constexpr uint16_t GPIO_PIN_CNF_PULL_Pullup = 3;
using nrf_gpiote_polarity_t = uint32_t;
constexpr uint16_t NRF_GPIOTE_POLARITY_HITOLO = 2;
constexpr uint16_t NRF_GPIOTE_POLARITY_TOGGLE = 3;
using nrfx_gpiote_pin_t = uint32_t;
typedef void (*nrfx_gpiote_event_handler_t)(nrfx_gpiote_pin_t, nrf_gpiote_polarity_t);
using nrf_gpio_pin_sense_t = uint32_t;
constexpr uint16_t GPIO_PIN_CNF_SENSE_Low = 3;
struct nrfx_gpiote_in_config_t {
bool skip_gpio_setup = false;
bool hi_accuracy = false;
bool is_watcher = false;
nrf_gpiote_polarity_t sense;
nrf_gpio_pin_pull_t pull;
};
/**
* @brief Function for resetting pin configuration to its default state.
*
* @param pin_number Specifies the pin number.
*/
void nrf_gpio_cfg_default(uint32_t pin_number);
/**
* @brief Function for setting a GPIO pin.
*
* Note that the pin must be configured as an output for this function to have any effect.
*
* @param pin_number Specifies the pin number to set.
*/
void nrf_gpio_pin_set(uint32_t pin_number);
// read pin stub, intended to forward right mouse button as PinMap::Button
uint32_t nrf_gpio_pin_read(uint32_t pin_number);
/**
* @brief Function for configuring the given GPIO pin number as output, hiding inner details.
* This function can be used to configure a pin as simple output with gate driving GPIO_PIN_CNF_DRIVE_S0S1 (normal cases).
*
* @param pin_number Specifies the pin number.
*
* @note Sense capability on the pin is disabled and input is disconnected from the buffer as the pins are configured as output.
*/
void nrf_gpio_cfg_output(uint32_t pin_number);
/**
* @brief Function for clearing a GPIO pin.
*
* Note that the pin must be configured as an output for this
* function to have any effect.
*
* @param pin_number Specifies the pin number to clear.
*/
void nrf_gpio_pin_clear(uint32_t pin_number);
/**
* @brief Function for configuring the GPIO pin range as input pins with given initial value set, hiding inner details.
* This function can be used to configure pin range as simple input.
*
* @param pin_range_start Specifies the start number (inclusive) in the range of pin numbers to be configured (allowed values 0-30).
*
* @param pin_range_end Specifies the end number (inclusive) in the range of pin numbers to be configured (allowed values 0-30).
*
* @param pull_config State of the pin range pull resistor (no pull, pulled down, or pulled high).
*
* @note For configuring only one pin as input, use @ref nrf_gpio_cfg_input.
* Sense capability on the pin is disabled and input is connected to buffer so that the GPIO->IN register is readable.
*/
void nrf_gpio_range_cfg_input(uint32_t pin_range_start,
uint32_t pin_range_end,
nrf_gpio_pin_pull_t pull_config);
/**
* @brief Function for configuring the given GPIO pin number as input, hiding inner details.
* This function can be used to configure a pin as simple input.
*
* @param pin_number Specifies the pin number.
* @param pull_config State of the pin range pull resistor (no pull, pulled down, or pulled high).
*
* @note Sense capability on the pin is disabled and input is connected to buffer so that the GPIO->IN register is readable.
*/
void nrf_gpio_cfg_input(uint32_t pin_number, nrf_gpio_pin_pull_t pull_config);
void nrfx_gpiote_in_init(uint32_t pin_number, nrfx_gpiote_in_config_t *config, nrfx_gpiote_event_handler_t);
void nrfx_gpiote_in_event_enable(uint32_t pin_number, bool enable);
void nrf_gpio_cfg_sense_input(uint32_t pin_number, nrf_gpio_pin_pull_t pin_pull, nrf_gpio_pin_sense_t sense);
void APP_GPIOTE_INIT(uint32_t max_users);
#endif // NRF_GPIO_H__

@ -0,0 +1,15 @@
#include "hal/nrf_rtc.h"
#include "task.h"
#include <chrono>
#include <stdexcept>
uint32_t nrf_rtc_counter_get(NRF_RTC_Type p_reg)
{
if (p_reg == portNRF_RTC_REG) {
TickType_t ticks_now = xTaskGetTickCount();
return ticks_now;
}
throw std::runtime_error("nrf_rtc_counter_get: unhandled NRF_RTC_Type: " + std::to_string(p_reg));
return 0;
}

@ -0,0 +1,7 @@
#pragma once
#include "portmacro_cmsis.h"
// normal version has pointer to register 'NRF_RTC_Type * p_reg', but we just simulate
// the return value according to known register pointer
uint32_t nrf_rtc_counter_get(NRF_RTC_Type p_reg);

@ -0,0 +1,3 @@
#pragma once
#include <hal/nrf_gpio.h>

@ -0,0 +1,54 @@
/**
* Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup nrf_log Logger module
* @{
* @ingroup app_common
*
* @brief The nrfx_log module interface.
*/
#ifndef NRFX_LOG_H_
#define NRFX_LOG_H_
#include "libraries/log/nrf_log.h"
#endif // NRFX_LOG_H_

@ -0,0 +1,3 @@
#include "portmacro_cmsis.h"
void portYIELD_FROM_ISR(BaseType_t) {}

@ -0,0 +1,49 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef PORTMACRO_CMSIS_H
#define PORTMACRO_CMSIS_H
#include <cstdint>
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
#define pdFALSE ( ( BaseType_t ) 0 )
#define pdTRUE ( ( BaseType_t ) 1 )
#define pdPASS pdTRUE
/* RTC register */
using NRF_RTC_Type = uint32_t;
constexpr NRF_RTC_Type portNRF_RTC_REG = 1;
void portYIELD_FROM_ISR(BaseType_t);
#endif /* PORTMACRO_CMSIS_H */

@ -0,0 +1,46 @@
#include "queue.h"
#include <stdexcept>
#include <SDL2/SDL.h>
QueueHandle_t xQueueCreate(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize)
{
QueueHandle_t xQueue;
if (uxItemSize != 1) {
throw std::runtime_error("uxItemSize must be 1");
}
xQueue.queue.reserve(uxQueueLength);
return xQueue;
}
BaseType_t xQueueSend(QueueHandle_t &xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait)
{
std::lock_guard<std::mutex> guard(xQueue.mutex);
xQueue.queue.push_back(*reinterpret_cast<const uint8_t *const>(pvItemToQueue));
return true;
}
BaseType_t xQueueSendFromISR(QueueHandle_t &xQueue, const void * const pvItemToQueue, BaseType_t *xHigherPriorityTaskWoken)
{
TickType_t xTicksToWait = 0;
*xHigherPriorityTaskWoken = pdFALSE;
return xQueueSend(xQueue, pvItemToQueue, 0.0);
}
BaseType_t xQueueReceive(QueueHandle_t &xQueue, void * const pvBuffer, TickType_t xTicksToWait)
{
while (xQueue.queue.empty()) {
if (xTicksToWait <= 25) {
return false;
}
SDL_Delay(25);
xTicksToWait -= 25;
}
if (xQueue.queue.empty()) {
return false;
}
std::lock_guard<std::mutex> guard(xQueue.mutex);
uint8_t *buf = reinterpret_cast<uint8_t * const>(pvBuffer);
*buf = xQueue.queue.at(0);
xQueue.queue.erase(xQueue.queue.begin());
return true;
}

@ -0,0 +1,31 @@
#pragma once
#include "portmacro_cmsis.h"
#include <mutex>
#include <vector>
/**
* Type by which queues are referenced. For example, a call to xQueueCreate()
* returns an QueueHandle_t variable that can then be used as a parameter to
* xQueueSend(), xQueueReceive(), etc.
*/
//typedef void * QueueHandle_t;
struct QueueHandle_t {
std::mutex mutex;
std::vector<uint8_t> queue;
QueueHandle_t() {}
QueueHandle_t(const QueueHandle_t &o) {
queue=o.queue;
}
QueueHandle_t &operator=(const QueueHandle_t &o) {
queue=o.queue;
return *this;
}
};
//using QueueHandle_t = std::vector<uint8_t>;
QueueHandle_t xQueueCreate(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize);
BaseType_t xQueueSend(QueueHandle_t &xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait);
BaseType_t xQueueSendFromISR(QueueHandle_t &xQueue, const void * const pvItemToQueue, BaseType_t *xHigherPriorityTaskWoken);
BaseType_t xQueueReceive(QueueHandle_t &xQueue, void * const pvBuffer, TickType_t xTicksToWait );

@ -0,0 +1,87 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#include <SDL2/SDL.h>
/* Standard includes. */
#include <chrono>
/* FreeRTOS includes. */
//#include "FreeRTOS.h"
#include "task.h"
//#include "timers.h"
//#include "stack_macros.h"
TickType_t xTaskGetTickCount()
{
static auto start = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::duration<TickType_t, std::ratio<1, configTICK_RATE_HZ>>>(now - start);
return diff.count();
}
/*-----------------------------------------------------------*/
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime) {
return 0;
}
void vTaskDelay( const TickType_t xTicksToDelay ) {
SDL_Delay(xTicksToDelay);
}
int sdl_function_wrapper(void *instance)
{
TaskHandle_t * task_handle = static_cast<TaskHandle_t*>(instance);
task_handle->task_fn(task_handle->instance);
return 0;
}
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
pxCreatedTask->task_fn = pxTaskCode;
pxCreatedTask->instance = pvParameters;
pxCreatedTask->thread_handle = SDL_CreateThread(sdl_function_wrapper, pcName, pxCreatedTask);
return pxCreatedTask->thread_handle != nullptr;
}
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify) {
return pdPASS;
}
TaskHandle_t xTaskGetCurrentTaskHandle() {
return {};
}
BaseType_t xTaskGetSchedulerState() {
return taskSCHEDULER_NOT_STARTED;
}

@ -0,0 +1,326 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef INC_TASK_H
#define INC_TASK_H
#include "portmacro_cmsis.h"
#include <cstdint>
// copied from InfiniTime/src/FreeRTOSConfig.h
#define configTICK_RATE_HZ 1024
#define configSTACK_DEPTH_TYPE uint16_t
/*-----------------------------------------------------------
* TASK UTILITIES
*----------------------------------------------------------*/
/**
* task. h
* <PRE>TickType_t xTaskGetTickCount( void );</PRE>
*
* @return The count of ticks since vTaskStartScheduler was called.
*
* \defgroup xTaskGetTickCount xTaskGetTickCount
* \ingroup TaskUtils
*/
TickType_t xTaskGetTickCount();
typedef void (*TaskFunction_t)(void *instance);
/**
* task. h
*
* Type by which tasks are referenced. For example, a call to xTaskCreate
* returns (via a pointer parameter) an TaskHandle_t variable that can then
* be used as a parameter to vTaskDelete to delete the task.
*
* \defgroup TaskHandle_t TaskHandle_t
* \ingroup Tasks
*/
//typedef void * TaskHandle_t;
struct TaskHandle_t {
void *thread_handle = nullptr;
TaskFunction_t task_fn;
void *instance = nullptr;
};
/* Task states returned by eTaskGetState. */
enum eTaskState
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
};
/* Actions that can be performed when vTaskNotify() is called. */
enum eNotifyAction
{
eNoAction = 0, /* Notify the task without updating its notify value. */
eSetBits, /* Set bits in the task's notification value. */
eIncrement, /* Increment the task's notification value. */
eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */
eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */
};
/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is
0 to generate more optimal code when configASSERT() is defined as the constant
is used in assert() statements. */
#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 )
#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 )
#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 )
/* Used with the uxTaskGetSystemState() function to return the state of each task
in the system. */
struct TaskStatus_t {
TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */
const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
UBaseType_t xTaskNumber; /* A number unique to the task. */
eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */
UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */
UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
//StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */
uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */
};
/**
* configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for
* uxTaskGetSystemState() to be available.
*
* uxTaskGetSystemState() populates an TaskStatus_t structure for each task in
* the system. TaskStatus_t structures contain, among other things, members
* for the task handle, task name, task priority, task state, and total amount
* of run time consumed by the task. See the TaskStatus_t structure
* definition in this file for the full member list.
*
* NOTE: This function is intended for debugging use only as its use results in
* the scheduler remaining suspended for an extended period.
*
* @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures.
* The array must contain at least one TaskStatus_t structure for each task
* that is under the control of the RTOS. The number of tasks under the control
* of the RTOS can be determined using the uxTaskGetNumberOfTasks() API function.
*
* @param uxArraySize The size of the array pointed to by the pxTaskStatusArray
* parameter. The size is specified as the number of indexes in the array, or
* the number of TaskStatus_t structures contained in the array, not by the
* number of bytes in the array.
*
* @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in
* FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to the
* total run time (as defined by the run time stats clock, see
* http://www.freertos.org/rtos-run-time-stats.html) since the target booted.
* pulTotalRunTime can be set to NULL to omit the total run time information.
*
* @return The number of TaskStatus_t structures that were populated by
* uxTaskGetSystemState(). This should equal the number returned by the
* uxTaskGetNumberOfTasks() API function, but will be zero if the value passed
* in the uxArraySize parameter was too small.
*/
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime);
/**
* task. h
* <pre>void vTaskDelay( const TickType_t xTicksToDelay );</pre>
*
* Delay a task for a given number of ticks. The actual time that the
* task remains blocked depends on the tick rate. The constant
* portTICK_PERIOD_MS can be used to calculate real time from the tick
* rate - with the resolution of one tick period.
*
* INCLUDE_vTaskDelay must be defined as 1 for this function to be available.
* See the configuration section for more information.
*
*
* vTaskDelay() specifies a time at which the task wishes to unblock relative to
* the time at which vTaskDelay() is called. For example, specifying a block
* period of 100 ticks will cause the task to unblock 100 ticks after
* vTaskDelay() is called. vTaskDelay() does not therefore provide a good method
* of controlling the frequency of a periodic task as the path taken through the
* code, as well as other task and interrupt activity, will effect the frequency
* at which vTaskDelay() gets called and therefore the time at which the task
* next executes. See vTaskDelayUntil() for an alternative API function designed
* to facilitate fixed frequency execution. It does this by specifying an
* absolute time (rather than a relative time) at which the calling task should
* unblock.
*
* @param xTicksToDelay The amount of time, in tick periods, that
* the calling task should block.
*
* Example usage:
void vTaskFunction( void * pvParameters )
{
// Block for 500ms.
const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
for( ;; )
{
// Simply toggle the LED every 500ms, blocking between each toggle.
vToggleLED();
vTaskDelay( xDelay );
}
}
* \defgroup vTaskDelay vTaskDelay
* \ingroup TaskCtrl
*/
void vTaskDelay( const TickType_t xTicksToDelay );
/**
* task. h
*<pre>
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pvCreatedTask
);</pre>
*
* Create a new task and add it to the list of tasks that are ready to run.
*
* Internally, within the FreeRTOS implementation, tasks use two blocks of
* memory. The first block is used to hold the task's data structures. The
* second block is used by the task as its stack. If a task is created using
* xTaskCreate() then both blocks of memory are automatically dynamically
* allocated inside the xTaskCreate() function. (see
* http://www.freertos.org/a00111.html). If a task is created using
* xTaskCreateStatic() then the application writer must provide the required
* memory. xTaskCreateStatic() therefore allows a task to be created without
* using any dynamic memory allocation.
*
* See xTaskCreateStatic() for a version that does not use any dynamic memory
* allocation.
*
* xTaskCreate() can only be used to create a task that has unrestricted
* access to the entire microcontroller memory map. Systems that include MPU
* support can alternatively create an MPU constrained task using
* xTaskCreateRestricted().
*
* @param pvTaskCode Pointer to the task entry function. Tasks
* must be implemented to never return (i.e. continuous loop).
*
* @param pcName A descriptive name for the task. This is mainly used to
* facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default
* is 16.
*
* @param usStackDepth The size of the task stack specified as the number of
* variables the stack can hold - not the number of bytes. For example, if
* the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes
* will be allocated for stack storage.
*
* @param pvParameters Pointer that will be used as the parameter for the task
* being created.
*
* @param uxPriority The priority at which the task should run. Systems that
* include MPU support can optionally create tasks in a privileged (system)
* mode by setting bit portPRIVILEGE_BIT of the priority parameter. For
* example, to create a privileged task at priority 2 the uxPriority parameter
* should be set to ( 2 | portPRIVILEGE_BIT ).
*
* @param pvCreatedTask Used to pass back a handle by which the created task
* can be referenced.
*
* @return pdPASS if the task was successfully created and added to a ready
* list, otherwise an error code defined in the file projdefs.h
*/
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask );
/**
* task. h
* <PRE>BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );</PRE>
*
* configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro
* to be available.
*
* When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private
* "notification value", which is a 32-bit unsigned integer (uint32_t).
*
* Events can be sent to a task using an intermediary object. Examples of such
* objects are queues, semaphores, mutexes and event groups. Task notifications
* are a method of sending an event directly to a task without the need for such
* an intermediary object.
*
* A notification sent to a task can optionally perform an action, such as
* update, overwrite or increment the task's notification value. In that way
* task notifications can be used to send data to a task, or be used as light
* weight and fast binary or counting semaphores.
*
* xTaskNotifyGive() is a helper macro intended for use when task notifications
* are used as light weight and faster binary or counting semaphore equivalents.
* Actual FreeRTOS semaphores are given using the xSemaphoreGive() API function,
* the equivalent action that instead uses a task notification is
* xTaskNotifyGive().
*
* When task notifications are being used as a binary or counting semaphore
* equivalent then the task being notified should wait for the notification
* using the ulTaskNotificationTake() API function rather than the
* xTaskNotifyWait() API function.
*
* See http://www.FreeRTOS.org/RTOS-task-notifications.html for more details.
*
* @param xTaskToNotify The handle of the task being notified. The handle to a
* task can be returned from the xTaskCreate() API function used to create the
* task, and the handle of the currently running task can be obtained by calling
* xTaskGetCurrentTaskHandle().
*
* @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the
* eAction parameter set to eIncrement - so pdPASS is always returned.
*
* \defgroup xTaskNotifyGive xTaskNotifyGive
* \ingroup TaskNotifications
*/
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify);
//#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL )
//BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue );
/*
* Return the handle of the calling task.
*/
TaskHandle_t xTaskGetCurrentTaskHandle();
/*
* Returns the scheduler state as taskSCHEDULER_RUNNING,
* taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED.
*/
BaseType_t xTaskGetSchedulerState();
#endif /* INC_TASK_H */

@ -0,0 +1,69 @@
#include "timers.h"
#include <stdexcept>
uint32_t timer_callback_wrapper(uint32_t interval, void *param) {
TimerHandle_t *xTimer = static_cast<TimerHandle_t*>(param);
if (!xTimer->running) {
return 0;
}
xTimer->pxCallbackFunction(*xTimer);
if (xTimer->auto_reload) {
return xTimer->xTimerPeriodInTicks;
}
xTimer->running = false;
return 0; // cancel timer
}
void *pvTimerGetTimerID(const TimerHandle_t &xTimer) { // return pvTimerID from xTimerCreate
return xTimer.pvTimerID;
}
void vTimerSetTimerID(TimerHandle_t &xTimer, void *pvNewID) {
xTimer.pvTimerID = pvNewID;
}
TimerHandle_t xTimerCreate(const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload, // false=one-shot, true=recurring
void * const pvTimerID, // pointer passed to callback
TimerCallbackFunction_t pxCallbackFunction)
{
TimerHandle_t xTimer;
xTimer.xTimerPeriodInTicks = xTimerPeriodInTicks;
xTimer.auto_reload = uxAutoReload == pdTRUE;
xTimer.timer_name = pcTimerName;
xTimer.pvTimerID = pvTimerID;
xTimer.pxCallbackFunction = pxCallbackFunction;
return xTimer;
}
bool xTimerStart(TimerHandle_t &xTimer, TickType_t xTicksToWait) {
xTimer.running = true;
xTimer.timer_id = SDL_AddTimer(xTimer.xTimerPeriodInTicks, timer_callback_wrapper, &xTimer);
if (xTimer.pxCallbackFunction == nullptr) {
throw std::runtime_error("xTimerStart called before xTimerCreate");
}
return xTimer.timer_id != 0;
}
bool xTimerChangePeriod(TimerHandle_t &xTimer, TickType_t xNewPeriod, TickType_t xTicksToWait) {
if (xTimer.running) {
xTimerStop(xTimer, xTicksToWait);
xTimer.xTimerPeriodInTicks = xNewPeriod;
xTimerStart(xTimer, xTicksToWait);
} else {
xTimer.xTimerPeriodInTicks = xNewPeriod;
}
return true;
}
bool xTimerReset(TimerHandle_t &xTimer, TickType_t xTicksToWait) {
if (xTimer.running) {
xTimerStop(xTimer, xTicksToWait);
}
return xTimerStart(xTimer, xTicksToWait);
}
bool xTimerStop(TimerHandle_t &xTimer, TickType_t xTicksToWait) {
xTimer.running = false;
return SDL_RemoveTimer(xTimer.timer_id);
}

@ -0,0 +1,150 @@
/*
* FreeRTOS Kernel V10.0.0
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. If you wish to use our Amazon
* FreeRTOS name, please do so in a fair use way that does not cause confusion.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#pragma once
#include "portmacro_cmsis.h" // TickType_t
#include <SDL2/SDL.h>
#include <chrono>
#include <cstdint>
#include <string>
class TimerHandle_t;
/*
* Defines the prototype to which timer callback functions must conform.
*/
typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer );
struct TimerHandle_t {
bool running = false;
bool auto_reload = false;
SDL_TimerID timer_id = 0;
TickType_t xTimerPeriodInTicks;
std::string timer_name;
void * pvTimerID;
TimerCallbackFunction_t pxCallbackFunction;
};
constexpr uint32_t pdMS_TO_TICKS(uint32_t ticks) {
return ticks;
}
/**
* void *pvTimerGetTimerID( TimerHandle_t xTimer );
*
* Returns the ID assigned to the timer.
*
* IDs are assigned to timers using the pvTimerID parameter of the call to
* xTimerCreated() that was used to create the timer, and by calling the
* vTimerSetTimerID() API function.
*
* If the same callback function is assigned to multiple timers then the timer
* ID can be used as time specific (timer local) storage.
*
* @param xTimer The timer being queried.
*
* @return The ID assigned to the timer being queried.
*
* Example usage:
*
* See the xTimerCreate() API function example usage scenario.
*/
void *pvTimerGetTimerID(const TimerHandle_t &xTimer ); // return pvTimerID from xTimerCreate
/**
* void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );
*
* Sets the ID assigned to the timer.
*
* IDs are assigned to timers using the pvTimerID parameter of the call to
* xTimerCreated() that was used to create the timer.
*
* If the same callback function is assigned to multiple timers then the timer
* ID can be used as time specific (timer local) storage.
*
* @param xTimer The timer being updated.
*
* @param pvNewID The ID to assign to the timer.
*
* Example usage:
*
* See the xTimerCreate() API function example usage scenario.
*/
void vTimerSetTimerID(TimerHandle_t &xTimer, void *pvNewID);
TimerHandle_t xTimerCreate(const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload, // false=one-shot, true=recurring
void * const pvTimerID, // pointer passed to callback
TimerCallbackFunction_t pxCallbackFunction);
/**
* @param xTicksToWait Specifies the time, in ticks, that the calling task should
* be held in the Blocked state to wait for the stop command to be successfully
* sent to the timer command queue, should the queue already be full when
* xTimerStop() was called. xTicksToWait is ignored if xTimerStop() is called
* before the scheduler is started.
*/
bool xTimerStart(TimerHandle_t &xTimer, TickType_t xTicksToWait);
/*
* xTimerChangePeriod() changes the period of a timer that was previously
* created using the xTimerCreate() API function.
*
* xTimerChangePeriod() can be called to change the period of an active or
* dormant state timer.
*
* The configUSE_TIMERS configuration constant must be set to 1 for
* xTimerChangePeriod() to be available.
*/
bool xTimerChangePeriod(TimerHandle_t &xTimer, TickType_t xNewPeriod, TickType_t xTicksToWait);
/**
* xTimerReset() re-starts a timer that was previously created using the
* xTimerCreate() API function. If the timer had already been started and was
* already in the active state, then xTimerReset() will cause the timer to
* re-evaluate its expiry time so that it is relative to when xTimerReset() was
* called. If the timer was in the dormant state then xTimerReset() has
* equivalent functionality to the xTimerStart() API function.
*
* Resetting a timer ensures the timer is in the active state. If the timer
* is not stopped, deleted, or reset in the mean time, the callback function
* associated with the timer will get called 'n' ticks after xTimerReset() was
* called, where 'n' is the timers defined period.
*
* It is valid to call xTimerReset() before the scheduler has been started, but
* when this is done the timer will not actually start until the scheduler is
* started, and the timers expiry time will be relative to when the scheduler is
* started, not relative to when xTimerReset() was called.
*/
bool xTimerReset(TimerHandle_t &xTimer, TickType_t xTicksToWait);
bool xTimerStop(TimerHandle_t &xTimer, TickType_t xTicksToWait);