Merge branch 'JF002:develop' into improve-battery-percentage-to-battery-icon-mapping

This commit is contained in:
hassless 2021-06-18 17:17:26 +02:00 committed by GitHub
commit 84a6c88e98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 961 additions and 481 deletions

View file

@ -62,7 +62,7 @@ As of now, here is the list of achievements of this project:
* Firmware validation
* System information
- Supported by 3 companion apps (development is in progress):
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android)
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android via F-Droid)
* [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS and Linux)
* [Siglo](https://github.com/alexr4535/siglo) (on Linux)
* **[Experimental]** [WebBLEWatch](https://hubmartin.github.io/WebBLEWatch/) Synchronize time directly from your web browser. [video](https://youtu.be/IakiuhVDdrY)

View file

@ -1,7 +1,274 @@
# Memory analysis
The PineTime is equipped with the following memories:
- The internal RAM : **64KB**
- The internal Flash : **512KB**
- The external (SPI) Flash : **4MB**
Note that the NRF52832 cannot execute code stored in the external flash : we need to store the whole firmware in the internal flash memory, and use the external one to store graphicals assets, fonts...
This document describes how the RAM and Flash memories are used in InfiniTime and how to analyze and monitor their usage. It was written in the context of [this memory analysis effort](https://github.com/JF002/InfiniTime/issues/313).
## Code sections
A binary is composed of multiple sections. Most of the time, these sections are : .text, .rodata, .data and .bss but more sections can be defined in the linker script.
Here is a small description of these sections and where they end up in memory:
- **TEXT** = code (FLASH)
- **RODATA** = constants (FLASH)
- **DATA** = initialized variables (FLASH + RAM)
- **BSS** = uninitialized variables (RAM)
## Internal FLASH
The internal flash memory stores the whole firmware: code, variable that are not default-initialized, constants...
The content of the flash memory can be easily analyzed thanks to the MAP file generated by the compiler. This file lists all the symbols from the program along with their size and location (section and addresses) in RAM and FLASH.
![Map file](./memoryAnalysis/mapfile.png)
As you can see on the picture above, this file contains a lot of information and is not easily readable by a human being. Fortunately, you can easily find tools that parse and display the content of the MAP file in a more understandable way.
In this analysis, I used [Linkermapviz](https://github.com/PromyLOPh/linkermapviz).
### Linkermapviz
[Linkermapviz](https://github.com/PromyLOPh/linkermapviz) parses the MAP file and displays its content in a graphical way into an HTML page:
![linkermapviz](./memoryAnalysis/linkermapviz.png)
Using this tool, you can easily see the size of each symbol relative to the other one, and check what is using most of the space,...
Also, as Linkermapviz is written in Python, you can easily modify it to adapt it to your firmware, export data in another format,... For example, [I modified it to parse the contents of the MAP file and export it in a CSV file](https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620). I could later on open this file in LibreOffice Calc and use sort/filter functionality to search for specific symbols in specific files...
### Puncover
[Puncover](https://github.com/HBehrens/puncover) is another useful tools that analyses the binary file generated by the compiler (the .out file that contains all debug information). It provides valuable information about the symbols (data and code): name, position, size, max stack of each functions, callers, callees...
![Puncover](./memoryAnalysis/puncover.png)
Puncover is really easy to install:
- clone the repo and cd into the cloned directory
- setup a venv
- `python -m virtualenv venv`
- `source venv/bin/activate`
- Install : `pip install .`
- Run : `puncover --gcc_tools_base=/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi- --elf_file /path/to/build/directory/src/pinetime-app-1.1.0.out --src_root /path/to/sources --build_dir /path/to/build/directory`
- Replace
* `/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin` with the path to your gcc-arm-none-eabi toolchain
* `/path/to/build/directory/src/pinetime-app-1.1.0.out` with the path to the binary generated by GCC (.out file)
* `/path/to/sources` with the path to the root folder of the sources (checkout directory)
* `/path/to/build/directory` with the path to the build directory
- Launch a browser at http://localhost:5000/
### Analysis
Using the MAP file and tools, we can easily see what symbols are using most of the FLASH memory space. In this case, with no surprise, fonts and graphics are the biggest flash space consumer.
![Puncover](./memoryAnalysis/puncover-all-symbols.png)
This way, you can easily check what needs to be optimized : we should find a way to store big static data (like fonts and graphics) in the external flash memory, for example.
It's always a good idea to check the flash memory space when working on the project : this way, you can easily check that your developments are using a reasonable amount of space.
### Links
- Analysis with linkermapviz : https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620
- Analysis with Puncover : https://github.com/JF002/InfiniTime/issues/313#issuecomment-847311392
## RAM
RAM memory contains all the data that can be modified at run-time: variables, stack, heap...
### Data
RAM memory can be *statically* allocated, meaning that the size and position of the data are known at compile-time:
You can easily analyze the memory used by variables declared in the global scope using the MAP. You'll find them in the .BSS or .DATA sections. Linkermapviz and Puncover can be used to analyze their memory usage.
Variables declared in the scope of a function will be allocated on the stack. It means that the stack usage will vary according to the state of the program, and cannot be easily analyzed at compile time.
```
uint8_t buffer[1024]
int main() {
int a;
}
```
#### Analysis
In Infinitime 1.1, the biggest buffers are the buffers allocated for LVGL (14KB) and the one for FreeRTOS (16KB). Nimble also allocated 9KB of RAM.
### Stack
The stack will be used for everything except tasks, which have their own stack allocated by FreeRTOS. The stack is 8192B and is allocated in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L148).
An easy way to monitor its usage is by filling the section with a known pattern at boot time, then use the firmware and dump the memory. You can then check the maximum stack usage by checking the address from the beginning of the stack that were overwritten.
#### Fill the stack section by a known pattern:
Edit <NRFSDK>/modules/nrfx/mdk/gcc_startup_nrf52.S and add the following code after the copy of the data from read only memory to RAM at around line 243:
```
/* Loop to copy data from read only memory to RAM.
* The ranges of copy from/to are specified by following symbols:
* __etext: LMA of start of the section to copy from. Usually end of text
* __data_start__: VMA of start of the section to copy to.
* __bss_start__: VMA of end of the section to copy to. Normally __data_end__ is used, but by using __bss_start__
* the user can add their own initialized data section before BSS section with the INTERT AFTER command.
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__etext
ldr r2, =__data_start__
ldr r3, =__bss_start__
subs r3, r3, r2
ble .L_loop1_done
.L_loop1:
subs r3, r3, #4
ldr r0, [r1,r3]
str r0, [r2,r3]
bgt .L_loop1
.L_loop1_done:
/* Add this code to fill the stack section with 0xFFEEDDBB */
ldr r0, =__StackLimit
ldr r1, =8192
ldr r2, =0xFFEEDDBB
.L_fill:
str r2, [r0]
adds r0, 4
subs r1, 4
bne .L_fill
/* -- */
```
#### Dump RAM memory and check usage
Dumping the content of the ram is easy using JLink debugger and `nrfjprog`:
```
nrfjprog --readram ram.bin
```
You can then display the file using objdump:
```
hexdump ram.bin -v | less
```
The stack is positionned at the end of the RAM -> 0xFFFF. Its size is 8192 bytes, so the end of the stack is at 0xE000.
On the following dump, the maximum stack usage is 520 bytes (0xFFFF - 0xFDF8):
```
000fdb0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fdc0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fdd0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fde0 ddbb ffee ddbb ffee ddbb ffee ddbb ffee
000fdf0 ddbb ffee ddbb ffee ffff ffff c24b 0003
000fe00 ffff ffff ffff ffff ffff ffff 0000 0000
000fe10 0018 0000 0000 0000 0000 0000 fe58 2000
000fe20 0000 0000 0000 00ff ddbb 00ff 0018 0000
000fe30 929c 2000 0000 0000 0018 0000 0000 0000
000fe40 92c4 2000 0458 2000 0000 0000 80e7 0003
000fe50 0000 0000 8cd9 0003 ddbb ffee ddbb ffee
000fe60 00dc 2000 92c4 2000 0005 0000 929c 2000
000fe70 007f 0000 feb0 2000 92c4 2000 feb8 2000
000fe80 ddbb ffee 0005 0000 929c 2000 0000 0000
000fe90 aca0 2000 0000 0000 0028 0000 418b 0005
000fea0 02f4 2000 001f 0000 0000 0000 0013 0000
000feb0 b5a8 2000 2199 0005 b5a8 2000 2201 0005
000fec0 b5a8 2000 001e 0000 0000 0000 0013 0000
000fed0 b5b0 2000 0fe0 0006 b5a8 2000 0000 0000
000fee0 0013 0000 2319 0005 0013 0000 0000 0000
000fef0 0000 0000 3b1c 2000 3b1c 2000 d0e3 0000
000ff00 4b70 2000 54ac 2000 4b70 2000 ffff ffff
000ff10 0000 0000 1379 0003 6578 2000 0d75 0003
000ff20 6578 2000 ffff ffff 0000 0000 1379 0003
000ff30 000c 0000 cfeb 0002 39a1 2000 a824 2000
000ff40 0015 0000 cfeb 0002 39a1 2000 a824 2000
000ff50 39a1 2000 0015 0000 001b 0000 b4b9 0002
000ff60 0000 0000 a9f4 2000 4b70 2000 0d75 0003
000ff70 4b70 2000 ffff ffff 0000 0000 1379 0003
000ff80 ed00 e000 a820 2000 1000 4001 7fc0 2000
000ff90 7f64 2000 75a7 0001 a884 2000 7b04 2000
000ffa0 a8c0 2000 0000 0000 0000 0000 0000 0000
000ffb0 7fc0 2000 7f64 2000 8024 2000 a5a5 a5a5
000ffc0 ed00 e000 3fd5 0001 0000 0000 72c0 2000
000ffd0 0000 0000 72e4 2000 3f65 0001 7f64 2000
000ffe0 0000 2001 0000 0000 ef30 e000 0010 0000
000fff0 7fc0 2000 4217 0001 3f0a 0001 0000 6100
```
#### Analysis
According to my experimentations, we don't use the stack that much, and 8192 bytes is probably way too big for InfiniTime!
#### Links
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-851035070
### Heap
The heap is declared in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L136) and its current size is 8192 bytes. The heap is used for dynamic memory allocation(`malloc()`, `new`...).
Heap monitoring is not easy, but it seems that we can use the following code to know the current usage of the heap:
```
auto m = mallinfo();
NRF_LOG_INFO("heap : %d", m.uordblks);
```
#### Analysis
According to my experimentation, InfiniTime uses ~6000bytes of heap most of the time. Except when the Navigation app is launched, where the heap usage increases to... more than 9500 bytes (meaning that the heap overflows and could potentially corrupt the stack!!!). This is a bug that should be fixed in #362.
To know exactly what's consuming heap memory, you can `wrap` functions like `malloc()` into your own functions. In this wrapper, you can add logging code or put breakpoints:
- Add ` -Wl,-wrap,malloc` to the cmake variable `LINK_FLAGS` of the target you want to debug (pinetime-app, most probably)
- Add the following code in `main.cpp`
```
extern "C" {
void *__real_malloc (size_t);
void* __wrap_malloc(size_t size) {
return __real_malloc(size);
}
}
```
Now, your function `__wrap_malloc()` will be called instead of `malloc()`. You can call the actual malloc from the stdlib by calling `__real_malloc()`.
Using this technique, I was able to trace all malloc calls at boot (boot -> digital watchface):
- system task = 3464 bytes (SystemTask could potentially be declared as a global variable to avoid heap allocation here)
- string music = 31 (maybe we should not use std::string when not needed, as it does heap allocation)
- ble_att_svr_start = 1720
- ble gatts start = 40 + 88
- ble ll task = 24
- display app = 104
- digital clock = 96 + 152
- hr task = 304
#### Links
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-851035625
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-1-calculating-stack-size/
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-2-properly-allocating-stacks/
- https://www.embedded.com/mastering-stack-and-heap-for-system-reliability-part-3-avoiding-heap-errors/
## LVGL
I did a deep analysis of the usage of the buffer dedicated for lvgl (managed by lv_mem).
This buffer is used by lvgl to allocated memory for drivers (display/touch), screens, themes, and all widgets created by the apps.
The usage of this buffer can be monitored using this code :
```
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
NRF_LOG_INFO("\t Free %d / %d -- max %d", mon.free_size, mon.total_size, mon.max_used);
```
The most interesting metric is `mon.max_used` which specifies the maximum number of bytes that were used from this buffer since the initialization of lvgl.
According to my measurements, initializing the theme, display/touch driver and screens cost **4752** bytes!
Then, initializing the digital clock face costs **1541 bytes**.
For example a simple lv_label needs **~140 bytes** of memory.
I tried to monitor this max value while going through all the apps of InfiniTime 1.1 : the max value I've seen is **5660 bytes**. It means that we could probably **reduce the size of the buffer from 14KB to 6 - 10 KB** (we have to take the fragmentation of the memory into account).
### Links
- https://github.com/JF002/InfiniTime/issues/313#issuecomment-850890064
## FreeRTOS heap and task stack
FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h*
FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes,...).
FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes...).
The function `xPortGetFreeHeapSize()` returns the amount of memory available in this *ucHeap* buffer. If this value reaches 0, FreeRTOS runs out of memory.
@ -20,59 +287,3 @@ for (int i = 0; i < nb; i++) {
```
## Global heap
Heap is used for **dynamic memory allocation (malloc() / new)**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__HEAP_SIZE=8192` in *src/CMakeLists.txt*:
```
add_definitions(-D__HEAP_SIZE=8192)
```
You can trace the dynamic memory allocation by using the flag `--wrap` of the linker. When this flag is enabled, the linker will replace the calls to a specific function by a call to __wrap_the_function(). For example, if you specify `-Wl,-wrap,malloc` in the linker flags, the linker will replace all calls to `void* malloc(size_t)` by calls to `void* __wrap_malloc(size_t)`. This is a function you'll have to define in your code. In this function, you can call `__real_malloc()` to call the actual `malloc()' function.
This technic allows you to wrap all calls to malloc() with you own code.
In *src/CMakeLists.txt*:
```
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
...
LINK_FLAGS "-Wl,-wrap,malloc ..."
...
)
```
In *main.cpp*:
```
uint32_t totalMalloc = 0;
extern "C" {
extern void* __real_malloc(size_t s);
void *__wrap_malloc(size_t s) {
totalMalloc += s;
return __real_malloc(s);
}
}
```
This function sums all the memory that is allocated during the runtime. You can monitor or log this value. You can also place breakpoints in this function to determine where the dynamic memory allocation occurs in your code.
# Global stack
The stack is used to allocate memory used by functions : **parameters and local variables**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__STACK_SIZE=8192` in *src/CMakeLists.txt*:
```
add_definitions(-D__STACK_SIZE=8192)
```
*NOTE*: FreeRTOS uses its own stack buffer. Thus, the global stack is only used for main() and IRQ handlers. It should be possible to reduce its size to a much lower value.
**NOTE**: [?] How to track the global stack usage?
#LittleVGL buffer
*TODO*
#NimBLE buffers
*TODO*
#Tools
- https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC.

View file

@ -13,7 +13,7 @@ Based on Ubuntu 18.04 with the following build dependencies:
The `infinitime-build` image contains all the dependencies you need. The default `CMD` will compile sources found in `/sources`, so you need only mount your code.
This example will build the firmware, generate the MCUBoot image and generate the DFU file. Outputs will be written to **<project_root>/build/output**:
This example will build the firmware, generate the MCUBoot image and generate the DFU file. For cloning the repo, see [these instructions](../doc/buildAndProgram.md#clone-the-repo). Outputs will be written to **<project_root>/build/output**:
```bash
cd <project_root> # e.g. cd ./work/Pinetime

View file

@ -8,8 +8,9 @@ The following features are implemented:
- Time synchronization
- Notifications
- Music control
- Navigation with Puremaps
## Demo
[This video](https://seafile.codingfield.com/f/21c5d023452740279e36/) shows how to connect to the Pinetime and control the playback of the music on the phone.
Amazfish and Sailfish OS are running on the [Pinephone](https://www.pine64.org/pinephone/), another awesome device from Pine64.

View file

@ -1,7 +1,7 @@
# Integration with Gadgetbridge
[Gadgetbridge](https://gadgetbridge.org/) is an Android application that supports many smartwatches and fitness trackers.
The integration of InfiniTime (previously Pinetime-JF) is now merged into the master branch (https://codeberg.org/Freeyourgadget/Gadgetbridge/).
The integration of InfiniTime (previously Pinetime-JF) is now merged into the master branch (https://codeberg.org/Freeyourgadget/Gadgetbridge/) and initial support is available [starting with version 0.47](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/branch/master/CHANGELOG.md). Note that the official version is only available on F-Droid (as of May 2021), and the unofficial fork available on the Play Store is outdated and does not support Infinitime.
## Features
The following features are implemented:

View file

@ -1,16 +1,16 @@
# Using the releases
For each new *stable* version of Pinetime, a [release note](https://github.com/JF002/InfiniTime/releases) is created. It contains a description of the main changes in the release and some files you can use to flash the firmware in your Pinetime.
For each new *stable* version of IniniTime, a [release note](https://github.com/JF002/InfiniTime/releases) is created. It contains a description of the main changes in the release and some files you can use to flash the firmware to your Pinetime.
This page describes the files from the release notes and how to use them.
**NOTE :** the files included in different could be different. This page describes the release note of [version 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1), which is the version that'll probably be pre-programmed at the factory for the next batch of Pinetime devkits.
**NOTE :** the files included in different Releases could be different. This page describes the release notes of [version 0.7.1](https://github.com/JF002/InfiniTime/releases/tag/0.7.1), which is the version that is pre-programmed for the last batches of pinetimes but will be replaced with [1.0.0](https://github.com/jF002/infiniTime/releases/tag/1.0.0) around june 2021.
## Files included in the release note
## Files included in the release notes
### Standalone firmware
This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flash at offset 0, meaning it will erase any bootloader that might be present in memory.
This firmware is standalone, meaning that it does not need a bootloader to actually run. It is intended to be flashed at offset 0, meaning it will erase any bootloader that might be present in memory.
- **pinetime-app.out** : Output file of GCC containing debug symbols, useful is you want to debug the firmware using GDB.
- **pinetime-app.out** : Output file of GCC containing debug symbols, useful if you want to debug the firmware using GDB.
- **pinetime-app.hex** : Firmware in Intel HEX file format. Easier to use because it contains the offset in memory where it must be flashed, you don't need to specify it.
- **pintime-app.bin** : Firmware in binary format. When programming it, you have to specify the offset (0x00) in memory where it must be flashed.
- **pinetime-app.map** : Map file containing all the symbols, addresses in memory,...
@ -38,7 +38,7 @@ This firmware is a small utility firmware that writes the boot graphic in the ex
### Firmware with bootloader
This firmware is intended to be used with our [MCUBoot-based bootloader](../bootloader/README.md).
- **pinetime-mcuboot-app-image.hex** : Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed **@ 0x8000** into flash memory. If the [bootloader](../bootloader/README.md) has been successfully programmed, it should run this firmware after the next reset.
- **pinetime-mcuboot-app-image.hex**: Firmware wrapped into an MCUBoot image. This is **the** file that must be flashed at **0x8000** into the flash memory. If the [bootloader](../bootloader/README.md) has been successfully programmed, it should run this firmware after the next reset.
The following files are not directly usable by the bootloader:

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View file

@ -396,6 +396,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/FirmwareUpdate.cpp
displayapp/screens/Music.cpp
displayapp/screens/Navigation.cpp
displayapp/screens/Metronome.cpp
displayapp/screens/Motion.cpp
displayapp/screens/FirmwareValidation.cpp
displayapp/screens/ApplicationList.cpp
@ -531,7 +532,6 @@ list(APPEND RECOVERY_SOURCE_FILES
systemtask/SystemTask.cpp
drivers/TwiMaster.cpp
components/gfx/Gfx.cpp
displayapp/icons/infinitime/infinitime-nb.c
components/rle/RleDecoder.cpp
components/heartrate/HeartRateController.cpp
heartratetask/HeartRateTask.cpp
@ -558,7 +558,6 @@ list(APPEND RECOVERYLOADER_SOURCE_FILES
drivers/St7789.cpp
components/brightness/BrightnessController.cpp
displayapp/icons/infinitime/infinitime-nb.c
recoveryLoader.cpp
)
@ -592,6 +591,7 @@ set(INCLUDE_FILES
displayapp/Apps.h
displayapp/screens/Notifications.h
displayapp/screens/HeartRate.h
displayapp/screens/Metronome.h
displayapp/screens/Motion.h
displayapp/screens/Timer.h
drivers/St7789.h
@ -750,8 +750,8 @@ add_definitions(-DNIMBLE_CFG_CONTROLLER)
add_definitions(-DOS_CPUTIME_FREQ)
add_definitions(-DNRF52 -DNRF52832 -DNRF52832_XXAA -DNRF52_PAN_74 -DNRF52_PAN_64 -DNRF52_PAN_12 -DNRF52_PAN_58 -DNRF52_PAN_54 -DNRF52_PAN_31 -DNRF52_PAN_51 -DNRF52_PAN_36 -DNRF52_PAN_15 -DNRF52_PAN_20 -DNRF52_PAN_55 -DBOARD_PCA10040)
add_definitions(-DFREERTOS)
add_definitions(-D__STACK_SIZE=8192)
add_definitions(-D__HEAP_SIZE=8192)
add_definitions(-D__STACK_SIZE=1024)
add_definitions(-D__HEAP_SIZE=4096)
# NOTE : Add the following defines to enable debug mode of the NRF SDK:
#add_definitions(-DDEBUG)

View file

@ -62,7 +62,7 @@
#define configTICK_RATE_HZ 1024
#define configMAX_PRIORITIES (3)
#define configMINIMAL_STACK_SIZE (120)
#define configTOTAL_HEAP_SIZE (1024 * 16)
#define configTOTAL_HEAP_SIZE (1024 * 17)
#define configMAX_TASK_NAME_LEN (4)
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1

View file

@ -159,7 +159,7 @@ void AlertNotificationClient::OnNotification(ble_gap_event* event) {
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif));
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
}
}

View file

@ -79,7 +79,7 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle
break;
}
auto event = Pinetime::System::SystemTask::Messages::OnNewNotification;
auto event = Pinetime::System::Messages::OnNewNotification;
notificationManager.Push(std::move(notif));
systemTask.PushMessage(event);
}

View file

@ -205,7 +205,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Running);
bleController.FirmwareUpdateTotalBytes(0xffffffffu);
bleController.FirmwareUpdateCurrentBytes(0);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateStarted);
systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateStarted);
return 0;
} else {
NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType);
@ -279,7 +279,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
}
NRF_LOG_INFO("[DFU] -> Activate image and reset!");
bleController.StopFirmwareUpdate();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateFinished);
Reset();
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
return 0;
@ -304,7 +304,7 @@ void DfuService::Reset() {
notificationManager.Reset();
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
bleController.StopFirmwareUpdate();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateFinished);
}
DfuService::NotificationManager::NotificationManager() {

View file

@ -67,7 +67,7 @@ int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif));
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
}
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2020 JF, Adam Pigg, Avamander
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
@ -18,117 +18,68 @@
#include "MusicService.h"
#include "systemtask/SystemTask.h"
int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto musicService = static_cast<Pinetime::Controllers::MusicService*>(arg);
return musicService->OnCommand(conn_handle, attr_handle, ctxt);
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) {
msUuid.value[14] = msId[0];
msUuid.value[15] = msId[1];
msEventCharUuid.value[12] = msEventCharId[0];
msEventCharUuid.value[13] = msEventCharId[1];
msEventCharUuid.value[14] = msId[0];
msEventCharUuid.value[15] = msId[1];
msStatusCharUuid.value[12] = msStatusCharId[0];
msStatusCharUuid.value[13] = msStatusCharId[1];
msStatusCharUuid.value[14] = msId[0];
msStatusCharUuid.value[15] = msId[1];
msTrackCharUuid.value[12] = msTrackCharId[0];
msTrackCharUuid.value[13] = msTrackCharId[1];
msTrackCharUuid.value[14] = msId[0];
msTrackCharUuid.value[15] = msId[1];
msArtistCharUuid.value[12] = msArtistCharId[0];
msArtistCharUuid.value[13] = msArtistCharId[1];
msArtistCharUuid.value[14] = msId[0];
msArtistCharUuid.value[15] = msId[1];
msAlbumCharUuid.value[12] = msAlbumCharId[0];
msAlbumCharUuid.value[13] = msAlbumCharId[1];
msAlbumCharUuid.value[14] = msId[0];
msAlbumCharUuid.value[15] = msId[1];
msPositionCharUuid.value[12] = msPositionCharId[0];
msPositionCharUuid.value[13] = msPositionCharId[1];
msPositionCharUuid.value[14] = msId[0];
msPositionCharUuid.value[15] = msId[1];
msTotalLengthCharUuid.value[12] = msTotalLengthCharId[0];
msTotalLengthCharUuid.value[13] = msTotalLengthCharId[1];
msTotalLengthCharUuid.value[14] = msId[0];
msTotalLengthCharUuid.value[15] = msId[1];
msTrackNumberCharUuid.value[12] = msTrackNumberCharId[0];
msTrackNumberCharUuid.value[13] = msTrackNumberCharId[1];
msTrackNumberCharUuid.value[14] = msId[0];
msTrackNumberCharUuid.value[15] = msId[1];
msTrackTotalCharUuid.value[12] = msTrackTotalCharId[0];
msTrackTotalCharUuid.value[13] = msTrackTotalCharId[1];
msTrackTotalCharUuid.value[14] = msId[0];
msTrackTotalCharUuid.value[15] = msId[1];
msPlaybackSpeedCharUuid.value[12] = msPlaybackSpeedCharId[0];
msPlaybackSpeedCharUuid.value[13] = msPlaybackSpeedCharId[1];
msPlaybackSpeedCharUuid.value[14] = msId[0];
msPlaybackSpeedCharUuid.value[15] = msId[1];
msRepeatCharUuid.value[12] = msRepeatCharId[0];
msRepeatCharUuid.value[13] = msRepeatCharId[1];
msRepeatCharUuid.value[14] = msId[0];
msRepeatCharUuid.value[15] = msId[1];
msShuffleCharUuid.value[12] = msShuffleCharId[0];
msShuffleCharUuid.value[13] = msShuffleCharId[1];
msShuffleCharUuid.value[14] = msId[0];
msShuffleCharUuid.value[15] = msId[1];
characteristicDefinition[0] = {.uuid = (ble_uuid_t*) (&msEventCharUuid),
.access_cb = MSCallback,
characteristicDefinition[0] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msEventCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &eventHandle};
characteristicDefinition[1] = {
.uuid = (ble_uuid_t*) (&msStatusCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[2] = {
.uuid = (ble_uuid_t*) (&msTrackCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[3] = {
.uuid = (ble_uuid_t*) (&msArtistCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[4] = {
.uuid = (ble_uuid_t*) (&msAlbumCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[5] = {
.uuid = (ble_uuid_t*) (&msPositionCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[6] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid),
.access_cb = MSCallback,
characteristicDefinition[1] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msStatusCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[7] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid),
.access_cb = MSCallback,
characteristicDefinition[2] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[8] = {.uuid = (ble_uuid_t*) (&msTrackNumberCharUuid),
.access_cb = MSCallback,
characteristicDefinition[3] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msArtistCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[9] = {.uuid = (ble_uuid_t*) (&msTrackTotalCharUuid),
.access_cb = MSCallback,
characteristicDefinition[4] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msAlbumCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[10] = {.uuid = (ble_uuid_t*) (&msPlaybackSpeedCharUuid),
.access_cb = MSCallback,
characteristicDefinition[5] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msPositionCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[6] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[7] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[8] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackNumberCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[9] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackTotalCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[10] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msPlaybackSpeedCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[11] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msRepeatCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[12] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msShuffleCharUuid),
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[11] = {
.uuid = (ble_uuid_t*) (&msRepeatCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[12] = {
.uuid = (ble_uuid_t*) (&msShuffleCharUuid), .access_cb = MSCallback, .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 = (ble_uuid_t*) &msUuid, .characteristics = characteristicDefinition};
serviceDefinition[0] = {
.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = reinterpret_cast<ble_uuid_t*>(&msUuid), .characteristics = characteristicDefinition};
serviceDefinition[1] = {0};
artistName = "Waiting for";
@ -143,7 +94,7 @@ Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask&
}
void Pinetime::Controllers::MusicService::Init() {
int res = 0;
uint8_t res = 0;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);
@ -152,60 +103,67 @@ void Pinetime::Controllers::MusicService::Init() {
}
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);
uint8_t data[notifSize + 1];
char 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, (ble_uuid_t*) &msArtistCharUuid) == 0) {
char* s = &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msArtistCharUuid)) == 0) {
artistName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackCharUuid)) == 0) {
trackName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msAlbumCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msAlbumCharUuid)) == 0) {
albumName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msStatusCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msStatusCharUuid)) == 0) {
playing = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msRepeatCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msRepeatCharUuid)) == 0) {
repeat = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msShuffleCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msShuffleCharUuid)) == 0) {
shuffle = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msPositionCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msPositionCharUuid)) == 0) {
trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTotalLengthCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid)) == 0) {
trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackNumberCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackNumberCharUuid)) == 0) {
trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msTrackTotalCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackTotalCharUuid)) == 0) {
tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msPlaybackSpeedCharUuid) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msPlaybackSpeedCharUuid)) == 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() {
std::string Pinetime::Controllers::MusicService::getAlbum() const {
return albumName;
}
std::string Pinetime::Controllers::MusicService::getArtist() {
std::string Pinetime::Controllers::MusicService::getArtist() const {
return artistName;
}
std::string Pinetime::Controllers::MusicService::getTrack() {
std::string Pinetime::Controllers::MusicService::getTrack() const {
return trackName;
}
bool Pinetime::Controllers::MusicService::isPlaying() {
bool Pinetime::Controllers::MusicService::isPlaying() const {
return playing;
}
float Pinetime::Controllers::MusicService::getPlaybackSpeed() {
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) {
auto* om = ble_hs_mbuf_from_flat(&event, 1);
@ -216,12 +174,4 @@ void Pinetime::Controllers::MusicService::event(char event) {
}
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
int Pinetime::Controllers::MusicService::getProgress() {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() {
return trackLength;
}
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2020 JF, Adam Pigg, Avamander
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
@ -29,6 +29,8 @@
// 00000000-78fc-48fe-8e23-433b3a1942d0
#define MUSIC_SERVICE_UUID_BASE \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 }
#define MUSIC_SERVICE_CHAR_UUID(x, y) \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
namespace Pinetime {
namespace System {
@ -46,19 +48,19 @@ namespace Pinetime {
void event(char event);
std::string getArtist();
std::string getArtist() const;
std::string getTrack();
std::string getTrack() const;
std::string getAlbum();
std::string getAlbum() const;
int getProgress();
int getProgress() const;
int getTrackLength();
int getTrackLength() const;
float getPlaybackSpeed();
float getPlaybackSpeed() const;
bool isPlaying();
bool isPlaying() const;
static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00;
@ -71,34 +73,20 @@ namespace Pinetime {
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private:
static constexpr uint8_t msId[2] = {0x00, 0x00};
static constexpr uint8_t msEventCharId[2] = {0x01, 0x00};
static constexpr uint8_t msStatusCharId[2] = {0x02, 0x00};
static constexpr uint8_t msArtistCharId[2] = {0x03, 0x00};
static constexpr uint8_t msTrackCharId[2] = {0x04, 0x00};
static constexpr uint8_t msAlbumCharId[2] = {0x05, 0x00};
static constexpr uint8_t msPositionCharId[2] = {0x06, 0x00};
static constexpr uint8_t msTotalLengthCharId[2] = {0x07, 0x00};
static constexpr uint8_t msTrackNumberCharId[2] = {0x08, 0x00};
static constexpr uint8_t msTrackTotalCharId[2] = {0x09, 0x00};
static constexpr uint8_t msPlaybackSpeedCharId[2] = {0x0a, 0x00};
static constexpr uint8_t msRepeatCharId[2] = {0x0b, 0x00};
static constexpr uint8_t msShuffleCharId[2] = {0x0c, 0x00};
ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msEventCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msStatusCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msArtistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msTrackCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msAlbumCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msPositionCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msTotalLengthCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msTrackNumberCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msTrackTotalCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msPlaybackSpeedCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msRepeatCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msShuffleCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msEventCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x01, 0x00)};
ble_uuid128_t msStatusCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x02, 0x00)};
ble_uuid128_t msArtistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x03, 0x00)};
ble_uuid128_t msTrackCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x04, 0x00)};
ble_uuid128_t msAlbumCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x05, 0x00)};
ble_uuid128_t msPositionCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x06, 0x00)};
ble_uuid128_t msTotalLengthCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x07, 0x00)};
ble_uuid128_t msTrackNumberCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x08, 0x00)};
ble_uuid128_t msTrackTotalCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x09, 0x00)};
ble_uuid128_t msPlaybackSpeedCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0a, 0x00)};
ble_uuid128_t msRepeatCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0b, 0x00)};
ble_uuid128_t msShuffleCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0c, 0x00)};
struct ble_gatt_chr_def characteristicDefinition[14];
struct ble_gatt_svc_def serviceDefinition[2];

View file

@ -149,7 +149,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
bleController.Disconnect();
} else {
bleController.Connect();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleConnected);
systemTask.PushMessage(Pinetime::System::Messages::BleConnected);
connectionHandle = event->connect.conn_handle;
// Service discovery is deffered via systemtask
}

View file

@ -5,9 +5,6 @@
using namespace Pinetime::Controllers;
DateTime::DateTime(System::SystemTask& systemTask) : systemTask {systemTask} {
}
void DateTime::SetTime(
uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter) {
std::tm tm = {
@ -70,7 +67,8 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
// Notify new day to SystemTask
if (hour == 0 and not isMidnightAlreadyNotified) {
isMidnightAlreadyNotified = true;
systemTask.PushMessage(System::SystemTask::Messages::OnNewDay);
if(systemTask != nullptr)
systemTask->PushMessage(System::Messages::OnNewDay);
} else if (hour != 0) {
isMidnightAlreadyNotified = false;
}
@ -104,6 +102,10 @@ const char* DateTime::DayOfWeekShortToStringLow() {
return DateTime::DaysStringShortLow[(uint8_t) dayOfWeek];
}
void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}
char const* DateTime::DaysStringLow[] = {"--", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
char const* DateTime::DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

View file

@ -27,8 +27,6 @@ namespace Pinetime {
December
};
DateTime(System::SystemTask& systemTask);
void SetTime(uint16_t year,
uint8_t month,
uint8_t day,
@ -75,8 +73,9 @@ namespace Pinetime {
return uptime;
}
void Register(System::SystemTask* systemTask);
private:
System::SystemTask& systemTask;
uint16_t year = 0;
Months month = Months::Unknown;
uint8_t day = 0;
@ -90,6 +89,7 @@ namespace Pinetime {
std::chrono::seconds uptime {0};
bool isMidnightAlreadyNotified = false;
System::SystemTask* systemTask = nullptr;
static char const* DaysString[];
static char const* DaysStringShort[];

View file

@ -4,9 +4,6 @@
using namespace Pinetime::Controllers;
HeartRateController::HeartRateController(Pinetime::System::SystemTask& systemTask) : systemTask {systemTask} {
}
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
this->state = newState;
if (this->heartRate != heartRate) {

View file

@ -15,8 +15,7 @@ namespace Pinetime {
public:
enum class States { Stopped, NotEnoughData, NoTouch, Running };
explicit HeartRateController(System::SystemTask& systemTask);
HeartRateController() = default;
void Start();
void Stop();
void Update(States newState, uint8_t heartRate);
@ -32,7 +31,6 @@ namespace Pinetime {
void SetService(Pinetime::Controllers::HeartRateService* service);
private:
System::SystemTask& systemTask;
Applications::HeartRateTask* task = nullptr;
States state = States::Stopped;
uint8_t heartRate = 0;

View file

@ -38,9 +38,8 @@ namespace {
}
}
Ppg::Ppg(float spl)
: offset {spl},
hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
Ppg::Ppg()
: hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
agc {20, 0.971, 2},
lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} {
}
@ -67,13 +66,7 @@ float Ppg::HeartRate() {
dataIndex = 0;
return hr;
}
int cccount = 0;
float Ppg::ProcessHeartRate() {
if (cccount > 2)
asm("nop");
cccount++;
auto t0 = Trough(data.data(), dataIndex, 7, 48);
if (t0 < 0)
return 0;

View file

@ -8,8 +8,7 @@ namespace Pinetime {
namespace Controllers {
class Ppg {
public:
explicit Ppg(float spl);
Ppg();
int8_t Preprocess(float spl);
float HeartRate();

View file

@ -21,7 +21,7 @@ namespace Pinetime {
const uint8_t* buffer;
size_t size;
int encodedBufferIndex = 0;
size_t encodedBufferIndex = 0;
int y = 0;
uint16_t bp = 0;
uint16_t foregroundColor = 0xffff;

View file

@ -12,14 +12,17 @@ using namespace Pinetime::Controllers;
APP_TIMER_DEF(timerAppTimer);
TimerController::TimerController(System::SystemTask& systemTask) : systemTask{systemTask} {
namespace {
void TimerEnd(void* p_context) {
auto* controller = static_cast<Pinetime::Controllers::TimerController*> (p_context);
if(controller != nullptr)
controller->OnTimerEnd();
}
}
void TimerController::Init() {
app_timer_create(&timerAppTimer, APP_TIMER_MODE_SINGLE_SHOT, timerEnd);
app_timer_create(&timerAppTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEnd);
}
void TimerController::StartTimer(uint32_t duration) {
@ -47,13 +50,6 @@ uint32_t TimerController::GetTimeRemaining() {
return (static_cast<TickType_t>(deltaTicks) / static_cast<TickType_t>(configTICK_RATE_HZ)) * 1000;
}
void TimerController::timerEnd(void* p_context) {
auto* controller = static_cast<Controllers::TimerController*> (p_context);
controller->timerRunning = false;
controller->systemTask.PushMessage(System::SystemTask::Messages::OnTimerDone);
}
void TimerController::StopTimer() {
app_timer_stop(timerAppTimer);
timerRunning = false;
@ -61,4 +57,13 @@ void TimerController::StopTimer() {
bool TimerController::IsRunning() {
return timerRunning;
}
}
void TimerController::OnTimerEnd() {
timerRunning = false;
if(systemTask != nullptr)
systemTask->PushMessage(System::Messages::OnTimerDone);
}
void TimerController::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}

View file

@ -12,7 +12,7 @@ namespace Pinetime {
class TimerController {
public:
TimerController(Pinetime::System::SystemTask& systemTask);
TimerController() = default;
void Init();
@ -23,12 +23,13 @@ namespace Pinetime {
uint32_t GetTimeRemaining();
bool IsRunning();
void OnTimerEnd();
void Register(System::SystemTask* systemTask);
private:
System::SystemTask& systemTask;
static void timerEnd(void* p_context);
System::SystemTask* systemTask = nullptr;
TickType_t endTicks;
bool timerRunning = false;
};

View file

@ -21,6 +21,7 @@ namespace Pinetime {
HeartRate,
Navigation,
StopWatch,
Metronome,
Motion,
Steps,
QuickSettings,

View file

@ -18,6 +18,7 @@
#include "displayapp/screens/Paddle.h"
#include "displayapp/screens/StopWatch.h"
#include "displayapp/screens/Meter.h"
#include "displayapp/screens/Metronome.h"
#include "displayapp/screens/Music.h"
#include "displayapp/screens/Navigation.h"
#include "displayapp/screens/Notifications.h"
@ -32,6 +33,7 @@
#include "drivers/St7789.h"
#include "drivers/Watchdog.h"
#include "systemtask/SystemTask.h"
#include "systemtask/Messages.h"
#include "displayapp/screens/settings/QuickSettings.h"
#include "displayapp/screens/settings/Settings.h"
@ -44,6 +46,12 @@
using namespace Pinetime::Applications;
using namespace Pinetime::Applications::Display;
namespace {
static inline bool in_isr(void) {
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}
}
DisplayApp::DisplayApp(Drivers::St7789& lcd,
Components::LittleVgl& lvgl,
Drivers::Cst816S& touchPanel,
@ -51,7 +59,6 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings& settingsController,
@ -65,19 +72,20 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
bleController {bleController},
dateTimeController {dateTimeController},
watchdog {watchdog},
systemTask {systemTask},
notificationManager {notificationManager},
heartRateController {heartRateController},
settingsController {settingsController},
motorController {motorController},
motionController {motionController},
timerController {timerController} {
msgQueue = xQueueCreate(queueSize, itemSize);
// Start clock when smartwatch boots
LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
}
void DisplayApp::Start() {
msgQueue = xQueueCreate(queueSize, itemSize);
// Start clock when smartwatch boots
LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 800, this, 0, &taskHandle)) {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
@ -130,7 +138,7 @@ void DisplayApp::Refresh() {
vTaskDelay(100);
}
lcd.DisplayOff();
systemTask.PushMessage(System::SystemTask::Messages::OnDisplayTaskSleeping);
PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskSleeping);
state = States::Idle;
break;
case Messages::GoToRunning:
@ -139,7 +147,7 @@ void DisplayApp::Refresh() {
state = States::Running;
break;
case Messages::UpdateTimeOut:
systemTask.PushMessage(System::SystemTask::Messages::UpdateTimeOut);
PushMessageToSystemTask(System::Messages::UpdateTimeOut);
break;
case Messages::UpdateBleConnection:
// clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected :
@ -176,7 +184,7 @@ void DisplayApp::Refresh() {
LoadApp(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim);
break;
case TouchEvents::DoubleTap:
systemTask.PushMessage(System::SystemTask::Messages::GoToSleep);
PushMessageToSystemTask(System::Messages::GoToSleep);
break;
default:
break;
@ -188,7 +196,7 @@ void DisplayApp::Refresh() {
} break;
case Messages::ButtonPushed:
if (currentApp == Apps::Clock) {
systemTask.PushMessage(System::SystemTask::Messages::GoToSleep);
PushMessageToSystemTask(System::Messages::GoToSleep);
} else {
if (!currentScreen->OnButtonPushed()) {
LoadApp(returnToApp, returnDirection);
@ -206,6 +214,11 @@ void DisplayApp::Refresh() {
}
}
if(nextApp != Apps::None) {
LoadApp(nextApp, nextDirection);
nextApp = Apps::None;
}
if (state != States::Idle && touchMode == TouchModes::Polling) {
auto info = touchPanel.GetTouchInfo();
if (info.action == 2) { // 2 = contact
@ -224,7 +237,8 @@ void DisplayApp::RunningState() {
}
void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) {
LoadApp(app, direction);
nextApp = app;
nextDirection = direction;
}
void DisplayApp::ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent) {
@ -267,12 +281,12 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
case Apps::Notifications:
currentScreen = std::make_unique<Screens::Notifications>(
this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Normal);
this, notificationManager, systemTask->nimble().alertService(), Screens::Notifications::Modes::Normal);
ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
break;
case Apps::NotificationsPreview:
currentScreen = std::make_unique<Screens::Notifications>(
this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Preview);
this, notificationManager, systemTask->nimble().alertService(), Screens::Notifications::Modes::Preview);
ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
break;
case Apps::Timer:
@ -305,7 +319,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
currentScreen = std::make_unique<Screens::SettingDisplay>(this, settingsController);
ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break;
case Apps::SettingSteps:
case Apps::SettingSteps:
currentScreen = std::make_unique<Screens::SettingSteps>(this, settingsController);
ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break;
@ -318,10 +332,8 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
std::make_unique<Screens::SystemInfo>(this, dateTimeController, batteryController, brightnessController, bleController, watchdog);
ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break;
//
case Apps::FlashLight:
currentScreen = std::make_unique<Screens::FlashLight>(this, systemTask, brightnessController);
currentScreen = std::make_unique<Screens::FlashLight>(this, *systemTask, brightnessController);
ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None);
break;
case Apps::StopWatch:
@ -337,18 +349,21 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
currentScreen = std::make_unique<Screens::Paddle>(this, lvgl);
break;
case Apps::Music:
currentScreen = std::make_unique<Screens::Music>(this, systemTask.nimble().music());
currentScreen = std::make_unique<Screens::Music>(this, systemTask->nimble().music());
break;
case Apps::Navigation:
currentScreen = std::make_unique<Screens::Navigation>(this, systemTask.nimble().navigation());
currentScreen = std::make_unique<Screens::Navigation>(this, systemTask->nimble().navigation());
break;
case Apps::HeartRate:
currentScreen = std::make_unique<Screens::HeartRate>(this, heartRateController, systemTask);
currentScreen = std::make_unique<Screens::HeartRate>(this, heartRateController, *systemTask);
break;
case Apps::Metronome:
currentScreen = std::make_unique<Screens::Metronome>(this, motorController, *systemTask);
break;
case Apps::Motion:
currentScreen = std::make_unique<Screens::Motion>(this, motionController);
break;
case Apps::Steps:
case Apps::Steps:
currentScreen = std::make_unique<Screens::Steps>(this, motionController, settingsController);
break;
}
@ -359,12 +374,15 @@ void DisplayApp::IdleState() {
}
void DisplayApp::PushMessage(Messages msg) {
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(msgQueue, &msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
/* Actual macro used here is port specific. */
// TODO : should I do something here?
if(in_isr()) {
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(msgQueue, &msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
} else {
xQueueSend(msgQueue, &msg, portMAX_DELAY);
}
}
@ -425,3 +443,12 @@ void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) {
void DisplayApp::SetTouchMode(DisplayApp::TouchModes mode) {
touchMode = mode;
}
void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) {
if(systemTask != nullptr)
systemTask->PushMessage(message);
}
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}

View file

@ -4,6 +4,7 @@
#include <queue.h>
#include <task.h>
#include <memory>
#include <systemtask/Messages.h>
#include "Apps.h"
#include "LittleVgl.h"
#include "TouchEvents.h"
@ -49,7 +50,6 @@ namespace Pinetime {
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings& settingsController,
@ -64,6 +64,8 @@ namespace Pinetime {
void SetFullRefresh(FullRefreshDirections direction);
void SetTouchMode(TouchModes mode);
void Register(Pinetime::System::SystemTask* systemTask);
private:
Pinetime::Drivers::St7789& lcd;
Pinetime::Components::LittleVgl& lvgl;
@ -72,7 +74,7 @@ namespace Pinetime {
Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Drivers::WatchdogView& watchdog;
Pinetime::System::SystemTask& systemTask;
Pinetime::System::SystemTask* systemTask = nullptr;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::HeartRateController& heartRateController;
Pinetime::Controllers::Settings& settingsController;
@ -108,6 +110,10 @@ namespace Pinetime {
void Refresh();
void ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent);
void LoadApp(Apps app, DisplayApp::FullRefreshDirections direction);
void PushMessageToSystemTask(Pinetime::System::Messages message);
Apps nextApp = Apps::None;
DisplayApp::FullRefreshDirections nextDirection;
};
}
}

View file

@ -14,7 +14,6 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings& settingsController,
@ -22,10 +21,11 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController)
: lcd {lcd}, bleController {bleController} {
msgQueue = xQueueCreate(queueSize, itemSize);
}
void DisplayApp::Start() {
msgQueue = xQueueCreate(queueSize, itemSize);
if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 512, this, 0, &taskHandle))
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
@ -113,4 +113,8 @@ void DisplayApp::PushMessage(Display::Messages msg) {
/* Actual macro used here is port specific. */
// TODO : should I do something here?
}
}
}
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
}

View file

@ -39,7 +39,6 @@ namespace Pinetime {
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Drivers::WatchdogView& watchdog,
System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings& settingsController,
@ -48,6 +47,7 @@ namespace Pinetime {
Pinetime::Controllers::TimerController& timerController);
void Start();
void PushMessage(Pinetime::Applications::Display::Messages msg);
void Register(Pinetime::System::SystemTask* systemTask);
private:
TaskHandle_t taskHandle;

View file

@ -19,6 +19,10 @@ namespace Pinetime {
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) {

View file

@ -23,6 +23,10 @@ bool touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data) {
LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel)
: lcd {lcd}, touchPanel {touchPanel}, previousClick {0, 0} {
}
void LittleVgl::Init() {
lv_init();
InitTheme();
InitDisplay();

View file

@ -19,6 +19,8 @@ namespace Pinetime {
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);

View file

@ -48,6 +48,7 @@ static lv_style_t style_sw_bg;
static lv_style_t style_sw_indic;
static lv_style_t style_sw_knob;
static lv_style_t style_arc_bg;
static lv_style_t style_arc_knob;
static lv_style_t style_arc_indic;
static lv_style_t style_table_cell;
static lv_style_t style_pad_small;
@ -191,6 +192,7 @@ static void basic_init(void) {
lv_style_set_text_line_space(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 25);
lv_style_set_shadow_width(&style_ddlist_list, LV_STATE_DEFAULT, LV_VER_RES / 20);
lv_style_set_shadow_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_bg_color(&style_ddlist_list, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
style_init_reset(&style_ddlist_selected);
lv_style_set_bg_opa(&style_ddlist_selected, LV_STATE_DEFAULT, LV_OPA_COVER);
@ -239,6 +241,13 @@ static void basic_init(void) {
lv_style_set_line_color(&style_arc_bg, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_line_width(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(25));
lv_style_set_line_rounded(&style_arc_bg, LV_STATE_DEFAULT, true);
lv_style_set_pad_all(&style_arc_bg, LV_STATE_DEFAULT, LV_DPX(5));
lv_style_reset(&style_arc_knob);
lv_style_set_radius(&style_arc_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_style_set_bg_opa(&style_arc_knob, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_arc_knob, LV_STATE_DEFAULT, LV_PINETIME_LIGHT_GRAY);
lv_style_set_pad_all(&style_arc_knob, LV_STATE_DEFAULT, LV_DPX(5));
style_init_reset(&style_table_cell);
lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
@ -447,6 +456,10 @@ static void theme_apply(lv_obj_t* obj, lv_theme_style_t name) {
lv_obj_clean_style_list(obj, LV_ARC_PART_INDIC);
list = lv_obj_get_style_list(obj, LV_ARC_PART_INDIC);
_lv_style_list_add_style(list, &style_arc_indic);
lv_obj_clean_style_list(obj, LV_ARC_PART_KNOB);
list = lv_obj_get_style_list(obj, LV_ARC_PART_KNOB);
_lv_style_list_add_style(list, &style_arc_knob);
break;
case LV_THEME_SWITCH:

View file

@ -63,7 +63,7 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
{Symbols::paddle, Apps::Paddle},
{"2", Apps::Twos},
{"M", Apps::Motion},
{"", Apps::None},
{"b", Apps::Metronome},
{"", Apps::None},
}};

View file

@ -33,21 +33,17 @@ Clock::Clock(DisplayApp* app,
settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController},
screens {app,
settingsController.GetClockFace(),
{
[this]() -> std::unique_ptr<Screen> {
return WatchFaceDigitalScreen();
},
[this]() -> std::unique_ptr<Screen> {
return WatchFaceAnalogScreen();
},
// Examples for more watch faces
//[this]() -> std::unique_ptr<Screen> { return WatchFaceMinimalScreen(); },
//[this]() -> std::unique_ptr<Screen> { return WatchFaceCustomScreen(); }
},
Screens::ScreenListModes::LongPress} {
screen {[this, &settingsController]() {
switch (settingsController.GetClockFace()) {
case 0:
return WatchFaceDigitalScreen();
break;
case 1:
return WatchFaceAnalogScreen();
break;
}
return WatchFaceDigitalScreen();
}()} {
settingsController.SetAppMenu(0);
}
@ -56,12 +52,12 @@ Clock::~Clock() {
}
bool Clock::Refresh() {
screens.Refresh();
screen->Refresh();
return running;
}
bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return screens.OnTouchEvent(event);
return screen->OnTouchEvent(event);
}
std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {

View file

@ -4,8 +4,8 @@
#include <chrono>
#include <cstdint>
#include <memory>
#include <components/heartrate/HeartRateController.h>
#include "Screen.h"
#include "ScreenList.h"
#include "components/datetime/DateTimeController.h"
namespace Pinetime {
@ -47,7 +47,7 @@ namespace Pinetime {
Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
ScreenList<2> screens;
std::unique_ptr<Screen> screen;
std::unique_ptr<Screen> WatchFaceDigitalScreen();
std::unique_ptr<Screen> WatchFaceAnalogScreen();

View file

@ -39,14 +39,14 @@ FlashLight::FlashLight(Pinetime::Applications::DisplayApp* app,
backgroundAction->user_data = this;
lv_obj_set_event_cb(backgroundAction, event_handler);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
}
FlashLight::~FlashLight() {
lv_obj_clean(lv_scr_act());
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
brightness.Restore();
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
}
void FlashLight::OnClickEvent(lv_obj_t* obj, lv_event_t event) {

View file

@ -63,12 +63,12 @@ HeartRate::HeartRate(Pinetime::Applications::DisplayApp* app,
label_startStop = lv_label_create(btn_startStop, nullptr);
UpdateStartStopButton(isHrRunning);
if (isHrRunning)
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
}
HeartRate::~HeartRate() {
lv_obj_clean(lv_scr_act());
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
}
bool HeartRate::Refresh() {
@ -95,12 +95,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
heartRateController.Start();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::DisableSleeping);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
} else {
heartRateController.Stop();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
systemTask.PushMessage(Pinetime::System::SystemTask::Messages::EnableSleeping);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
}
}

View file

@ -0,0 +1,169 @@
#include "Metronome.h"
#include "Screen.h"
#include "Symbols.h"
#include "lvgl/lvgl.h"
#include "FreeRTOSConfig.h"
#include "task.h"
#include <string>
#include <tuple>
using namespace Pinetime::Applications::Screens;
namespace {
float calculateDelta(const TickType_t startTime, const TickType_t currentTime) {
TickType_t delta = 0;
// Take care of overflow
if (startTime > currentTime) {
delta = 0xffffffff - startTime;
delta += (currentTime + 1);
} else {
delta = currentTime - startTime;
}
return static_cast<float>(delta) / static_cast<float>(configTICK_RATE_HZ);
}
static void eventHandler(lv_obj_t* obj, lv_event_t event) {
Metronome* screen = static_cast<Metronome*>(obj->user_data);
screen->OnEvent(obj, event);
}
lv_obj_t* createLabel(const char* name, lv_obj_t* reference, lv_align_t align, lv_font_t* font, uint8_t x = 0, uint8_t y = 0) {
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_label_set_text(label, name);
lv_obj_align(label, reference, align, x, y);
return label;
}
}
Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask)
: Screen(app), running {true}, currentState {States::Stopped}, startTime {}, motorController {motorController}, systemTask {systemTask} {
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
bpmArc->user_data = this;
lv_obj_set_event_cb(bpmArc, eventHandler);
lv_arc_set_bg_angles(bpmArc, 0, 270);
lv_arc_set_rotation(bpmArc, 135);
lv_arc_set_range(bpmArc, 40, 220);
lv_arc_set_value(bpmArc, bpm);
lv_obj_set_size(bpmArc, 210, 210);
lv_arc_set_adjustable(bpmArc, true);
lv_obj_align(bpmArc, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 7);
bpmValue = createLabel(std::to_string(lv_arc_get_value(bpmArc)).c_str(), bpmArc, LV_ALIGN_IN_TOP_MID, &jetbrains_mono_76, 0, 55);
bpmLegend = createLabel("bpm", bpmValue, LV_ALIGN_OUT_BOTTOM_MID, &jetbrains_mono_bold_20, 0, 0);
bpmTap = lv_btn_create(lv_scr_act(), nullptr);
bpmTap->user_data = this;
lv_obj_set_event_cb(bpmTap, eventHandler);
lv_obj_set_style_local_bg_opa(bpmTap, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_height(bpmTap, 80);
lv_obj_align(bpmTap, bpmValue, LV_ALIGN_IN_TOP_MID, 0, 0);
bpbDropdown = lv_dropdown_create(lv_scr_act(), nullptr);
bpbDropdown->user_data = this;
lv_obj_set_event_cb(bpbDropdown, eventHandler);
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_MAIN, LV_STATE_DEFAULT, 20);
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_LIST, LV_STATE_DEFAULT, 20);
lv_obj_align(bpbDropdown, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 15, -4);
lv_dropdown_set_options(bpbDropdown, "1\n2\n3\n4\n5\n6\n7\n8\n9");
lv_dropdown_set_selected(bpbDropdown, bpb - 1);
bpbLegend = lv_label_create(bpbDropdown, nullptr);
lv_label_set_text(bpbLegend, "bpb");
lv_obj_align(bpbLegend, bpbDropdown, LV_ALIGN_IN_RIGHT_MID, -15, 0);
playPause = lv_btn_create(lv_scr_act(), nullptr);
playPause->user_data = this;
lv_obj_set_event_cb(playPause, eventHandler);
lv_obj_align(playPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -15, -10);
lv_obj_set_height(playPause, 39);
playPauseLabel = lv_label_create(playPause, nullptr);
lv_label_set_text(playPauseLabel, Symbols::play);
app->SetTouchMode(DisplayApp::TouchModes::Polling);
}
Metronome::~Metronome() {
app->SetTouchMode(DisplayApp::TouchModes::Gestures);
systemTask.PushMessage(System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
bool Metronome::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return true;
}
bool Metronome::Refresh() {
switch (currentState) {
case States::Stopped: {
break;
}
case States::Running: {
if (calculateDelta(startTime, xTaskGetTickCount()) >= (60.0 / bpm)) {
counter--;
startTime -= 60.0 / bpm;
startTime = xTaskGetTickCount();
if (counter == 0) {
counter = bpb;
motorController.SetDuration(90);
} else {
motorController.SetDuration(30);
}
}
break;
}
}
return running;
}
void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
switch (event) {
case LV_EVENT_VALUE_CHANGED: {
if (obj == bpmArc) {
bpm = lv_arc_get_value(bpmArc);
lv_label_set_text_fmt(bpmValue, "%03d", bpm);
} else if (obj == bpbDropdown) {
bpb = lv_dropdown_get_selected(obj) + 1;
}
break;
}
case LV_EVENT_PRESSED: {
if (obj == bpmTap) {
float timeDelta = calculateDelta(tappedTime, xTaskGetTickCount());
if (tappedTime == 0 || timeDelta > 3) {
tappedTime = xTaskGetTickCount();
} else {
bpm = ceil(60.0 / timeDelta);
lv_arc_set_value(bpmArc, bpm);
lv_label_set_text_fmt(bpmValue, "%03d", bpm);
tappedTime = xTaskGetTickCount();
}
}
break;
}
case LV_EVENT_CLICKED: {
if (obj == playPause) {
currentState = (currentState == States::Stopped ? States::Running : States::Stopped);
switch (currentState) {
case States::Stopped: {
lv_label_set_text(playPauseLabel, Symbols::play);
systemTask.PushMessage(System::Messages::EnableSleeping);
break;
}
case States::Running: {
lv_label_set_text(playPauseLabel, Symbols::pause);
systemTask.PushMessage(System::Messages::DisableSleeping);
startTime = xTaskGetTickCount();
counter = 1;
break;
}
}
}
break;
}
}
}

View file

@ -0,0 +1,34 @@
#pragma once
#include "systemtask/SystemTask.h"
#include "components/motor/MotorController.h"
#include <array>
namespace Pinetime::Applications::Screens {
class Metronome : public Screen {
public:
Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask);
~Metronome() override;
bool Refresh() override;
bool OnTouchEvent(TouchEvents event) override;
void OnEvent(lv_obj_t* obj, lv_event_t event);
enum class States { Running, Stopped };
private:
bool running;
States currentState;
TickType_t startTime;
TickType_t tappedTime = 0;
Controllers::MotorController& motorController;
System::SystemTask& systemTask;
uint16_t bpm = 120;
uint8_t bpb = 4;
uint8_t counter = 1;
lv_obj_t *bpmArc, *bpmTap, *bpmValue, *bpmLegend;
lv_obj_t *bpbDropdown, *bpbLegend;
lv_obj_t *playPause, *playPauseLabel;
};
}

View file

@ -15,12 +15,17 @@ namespace Pinetime {
public:
ScreenList(DisplayApp* app,
uint8_t initScreen,
std::array<std::function<std::unique_ptr<Screen>()>, N>&& screens,
const std::array<std::function<std::unique_ptr<Screen>()>, N>&& screens,
ScreenListModes mode)
: Screen(app), initScreen {initScreen}, screens {std::move(screens)}, mode {mode}, current {this->screens[initScreen]()} {
screenIndex = initScreen;
: Screen(app), initScreen {initScreen}, screens {std::move(screens)}, mode {mode}, screenIndex{initScreen}, current {this->screens[initScreen]()} {
}
ScreenList(const ScreenList&) = delete;
ScreenList& operator=(const ScreenList&) = delete;
ScreenList(ScreenList&&) = delete;
ScreenList& operator=(ScreenList&&) = delete;
~ScreenList() override {
lv_obj_clean(lv_scr_act());
}
@ -97,7 +102,7 @@ namespace Pinetime {
private:
uint8_t initScreen = 0;
std::array<std::function<std::unique_ptr<Screen>()>, N> screens;
const std::array<std::function<std::unique_ptr<Screen>()>, N> screens;
ScreenListModes mode = ScreenListModes::UpDown;
uint8_t screenIndex = 0;

View file

@ -61,22 +61,36 @@ StopWatch::StopWatch(DisplayApp* app)
lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_label_set_text(time, "00:00");
lv_obj_align(time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -45);
lv_obj_align(time, lv_scr_act(), LV_ALIGN_CENTER, 0, -45);
msecTime = lv_label_create(lv_scr_act(), nullptr);
// lv_obj_set_style_local_text_font(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_label_set_text(msecTime, "00");
lv_obj_align(msecTime, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 108, 3);
lv_obj_align(msecTime, lv_scr_act(), LV_ALIGN_CENTER, 0, 3);
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
btnPlayPause->user_data = this;
lv_obj_set_event_cb(btnPlayPause, play_pause_event_handler);
lv_obj_align(btnPlayPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, -10);
lv_obj_set_height(btnPlayPause, 40);
lv_obj_set_height(btnPlayPause, 50);
lv_obj_set_width(btnPlayPause, 115);
lv_obj_align(btnPlayPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
txtPlayPause = lv_label_create(btnPlayPause, nullptr);
lv_label_set_text(txtPlayPause, Symbols::play);
btnStopLap = lv_btn_create(lv_scr_act(), nullptr);
btnStopLap->user_data = this;
lv_obj_set_event_cb(btnStopLap, stop_lap_event_handler);
lv_obj_set_height(btnStopLap, 50);
lv_obj_set_width(btnStopLap, 115);
lv_obj_align(btnStopLap, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_obj_set_style_local_bg_color(btnStopLap, LV_BTN_PART_MAIN, LV_STATE_DISABLED, lv_color_hex(0x080808));
txtStopLap = lv_label_create(btnStopLap, nullptr);
lv_obj_set_style_local_text_color(txtStopLap, LV_BTN_PART_MAIN, LV_STATE_DISABLED, lv_color_hex(0x888888));
lv_label_set_text(txtStopLap, Symbols::stop);
lv_obj_set_state(btnStopLap, LV_STATE_DISABLED);
lv_obj_set_state(txtStopLap, LV_STATE_DISABLED);
lapOneText = lv_label_create(lv_scr_act(), nullptr);
// lv_obj_set_style_local_text_font(lapOneText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
lv_obj_set_style_local_text_color(lapOneText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
@ -88,9 +102,6 @@ StopWatch::StopWatch(DisplayApp* app)
lv_obj_set_style_local_text_color(lapTwoText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_obj_align(lapTwoText, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 50, 55);
lv_label_set_text(lapTwoText, "");
// We don't want this button in the init state
btnStopLap = nullptr;
}
StopWatch::~StopWatch() {
@ -115,10 +126,6 @@ bool StopWatch::Refresh() {
// Init state when an user first opens the app
// and when a stop/reset button is pressed
case States::Init: {
if (btnStopLap != nullptr) {
lv_obj_del(btnStopLap);
btnStopLap = nullptr;
}
// The initial default value
lv_label_set_text(time, "00:00");
lv_label_set_text(msecTime, "00");
@ -129,16 +136,14 @@ bool StopWatch::Refresh() {
lapNr = 0;
if (currentEvent == Events::Play) {
btnStopLap = lv_btn_create(lv_scr_act(), nullptr);
btnStopLap->user_data = this;
lv_obj_set_event_cb(btnStopLap, stop_lap_event_handler);
lv_obj_align(btnStopLap, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0);
lv_obj_set_height(btnStopLap, 40);
txtStopLap = lv_label_create(btnStopLap, nullptr);
lv_label_set_text(txtStopLap, Symbols::lapsFlag);
lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT);
lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT);
startTime = xTaskGetTickCount();
currentState = States::Running;
} else {
lv_obj_set_state(btnStopLap, LV_STATE_DISABLED);
lv_obj_set_state(txtStopLap, LV_STATE_DISABLED);
}
break;
}

View file

@ -81,7 +81,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
__TIME__);
lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label));
return std::make_unique<Screens::Label>(0, 5, app, label);
}
std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
@ -161,7 +161,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
brightnessController.ToString(),
resetReason);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
return std::unique_ptr<Screen>(new Screens::Label(1, 4, app, label));
return std::make_unique<Screens::Label>(1, 5, app, label);
}
std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
@ -195,10 +195,10 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
(int) mon.free_biggest_size,
0);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
return std::unique_ptr<Screen>(new Screens::Label(2, 5, app, label));
return std::make_unique<Screens::Label>(2, 5, app, label);
}
bool sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) {
bool SystemInfo::sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) {
return lhs.xTaskNumber < rhs.xTaskNumber;
}
@ -229,7 +229,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen4() {
lv_table_set_cell_value(infoTask, i + 1, 2, std::to_string(tasksStatus[i].usStackHighWaterMark).c_str());
}
}
return std::unique_ptr<Screen>(new Screens::Label(3, 5, app, infoTask));
return std::make_unique<Screens::Label>(3, 5, app, infoTask);
}
std::unique_ptr<Screen> SystemInfo::CreateScreen5() {
@ -245,5 +245,5 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen5() {
"#FFFF00 JF002/InfiniTime#");
lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
return std::unique_ptr<Screen>(new Screens::Label(4, 5, app, label));
return std::make_unique<Screens::Label>(4, 5, app, label);
}

View file

@ -43,6 +43,9 @@ namespace Pinetime {
Pinetime::Drivers::WatchdogView& watchdog;
ScreenList<5> screens;
static bool sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs);
std::unique_ptr<Screen> CreateScreen1();
std::unique_ptr<Screen> CreateScreen2();
std::unique_ptr<Screen> CreateScreen3();

View file

@ -7,12 +7,12 @@ using namespace Pinetime::Applications::Screens;
namespace {
static void ButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
QuickSettings* screen = static_cast<QuickSettings*>(obj->user_data);
auto* screen = static_cast<QuickSettings*>(obj->user_data);
screen->OnButtonEvent(obj, event);
}
static void lv_update_task(struct _lv_task_t* task) {
auto user_data = static_cast<QuickSettings*>(task->user_data);
auto* user_data = static_cast<QuickSettings*>(task->user_data);
user_data->UpdateScreen();
}
}

View file

@ -46,7 +46,7 @@ std::unique_ptr<Screen> Settings::CreateScreen1() {
{Symbols::clock, "Watch face", Apps::SettingWatchFace},
}};
return std::unique_ptr<Screen>(new Screens::List(0, 2, app, settingsController, applications));
return std::make_unique<Screens::List>(0, 2, app, settingsController, applications);
}
std::unique_ptr<Screen> Settings::CreateScreen2() {
@ -58,5 +58,5 @@ std::unique_ptr<Screen> Settings::CreateScreen2() {
{Symbols::list, "About", Apps::SysInfo},
}};
return std::unique_ptr<Screen>(new Screens::List(1, 2, app, settingsController, applications));
return std::make_unique<Screens::List>(1, 2, app, settingsController, applications);
}

View file

@ -16,7 +16,6 @@ namespace Pinetime {
bool Refresh() override;
void OnButtonEvent(lv_obj_t* object, lv_event_t event);
bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;
private:

View file

@ -103,8 +103,6 @@ Bma421::Values Bma421::Process() {
uint8_t activity = 0;
bma423_activity_output(&activity, &bma);
NRF_LOG_INFO("MOTION : %d - %d/%d/%d", steps, data.x, data.y, data.z);
// 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};
}

View file

@ -7,11 +7,14 @@
using namespace Pinetime::Drivers;
SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters& params) : spi {spi}, params {params} {
mutex = xSemaphoreCreateBinary();
ASSERT(mutex != NULL);
}
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);
@ -53,6 +56,7 @@ bool SpiMaster::Init() {
break;
case BitOrder::Lsb_Msb:
regConfig = 1;
break;
default:
return false;
}
@ -132,17 +136,17 @@ void SpiMaster::OnEndEvent() {
spiBaseAddress->TASKS_START = 1;
} else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (taskToNotify != nullptr) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
nrf_gpio_pin_set(this->pinCsn);
currentBufferAddr = 0;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
BaseType_t xHigherPriorityTaskWoken2 = pdFALSE;
xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken2);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken | xHigherPriorityTaskWoken2);
}
}

View file

@ -10,7 +10,6 @@ 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 };
@ -60,7 +59,7 @@ namespace Pinetime {
volatile uint32_t currentBufferAddr = 0;
volatile size_t currentBufferSize = 0;
volatile TaskHandle_t taskToNotify;
SemaphoreHandle_t mutex;
SemaphoreHandle_t mutex = nullptr;
};
}
}

View file

@ -9,10 +9,12 @@ using namespace Pinetime::Drivers;
// TODO use DMA/IRQ
TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module {module}, params {params} {
mutex = xSemaphoreCreateBinary();
}
void TwiMaster::Init() {
if(mutex == nullptr)
mutex = xSemaphoreCreateBinary();
NRF_GPIO->PIN_CNF[params.pinScl] =
((uint32_t) GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | ((uint32_t) GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t) GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) | ((uint32_t) GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |

View file

@ -31,7 +31,7 @@ namespace Pinetime {
ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
void FixHwFreezed();
NRF_TWIM_Type* twiBaseAddress;
SemaphoreHandle_t mutex;
SemaphoreHandle_t mutex = nullptr;
const Modules module;
const Parameters params;
static constexpr uint8_t maxDataSize {16};

View file

@ -6,12 +6,13 @@
using namespace Pinetime::Applications;
HeartRateTask::HeartRateTask(Drivers::Hrs3300& heartRateSensor, Controllers::HeartRateController& controller)
: heartRateSensor {heartRateSensor}, controller {controller}, ppg {static_cast<float>(heartRateSensor.ReadHrs())} {
messageQueue = xQueueCreate(10, 1);
controller.SetHeartRateTask(this);
: 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);
}

View file

@ -33,7 +33,7 @@
#include "components/ble/NotificationManager.h"
#include "components/motor/MotorController.h"
#include "components/datetime/DateTimeController.h"
#include "components/settings/Settings.h"
#include "components/heartrate/HeartRateController.h"
#include "drivers/Spi.h"
#include "drivers/SpiMaster.h"
#include "drivers/SpiNorFlash.h"
@ -50,8 +50,6 @@ Pinetime::Logging::NrfLogger logger;
Pinetime::Logging::DummyLogger logger;
#endif
#include <memory>
static constexpr uint8_t pinSpiSck = 2;
static constexpr uint8_t pinSpiMosi = 3;
static constexpr uint8_t pinSpiMiso = 4;
@ -108,15 +106,59 @@ void ble_manager_set_ble_connection_callback(void (*connection)());
void ble_manager_set_ble_disconnection_callback(void (*disconnection)());
static constexpr uint8_t pinTouchIrq = 28;
static constexpr uint8_t pinPowerPresentIrq = 19;
std::unique_ptr<Pinetime::System::SystemTask> systemTask;
Pinetime::Controllers::Settings settingsController {spiNorFlash};
Pinetime::Controllers::MotorController motorController {settingsController};
Pinetime::Controllers::HeartRateController heartRateController;
Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController);
Pinetime::Controllers::DateTime dateTimeController;
Pinetime::Drivers::Watchdog watchdog;
Pinetime::Drivers::WatchdogView watchdogView(watchdog);
Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::TimerController timerController;
Pinetime::Applications::DisplayApp displayApp(lcd,
lvgl,
touchPanel,
batteryController,
bleController,
dateTimeController,
watchdogView,
notificationManager,
heartRateController,
settingsController,
motorController,
motionController,
timerController);
Pinetime::System::SystemTask systemTask(spi,
lcd,
spiNorFlash,
twiMaster,
touchPanel,
lvgl,
batteryController,
bleController,
dateTimeController,
timerController,
watchdog,
notificationManager,
motorController,
heartRateSensor,
motionController,
motionSensor,
settingsController,
heartRateController,
displayApp,
heartRateApp);
void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
if (pin == pinTouchIrq) {
systemTask->OnTouchEvent();
systemTask.OnTouchEvent();
return;
}
@ -141,12 +183,12 @@ void vApplicationIdleHook(void) {
void DebounceTimerChargeCallback(TimerHandle_t xTimer) {
xTimerStop(xTimer, 0);
systemTask->PushMessage(Pinetime::System::SystemTask::Messages::OnChargingEvent);
systemTask.PushMessage(Pinetime::System::Messages::OnChargingEvent);
}
void DebounceTimerCallback(TimerHandle_t xTimer) {
xTimerStop(xTimer, 0);
systemTask->OnButtonPushed();
systemTask.OnButtonPushed();
}
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) {
@ -264,19 +306,9 @@ int main(void) {
debounceTimer = xTimerCreate("debounceTimer", 200, pdFALSE, (void*) 0, DebounceTimerCallback);
debounceChargeTimer = xTimerCreate("debounceTimerCharge", 200, pdFALSE, (void*) 0, DebounceTimerChargeCallback);
systemTask = std::make_unique<Pinetime::System::SystemTask>(spi,
lcd,
spiNorFlash,
twiMaster,
touchPanel,
lvgl,
batteryController,
bleController,
motorController,
heartRateSensor,
motionSensor,
settingsController);
systemTask->Start();
lvgl.Init();
systemTask.Start();
nimble_port_init();
vTaskStartScheduler();

26
src/systemtask/Messages.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
namespace Pinetime {
namespace System {
enum class Messages {
GoToSleep,
GoToRunning,
TouchWakeUp,
OnNewTime,
OnNewNotification,
OnTimerDone,
OnNewCall,
BleConnected,
UpdateTimeOut,
BleFirmwareUpdateStarted,
BleFirmwareUpdateFinished,
OnTouchEvent,
OnButtonEvent,
OnDisplayTaskSleeping,
EnableSleeping,
DisableSleeping,
OnNewDay,
OnChargingEvent
};
}
}

View file

@ -27,6 +27,12 @@
using namespace Pinetime::System;
namespace {
static inline bool in_isr(void) {
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}
}
void IdleTimerCallback(TimerHandle_t xTimer) {
NRF_LOG_INFO("IdleTimerCallback");
@ -42,10 +48,18 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
Components::LittleVgl& lvgl,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Controllers::TimerController& timerController,
Drivers::Watchdog& watchdog,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::MotorController& motorController,
Pinetime::Drivers::Hrs3300& heartRateSensor,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Drivers::Bma421& motionSensor,
Controllers::Settings& settingsController)
Controllers::Settings& settingsController,
Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Applications::DisplayApp& displayApp,
Pinetime::Applications::HeartRateTask& heartRateApp)
: spi {spi},
lcd {lcd},
spiNorFlash {spiNorFlash},
@ -53,21 +67,25 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
touchPanel {touchPanel},
lvgl {lvgl},
batteryController {batteryController},
heartRateController {*this},
bleController {bleController},
dateTimeController {*this},
timerController {*this},
watchdog {},
watchdogView {watchdog},
dateTimeController {dateTimeController},
timerController {timerController},
watchdog {watchdog},
notificationManager{notificationManager},
motorController {motorController},
heartRateSensor {heartRateSensor},
motionSensor {motionSensor},
settingsController {settingsController},
nimbleController(*this, bleController, dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController) {
systemTasksMsgQueue = xQueueCreate(10, 1);
heartRateController{heartRateController},
nimbleController(*this, bleController, dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController),
motionController{motionController},
displayApp{displayApp},
heartRateApp(heartRateApp) {
}
void SystemTask::Start() {
systemTasksMsgQueue = xQueueCreate(10, 1);
if (pdPASS != xTaskCreate(SystemTask::Process, "MAIN", 350, this, 0, &taskHandle))
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
@ -96,9 +114,11 @@ void SystemTask::Work() {
twiMaster.Init();
touchPanel.Init();
dateTimeController.Register(this);
batteryController.Init();
motorController.Init();
motionSensor.SoftReset();
timerController.Register(this);
timerController.Init();
// Reset the TWI device because the motion sensor chip most probably crashed it...
@ -108,28 +128,14 @@ void SystemTask::Work() {
motionSensor.Init();
settingsController.Init();
displayApp = std::make_unique<Pinetime::Applications::DisplayApp>(lcd,
lvgl,
touchPanel,
batteryController,
bleController,
dateTimeController,
watchdogView,
*this,
notificationManager,
heartRateController,
settingsController,
motorController,
motionController,
timerController);
displayApp->Start();
displayApp.Register(this);
displayApp.Start();
displayApp->PushMessage(Pinetime::Applications::Display::Messages::UpdateBatteryLevel);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateBatteryLevel);
heartRateSensor.Init();
heartRateSensor.Disable();
heartRateApp = std::make_unique<Pinetime::Applications::HeartRateTask>(heartRateSensor, heartRateController);
heartRateApp->Start();
heartRateApp.Start();
nrf_gpio_cfg_sense_input(pinButton, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pulldown, (nrf_gpio_pin_sense_t) GPIO_PIN_CNF_SENSE_High);
nrf_gpio_cfg_output(15);
@ -208,9 +214,9 @@ void SystemTask::Work() {
spiNorFlash.Wakeup();
lcd.Wakeup();
displayApp->PushMessage(Pinetime::Applications::Display::Messages::GoToRunning);
displayApp->PushMessage(Pinetime::Applications::Display::Messages::UpdateBatteryLevel);
heartRateApp->PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateBatteryLevel);
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp);
isSleeping = false;
isWakingUp = false;
@ -230,26 +236,26 @@ void SystemTask::Work() {
isGoingToSleep = true;
NRF_LOG_INFO("[systemtask] Going to sleep");
xTimerStop(idleTimer, 0);
displayApp->PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
heartRateApp->PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
break;
case Messages::OnNewTime:
ReloadIdleTimer();
displayApp->PushMessage(Pinetime::Applications::Display::Messages::UpdateDateTime);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateDateTime);
break;
case Messages::OnNewNotification:
if (isSleeping && !isWakingUp) {
GoToRunning();
}
motorController.SetDuration(35);
displayApp->PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
break;
case Messages::OnTimerDone:
if (isSleeping && !isWakingUp) {
GoToRunning();
}
motorController.SetDuration(35);
displayApp->PushMessage(Pinetime::Applications::Display::Messages::TimerDone);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TimerDone);
break;
case Messages::BleConnected:
ReloadIdleTimer();
@ -260,7 +266,7 @@ void SystemTask::Work() {
doNotGoToSleep = true;
if (isSleeping && !isWakingUp)
GoToRunning();
displayApp->PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
break;
case Messages::BleFirmwareUpdateFinished:
doNotGoToSleep = false;
@ -359,7 +365,7 @@ void SystemTask::OnButtonPushed() {
if (!isSleeping) {
NRF_LOG_INFO("[systemtask] Button pushed");
PushMessage(Messages::OnButtonEvent);
displayApp->PushMessage(Pinetime::Applications::Display::Messages::ButtonPushed);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::ButtonPushed);
} else {
if (!isWakingUp) {
NRF_LOG_INFO("[systemtask] Button pushed, waking up");
@ -380,7 +386,7 @@ void SystemTask::OnTouchEvent() {
return;
if (!isSleeping) {
PushMessage(Messages::OnTouchEvent);
displayApp->PushMessage(Pinetime::Applications::Display::Messages::TouchEvent);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent);
} else if (!isWakingUp) {
if (settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::None or
settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist)
@ -389,16 +395,22 @@ void SystemTask::OnTouchEvent() {
}
}
void SystemTask::PushMessage(SystemTask::Messages msg) {
void SystemTask::PushMessage(System::Messages msg) {
if (msg == Messages::GoToSleep) {
isGoingToSleep = true;
}
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
/* Actual macro used here is port specific. */
// TODO: should I do something here?
if(in_isr()) {
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
/* Actual macro used here is port specific. */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
} else {
xQueueSend(systemTasksMsgQueue, &msg, portMAX_DELAY);
}
}

View file

@ -6,7 +6,6 @@
#include <task.h>
#include <timers.h>
#include <heartratetask/HeartRateTask.h>
#include <components/heartrate/HeartRateController.h>
#include <components/settings/Settings.h>
#include <drivers/Bma421.h>
#include <components/motion/MotionController.h>
@ -27,6 +26,7 @@
#endif
#include "drivers/Watchdog.h"
#include "Messages.h"
namespace Pinetime {
namespace Drivers {
@ -40,27 +40,6 @@ namespace Pinetime {
namespace System {
class SystemTask {
public:
enum class Messages {
GoToSleep,
GoToRunning,
TouchWakeUp,
OnNewTime,
OnNewNotification,
OnTimerDone,
OnNewCall,
BleConnected,
UpdateTimeOut,
BleFirmwareUpdateStarted,
BleFirmwareUpdateFinished,
OnTouchEvent,
OnButtonEvent,
OnDisplayTaskSleeping,
EnableSleeping,
DisableSleeping,
OnNewDay,
OnChargingEvent
};
SystemTask(Drivers::SpiMaster& spi,
Drivers::St7789& lcd,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
@ -69,10 +48,18 @@ namespace Pinetime {
Components::LittleVgl& lvgl,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Controllers::TimerController& timerController,
Drivers::Watchdog& watchdog,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::MotorController& motorController,
Pinetime::Drivers::Hrs3300& heartRateSensor,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Drivers::Bma421& motionSensor,
Controllers::Settings& settingsController);
Controllers::Settings& settingsController,
Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Applications::DisplayApp& displayApp,
Pinetime::Applications::HeartRateTask& heartRateApp);
void Start();
void PushMessage(Messages msg);
@ -96,27 +83,29 @@ namespace Pinetime {
Pinetime::Drivers::Cst816S& touchPanel;
Pinetime::Components::LittleVgl& lvgl;
Pinetime::Controllers::Battery& batteryController;
std::unique_ptr<Pinetime::Applications::DisplayApp> displayApp;
Pinetime::Controllers::HeartRateController heartRateController;
std::unique_ptr<Pinetime::Applications::HeartRateTask> heartRateApp;
Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime dateTimeController;
Pinetime::Controllers::TimerController timerController;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Controllers::TimerController& timerController;
QueueHandle_t systemTasksMsgQueue;
std::atomic<bool> isSleeping {false};
std::atomic<bool> isGoingToSleep {false};
std::atomic<bool> isWakingUp {false};
Pinetime::Drivers::Watchdog watchdog;
Pinetime::Drivers::WatchdogView watchdogView;
Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Drivers::Watchdog& watchdog;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::MotorController& motorController;
Pinetime::Drivers::Hrs3300& heartRateSensor;
Pinetime::Drivers::Bma421& motionSensor;
Pinetime::Controllers::Settings& settingsController;
Pinetime::Controllers::HeartRateController& heartRateController;
Pinetime::Controllers::NimbleController nimbleController;
Controllers::BrightnessController brightnessController;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::MotionController& motionController;
Pinetime::Applications::DisplayApp& displayApp;
Pinetime::Applications::HeartRateTask& heartRateApp;
static constexpr uint8_t pinSpiSck = 2;
static constexpr uint8_t pinSpiMosi = 3;