Compare commits
9 Commits
wb/fucktim
...
wb/roman-n
| Author | SHA1 | Date | |
|---|---|---|---|
|
49c801b827
|
|||
|
|
f6a5588544 | ||
|
|
41518cc529 | ||
|
|
c20db14c43 | ||
|
|
9520b2ec9b | ||
|
|
8cf38a3469 | ||
|
|
83cae9b5bc | ||
|
|
bb743689e1 | ||
|
|
4b854a5d09 |
18
.github/workflows/main.yml
vendored
@@ -46,30 +46,20 @@ jobs:
|
|||||||
# Unzip the package because Upload Artifact will zip up the files
|
# Unzip the package because Upload Artifact will zip up the files
|
||||||
- name: Unzip DFU package
|
- name: Unzip DFU package
|
||||||
run: unzip ./build/output/pinetime-mcuboot-app-dfu-*.zip -d ./build/output/pinetime-mcuboot-app-dfu
|
run: unzip ./build/output/pinetime-mcuboot-app-dfu-*.zip -d ./build/output/pinetime-mcuboot-app-dfu
|
||||||
- name: Set ref_name, but replace slashes with dashes.
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
ref_name: ${{ github.head_ref || github.ref_name }}
|
|
||||||
run: echo "REF_NAME=${ref_name//\//-}" >> $GITHUB_ENV
|
|
||||||
- name: Upload DFU artifacts
|
- name: Upload DFU artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: InfiniTime DFU ${{ env.REF_NAME }}
|
name: InfiniTime DFU ${{ github.head_ref }}
|
||||||
path: ./build/output/pinetime-mcuboot-app-dfu/*
|
path: ./build/output/pinetime-mcuboot-app-dfu/*
|
||||||
- name: Upload MCUBoot image artifacts
|
- name: Upload MCUBoot image artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: InfiniTime MCUBoot image ${{ env.REF_NAME }}
|
name: InfiniTime MCUBoot image ${{ github.head_ref }}
|
||||||
path: ./build/output/pinetime-mcuboot-app-image-*.bin
|
path: ./build/output/pinetime-mcuboot-app-image-*.bin
|
||||||
- name: Upload standalone ELF artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: InfiniTime image ${{ env.REF_NAME }}
|
|
||||||
path: ./build/output/src/pinetime-app-*.out
|
|
||||||
- name: Upload resources artifacts
|
- name: Upload resources artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: InfiniTime resources ${{ env.REF_NAME }}
|
name: InfiniTime resources ${{ github.head_ref }}
|
||||||
path: ./build/output/infinitime-resources-*.zip
|
path: ./build/output/infinitime-resources-*.zip
|
||||||
|
|
||||||
build-simulator:
|
build-simulator:
|
||||||
@@ -110,7 +100,7 @@ jobs:
|
|||||||
- name: Upload simulator executable
|
- name: Upload simulator executable
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: infinisim-${{ env.REF_NAME }}
|
name: infinisim-${{ github.head_ref }}
|
||||||
path: build_lv_sim/infinisim
|
path: build_lv_sim/infinisim
|
||||||
|
|
||||||
get-base-ref-size:
|
get-base-ref-size:
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -50,3 +50,5 @@ src/arm-none-eabi
|
|||||||
|
|
||||||
# clangd
|
# clangd
|
||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
|
nRF5_SDK/
|
||||||
|
|||||||
6
.gitmodules
vendored
@@ -4,6 +4,12 @@
|
|||||||
[submodule "src/libs/littlefs"]
|
[submodule "src/libs/littlefs"]
|
||||||
path = src/libs/littlefs
|
path = src/libs/littlefs
|
||||||
url = https://github.com/littlefs-project/littlefs.git
|
url = https://github.com/littlefs-project/littlefs.git
|
||||||
|
[submodule "src/libs/QCBOR"]
|
||||||
|
path = src/libs/QCBOR
|
||||||
|
url = https://github.com/laurencelundblade/QCBOR.git
|
||||||
[submodule "src/libs/arduinoFFT"]
|
[submodule "src/libs/arduinoFFT"]
|
||||||
path = src/libs/arduinoFFT
|
path = src/libs/arduinoFFT
|
||||||
url = https://github.com/kosme/arduinoFFT.git
|
url = https://github.com/kosme/arduinoFFT.git
|
||||||
|
[submodule "src/libs/sunset"]
|
||||||
|
path = src/libs/sunset
|
||||||
|
url = https://github.com/buelowp/sunset.git
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
|
|||||||
|
|
||||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
|
||||||
|
|
||||||
project(pinetime VERSION 1.15.0 LANGUAGES C CXX ASM)
|
project(pinetime VERSION 1.14.0 LANGUAGES C CXX ASM)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|||||||
39
README.md
@@ -1,50 +1,27 @@
|
|||||||
<div align="center">
|
# [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<br>
|
Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++.
|
||||||
|
|
||||||
[](https://github.com/InfiniTimeOrg/InfiniTime/releases)
|
|
||||||
[](https://github.com/InfiniTimeOrg/InfiniLink/blob/main/LICENSE)
|
|
||||||
[](https://github.com/InfiniTimeOrg/InfiniTime/issues)
|
|
||||||
[](https://github.com/InfiniTimeOrg/InfiniTime/pulls)
|
|
||||||
[](https://github.com/InfiniTimeOrg/InfiniTime)
|
|
||||||
[](https://github.com/InfiniTimeOrg/InfiniTime/stargazers)
|
|
||||||
[](https://github.com/InfiniTimeOrg/InfiniTime/network/members)
|
|
||||||
|
|
||||||
# InfiniTime
|
|
||||||
|
|
||||||
*Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++.*
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## New to InfiniTime?
|
## New to InfiniTime?
|
||||||
|
|
||||||
- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md)
|
- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md)
|
||||||
- [Updating the software](doc/gettingStarted/updating-software.md)
|
- [Updating the software](doc/gettingStarted/updating-software.md)
|
||||||
- [About the firmware and bootloader](doc/gettingStarted/about-software.md)
|
- [About the firmware and bootloader](doc/gettingStarted/about-software.md)
|
||||||
- [Available apps](doc/gettingStarted/Applications.md)
|
- [PineTimeStyle Watch face](https://wiki.pine64.org/wiki/PineTimeStyle)
|
||||||
- [Available watch faces](/doc/gettingStarted/Watchfaces.md)
|
- [Weather integration](https://wiki.pine64.org/wiki/Infinitime-Weather)
|
||||||
- [PineTimeStyle Watch face](https://pine64.org/documentation/PineTime/Watchfaces/PineTimeStyle)
|
|
||||||
- [Weather integration](https://pine64.org/documentation/PineTime/Software/InfiniTime_weather/)
|
|
||||||
|
|
||||||
### Companion apps
|
### Companion apps
|
||||||
|
|
||||||
- [Gadgetbridge](https://gadgetbridge.org/) (Android)
|
- [Gadgetbridge](https://gadgetbridge.org/) (Android)
|
||||||
- [Amazfish](https://github.com/piggz/harbour-amazfish/) ([SailfishOS](https://sailfishos-chum.github.io/apps/harbour-amazfish/), [Ubuntu Touch](https://open-store.io/app/uk.co.piggz.amazfish), [Flatpak](https://flathub.org/apps/uk.co.piggz.amazfish))
|
- [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS)
|
||||||
- [Siglo](https://github.com/alexr4535/siglo) (Linux)
|
- [Siglo](https://github.com/alexr4535/siglo) (Linux)
|
||||||
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS)
|
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS)
|
||||||
- [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux)
|
- [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux)
|
||||||
- [WatchMate](https://github.com/azymohliad/watchmate) (Linux)
|
- [WatchMate](https://github.com/azymohliad/watchmate) (Linux)
|
||||||
- [InfiniTimeExplorer](https://infinitimeexplorer.netlify.app) (Web)
|
|
||||||
|
|
||||||
<br>
|
***Note**: We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).*
|
||||||
|
|
||||||
> *InfiniTimeExplorer is only compatible with web browsers that support Web BLE. Current fully supported browsers include Chrome and Microsoft Edge.*
|
|
||||||
>
|
|
||||||
> *We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).*
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@@ -58,7 +35,7 @@
|
|||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
- [How to contribute](CONTRIBUTING.md)
|
- [How to contribute?](CONTRIBUTING.md)
|
||||||
- [Coding conventions](doc/coding-convention.md)
|
- [Coding conventions](doc/coding-convention.md)
|
||||||
|
|
||||||
### Build, flash and debug
|
### Build, flash and debug
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
# Applications
|
|
||||||
|
|
||||||
InfiniTime has 13 apps on the `main` branch at the time of writing.
|
|
||||||
|
|
||||||
## List of apps
|
|
||||||
- Stopwatch
|
|
||||||
- Alarm
|
|
||||||
- Timer
|
|
||||||
- Steps
|
|
||||||
- Heartrate
|
|
||||||
- Music
|
|
||||||
- InfiniPaint
|
|
||||||
- Paddle
|
|
||||||
- 2
|
|
||||||
- InfiniDice
|
|
||||||
- Metronome
|
|
||||||
- Maps
|
|
||||||
- Weather
|
|
||||||
|
|
||||||
### Stopwatch
|
|
||||||

|
|
||||||
- Press the Start button (bottom right) to start or stop the timer.
|
|
||||||
- You can also press the side button while the timer is running to pause the timer.
|
|
||||||
- Press the Flag button (bottom left) to add a lap.
|
|
||||||
- The stopwatch will not yet continue counting time while the app is closed.
|
|
||||||
|
|
||||||
### Alarm
|
|
||||||

|
|
||||||
- Ajust the time with the time picker.
|
|
||||||
- Press the Info button in the top middle to see time remaning.
|
|
||||||
- Use the toggle in the bottom left to turn the alarm on/off.
|
|
||||||
- Use the button in the bottom right to change the alarm frequency.
|
|
||||||
- You can choose between once, daily, or Monday - Friday.
|
|
||||||
|
|
||||||
### Timer
|
|
||||||

|
|
||||||
- Ajust how long the timer should go for with the time picker.
|
|
||||||
- Press the Start button at the bottom to start/stop the timer.
|
|
||||||
|
|
||||||
### Steps
|
|
||||||

|
|
||||||
- The total count of steps for the current display will show in the middle of the screen.
|
|
||||||
- The Reset button in the bottom middle resets the Trip counter. (Total of all steps taken.)
|
|
||||||
- The progress circle shows the percentage of your daily goal completed.
|
|
||||||
|
|
||||||
### Heartrate
|
|
||||||

|
|
||||||
- Press Start to start measuring your heartrate.
|
|
||||||
- It may take a bit to get the first measurement.
|
|
||||||
|
|
||||||
### Music
|
|
||||||

|
|
||||||
- This app shows currently playing music.
|
|
||||||
- Please note that this app is not very useful without a device connected.
|
|
||||||
- Press the button in the center to play/pause, and the buttons on the left and right to go to the previous and next tracks, respectively.
|
|
||||||
- Swipe up to get to volume controls.
|
|
||||||
|
|
||||||
### InfiniPaint
|
|
||||||

|
|
||||||
- This app does not allow you to swipe from the top to exit, use the side button instead.
|
|
||||||
- Draw on the screen to add lines.
|
|
||||||
- Hold down in one spot to change paint colors.
|
|
||||||
|
|
||||||
### Paddle
|
|
||||||

|
|
||||||
- This app does not allow you to swipe from the top to exit, use the side button instead.
|
|
||||||
- Drag your finger to move the paddle.
|
|
||||||
- Goal: Don't let the ball go off the left side of the screen.
|
|
||||||
|
|
||||||
### 2
|
|
||||||

|
|
||||||
- This app does not allow you to swipe from the top to exit, use the side button instead.
|
|
||||||
- Play a game of 2048.
|
|
||||||
- Swipe up, down, left, or right tomove the tiles.
|
|
||||||
- When two tiles with the same number run into each other, they will add together.
|
|
||||||
- Goal: Don't let the screen fill up with tiles, and get to the 2048 tile to win.
|
|
||||||
|
|
||||||
### InfiniDice
|
|
||||||

|
|
||||||
- Ajust the count to change the number of dice.
|
|
||||||
- Ajust the sides to change the number of sides.
|
|
||||||
- Press the button at the bottom to roll.
|
|
||||||
- The result will be on the right side of the screen.
|
|
||||||
|
|
||||||
### Metronome
|
|
||||||

|
|
||||||
- Ajust the BPM with the circular slider.
|
|
||||||
- A bug currently makes it always snap to 98 BPM.
|
|
||||||
- Use the button in the bottom left to start the metronome.
|
|
||||||
|
|
||||||
### Maps
|
|
||||||

|
|
||||||
- This app shows info from a navigation app.
|
|
||||||
- Please note that this app is not very useful without a device connected.
|
|
||||||
|
|
||||||
### Weather
|
|
||||||

|
|
||||||
- This app shows weather info.
|
|
||||||
- Please note that this app is not very useful without a device connected.
|
|
||||||
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,31 +0,0 @@
|
|||||||
# Watchfaces
|
|
||||||
|
|
||||||
InfiniTime has 6 apps on the `main` branch at the time of writing.
|
|
||||||
|
|
||||||
## List of apps
|
|
||||||
- Digital
|
|
||||||
- Analog
|
|
||||||
- PineTimeStyle
|
|
||||||
- Terminal
|
|
||||||
- Infinineat
|
|
||||||
- Casio G7710
|
|
||||||
|
|
||||||
### Digital
|
|
||||||

|
|
||||||
|
|
||||||
### Analog
|
|
||||||

|
|
||||||
|
|
||||||
### PineTimeStyle
|
|
||||||

|
|
||||||
- You can long-press on the display to change colors, step style, and weather.
|
|
||||||
|
|
||||||
### Terminal
|
|
||||||

|
|
||||||
|
|
||||||
### Infinineat
|
|
||||||

|
|
||||||
- You can long-press on the display to change colors.
|
|
||||||
|
|
||||||
### Casio G7710
|
|
||||||

|
|
||||||
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 33 KiB |
@@ -14,7 +14,7 @@ You can sync the time using companion apps.
|
|||||||
|
|
||||||
- Gadgetbridge automatically synchronizes the time when you connect it to your watch. More information on Gadgetbridge [here](/doc/gettingStarted/ota-gadgetbridge.md)
|
- Gadgetbridge automatically synchronizes the time when you connect it to your watch. More information on Gadgetbridge [here](/doc/gettingStarted/ota-gadgetbridge.md)
|
||||||
- [Sync the time with NRFConnect](/doc/gettingStarted/time-nrfconnect.md)
|
- [Sync the time with NRFConnect](/doc/gettingStarted/time-nrfconnect.md)
|
||||||
- [Sync the time with your browser](https://hubmartin.github.io/WebBLEWatch/)
|
- Sync the time with your browser https://hubmartin.github.io/WebBLEWatch/
|
||||||
|
|
||||||
You can also set the time in the settings without a companion app. (version >1.7.0)
|
You can also set the time in the settings without a companion app. (version >1.7.0)
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ On the bottom right, you can see how many steps you have taken today.
|
|||||||

|

|
||||||
|
|
||||||
- Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu.
|
- Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu.
|
||||||
- Swipe **down** to display the notification panel. Notifications sent by your companion app will be displayed here.
|
- Swipe **down** to display the notification panel. Notification sent by your companion app will be displayed here.
|
||||||
- Swipe **right** to display the Quick Actions menu. This menu allows you to
|
- Swipe **right** to display the Quick Actions menu. This menu allows you to
|
||||||
- Set the brightness of the display
|
- Set the brightness of the display
|
||||||
- Start the **flashlight** app
|
- Start the **flashlight** app
|
||||||
|
|||||||
@@ -1,35 +1,29 @@
|
|||||||
# Connecting to Gadgetbridge
|
# Connecting to Gadgetbridge
|
||||||
|
|
||||||
Launch Gadgetbridge and tap on the menu button in the top left:
|
Launch Gadgetbridge and tap on the **"+"** button on the bottom right to add a new device:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Press the "Connect new device" button:
|
Wait for the scan to complete, your PineTime should be detected:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Your PineTime should appear on the list. Tap on it.
|
|
||||||
|
|
||||||
Tap on it. Gadgdetbridge will pair and connect to your device:
|
Tap on it. Gadgdetbridge will pair and connect to your device:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# Updating with Gadgetbridge
|
# Updating with Gadgetbridge
|
||||||
|
|
||||||
Now that Gadgetbridge is connected to your PineTime, press the three dots on the device card:
|
Now that Gadgetbridge is connected to your PineTime, use a file browser application and find the DFU file (`pinetime-mcuboot-app-dfu-x.x.x.zip`) you downloaded previously. Tap on it and open it using the Gadgetbridge application/firmware installer:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Now press the "File Installer" button:
|
Read the warning carefully and tap **Install**:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Select the firmware you downloaded (`pinetime-mcuboot-app-dfu-x.x.x.zip`) from the [Releases tab](https://github.com/InfiniTimeOrg/InfiniTime/releases/latest):
|
Wait for the transfer to finish. Your PineTime should reset and reboot with the new version of InfiniTime!
|
||||||
|
|
||||||
|
Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and Firmware option and click **validate**. Otherwise after reboot the previous firmware will be used.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Wait for the transfer to finish. There will be a progress bar on both the watch and the phone. Your PineTime should reboot with the new version of InfiniTime!
|
|
||||||
|
|
||||||
Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and scroll to the Firmware option and click **validate**. Otherwise, after reboot the previous firmware will be used.
|
|
||||||
|
|
||||||

|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ If you just want to flash or upgrade InfiniTime on your PineTime, this page is f
|
|||||||
|
|
||||||
You can check the InfiniTime version by first swiping right on the watch face to open quick settings, tapping the cogwheel to open settings, swipe up until you find an entry named "About" and tap on it.
|
You can check the InfiniTime version by first swiping right on the watch face to open quick settings, tapping the cogwheel to open settings, swipe up until you find an entry named "About" and tap on it.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
PineTimes shipped after June 2021 will ship with the latest version of [the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/tag/1.0.0) and [recovery firmware](https://github.com/InfiniTimeOrg/InfiniTime/releases/tag/0.14.1)
|
PineTimes shipped after June 2021 will ship with the latest version of [the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/tag/1.0.0) and [recovery firmware](https://github.com/InfiniTimeOrg/InfiniTime/releases/tag/0.14.1)
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ Since those resources are not part of the firmware, they need to be flashed and
|
|||||||
Resources are packaged into a single .zip file named `infinitime-resources-x.y.z.zip` (where `x`, `y` and `z` are the version numbers of InfiniTime).
|
Resources are packaged into a single .zip file named `infinitime-resources-x.y.z.zip` (where `x`, `y` and `z` are the version numbers of InfiniTime).
|
||||||
You can use the companion app of your choice to flash the resources.
|
You can use the companion app of your choice to flash the resources.
|
||||||
|
|
||||||
**Note: at the time of writing this page, [Amazfish](https://github.com/piggz/harbour-amazfish) and [ITD](https://gitea.arsenm.dev/Arsen6331/itd) have already integrated this functionality. Other companion apps will hopefully implement it soon!*
|
**Note : at the time of writing this page, [Amazfish](https://github.com/piggz/harbour-amazfish) and [ITD](https://gitea.arsenm.dev/Arsen6331/itd) have already integrated this functionality. Other companion apps will hopefully implement it soon!*
|
||||||
|
|
||||||
## Amazfish
|
## Amazfish
|
||||||
Use the `Download file` functionality of Amazfish.
|
Use the `Download file` functionality of Amazfish.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 846 KiB |
@@ -65,8 +65,5 @@ RUN bash -c "source /opt/build.sh; GetMcuBoot;"
|
|||||||
# Add the infinitime user for connecting devcontainer
|
# Add the infinitime user for connecting devcontainer
|
||||||
RUN adduser infinitime
|
RUN adduser infinitime
|
||||||
|
|
||||||
# Configure Git to accept the /sources directory as safe
|
|
||||||
RUN git config --global --add safe.directory /sources
|
|
||||||
|
|
||||||
ENV SOURCES_DIR /sources
|
ENV SOURCES_DIR /sources
|
||||||
CMD ["/opt/build.sh"]
|
CMD ["/opt/build.sh"]
|
||||||
|
|||||||
@@ -174,6 +174,11 @@ set(LITTLEFS_SRC
|
|||||||
libs/littlefs/lfs.c
|
libs/littlefs/lfs.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(SUNSET_SRC
|
||||||
|
libs/sunset/src/sunset.h
|
||||||
|
libs/sunset/src/sunset.cpp
|
||||||
|
)
|
||||||
|
|
||||||
set(LVGL_SRC
|
set(LVGL_SRC
|
||||||
libs/lv_conf.h
|
libs/lv_conf.h
|
||||||
libs/lvgl/lvgl.h
|
libs/lvgl/lvgl.h
|
||||||
@@ -366,8 +371,6 @@ list(APPEND SOURCE_FILES
|
|||||||
displayapp/DisplayApp.cpp
|
displayapp/DisplayApp.cpp
|
||||||
displayapp/screens/Screen.cpp
|
displayapp/screens/Screen.cpp
|
||||||
displayapp/screens/Tile.cpp
|
displayapp/screens/Tile.cpp
|
||||||
displayapp/screens/InfiniPaint.cpp
|
|
||||||
displayapp/screens/Paddle.cpp
|
|
||||||
displayapp/screens/StopWatch.cpp
|
displayapp/screens/StopWatch.cpp
|
||||||
displayapp/screens/BatteryIcon.cpp
|
displayapp/screens/BatteryIcon.cpp
|
||||||
displayapp/screens/BleIcon.cpp
|
displayapp/screens/BleIcon.cpp
|
||||||
@@ -376,14 +379,9 @@ list(APPEND SOURCE_FILES
|
|||||||
displayapp/screens/Label.cpp
|
displayapp/screens/Label.cpp
|
||||||
displayapp/screens/FirmwareUpdate.cpp
|
displayapp/screens/FirmwareUpdate.cpp
|
||||||
displayapp/screens/Music.cpp
|
displayapp/screens/Music.cpp
|
||||||
displayapp/screens/Navigation.cpp
|
|
||||||
displayapp/screens/Metronome.cpp
|
|
||||||
displayapp/screens/Motion.cpp
|
|
||||||
displayapp/screens/Weather.cpp
|
|
||||||
displayapp/screens/FirmwareValidation.cpp
|
displayapp/screens/FirmwareValidation.cpp
|
||||||
displayapp/screens/ApplicationList.cpp
|
displayapp/screens/ApplicationList.cpp
|
||||||
displayapp/screens/Notifications.cpp
|
displayapp/screens/Notifications.cpp
|
||||||
displayapp/screens/Twos.cpp
|
|
||||||
displayapp/screens/HeartRate.cpp
|
displayapp/screens/HeartRate.cpp
|
||||||
displayapp/screens/FlashLight.cpp
|
displayapp/screens/FlashLight.cpp
|
||||||
displayapp/screens/List.cpp
|
displayapp/screens/List.cpp
|
||||||
@@ -418,14 +416,12 @@ list(APPEND SOURCE_FILES
|
|||||||
displayapp/screens/settings/SettingChimes.cpp
|
displayapp/screens/settings/SettingChimes.cpp
|
||||||
displayapp/screens/settings/SettingShakeThreshold.cpp
|
displayapp/screens/settings/SettingShakeThreshold.cpp
|
||||||
displayapp/screens/settings/SettingBluetooth.cpp
|
displayapp/screens/settings/SettingBluetooth.cpp
|
||||||
|
displayapp/screens/settings/SettingLocation.cpp
|
||||||
|
|
||||||
## Watch faces
|
## Watch faces
|
||||||
displayapp/screens/WatchFaceAnalog.cpp
|
|
||||||
displayapp/screens/WatchFaceDigital.cpp
|
displayapp/screens/WatchFaceDigital.cpp
|
||||||
displayapp/screens/WatchFaceInfineat.cpp
|
displayapp/screens/WatchFaceFuzzy.cpp
|
||||||
displayapp/screens/WatchFaceTerminal.cpp
|
displayapp/screens/WatchFaceSundial.cpp
|
||||||
displayapp/screens/WatchFacePineTimeStyle.cpp
|
|
||||||
displayapp/screens/WatchFaceCasioStyleG7710.cpp
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
@@ -478,7 +474,6 @@ list(APPEND SOURCE_FILES
|
|||||||
|
|
||||||
systemtask/SystemTask.cpp
|
systemtask/SystemTask.cpp
|
||||||
systemtask/SystemMonitor.cpp
|
systemtask/SystemMonitor.cpp
|
||||||
systemtask/WakeLock.cpp
|
|
||||||
drivers/TwiMaster.cpp
|
drivers/TwiMaster.cpp
|
||||||
|
|
||||||
heartratetask/HeartRateTask.cpp
|
heartratetask/HeartRateTask.cpp
|
||||||
@@ -543,7 +538,6 @@ list(APPEND RECOVERY_SOURCE_FILES
|
|||||||
|
|
||||||
systemtask/SystemTask.cpp
|
systemtask/SystemTask.cpp
|
||||||
systemtask/SystemMonitor.cpp
|
systemtask/SystemMonitor.cpp
|
||||||
systemtask/WakeLock.cpp
|
|
||||||
drivers/TwiMaster.cpp
|
drivers/TwiMaster.cpp
|
||||||
components/rle/RleDecoder.cpp
|
components/rle/RleDecoder.cpp
|
||||||
components/heartrate/HeartRateController.cpp
|
components/heartrate/HeartRateController.cpp
|
||||||
@@ -592,9 +586,7 @@ set(INCLUDE_FILES
|
|||||||
displayapp/TouchEvents.h
|
displayapp/TouchEvents.h
|
||||||
displayapp/screens/Screen.h
|
displayapp/screens/Screen.h
|
||||||
displayapp/screens/Tile.h
|
displayapp/screens/Tile.h
|
||||||
displayapp/screens/InfiniPaint.h
|
|
||||||
displayapp/screens/StopWatch.h
|
displayapp/screens/StopWatch.h
|
||||||
displayapp/screens/Paddle.h
|
|
||||||
displayapp/screens/BatteryIcon.h
|
displayapp/screens/BatteryIcon.h
|
||||||
displayapp/screens/BleIcon.h
|
displayapp/screens/BleIcon.h
|
||||||
displayapp/screens/NotificationIcon.h
|
displayapp/screens/NotificationIcon.h
|
||||||
@@ -608,11 +600,7 @@ set(INCLUDE_FILES
|
|||||||
displayapp/Apps.h
|
displayapp/Apps.h
|
||||||
displayapp/screens/Notifications.h
|
displayapp/screens/Notifications.h
|
||||||
displayapp/screens/HeartRate.h
|
displayapp/screens/HeartRate.h
|
||||||
displayapp/screens/Metronome.h
|
|
||||||
displayapp/screens/Motion.h
|
|
||||||
displayapp/screens/Timer.h
|
displayapp/screens/Timer.h
|
||||||
displayapp/screens/Dice.h
|
|
||||||
displayapp/screens/Alarm.h
|
|
||||||
displayapp/Colors.h
|
displayapp/Colors.h
|
||||||
displayapp/widgets/Counter.h
|
displayapp/widgets/Counter.h
|
||||||
displayapp/widgets/PageIndicator.h
|
displayapp/widgets/PageIndicator.h
|
||||||
@@ -662,7 +650,6 @@ set(INCLUDE_FILES
|
|||||||
displayapp/InfiniTimeTheme.h
|
displayapp/InfiniTimeTheme.h
|
||||||
systemtask/SystemTask.h
|
systemtask/SystemTask.h
|
||||||
systemtask/SystemMonitor.h
|
systemtask/SystemMonitor.h
|
||||||
systemtask/WakeLock.h
|
|
||||||
displayapp/screens/Symbols.h
|
displayapp/screens/Symbols.h
|
||||||
drivers/TwiMaster.h
|
drivers/TwiMaster.h
|
||||||
heartratetask/HeartRateTask.h
|
heartratetask/HeartRateTask.h
|
||||||
@@ -863,6 +850,7 @@ target_compile_options(nrf-sdk PRIVATE
|
|||||||
$<$<CONFIG:RELEASE>: ${RELEASE_FLAGS}>
|
$<$<CONFIG:RELEASE>: ${RELEASE_FLAGS}>
|
||||||
$<$<COMPILE_LANGUAGE:CXX>: ${CXX_FLAGS}>
|
$<$<COMPILE_LANGUAGE:CXX>: ${CXX_FLAGS}>
|
||||||
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
|
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
|
||||||
|
-O3
|
||||||
)
|
)
|
||||||
|
|
||||||
# NimBLE
|
# NimBLE
|
||||||
@@ -901,13 +889,25 @@ target_compile_options(littlefs PRIVATE
|
|||||||
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
|
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# SUNSET_SRC
|
||||||
|
add_library(sunset STATIC ${SUNSET_SRC})
|
||||||
|
target_include_directories(sunset SYSTEM PUBLIC . ../)
|
||||||
|
target_include_directories(sunset SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
|
||||||
|
target_compile_options(sunset PRIVATE
|
||||||
|
${COMMON_FLAGS}
|
||||||
|
$<$<CONFIG:DEBUG>: ${DEBUG_FLAGS}>
|
||||||
|
$<$<CONFIG:RELEASE>: ${RELEASE_FLAGS}>
|
||||||
|
$<$<COMPILE_LANGUAGE:CXX>: ${CXX_FLAGS}>
|
||||||
|
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
|
||||||
|
)
|
||||||
|
|
||||||
# Build autonomous binary (without support for bootloader)
|
# Build autonomous binary (without support for bootloader)
|
||||||
set(EXECUTABLE_NAME "pinetime-app")
|
set(EXECUTABLE_NAME "pinetime-app")
|
||||||
set(EXECUTABLE_FILE_NAME ${EXECUTABLE_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
|
set(EXECUTABLE_FILE_NAME ${EXECUTABLE_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
|
||||||
set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld")
|
set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld")
|
||||||
add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
|
add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
|
||||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME})
|
set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME})
|
||||||
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs infinitime_fonts infinitime_apps)
|
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs sunset infinitime_fonts infinitime_apps)
|
||||||
target_compile_options(${EXECUTABLE_NAME} PUBLIC
|
target_compile_options(${EXECUTABLE_NAME} PUBLIC
|
||||||
${COMMON_FLAGS}
|
${COMMON_FLAGS}
|
||||||
${WARNING_FLAGS}
|
${WARNING_FLAGS}
|
||||||
@@ -941,7 +941,7 @@ set(IMAGE_MCUBOOT_FILE_NAME_BIN ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERS
|
|||||||
set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
|
set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
|
||||||
set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld")
|
set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld")
|
||||||
add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES})
|
add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES})
|
||||||
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs infinitime_fonts infinitime_apps)
|
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs sunset infinitime_fonts infinitime_apps)
|
||||||
set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME})
|
set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME})
|
||||||
target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC
|
target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC
|
||||||
${COMMON_FLAGS}
|
${COMMON_FLAGS}
|
||||||
@@ -983,7 +983,7 @@ endif()
|
|||||||
set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery")
|
set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery")
|
||||||
set(EXECUTABLE_RECOVERY_FILE_NAME ${EXECUTABLE_RECOVERY_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
|
set(EXECUTABLE_RECOVERY_FILE_NAME ${EXECUTABLE_RECOVERY_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
|
||||||
add_executable(${EXECUTABLE_RECOVERY_NAME} ${RECOVERY_SOURCE_FILES})
|
add_executable(${EXECUTABLE_RECOVERY_NAME} ${RECOVERY_SOURCE_FILES})
|
||||||
target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs infinitime_fonts infinitime_apps)
|
target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs sunset infinitime_fonts infinitime_apps)
|
||||||
set_target_properties(${EXECUTABLE_RECOVERY_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_FILE_NAME})
|
set_target_properties(${EXECUTABLE_RECOVERY_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_FILE_NAME})
|
||||||
target_compile_definitions(${EXECUTABLE_RECOVERY_NAME} PUBLIC "PINETIME_IS_RECOVERY")
|
target_compile_definitions(${EXECUTABLE_RECOVERY_NAME} PUBLIC "PINETIME_IS_RECOVERY")
|
||||||
target_compile_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC
|
target_compile_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC
|
||||||
@@ -1015,7 +1015,7 @@ set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-$
|
|||||||
set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME_HEX ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.hex)
|
set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME_HEX ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.hex)
|
||||||
set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
|
set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
|
||||||
add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES})
|
add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES})
|
||||||
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs infinitime_fonts infinitime_apps)
|
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs sunset infinitime_fonts infinitime_apps)
|
||||||
set_target_properties(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME})
|
set_target_properties(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME})
|
||||||
target_compile_definitions(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC "PINETIME_IS_RECOVERY")
|
target_compile_definitions(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC "PINETIME_IS_RECOVERY")
|
||||||
target_compile_options(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC
|
target_compile_options(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC
|
||||||
|
|||||||
@@ -62,7 +62,8 @@
|
|||||||
#define configTICK_RATE_HZ 1024
|
#define configTICK_RATE_HZ 1024
|
||||||
#define configMAX_PRIORITIES (3)
|
#define configMAX_PRIORITIES (3)
|
||||||
#define configMINIMAL_STACK_SIZE (120)
|
#define configMINIMAL_STACK_SIZE (120)
|
||||||
#define configTOTAL_HEAP_SIZE (1024 * 40)
|
// how much heap can one smartwatch need, michael, 40kb?
|
||||||
|
#define configTOTAL_HEAP_SIZE (1024 * 39)
|
||||||
#define configMAX_TASK_NAME_LEN (4)
|
#define configMAX_TASK_NAME_LEN (4)
|
||||||
#define configUSE_16_BIT_TICKS 0
|
#define configUSE_16_BIT_TICKS 0
|
||||||
#define configIDLE_SHOULD_YIELD 1
|
#define configIDLE_SHOULD_YIELD 1
|
||||||
|
|||||||
@@ -19,13 +19,11 @@
|
|||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <libraries/log/nrf_log.h>
|
|
||||||
|
|
||||||
using namespace Pinetime::Controllers;
|
using namespace Pinetime::Controllers;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
AlarmController::AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs)
|
AlarmController::AlarmController(Controllers::DateTime& dateTimeController) : dateTimeController {dateTimeController} {
|
||||||
: dateTimeController {dateTimeController}, fs {fs} {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -38,28 +36,11 @@ namespace {
|
|||||||
void AlarmController::Init(System::SystemTask* systemTask) {
|
void AlarmController::Init(System::SystemTask* systemTask) {
|
||||||
this->systemTask = systemTask;
|
this->systemTask = systemTask;
|
||||||
alarmTimer = xTimerCreate("Alarm", 1, pdFALSE, this, SetOffAlarm);
|
alarmTimer = xTimerCreate("Alarm", 1, pdFALSE, this, SetOffAlarm);
|
||||||
LoadSettingsFromFile();
|
|
||||||
if (alarm.isEnabled) {
|
|
||||||
NRF_LOG_INFO("[AlarmController] Loaded alarm was enabled, scheduling");
|
|
||||||
ScheduleAlarm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlarmController::SaveAlarm() {
|
|
||||||
// verify if it is necessary to save
|
|
||||||
if (alarmChanged) {
|
|
||||||
SaveSettingsToFile();
|
|
||||||
}
|
|
||||||
alarmChanged = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) {
|
void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) {
|
||||||
if (alarm.hours == alarmHr && alarm.minutes == alarmMin) {
|
hours = alarmHr;
|
||||||
return;
|
minutes = alarmMin;
|
||||||
}
|
|
||||||
alarm.hours = alarmHr;
|
|
||||||
alarm.minutes = alarmMin;
|
|
||||||
alarmChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::ScheduleAlarm() {
|
void AlarmController::ScheduleAlarm() {
|
||||||
@@ -72,19 +53,18 @@ void AlarmController::ScheduleAlarm() {
|
|||||||
tm* tmAlarmTime = std::localtime(&ttAlarmTime);
|
tm* tmAlarmTime = std::localtime(&ttAlarmTime);
|
||||||
|
|
||||||
// If the time being set has already passed today,the alarm should be set for tomorrow
|
// If the time being set has already passed today,the alarm should be set for tomorrow
|
||||||
if (alarm.hours < dateTimeController.Hours() ||
|
if (hours < dateTimeController.Hours() || (hours == dateTimeController.Hours() && minutes <= dateTimeController.Minutes())) {
|
||||||
(alarm.hours == dateTimeController.Hours() && alarm.minutes <= dateTimeController.Minutes())) {
|
|
||||||
tmAlarmTime->tm_mday += 1;
|
tmAlarmTime->tm_mday += 1;
|
||||||
// tm_wday doesn't update automatically
|
// tm_wday doesn't update automatically
|
||||||
tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
|
tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmAlarmTime->tm_hour = alarm.hours;
|
tmAlarmTime->tm_hour = hours;
|
||||||
tmAlarmTime->tm_min = alarm.minutes;
|
tmAlarmTime->tm_min = minutes;
|
||||||
tmAlarmTime->tm_sec = 0;
|
tmAlarmTime->tm_sec = 0;
|
||||||
|
|
||||||
// if alarm is in weekday-only mode, make sure it shifts to the next weekday
|
// if alarm is in weekday-only mode, make sure it shifts to the next weekday
|
||||||
if (alarm.recurrence == RecurType::Weekdays) {
|
if (recurrence == RecurType::Weekdays) {
|
||||||
if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day
|
if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day
|
||||||
tmAlarmTime->tm_mday += 1;
|
tmAlarmTime->tm_mday += 1;
|
||||||
} else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
|
} else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
|
||||||
@@ -99,10 +79,7 @@ void AlarmController::ScheduleAlarm() {
|
|||||||
xTimerChangePeriod(alarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0);
|
xTimerChangePeriod(alarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0);
|
||||||
xTimerStart(alarmTimer, 0);
|
xTimerStart(alarmTimer, 0);
|
||||||
|
|
||||||
if (!alarm.isEnabled) {
|
state = AlarmState::Set;
|
||||||
alarm.isEnabled = true;
|
|
||||||
alarmChanged = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AlarmController::SecondsToAlarm() const {
|
uint32_t AlarmController::SecondsToAlarm() const {
|
||||||
@@ -111,72 +88,20 @@ uint32_t AlarmController::SecondsToAlarm() const {
|
|||||||
|
|
||||||
void AlarmController::DisableAlarm() {
|
void AlarmController::DisableAlarm() {
|
||||||
xTimerStop(alarmTimer, 0);
|
xTimerStop(alarmTimer, 0);
|
||||||
isAlerting = false;
|
state = AlarmState::Not_Set;
|
||||||
if (alarm.isEnabled) {
|
|
||||||
alarm.isEnabled = false;
|
|
||||||
alarmChanged = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::SetOffAlarmNow() {
|
void AlarmController::SetOffAlarmNow() {
|
||||||
isAlerting = true;
|
state = AlarmState::Alerting;
|
||||||
systemTask->PushMessage(System::Messages::SetOffAlarm);
|
systemTask->PushMessage(System::Messages::SetOffAlarm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::StopAlerting() {
|
void AlarmController::StopAlerting() {
|
||||||
isAlerting = false;
|
// Alarm state is off unless this is a recurring alarm
|
||||||
// Disable alarm unless it is recurring
|
if (recurrence == RecurType::None) {
|
||||||
if (alarm.recurrence == RecurType::None) {
|
state = AlarmState::Not_Set;
|
||||||
alarm.isEnabled = false;
|
|
||||||
alarmChanged = true;
|
|
||||||
} else {
|
} else {
|
||||||
// set next instance
|
// set next instance
|
||||||
ScheduleAlarm();
|
ScheduleAlarm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::SetRecurrence(RecurType recurrence) {
|
|
||||||
if (alarm.recurrence != recurrence) {
|
|
||||||
alarm.recurrence = recurrence;
|
|
||||||
alarmChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlarmController::LoadSettingsFromFile() {
|
|
||||||
lfs_file_t alarmFile;
|
|
||||||
AlarmSettings alarmBuffer;
|
|
||||||
|
|
||||||
if (fs.FileOpen(&alarmFile, "/.system/alarm.dat", LFS_O_RDONLY) != LFS_ERR_OK) {
|
|
||||||
NRF_LOG_WARNING("[AlarmController] Failed to open alarm data file");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.FileRead(&alarmFile, reinterpret_cast<uint8_t*>(&alarmBuffer), sizeof(alarmBuffer));
|
|
||||||
fs.FileClose(&alarmFile);
|
|
||||||
if (alarmBuffer.version != alarmFormatVersion) {
|
|
||||||
NRF_LOG_WARNING("[AlarmController] Loaded alarm settings has version %u instead of %u, discarding",
|
|
||||||
alarmBuffer.version,
|
|
||||||
alarmFormatVersion);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
alarm = alarmBuffer;
|
|
||||||
NRF_LOG_INFO("[AlarmController] Loaded alarm settings from file");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlarmController::SaveSettingsToFile() const {
|
|
||||||
lfs_dir systemDir;
|
|
||||||
if (fs.DirOpen("/.system", &systemDir) != LFS_ERR_OK) {
|
|
||||||
fs.DirCreate("/.system");
|
|
||||||
}
|
|
||||||
fs.DirClose(&systemDir);
|
|
||||||
lfs_file_t alarmFile;
|
|
||||||
if (fs.FileOpen(&alarmFile, "/.system/alarm.dat", LFS_O_WRONLY | LFS_O_CREAT) != LFS_ERR_OK) {
|
|
||||||
NRF_LOG_WARNING("[AlarmController] Failed to open alarm data file for saving");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.FileWrite(&alarmFile, reinterpret_cast<const uint8_t*>(&alarm), sizeof(alarm));
|
|
||||||
fs.FileClose(&alarmFile);
|
|
||||||
NRF_LOG_INFO("[AlarmController] Saved alarm settings with format version %u to file", alarm.version);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -30,65 +30,47 @@ namespace Pinetime {
|
|||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
class AlarmController {
|
class AlarmController {
|
||||||
public:
|
public:
|
||||||
AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs);
|
AlarmController(Controllers::DateTime& dateTimeController);
|
||||||
|
|
||||||
void Init(System::SystemTask* systemTask);
|
void Init(System::SystemTask* systemTask);
|
||||||
void SaveAlarm();
|
|
||||||
void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
|
void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
|
||||||
void ScheduleAlarm();
|
void ScheduleAlarm();
|
||||||
void DisableAlarm();
|
void DisableAlarm();
|
||||||
void SetOffAlarmNow();
|
void SetOffAlarmNow();
|
||||||
uint32_t SecondsToAlarm() const;
|
uint32_t SecondsToAlarm() const;
|
||||||
void StopAlerting();
|
void StopAlerting();
|
||||||
|
enum class AlarmState { Not_Set, Set, Alerting };
|
||||||
enum class RecurType { None, Daily, Weekdays };
|
enum class RecurType { None, Daily, Weekdays };
|
||||||
|
|
||||||
uint8_t Hours() const {
|
uint8_t Hours() const {
|
||||||
return alarm.hours;
|
return hours;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Minutes() const {
|
uint8_t Minutes() const {
|
||||||
return alarm.minutes;
|
return minutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsAlerting() const {
|
AlarmState State() const {
|
||||||
return isAlerting;
|
return state;
|
||||||
}
|
|
||||||
|
|
||||||
bool IsEnabled() const {
|
|
||||||
return alarm.isEnabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecurType Recurrence() const {
|
RecurType Recurrence() const {
|
||||||
return alarm.recurrence;
|
return recurrence;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetRecurrence(RecurType recurrence);
|
void SetRecurrence(RecurType recurType) {
|
||||||
|
recurrence = recurType;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Versions 255 is reserved for now, so the version field can be made
|
|
||||||
// bigger, should it ever be needed.
|
|
||||||
static constexpr uint8_t alarmFormatVersion = 1;
|
|
||||||
|
|
||||||
struct AlarmSettings {
|
|
||||||
uint8_t version = alarmFormatVersion;
|
|
||||||
uint8_t hours = 7;
|
|
||||||
uint8_t minutes = 0;
|
|
||||||
RecurType recurrence = RecurType::None;
|
|
||||||
bool isEnabled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool isAlerting = false;
|
|
||||||
bool alarmChanged = false;
|
|
||||||
|
|
||||||
Controllers::DateTime& dateTimeController;
|
Controllers::DateTime& dateTimeController;
|
||||||
Controllers::FS& fs;
|
|
||||||
System::SystemTask* systemTask = nullptr;
|
System::SystemTask* systemTask = nullptr;
|
||||||
TimerHandle_t alarmTimer;
|
TimerHandle_t alarmTimer;
|
||||||
AlarmSettings alarm;
|
uint8_t hours = 7;
|
||||||
|
uint8_t minutes = 0;
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> alarmTime;
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> alarmTime;
|
||||||
|
AlarmState state = AlarmState::Not_Set;
|
||||||
void LoadSettingsFromFile();
|
RecurType recurrence = RecurType::None;
|
||||||
void SaveSettingsToFile() const;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,11 +124,9 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
|
|||||||
bootloaderSize,
|
bootloaderSize,
|
||||||
applicationSize);
|
applicationSize);
|
||||||
|
|
||||||
// Wait until SystemTask has disabled sleeping
|
// wait until SystemTask has finished waking up all devices
|
||||||
// This isn't quite correct, as we don't actually know
|
while (systemTask.IsSleeping()) {
|
||||||
// if BleFirmwareUpdateStarted has been received yet
|
vTaskDelay(50); // 50ms
|
||||||
while (!systemTask.IsSleepDisabled()) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dfuImage.Erase();
|
dfuImage.Erase();
|
||||||
|
|||||||
@@ -77,10 +77,6 @@ namespace Pinetime {
|
|||||||
uint16_t ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t const* p_crc);
|
uint16_t ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t const* p_crc);
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr ble_uuid128_t serviceUuid {
|
|
||||||
.u {.type = BLE_UUID_TYPE_128},
|
|
||||||
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pinetime::System::SystemTask& systemTask;
|
Pinetime::System::SystemTask& systemTask;
|
||||||
Pinetime::Controllers::Ble& bleController;
|
Pinetime::Controllers::Ble& bleController;
|
||||||
@@ -94,6 +90,10 @@ namespace Pinetime {
|
|||||||
|
|
||||||
uint16_t revision {0x0008};
|
uint16_t revision {0x0008};
|
||||||
|
|
||||||
|
static constexpr ble_uuid128_t serviceUuid {
|
||||||
|
.u {.type = BLE_UUID_TYPE_128},
|
||||||
|
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
|
||||||
|
|
||||||
static constexpr ble_uuid128_t packetCharacteristicUuid {
|
static constexpr ble_uuid128_t packetCharacteristicUuid {
|
||||||
.u {.type = BLE_UUID_TYPE_128},
|
.u {.type = BLE_UUID_TYPE_128},
|
||||||
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00}};
|
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00}};
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ namespace Pinetime {
|
|||||||
void SubscribeNotification(uint16_t attributeHandle);
|
void SubscribeNotification(uint16_t attributeHandle);
|
||||||
void UnsubscribeNotification(uint16_t attributeHandle);
|
void UnsubscribeNotification(uint16_t attributeHandle);
|
||||||
|
|
||||||
static constexpr uint16_t heartRateServiceId {0x180D};
|
|
||||||
static constexpr ble_uuid16_t heartRateServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateServiceId};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NimbleController& nimble;
|
NimbleController& nimble;
|
||||||
Controllers::HeartRateController& heartRateController;
|
Controllers::HeartRateController& heartRateController;
|
||||||
|
static constexpr uint16_t heartRateServiceId {0x180D};
|
||||||
static constexpr uint16_t heartRateMeasurementId {0x2A37};
|
static constexpr uint16_t heartRateMeasurementId {0x2A37};
|
||||||
|
|
||||||
|
static constexpr ble_uuid16_t heartRateServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateServiceId};
|
||||||
|
|
||||||
static constexpr ble_uuid16_t heartRateMeasurementUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateMeasurementId};
|
static constexpr ble_uuid16_t heartRateMeasurementUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateMeasurementId};
|
||||||
|
|
||||||
struct ble_gatt_chr_def characteristicDefinition[2];
|
struct ble_gatt_chr_def characteristicDefinition[2];
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
#include "components/ble/MusicService.h"
|
#include "components/ble/MusicService.h"
|
||||||
#include "components/ble/NimbleController.h"
|
#include "components/ble/NimbleController.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <FreeRTOS.h>
|
|
||||||
#include <task.h>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
|
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <host/ble_uuid.h>
|
#include <host/ble_uuid.h>
|
||||||
#undef max
|
#undef max
|
||||||
#undef min
|
#undef min
|
||||||
#include <FreeRTOS.h>
|
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
|
|||||||
@@ -158,10 +158,7 @@ void NimbleController::StartAdvertising() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
||||||
fields.uuids16 = &HeartRateService::heartRateServiceUuid;
|
fields.uuids128 = &dfuServiceUuid;
|
||||||
fields.num_uuids16 = 1;
|
|
||||||
fields.uuids16_is_complete = 1;
|
|
||||||
fields.uuids128 = &DfuService::serviceUuid;
|
|
||||||
fields.num_uuids128 = 1;
|
fields.num_uuids128 = 1;
|
||||||
fields.uuids128_is_complete = 1;
|
fields.uuids128_is_complete = 1;
|
||||||
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||||
@@ -454,15 +451,9 @@ void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
|
|||||||
/* Wakeup Spi and SpiNorFlash before accessing the file system
|
/* Wakeup Spi and SpiNorFlash before accessing the file system
|
||||||
* This should be fixed in the FS driver
|
* This should be fixed in the FS driver
|
||||||
*/
|
*/
|
||||||
|
systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
||||||
|
vTaskDelay(10);
|
||||||
// This isn't quite correct
|
|
||||||
// SystemTask could receive EnableSleeping right after passing this check
|
|
||||||
// We need some guarantee that the SystemTask has processed the above message
|
|
||||||
// before we can continue
|
|
||||||
while (!systemTask.IsSleepDisabled()) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
lfs_file_t file_p;
|
lfs_file_t file_p;
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,10 @@ namespace Pinetime {
|
|||||||
uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
|
uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||||
uint8_t fastAdvCount = 0;
|
uint8_t fastAdvCount = 0;
|
||||||
uint8_t bondId[16] = {0};
|
uint8_t bondId[16] = {0};
|
||||||
|
|
||||||
|
ble_uuid128_t dfuServiceUuid {
|
||||||
|
.u {.type = BLE_UUID_TYPE_128},
|
||||||
|
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
|
||||||
};
|
};
|
||||||
|
|
||||||
static NimbleController* nptr;
|
static NimbleController* nptr;
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ namespace {
|
|||||||
std::memcpy(cityName.data(), &dataBuffer[16], 32);
|
std::memcpy(cityName.data(), &dataBuffer[16], 32);
|
||||||
cityName[32] = '\0';
|
cityName[32] = '\0';
|
||||||
return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
|
return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
|
||||||
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[10])),
|
ToInt16(&dataBuffer[10]),
|
||||||
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[12])),
|
ToInt16(&dataBuffer[12]),
|
||||||
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[14])),
|
ToInt16(&dataBuffer[14]),
|
||||||
SimpleWeatherService::Icons {dataBuffer[16 + 32]},
|
SimpleWeatherService::Icons {dataBuffer[16 + 32]},
|
||||||
std::move(cityName));
|
std::move(cityName));
|
||||||
}
|
}
|
||||||
@@ -52,12 +52,12 @@ namespace {
|
|||||||
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
|
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
|
||||||
auto timestamp = static_cast<uint64_t>(ToUInt64(&dataBuffer[2]));
|
auto timestamp = static_cast<uint64_t>(ToUInt64(&dataBuffer[2]));
|
||||||
|
|
||||||
std::array<std::optional<SimpleWeatherService::Forecast::Day>, SimpleWeatherService::MaxNbForecastDays> days;
|
std::array<SimpleWeatherService::Forecast::Day, SimpleWeatherService::MaxNbForecastDays> days;
|
||||||
const uint8_t nbDaysInBuffer = dataBuffer[10];
|
const uint8_t nbDaysInBuffer = dataBuffer[10];
|
||||||
const uint8_t nbDays = std::min(SimpleWeatherService::MaxNbForecastDays, nbDaysInBuffer);
|
const uint8_t nbDays = std::min(SimpleWeatherService::MaxNbForecastDays, nbDaysInBuffer);
|
||||||
for (int i = 0; i < nbDays; i++) {
|
for (int i = 0; i < nbDays; i++) {
|
||||||
days[i] = SimpleWeatherService::Forecast::Day {SimpleWeatherService::Temperature(ToInt16(&dataBuffer[11 + (i * 5)])),
|
days[i] = SimpleWeatherService::Forecast::Day {ToInt16(&dataBuffer[11 + (i * 5)]),
|
||||||
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[13 + (i * 5)])),
|
ToInt16(&dataBuffer[13 + (i * 5)]),
|
||||||
SimpleWeatherService::Icons {dataBuffer[15 + (i * 5)]}};
|
SimpleWeatherService::Icons {dataBuffer[15 + (i * 5)]}};
|
||||||
}
|
}
|
||||||
return SimpleWeatherService::Forecast {timestamp, nbDays, days};
|
return SimpleWeatherService::Forecast {timestamp, nbDays, days};
|
||||||
@@ -80,7 +80,7 @@ int WeatherCallback(uint16_t /*connHandle*/, uint16_t /*attrHandle*/, struct ble
|
|||||||
return static_cast<Pinetime::Controllers::SimpleWeatherService*>(arg)->OnCommand(ctxt);
|
return static_cast<Pinetime::Controllers::SimpleWeatherService*>(arg)->OnCommand(ctxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleWeatherService::SimpleWeatherService(DateTime& dateTimeController) : dateTimeController(dateTimeController) {
|
SimpleWeatherService::SimpleWeatherService(const DateTime& dateTimeController) : dateTimeController(dateTimeController) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleWeatherService::Init() {
|
void SimpleWeatherService::Init() {
|
||||||
@@ -98,9 +98,9 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
|
|||||||
currentWeather = CreateCurrentWeather(dataBuffer);
|
currentWeather = CreateCurrentWeather(dataBuffer);
|
||||||
NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
|
NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
|
||||||
currentWeather->timestamp,
|
currentWeather->timestamp,
|
||||||
currentWeather->temperature.PreciseCelsius(),
|
currentWeather->temperature,
|
||||||
currentWeather->minTemperature.PreciseCelsius(),
|
currentWeather->minTemperature,
|
||||||
currentWeather->maxTemperature.PreciseCelsius(),
|
currentWeather->maxTemperature,
|
||||||
currentWeather->iconId,
|
currentWeather->iconId,
|
||||||
currentWeather->location.data());
|
currentWeather->location.data());
|
||||||
}
|
}
|
||||||
@@ -112,9 +112,9 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
|
|||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
NRF_LOG_INFO("\t[%d] Min: %d - Max : %d - Icon : %d",
|
NRF_LOG_INFO("\t[%d] Min: %d - Max : %d - Icon : %d",
|
||||||
i,
|
i,
|
||||||
forecast->days[i]->minTemperature.PreciseCelsius(),
|
forecast->days[i].minTemperature,
|
||||||
forecast->days[i]->maxTemperature.PreciseCelsius(),
|
forecast->days[i].maxTemperature,
|
||||||
forecast->days[i]->iconId);
|
forecast->days[i].iconId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||||
@@ -40,7 +40,7 @@ namespace Pinetime {
|
|||||||
|
|
||||||
class SimpleWeatherService {
|
class SimpleWeatherService {
|
||||||
public:
|
public:
|
||||||
explicit SimpleWeatherService(DateTime& dateTimeController);
|
explicit SimpleWeatherService(const DateTime& dateTimeController);
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
@@ -61,42 +61,13 @@ namespace Pinetime {
|
|||||||
Unknown = 255
|
Unknown = 255
|
||||||
};
|
};
|
||||||
|
|
||||||
class Temperature {
|
|
||||||
public:
|
|
||||||
explicit Temperature(int16_t raw) : raw {raw} {
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int16_t PreciseCelsius() const {
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int16_t PreciseFahrenheit() const {
|
|
||||||
return raw * 9 / 5 + 3200;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int16_t Celsius() const {
|
|
||||||
return (PreciseCelsius() + 50) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int16_t Fahrenheit() const {
|
|
||||||
return (PreciseFahrenheit() + 50) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Temperature& other) const {
|
|
||||||
return raw == other.raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int16_t raw;
|
|
||||||
};
|
|
||||||
|
|
||||||
using Location = std::array<char, 33>; // 32 char + \0 (end of string)
|
using Location = std::array<char, 33>; // 32 char + \0 (end of string)
|
||||||
|
|
||||||
struct CurrentWeather {
|
struct CurrentWeather {
|
||||||
CurrentWeather(uint64_t timestamp,
|
CurrentWeather(uint64_t timestamp,
|
||||||
Temperature temperature,
|
int16_t temperature,
|
||||||
Temperature minTemperature,
|
int16_t minTemperature,
|
||||||
Temperature maxTemperature,
|
int16_t maxTemperature,
|
||||||
Icons iconId,
|
Icons iconId,
|
||||||
Location&& location)
|
Location&& location)
|
||||||
: timestamp {timestamp},
|
: timestamp {timestamp},
|
||||||
@@ -108,9 +79,9 @@ namespace Pinetime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
Temperature temperature;
|
int16_t temperature;
|
||||||
Temperature minTemperature;
|
int16_t minTemperature;
|
||||||
Temperature maxTemperature;
|
int16_t maxTemperature;
|
||||||
Icons iconId;
|
Icons iconId;
|
||||||
Location location;
|
Location location;
|
||||||
|
|
||||||
@@ -122,14 +93,14 @@ namespace Pinetime {
|
|||||||
uint8_t nbDays;
|
uint8_t nbDays;
|
||||||
|
|
||||||
struct Day {
|
struct Day {
|
||||||
Temperature minTemperature;
|
int16_t minTemperature;
|
||||||
Temperature maxTemperature;
|
int16_t maxTemperature;
|
||||||
Icons iconId;
|
Icons iconId;
|
||||||
|
|
||||||
bool operator==(const Day& other) const;
|
bool operator==(const Day& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<std::optional<Day>, MaxNbForecastDays> days;
|
std::array<Day, MaxNbForecastDays> days;
|
||||||
|
|
||||||
bool operator==(const Forecast& other) const;
|
bool operator==(const Forecast& other) const;
|
||||||
};
|
};
|
||||||
@@ -137,6 +108,10 @@ namespace Pinetime {
|
|||||||
std::optional<CurrentWeather> Current() const;
|
std::optional<CurrentWeather> Current() const;
|
||||||
std::optional<Forecast> GetForecast() const;
|
std::optional<Forecast> GetForecast() const;
|
||||||
|
|
||||||
|
static int16_t CelsiusToFahrenheit(int16_t celsius) {
|
||||||
|
return celsius * 9 / 5 + 3200;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 00050000-78fc-48fe-8e23-433b3a1942d0
|
// 00050000-78fc-48fe-8e23-433b3a1942d0
|
||||||
static constexpr ble_uuid128_t BaseUuid() {
|
static constexpr ble_uuid128_t BaseUuid() {
|
||||||
@@ -165,7 +140,7 @@ namespace Pinetime {
|
|||||||
|
|
||||||
uint16_t eventHandle {};
|
uint16_t eventHandle {};
|
||||||
|
|
||||||
Pinetime::Controllers::DateTime& dateTimeController;
|
const Pinetime::Controllers::DateTime& dateTimeController;
|
||||||
|
|
||||||
std::optional<CurrentWeather> currentWeather;
|
std::optional<CurrentWeather> currentWeather;
|
||||||
std::optional<Forecast> forecast;
|
std::optional<Forecast> forecast;
|
||||||
|
|||||||
@@ -2,138 +2,38 @@
|
|||||||
#include <hal/nrf_gpio.h>
|
#include <hal/nrf_gpio.h>
|
||||||
#include "displayapp/screens/Symbols.h"
|
#include "displayapp/screens/Symbols.h"
|
||||||
#include "drivers/PinMap.h"
|
#include "drivers/PinMap.h"
|
||||||
#include <libraries/delay/nrf_delay.h>
|
|
||||||
using namespace Pinetime::Controllers;
|
using namespace Pinetime::Controllers;
|
||||||
|
|
||||||
namespace {
|
|
||||||
// reinterpret_cast is not constexpr so this is the best we can do
|
|
||||||
static NRF_RTC_Type* const RTC = reinterpret_cast<NRF_RTC_Type*>(NRF_RTC2_BASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrightnessController::Init() {
|
void BrightnessController::Init() {
|
||||||
nrf_gpio_cfg_output(PinMap::LcdBacklightLow);
|
nrf_gpio_cfg_output(PinMap::LcdBacklightLow);
|
||||||
nrf_gpio_cfg_output(PinMap::LcdBacklightMedium);
|
nrf_gpio_cfg_output(PinMap::LcdBacklightMedium);
|
||||||
nrf_gpio_cfg_output(PinMap::LcdBacklightHigh);
|
nrf_gpio_cfg_output(PinMap::LcdBacklightHigh);
|
||||||
|
|
||||||
nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
|
|
||||||
nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
|
|
||||||
nrf_gpio_pin_clear(PinMap::LcdBacklightHigh);
|
|
||||||
|
|
||||||
static_assert(timerFrequency == 32768, "Change the prescaler below");
|
|
||||||
RTC->PRESCALER = 0;
|
|
||||||
// CC1 switches the backlight on (pin transitions from high to low) and resets the counter to 0
|
|
||||||
RTC->CC[1] = timerPeriod;
|
|
||||||
// Enable compare events for CC0,CC1
|
|
||||||
RTC->EVTEN = 0b0000'0000'0000'0011'0000'0000'0000'0000;
|
|
||||||
// Disable all interrupts
|
|
||||||
RTC->INTENCLR = 0b0000'0000'0000'1111'0000'0000'0000'0011;
|
|
||||||
Set(level);
|
Set(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrightnessController::ApplyBrightness(uint16_t rawBrightness) {
|
|
||||||
// The classic off, low, medium, high brightnesses are at {0, timerPeriod, timerPeriod*2, timerPeriod*3}
|
|
||||||
// These brightness levels do not use PWM: they only set/clear the corresponding pins
|
|
||||||
// Any brightness level between the above levels is achieved with efficient RTC based PWM on the next pin up
|
|
||||||
// E.g 2.5*timerPeriod corresponds to medium brightness with 50% PWM on the high pin
|
|
||||||
// Note: Raw brightness does not necessarily correspond to a linear perceived brightness
|
|
||||||
|
|
||||||
uint8_t pin;
|
|
||||||
if (rawBrightness > 2 * timerPeriod) {
|
|
||||||
rawBrightness -= 2 * timerPeriod;
|
|
||||||
pin = PinMap::LcdBacklightHigh;
|
|
||||||
} else if (rawBrightness > timerPeriod) {
|
|
||||||
rawBrightness -= timerPeriod;
|
|
||||||
pin = PinMap::LcdBacklightMedium;
|
|
||||||
} else {
|
|
||||||
pin = PinMap::LcdBacklightLow;
|
|
||||||
}
|
|
||||||
if (rawBrightness == timerPeriod || rawBrightness == 0) {
|
|
||||||
if (lastPin != UNSET) {
|
|
||||||
RTC->TASKS_STOP = 1;
|
|
||||||
nrf_delay_us(rtcStopTime);
|
|
||||||
nrf_ppi_channel_disable(ppiBacklightOff);
|
|
||||||
nrf_ppi_channel_disable(ppiBacklightOn);
|
|
||||||
nrfx_gpiote_out_uninit(lastPin);
|
|
||||||
nrf_gpio_cfg_output(lastPin);
|
|
||||||
}
|
|
||||||
lastPin = UNSET;
|
|
||||||
if (rawBrightness == 0) {
|
|
||||||
nrf_gpio_pin_set(pin);
|
|
||||||
} else {
|
|
||||||
nrf_gpio_pin_clear(pin);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If the pin on which we are doing PWM is changing
|
|
||||||
// Disable old PWM channel (if exists) and set up new one
|
|
||||||
if (lastPin != pin) {
|
|
||||||
if (lastPin != UNSET) {
|
|
||||||
RTC->TASKS_STOP = 1;
|
|
||||||
nrf_delay_us(rtcStopTime);
|
|
||||||
nrf_ppi_channel_disable(ppiBacklightOff);
|
|
||||||
nrf_ppi_channel_disable(ppiBacklightOn);
|
|
||||||
nrfx_gpiote_out_uninit(lastPin);
|
|
||||||
nrf_gpio_cfg_output(lastPin);
|
|
||||||
}
|
|
||||||
nrfx_gpiote_out_config_t gpioteCfg = {.action = NRF_GPIOTE_POLARITY_TOGGLE,
|
|
||||||
.init_state = NRF_GPIOTE_INITIAL_VALUE_LOW,
|
|
||||||
.task_pin = true};
|
|
||||||
APP_ERROR_CHECK(nrfx_gpiote_out_init(pin, &gpioteCfg));
|
|
||||||
nrfx_gpiote_out_task_enable(pin);
|
|
||||||
nrf_ppi_channel_endpoint_setup(ppiBacklightOff,
|
|
||||||
reinterpret_cast<uint32_t>(&RTC->EVENTS_COMPARE[0]),
|
|
||||||
nrfx_gpiote_out_task_addr_get(pin));
|
|
||||||
nrf_ppi_channel_endpoint_setup(ppiBacklightOn,
|
|
||||||
reinterpret_cast<uint32_t>(&RTC->EVENTS_COMPARE[1]),
|
|
||||||
nrfx_gpiote_out_task_addr_get(pin));
|
|
||||||
nrf_ppi_fork_endpoint_setup(ppiBacklightOn, reinterpret_cast<uint32_t>(&RTC->TASKS_CLEAR));
|
|
||||||
nrf_ppi_channel_enable(ppiBacklightOff);
|
|
||||||
nrf_ppi_channel_enable(ppiBacklightOn);
|
|
||||||
} else {
|
|
||||||
// If the pin used for PWM isn't changing, we only need to set the pin state to the initial value (low)
|
|
||||||
RTC->TASKS_STOP = 1;
|
|
||||||
nrf_delay_us(rtcStopTime);
|
|
||||||
// Due to errata 20,179 and the intricacies of RTC timing, keep it simple: override the pin state
|
|
||||||
nrfx_gpiote_out_task_force(pin, false);
|
|
||||||
}
|
|
||||||
// CC0 switches the backlight off (pin transitions from low to high)
|
|
||||||
RTC->CC[0] = rawBrightness;
|
|
||||||
RTC->TASKS_CLEAR = 1;
|
|
||||||
RTC->TASKS_START = 1;
|
|
||||||
lastPin = pin;
|
|
||||||
}
|
|
||||||
switch (pin) {
|
|
||||||
case PinMap::LcdBacklightHigh:
|
|
||||||
nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
|
|
||||||
nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
|
|
||||||
break;
|
|
||||||
case PinMap::LcdBacklightMedium:
|
|
||||||
nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
|
|
||||||
nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
|
|
||||||
break;
|
|
||||||
case PinMap::LcdBacklightLow:
|
|
||||||
nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
|
|
||||||
nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrightnessController::Set(BrightnessController::Levels level) {
|
void BrightnessController::Set(BrightnessController::Levels level) {
|
||||||
this->level = level;
|
this->level = level;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
default:
|
default:
|
||||||
case Levels::High:
|
case Levels::High:
|
||||||
ApplyBrightness(3 * timerPeriod);
|
nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
|
||||||
|
nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
|
||||||
|
nrf_gpio_pin_clear(PinMap::LcdBacklightHigh);
|
||||||
break;
|
break;
|
||||||
case Levels::Medium:
|
case Levels::Medium:
|
||||||
ApplyBrightness(2 * timerPeriod);
|
nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
|
||||||
|
nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
|
||||||
|
nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
|
||||||
break;
|
break;
|
||||||
case Levels::Low:
|
case Levels::Low:
|
||||||
ApplyBrightness(timerPeriod);
|
nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
|
||||||
break;
|
nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
|
||||||
case Levels::AlwaysOn:
|
nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
|
||||||
ApplyBrightness(timerPeriod / 10);
|
|
||||||
break;
|
break;
|
||||||
case Levels::Off:
|
case Levels::Off:
|
||||||
ApplyBrightness(0);
|
nrf_gpio_pin_set(PinMap::LcdBacklightLow);
|
||||||
|
nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
|
||||||
|
nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,11 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "nrf_ppi.h"
|
|
||||||
#include "nrfx_gpiote.h"
|
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
class BrightnessController {
|
class BrightnessController {
|
||||||
public:
|
public:
|
||||||
enum class Levels { Off, AlwaysOn, Low, Medium, High };
|
enum class Levels { Off, Low, Medium, High };
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
void Set(Levels level);
|
void Set(Levels level);
|
||||||
@@ -23,25 +20,6 @@ namespace Pinetime {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Levels level = Levels::High;
|
Levels level = Levels::High;
|
||||||
static constexpr uint8_t UNSET = UINT8_MAX;
|
|
||||||
uint8_t lastPin = UNSET;
|
|
||||||
// Maximum time (μs) it takes for the RTC to fully stop
|
|
||||||
static constexpr uint8_t rtcStopTime = 46;
|
|
||||||
// Frequency of timer used for PWM (Hz)
|
|
||||||
static constexpr uint16_t timerFrequency = 32768;
|
|
||||||
// Backlight PWM frequency (Hz)
|
|
||||||
static constexpr uint16_t pwmFreq = 1000;
|
|
||||||
// Wraparound point in timer ticks
|
|
||||||
// Defines the number of brightness levels between each pin
|
|
||||||
static constexpr uint16_t timerPeriod = timerFrequency / pwmFreq;
|
|
||||||
// Warning: nimble reserves some PPIs
|
|
||||||
// https://github.com/InfiniTimeOrg/InfiniTime/blob/034d83fe6baf1ab3875a34f8cee387e24410a824/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c#L53
|
|
||||||
// SpiMaster uses PPI 0 for an erratum workaround
|
|
||||||
// Channel 1, 2 should be free to use
|
|
||||||
static constexpr nrf_ppi_channel_t ppiBacklightOn = NRF_PPI_CHANNEL1;
|
|
||||||
static constexpr nrf_ppi_channel_t ppiBacklightOff = NRF_PPI_CHANNEL2;
|
|
||||||
|
|
||||||
void ApplyBrightness(uint16_t val);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,22 @@
|
|||||||
#include "components/datetime/DateTimeController.h"
|
#include "components/datetime/DateTimeController.h"
|
||||||
#include <libraries/log/nrf_log.h>
|
#include <libraries/log/nrf_log.h>
|
||||||
#include <systemtask/SystemTask.h>
|
#include <systemtask/SystemTask.h>
|
||||||
#include <hal/nrf_rtc.h>
|
|
||||||
#include "nrf_assert.h"
|
|
||||||
|
|
||||||
using namespace Pinetime::Controllers;
|
using namespace Pinetime::Controllers;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr const char* const DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
char const* DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
||||||
constexpr const char* const DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
|
char const* DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
|
||||||
constexpr const char* const MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
|
char const* MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
|
||||||
constexpr const char* const MonthsStringLow[] =
|
char const* MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||||
{"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
||||||
|
|
||||||
constexpr int compileTimeAtoi(const char* str) {
|
|
||||||
int result = 0;
|
|
||||||
while (*str >= '0' && *str <= '9') {
|
|
||||||
result = result * 10 + *str - '0';
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime::DateTime(Controllers::Settings& settingsController) : settingsController {settingsController} {
|
DateTime::DateTime(Controllers::Settings& settingsController) : settingsController {settingsController} {
|
||||||
mutex = xSemaphoreCreateMutex();
|
|
||||||
ASSERT(mutex != nullptr);
|
|
||||||
xSemaphoreGive(mutex);
|
|
||||||
|
|
||||||
// __DATE__ is a string of the format "MMM DD YYYY", so an offset of 7 gives the start of the year
|
|
||||||
SetTime(compileTimeAtoi(&__DATE__[7]), 1, 1, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTime::SetCurrentTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> t) {
|
void DateTime::SetCurrentTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> t) {
|
||||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
|
||||||
this->currentDateTime = t;
|
this->currentDateTime = t;
|
||||||
UpdateTime(previousSystickCounter, true); // Update internal state without updating the time
|
UpdateTime(previousSystickCounter); // Update internal state without updating the time
|
||||||
xSemaphoreGive(mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
|
void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
|
||||||
@@ -49,19 +29,15 @@ void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour,
|
|||||||
/* .tm_year = */ year - 1900,
|
/* .tm_year = */ year - 1900,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tm.tm_isdst = -1; // Use DST value from local time zone
|
||||||
|
currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
||||||
|
|
||||||
NRF_LOG_INFO("%d %d %d ", day, month, year);
|
NRF_LOG_INFO("%d %d %d ", day, month, year);
|
||||||
NRF_LOG_INFO("%d %d %d ", hour, minute, second);
|
NRF_LOG_INFO("%d %d %d ", hour, minute, second);
|
||||||
|
|
||||||
tm.tm_isdst = -1; // Use DST value from local time zone
|
UpdateTime(previousSystickCounter);
|
||||||
|
|
||||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
|
||||||
currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
|
||||||
UpdateTime(previousSystickCounter, true);
|
|
||||||
xSemaphoreGive(mutex);
|
|
||||||
|
|
||||||
if (systemTask != nullptr) {
|
|
||||||
systemTask->PushMessage(System::Messages::OnNewTime);
|
systemTask->PushMessage(System::Messages::OnNewTime);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTime::SetTimeZone(int8_t timezone, int8_t dst) {
|
void DateTime::SetTimeZone(int8_t timezone, int8_t dst) {
|
||||||
@@ -69,34 +45,25 @@ void DateTime::SetTimeZone(int8_t timezone, int8_t dst) {
|
|||||||
dstOffset = dst;
|
dstOffset = dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> DateTime::CurrentDateTime() {
|
void DateTime::UpdateTime(uint32_t systickCounter) {
|
||||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
|
||||||
UpdateTime(nrf_rtc_counter_get(portNRF_RTC_REG), false);
|
|
||||||
xSemaphoreGive(mutex);
|
|
||||||
return currentDateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DateTime::UpdateTime(uint32_t systickCounter, bool forceUpdate) {
|
|
||||||
// Handle systick counter overflow
|
// Handle systick counter overflow
|
||||||
uint32_t systickDelta = 0;
|
uint32_t systickDelta = 0;
|
||||||
if (systickCounter < previousSystickCounter) {
|
if (systickCounter < previousSystickCounter) {
|
||||||
systickDelta = static_cast<uint32_t>(portNRF_RTC_MAXTICKS) - previousSystickCounter;
|
systickDelta = 0xffffff - previousSystickCounter;
|
||||||
systickDelta += systickCounter + 1;
|
systickDelta += systickCounter + 1;
|
||||||
} else {
|
} else {
|
||||||
systickDelta = systickCounter - previousSystickCounter;
|
systickDelta = systickCounter - previousSystickCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto correctedDelta = systickDelta / configTICK_RATE_HZ;
|
/*
|
||||||
// If a second hasn't passed, there is nothing to do
|
* 1000 ms = 1024 ticks
|
||||||
// If the time has been changed, set forceUpdate to trigger internal state updates
|
*/
|
||||||
if (correctedDelta == 0 && !forceUpdate) {
|
auto correctedDelta = systickDelta / 1024;
|
||||||
return;
|
auto rest = systickDelta % 1024;
|
||||||
}
|
|
||||||
auto rest = systickDelta % configTICK_RATE_HZ;
|
|
||||||
if (systickCounter >= rest) {
|
if (systickCounter >= rest) {
|
||||||
previousSystickCounter = systickCounter - rest;
|
previousSystickCounter = systickCounter - rest;
|
||||||
} else {
|
} else {
|
||||||
previousSystickCounter = static_cast<uint32_t>(portNRF_RTC_MAXTICKS) - (rest - systickCounter - 1);
|
previousSystickCounter = 0xffffff - (rest - systickCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDateTime += std::chrono::seconds(correctedDelta);
|
currentDateTime += std::chrono::seconds(correctedDelta);
|
||||||
@@ -162,7 +129,7 @@ std::string DateTime::FormattedTime() {
|
|||||||
auto hour = Hours();
|
auto hour = Hours();
|
||||||
auto minute = Minutes();
|
auto minute = Minutes();
|
||||||
// Return time as a string in 12- or 24-hour format
|
// Return time as a string in 12- or 24-hour format
|
||||||
char buff[9];
|
char buff[11];
|
||||||
if (settingsController.GetClockType() == ClockType::H12) {
|
if (settingsController.GetClockType() == ClockType::H12) {
|
||||||
uint8_t hour12;
|
uint8_t hour12;
|
||||||
const char* amPmStr;
|
const char* amPmStr;
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "components/settings/Settings.h"
|
#include "components/settings/Settings.h"
|
||||||
#include <FreeRTOS.h>
|
|
||||||
#include <semphr.h>
|
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace System {
|
namespace System {
|
||||||
@@ -41,12 +39,14 @@ namespace Pinetime {
|
|||||||
*
|
*
|
||||||
* used to update difference between utc and local time (see UtcOffset())
|
* used to update difference between utc and local time (see UtcOffset())
|
||||||
*
|
*
|
||||||
* parameters are in quarters of an hour. Following the BLE CTS specification,
|
* parameters are in quarters of an our. Following the BLE CTS specification,
|
||||||
* timezone is expected to be constant over DST which will be reported in
|
* timezone is expected to be constant over DST which will be reported in
|
||||||
* dst field.
|
* dst field.
|
||||||
*/
|
*/
|
||||||
void SetTimeZone(int8_t timezone, int8_t dst);
|
void SetTimeZone(int8_t timezone, int8_t dst);
|
||||||
|
|
||||||
|
void UpdateTime(uint32_t systickCounter);
|
||||||
|
|
||||||
uint16_t Year() const {
|
uint16_t Year() const {
|
||||||
return 1900 + localTime.tm_year;
|
return 1900 + localTime.tm_year;
|
||||||
}
|
}
|
||||||
@@ -124,10 +124,12 @@ namespace Pinetime {
|
|||||||
static const char* MonthShortToStringLow(Months month);
|
static const char* MonthShortToStringLow(Months month);
|
||||||
static const char* DayOfWeekShortToStringLow(Days day);
|
static const char* DayOfWeekShortToStringLow(Days day);
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime();
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
|
||||||
|
return currentDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> UTCDateTime() {
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> UTCDateTime() const {
|
||||||
return CurrentDateTime() - std::chrono::seconds((tzOffset + dstOffset) * 15 * 60);
|
return currentDateTime - std::chrono::seconds((tzOffset + dstOffset) * 15 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::seconds Uptime() const {
|
std::chrono::seconds Uptime() const {
|
||||||
@@ -139,14 +141,10 @@ namespace Pinetime {
|
|||||||
std::string FormattedTime();
|
std::string FormattedTime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateTime(uint32_t systickCounter, bool forceUpdate);
|
|
||||||
|
|
||||||
std::tm localTime;
|
std::tm localTime;
|
||||||
int8_t tzOffset = 0;
|
int8_t tzOffset = 0;
|
||||||
int8_t dstOffset = 0;
|
int8_t dstOffset = 0;
|
||||||
|
|
||||||
SemaphoreHandle_t mutex = nullptr;
|
|
||||||
|
|
||||||
uint32_t previousSystickCounter = 0;
|
uint32_t previousSystickCounter = 0;
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
|
||||||
std::chrono::seconds uptime {0};
|
std::chrono::seconds uptime {0};
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
# Refactoring needed
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
The [PR #2041 - Continuous time updates](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041) highlighted some
|
|
||||||
limitations in the design of DateTimeController: the granularity of the time returned by `DateTime::CurrentDateTime()`
|
|
||||||
is limited by the frequency at which SystemTask calls `DateTime::UpdateTime()`, which is currently set to 100ms.
|
|
||||||
|
|
||||||
@mark9064 provided more details
|
|
||||||
in [this comment](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041#issuecomment-2048528967).
|
|
||||||
|
|
||||||
The [PR #2041 - Continuous time updates](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041) provided some changes
|
|
||||||
to `DateTime` controller that improves the granularity of the time returned by `DateTime::CurrentDateTime()`.
|
|
||||||
|
|
||||||
However, the review showed that `DateTime` cannot be `const` anymore, even when it's only used to get the current time,
|
|
||||||
since `DateTime::CurrentDateTime()` changes the internal state of the instance.
|
|
||||||
|
|
||||||
We tried to identify alternative implementation that would have maintained the "const correctness" but we eventually
|
|
||||||
figured that this would lead to a re-design of `DateTime` which was out of scope of the initial PR (Continuous time
|
|
||||||
updates and always on display).
|
|
||||||
|
|
||||||
So we decided to merge this PR #2041 and agree to fix/improve `DateTime` later on.
|
|
||||||
|
|
||||||
## What needs to be done?
|
|
||||||
|
|
||||||
Improve/redesign `DateTime` so that it
|
|
||||||
|
|
||||||
* provides a very granular (ideally down to the millisecond) date and time via `CurrentDateTime()`.
|
|
||||||
* can be declared/passed as `const` when it's only used to **get** the time.
|
|
||||||
* limits the use of mutex as much as possible (an ideal implementation would not use any mutex, but this might not be
|
|
||||||
possible).
|
|
||||||
* improves the design of `DateTime::Seconds()`, `DateTime::Minutes()`, `DateTime::Hours()`, etc as
|
|
||||||
explained [in this comment](https://github.com/InfiniTimeOrg/InfiniTime/pull/2054#pullrequestreview-2037033105).
|
|
||||||
|
|
||||||
Once this redesign is implemented, all instances/references to `DateTime` should be reviewed and updated to use `const`
|
|
||||||
where appropriate.
|
|
||||||
|
|
||||||
Please check the following PR to get more context about this redesign:
|
|
||||||
|
|
||||||
* [#2041 - Continuous time updates by @mark9064](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041)
|
|
||||||
* [#2054 - Continuous time update - Alternative implementation to #2041 by @JF002](https://github.com/InfiniTimeOrg/InfiniTime/pull/2054)
|
|
||||||
@@ -142,7 +142,7 @@ Ppg::Ppg() {
|
|||||||
spectrum.fill(0.0f);
|
spectrum.fill(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t Ppg::Preprocess(uint16_t hrs, uint16_t als) {
|
int8_t Ppg::Preprocess(uint32_t hrs, uint32_t als) {
|
||||||
if (dataIndex < dataLength) {
|
if (dataIndex < dataLength) {
|
||||||
dataHRS[dataIndex++] = hrs;
|
dataHRS[dataIndex++] = hrs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Pinetime {
|
|||||||
class Ppg {
|
class Ppg {
|
||||||
public:
|
public:
|
||||||
Ppg();
|
Ppg();
|
||||||
int8_t Preprocess(uint16_t hrs, uint16_t als);
|
int8_t Preprocess(uint32_t hrs, uint32_t als);
|
||||||
int HeartRate();
|
int HeartRate();
|
||||||
void Reset(bool resetDaqBuffer);
|
void Reset(bool resetDaqBuffer);
|
||||||
static constexpr int deltaTms = 100;
|
static constexpr int deltaTms = 100;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Pinetime {
|
|||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
class Settings {
|
class Settings {
|
||||||
public:
|
public:
|
||||||
enum class ClockType : uint8_t { H24, H12 };
|
enum class ClockType : uint8_t { H24, H12, FUZZY };
|
||||||
enum class WeatherFormat : uint8_t { Metric, Imperial };
|
enum class WeatherFormat : uint8_t { Metric, Imperial };
|
||||||
enum class Notification : uint8_t { On, Off, Sleep };
|
enum class Notification : uint8_t { On, Off, Sleep };
|
||||||
enum class ChimesOption : uint8_t { None, Hours, HalfHours };
|
enum class ChimesOption : uint8_t { None, Hours, HalfHours };
|
||||||
@@ -50,6 +50,12 @@ namespace Pinetime {
|
|||||||
int colorIndex = 0;
|
int colorIndex = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Location {
|
||||||
|
int16_t latitude;
|
||||||
|
int16_t longitude;
|
||||||
|
int8_t tzOffset;
|
||||||
|
};
|
||||||
|
|
||||||
Settings(Pinetime::Controllers::FS& fs);
|
Settings(Pinetime::Controllers::FS& fs);
|
||||||
|
|
||||||
Settings(const Settings&) = delete;
|
Settings(const Settings&) = delete;
|
||||||
@@ -214,21 +220,6 @@ namespace Pinetime {
|
|||||||
return settings.screenTimeOut;
|
return settings.screenTimeOut;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GetAlwaysOnDisplay() const {
|
|
||||||
return settings.alwaysOnDisplay && GetNotificationStatus() != Notification::Sleep;
|
|
||||||
};
|
|
||||||
|
|
||||||
void SetAlwaysOnDisplaySetting(bool state) {
|
|
||||||
if (state != settings.alwaysOnDisplay) {
|
|
||||||
settingsChanged = true;
|
|
||||||
}
|
|
||||||
settings.alwaysOnDisplay = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetAlwaysOnDisplaySetting() const {
|
|
||||||
return settings.alwaysOnDisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetShakeThreshold(uint16_t thresh) {
|
void SetShakeThreshold(uint16_t thresh) {
|
||||||
if (settings.shakeWakeThreshold != thresh) {
|
if (settings.shakeWakeThreshold != thresh) {
|
||||||
settings.shakeWakeThreshold = thresh;
|
settings.shakeWakeThreshold = thresh;
|
||||||
@@ -290,6 +281,21 @@ namespace Pinetime {
|
|||||||
return settings.stepsGoal;
|
return settings.stepsGoal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SetLocation(Location loc) {
|
||||||
|
if (
|
||||||
|
loc.latitude != settings.location.latitude ||
|
||||||
|
loc.longitude != settings.location.longitude ||
|
||||||
|
loc.tzOffset != settings.location.tzOffset
|
||||||
|
) {
|
||||||
|
settingsChanged = true;
|
||||||
|
}
|
||||||
|
settings.location = loc;
|
||||||
|
};
|
||||||
|
|
||||||
|
Location GetLocation() const {
|
||||||
|
return settings.location;
|
||||||
|
};
|
||||||
|
|
||||||
void SetBleRadioEnabled(bool enabled) {
|
void SetBleRadioEnabled(bool enabled) {
|
||||||
bleRadioEnabled = enabled;
|
bleRadioEnabled = enabled;
|
||||||
};
|
};
|
||||||
@@ -301,15 +307,13 @@ namespace Pinetime {
|
|||||||
private:
|
private:
|
||||||
Pinetime::Controllers::FS& fs;
|
Pinetime::Controllers::FS& fs;
|
||||||
|
|
||||||
static constexpr uint32_t settingsVersion = 0x0008;
|
static constexpr uint32_t settingsVersion = 0x0007;
|
||||||
|
|
||||||
struct SettingsData {
|
struct SettingsData {
|
||||||
uint32_t version = settingsVersion;
|
uint32_t version = settingsVersion;
|
||||||
uint32_t stepsGoal = 10000;
|
uint32_t stepsGoal = 10000;
|
||||||
uint32_t screenTimeOut = 15000;
|
uint32_t screenTimeOut = 15000;
|
||||||
|
|
||||||
bool alwaysOnDisplay = false;
|
|
||||||
|
|
||||||
ClockType clockType = ClockType::H24;
|
ClockType clockType = ClockType::H24;
|
||||||
WeatherFormat weatherFormat = WeatherFormat::Metric;
|
WeatherFormat weatherFormat = WeatherFormat::Metric;
|
||||||
Notification notificationStatus = Notification::On;
|
Notification notificationStatus = Notification::On;
|
||||||
@@ -325,6 +329,8 @@ namespace Pinetime {
|
|||||||
uint16_t shakeWakeThreshold = 150;
|
uint16_t shakeWakeThreshold = 150;
|
||||||
|
|
||||||
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
|
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
|
||||||
|
|
||||||
|
Location location = {(int16_t)44,(int16_t)-123,(int8_t)-8};
|
||||||
};
|
};
|
||||||
|
|
||||||
SettingsData settings;
|
SettingsData settings;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
#include "displayapp/screens/settings/SettingChimes.h"
|
#include "displayapp/screens/settings/SettingChimes.h"
|
||||||
#include "displayapp/screens/settings/SettingShakeThreshold.h"
|
#include "displayapp/screens/settings/SettingShakeThreshold.h"
|
||||||
#include "displayapp/screens/settings/SettingBluetooth.h"
|
#include "displayapp/screens/settings/SettingBluetooth.h"
|
||||||
|
#include "displayapp/screens/settings/SettingLocation.h"
|
||||||
|
|
||||||
#include "libs/lv_conf.h"
|
#include "libs/lv_conf.h"
|
||||||
#include "UserApps.h"
|
#include "UserApps.h"
|
||||||
@@ -81,8 +82,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
|
|||||||
Pinetime::Controllers::AlarmController& alarmController,
|
Pinetime::Controllers::AlarmController& alarmController,
|
||||||
Pinetime::Controllers::BrightnessController& brightnessController,
|
Pinetime::Controllers::BrightnessController& brightnessController,
|
||||||
Pinetime::Controllers::TouchHandler& touchHandler,
|
Pinetime::Controllers::TouchHandler& touchHandler,
|
||||||
Pinetime::Controllers::FS& filesystem,
|
Pinetime::Controllers::FS& filesystem)
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash)
|
|
||||||
: lcd {lcd},
|
: lcd {lcd},
|
||||||
touchPanel {touchPanel},
|
touchPanel {touchPanel},
|
||||||
batteryController {batteryController},
|
batteryController {batteryController},
|
||||||
@@ -98,7 +98,6 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
|
|||||||
brightnessController {brightnessController},
|
brightnessController {brightnessController},
|
||||||
touchHandler {touchHandler},
|
touchHandler {touchHandler},
|
||||||
filesystem {filesystem},
|
filesystem {filesystem},
|
||||||
spiNorFlash {spiNorFlash},
|
|
||||||
lvgl {lcd, filesystem},
|
lvgl {lcd, filesystem},
|
||||||
timer(this, TimerCallback),
|
timer(this, TimerCallback),
|
||||||
controllers {batteryController,
|
controllers {batteryController,
|
||||||
@@ -156,44 +155,6 @@ void DisplayApp::InitHw() {
|
|||||||
lcd.Init();
|
lcd.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
TickType_t DisplayApp::CalculateSleepTime() {
|
|
||||||
// Calculates how many system ticks DisplayApp should sleep before rendering the next AOD frame
|
|
||||||
// Next frame time is frame count * refresh period (ms) * tick rate
|
|
||||||
|
|
||||||
auto RoundedDiv = [](uint32_t a, uint32_t b) {
|
|
||||||
return ((a + (b / 2)) / b);
|
|
||||||
};
|
|
||||||
// RoundedDiv overflows when numerator + (denominator floordiv 2) > uint32 max
|
|
||||||
// in this case around 9 hours (=overflow frame count / always on refresh period)
|
|
||||||
constexpr TickType_t overflowFrameCount = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod);
|
|
||||||
|
|
||||||
TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime;
|
|
||||||
// Divide both the numerator and denominator by 8 (=GCD(1000,1024))
|
|
||||||
// to increase the number of ticks (frames) before the overflow tick is reached
|
|
||||||
TickType_t targetRenderTick = RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnFrameCount * alwaysOnRefreshPeriod, 1000 / 8);
|
|
||||||
|
|
||||||
// Assumptions
|
|
||||||
|
|
||||||
// Tick rate is multiple of 8
|
|
||||||
// Needed for division trick above
|
|
||||||
static_assert(configTICK_RATE_HZ % 8 == 0);
|
|
||||||
|
|
||||||
// Frame count must always wraparound more often than the system tick count does
|
|
||||||
// Always on overflow time (ms) < system tick overflow time (ms)
|
|
||||||
// Using 64bit ints here to avoid overflow
|
|
||||||
static_assert((uint64_t) overflowFrameCount * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ);
|
|
||||||
|
|
||||||
if (alwaysOnFrameCount == overflowFrameCount) {
|
|
||||||
alwaysOnFrameCount = 0;
|
|
||||||
alwaysOnStartTime = xTaskGetTickCount();
|
|
||||||
}
|
|
||||||
if (targetRenderTick > ticksElapsed) {
|
|
||||||
return targetRenderTick - ticksElapsed;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayApp::Refresh() {
|
void DisplayApp::Refresh() {
|
||||||
auto LoadPreviousScreen = [this]() {
|
auto LoadPreviousScreen = [this]() {
|
||||||
FullRefreshDirections returnDirection;
|
FullRefreshDirections returnDirection;
|
||||||
@@ -217,6 +178,21 @@ void DisplayApp::Refresh() {
|
|||||||
LoadScreen(returnAppStack.Pop(), returnDirection);
|
LoadScreen(returnAppStack.Pop(), returnDirection);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto DimScreen = [this]() {
|
||||||
|
if (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
|
||||||
|
isDimmed = true;
|
||||||
|
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto RestoreBrightness = [this]() {
|
||||||
|
if (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
|
||||||
|
isDimmed = false;
|
||||||
|
lv_disp_trig_activity(nullptr);
|
||||||
|
ApplyBrightness();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
auto IsPastDimTime = [this]() -> bool {
|
auto IsPastDimTime = [this]() -> bool {
|
||||||
return lv_disp_get_inactive_time(nullptr) >= pdMS_TO_TICKS(settingsController.GetScreenTimeOut() - 2000);
|
return lv_disp_get_inactive_time(nullptr) >= pdMS_TO_TICKS(settingsController.GetScreenTimeOut() - 2000);
|
||||||
};
|
};
|
||||||
@@ -230,27 +206,6 @@ void DisplayApp::Refresh() {
|
|||||||
case States::Idle:
|
case States::Idle:
|
||||||
queueTimeout = portMAX_DELAY;
|
queueTimeout = portMAX_DELAY;
|
||||||
break;
|
break;
|
||||||
case States::AOD:
|
|
||||||
if (!currentScreen->IsRunning()) {
|
|
||||||
LoadPreviousScreen();
|
|
||||||
}
|
|
||||||
// Check we've slept long enough
|
|
||||||
// Might not be true if the loop received an event
|
|
||||||
// If not true, then wait that amount of time
|
|
||||||
queueTimeout = CalculateSleepTime();
|
|
||||||
if (queueTimeout == 0) {
|
|
||||||
// Only advance the tick count when LVGL is done
|
|
||||||
// Otherwise keep running the task handler while it still has things to draw
|
|
||||||
// Note: under high graphics load, LVGL will always have more work to do
|
|
||||||
if (lv_task_handler() > 0) {
|
|
||||||
// Drop frames that we've missed if drawing/event handling took way longer than expected
|
|
||||||
while (queueTimeout == 0) {
|
|
||||||
alwaysOnFrameCount += 1;
|
|
||||||
queueTimeout = CalculateSleepTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case States::Running:
|
case States::Running:
|
||||||
if (!currentScreen->IsRunning()) {
|
if (!currentScreen->IsRunning()) {
|
||||||
LoadPreviousScreen();
|
LoadPreviousScreen();
|
||||||
@@ -259,27 +214,14 @@ void DisplayApp::Refresh() {
|
|||||||
|
|
||||||
if (!systemTask->IsSleepDisabled() && IsPastDimTime()) {
|
if (!systemTask->IsSleepDisabled() && IsPastDimTime()) {
|
||||||
if (!isDimmed) {
|
if (!isDimmed) {
|
||||||
isDimmed = true;
|
DimScreen();
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
|
||||||
}
|
}
|
||||||
if (IsPastSleepTime() && uxQueueMessagesWaiting(msgQueue) == 0) {
|
if (IsPastSleepTime()) {
|
||||||
PushMessageToSystemTask(System::Messages::GoToSleep);
|
systemTask->PushMessage(System::Messages::GoToSleep);
|
||||||
// Can't set state to Idle here, something may send
|
state = States::Idle;
|
||||||
// DisableSleeping before this GoToSleep arrives
|
|
||||||
// Instead we check we have no messages queued before sending GoToSleep
|
|
||||||
// This works as the SystemTask is higher priority than DisplayApp
|
|
||||||
// As soon as we send GoToSleep, SystemTask pre-empts DisplayApp
|
|
||||||
// Whenever DisplayApp is running again, it is guaranteed that
|
|
||||||
// SystemTask has handled the message
|
|
||||||
// If it responded, we will have a GoToSleep waiting in the queue
|
|
||||||
// By checking that there are no messages in the queue, we avoid
|
|
||||||
// resending GoToSleep when we already have a response
|
|
||||||
// SystemTask is resilient to duplicate messages, this is an
|
|
||||||
// optimisation to reduce pressure on the message queues
|
|
||||||
}
|
}
|
||||||
} else if (isDimmed) {
|
} else if (isDimmed) {
|
||||||
isDimmed = false;
|
RestoreBrightness();
|
||||||
ApplyBrightness();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -290,73 +232,30 @@ void DisplayApp::Refresh() {
|
|||||||
Messages msg;
|
Messages msg;
|
||||||
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
|
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Messages::GoToSleep:
|
case Messages::DimScreen:
|
||||||
case Messages::GoToAOD:
|
DimScreen();
|
||||||
// Checking if SystemTask is sleeping is purely an optimisation.
|
|
||||||
// If it's no longer sleeping since it sent GoToSleep, it has
|
|
||||||
// cancelled the sleep and transitioned directly from
|
|
||||||
// GoingToSleep->Running, so we are about to receive GoToRunning
|
|
||||||
// and can ignore this message. If it wasn't ignored, DisplayApp
|
|
||||||
// would go to sleep and then immediately re-wake
|
|
||||||
if (state != States::Running || !systemTask->IsSleeping()) {
|
|
||||||
break;
|
break;
|
||||||
}
|
case Messages::RestoreBrightness:
|
||||||
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) {
|
RestoreBrightness();
|
||||||
|
break;
|
||||||
|
case Messages::GoToSleep:
|
||||||
|
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
|
||||||
brightnessController.Lower();
|
brightnessController.Lower();
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
}
|
}
|
||||||
// Turn brightness down (or set to AlwaysOn mode)
|
|
||||||
if (msg == Messages::GoToAOD) {
|
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::AlwaysOn);
|
|
||||||
} else {
|
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::Off);
|
|
||||||
}
|
|
||||||
// Since the active screen is not really an app, go back to Clock.
|
|
||||||
if (currentApp == Apps::Launcher || currentApp == Apps::Notifications || currentApp == Apps::QuickSettings ||
|
|
||||||
currentApp == Apps::Settings) {
|
|
||||||
LoadScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
|
|
||||||
// Wait for the clock app to load before moving on.
|
|
||||||
while (!lv_task_handler()) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Clear any ongoing touch pressed events
|
|
||||||
// Without this LVGL gets stuck in the pressed state and will keep refreshing the
|
|
||||||
// display activity timer causing the screen to never sleep after timeout
|
|
||||||
lvgl.ClearTouchState();
|
|
||||||
if (msg == Messages::GoToAOD) {
|
|
||||||
lcd.LowPowerOn();
|
|
||||||
// Record idle entry time
|
|
||||||
alwaysOnFrameCount = 0;
|
|
||||||
alwaysOnStartTime = xTaskGetTickCount();
|
|
||||||
PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskAOD);
|
|
||||||
state = States::AOD;
|
|
||||||
} else {
|
|
||||||
lcd.Sleep();
|
lcd.Sleep();
|
||||||
PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskSleeping);
|
PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskSleeping);
|
||||||
state = States::Idle;
|
state = States::Idle;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Messages::NotifyDeviceActivity:
|
|
||||||
lv_disp_trig_activity(nullptr);
|
|
||||||
break;
|
break;
|
||||||
case Messages::GoToRunning:
|
case Messages::GoToRunning:
|
||||||
// If SystemTask is sleeping, the GoToRunning message is old
|
|
||||||
// and must be ignored. Otherwise DisplayApp will use SPI
|
|
||||||
// that is powered down and cause bad behaviour
|
|
||||||
if (state == States::Running || systemTask->IsSleeping()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (state == States::AOD) {
|
|
||||||
lcd.LowPowerOff();
|
|
||||||
} else {
|
|
||||||
lcd.Wakeup();
|
lcd.Wakeup();
|
||||||
}
|
|
||||||
lv_disp_trig_activity(nullptr);
|
lv_disp_trig_activity(nullptr);
|
||||||
ApplyBrightness();
|
ApplyBrightness();
|
||||||
state = States::Running;
|
state = States::Running;
|
||||||
break;
|
break;
|
||||||
case Messages::UpdateBleConnection:
|
case Messages::UpdateBleConnection:
|
||||||
// Only used for recovery firmware
|
// clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected :
|
||||||
|
// Screens::Clock::BleConnectionStates::NotConnected);
|
||||||
break;
|
break;
|
||||||
case Messages::NewNotification:
|
case Messages::NewNotification:
|
||||||
LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down);
|
LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down);
|
||||||
@@ -471,17 +370,22 @@ void DisplayApp::Refresh() {
|
|||||||
case Messages::BleRadioEnableToggle:
|
case Messages::BleRadioEnableToggle:
|
||||||
PushMessageToSystemTask(System::Messages::BleRadioEnableToggle);
|
PushMessageToSystemTask(System::Messages::BleRadioEnableToggle);
|
||||||
break;
|
break;
|
||||||
|
case Messages::UpdateDateTime:
|
||||||
|
// Added to remove warning
|
||||||
|
// What should happen here?
|
||||||
|
break;
|
||||||
case Messages::Chime:
|
case Messages::Chime:
|
||||||
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
|
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
|
||||||
motorController.RunForDuration(35);
|
motorController.RunForDuration(35);
|
||||||
break;
|
break;
|
||||||
case Messages::OnChargingEvent:
|
case Messages::OnChargingEvent:
|
||||||
|
RestoreBrightness();
|
||||||
motorController.RunForDuration(15);
|
motorController.RunForDuration(15);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == States::Running && touchHandler.IsTouching()) {
|
if (touchHandler.IsTouching()) {
|
||||||
currentScreen->OnTouchEvent(touchHandler.GetX(), touchHandler.GetY());
|
currentScreen->OnTouchEvent(touchHandler.GetX(), touchHandler.GetY());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,7 +508,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
|||||||
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
|
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
|
||||||
break;
|
break;
|
||||||
case Apps::SettingDisplay:
|
case Apps::SettingDisplay:
|
||||||
currentScreen = std::make_unique<Screens::SettingDisplay>(settingsController);
|
currentScreen = std::make_unique<Screens::SettingDisplay>(this, settingsController);
|
||||||
break;
|
break;
|
||||||
case Apps::SettingSteps:
|
case Apps::SettingSteps:
|
||||||
currentScreen = std::make_unique<Screens::SettingSteps>(settingsController);
|
currentScreen = std::make_unique<Screens::SettingSteps>(settingsController);
|
||||||
@@ -612,6 +516,9 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
|||||||
case Apps::SettingSetDateTime:
|
case Apps::SettingSetDateTime:
|
||||||
currentScreen = std::make_unique<Screens::SettingSetDateTime>(this, dateTimeController, settingsController);
|
currentScreen = std::make_unique<Screens::SettingSetDateTime>(this, dateTimeController, settingsController);
|
||||||
break;
|
break;
|
||||||
|
case Apps::SettingLocation:
|
||||||
|
currentScreen = std::make_unique<Screens::SettingLocation>(settingsController);
|
||||||
|
break;
|
||||||
case Apps::SettingChimes:
|
case Apps::SettingChimes:
|
||||||
currentScreen = std::make_unique<Screens::SettingChimes>(settingsController);
|
currentScreen = std::make_unique<Screens::SettingChimes>(settingsController);
|
||||||
break;
|
break;
|
||||||
@@ -632,8 +539,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
|||||||
bleController,
|
bleController,
|
||||||
watchdog,
|
watchdog,
|
||||||
motionController,
|
motionController,
|
||||||
touchPanel,
|
touchPanel);
|
||||||
spiNorFlash);
|
|
||||||
break;
|
break;
|
||||||
case Apps::FlashLight:
|
case Apps::FlashLight:
|
||||||
currentScreen = std::make_unique<Screens::FlashLight>(*systemTask, brightnessController);
|
currentScreen = std::make_unique<Screens::FlashLight>(*systemTask, brightnessController);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace Pinetime {
|
|||||||
namespace Applications {
|
namespace Applications {
|
||||||
class DisplayApp {
|
class DisplayApp {
|
||||||
public:
|
public:
|
||||||
enum class States { Idle, Running, AOD };
|
enum class States { Idle, Running };
|
||||||
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
|
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
|
||||||
|
|
||||||
DisplayApp(Drivers::St7789& lcd,
|
DisplayApp(Drivers::St7789& lcd,
|
||||||
@@ -66,8 +66,7 @@ namespace Pinetime {
|
|||||||
Pinetime::Controllers::AlarmController& alarmController,
|
Pinetime::Controllers::AlarmController& alarmController,
|
||||||
Pinetime::Controllers::BrightnessController& brightnessController,
|
Pinetime::Controllers::BrightnessController& brightnessController,
|
||||||
Pinetime::Controllers::TouchHandler& touchHandler,
|
Pinetime::Controllers::TouchHandler& touchHandler,
|
||||||
Pinetime::Controllers::FS& filesystem,
|
Pinetime::Controllers::FS& filesystem);
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash);
|
|
||||||
void Start(System::BootErrors error);
|
void Start(System::BootErrors error);
|
||||||
void PushMessage(Display::Messages msg);
|
void PushMessage(Display::Messages msg);
|
||||||
|
|
||||||
@@ -97,7 +96,6 @@ namespace Pinetime {
|
|||||||
Pinetime::Controllers::BrightnessController& brightnessController;
|
Pinetime::Controllers::BrightnessController& brightnessController;
|
||||||
Pinetime::Controllers::TouchHandler& touchHandler;
|
Pinetime::Controllers::TouchHandler& touchHandler;
|
||||||
Pinetime::Controllers::FS& filesystem;
|
Pinetime::Controllers::FS& filesystem;
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
|
|
||||||
|
|
||||||
Pinetime::Controllers::FirmwareValidator validator;
|
Pinetime::Controllers::FirmwareValidator validator;
|
||||||
Pinetime::Components::LittleVgl lvgl;
|
Pinetime::Components::LittleVgl lvgl;
|
||||||
@@ -137,13 +135,6 @@ namespace Pinetime {
|
|||||||
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
|
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
|
||||||
|
|
||||||
bool isDimmed = false;
|
bool isDimmed = false;
|
||||||
|
|
||||||
TickType_t CalculateSleepTime();
|
|
||||||
TickType_t alwaysOnFrameCount;
|
|
||||||
TickType_t alwaysOnStartTime;
|
|
||||||
// If this is to be changed, make sure the actual always on refresh rate is changed
|
|
||||||
// by configuring the LCD refresh timings
|
|
||||||
static constexpr uint32_t alwaysOnRefreshPeriod = 500;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
|
|||||||
Pinetime::Controllers::AlarmController& /*alarmController*/,
|
Pinetime::Controllers::AlarmController& /*alarmController*/,
|
||||||
Pinetime::Controllers::BrightnessController& /*brightnessController*/,
|
Pinetime::Controllers::BrightnessController& /*brightnessController*/,
|
||||||
Pinetime::Controllers::TouchHandler& /*touchHandler*/,
|
Pinetime::Controllers::TouchHandler& /*touchHandler*/,
|
||||||
Pinetime::Controllers::FS& /*filesystem*/,
|
Pinetime::Controllers::FS& /*filesystem*/)
|
||||||
Pinetime::Drivers::SpiNorFlash& /*spiNorFlash*/)
|
|
||||||
: lcd {lcd}, bleController {bleController} {
|
: lcd {lcd}, bleController {bleController} {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ namespace Pinetime {
|
|||||||
class St7789;
|
class St7789;
|
||||||
class Cst816S;
|
class Cst816S;
|
||||||
class Watchdog;
|
class Watchdog;
|
||||||
class SpiNorFlash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
@@ -60,8 +59,7 @@ namespace Pinetime {
|
|||||||
Pinetime::Controllers::AlarmController& alarmController,
|
Pinetime::Controllers::AlarmController& alarmController,
|
||||||
Pinetime::Controllers::BrightnessController& brightnessController,
|
Pinetime::Controllers::BrightnessController& brightnessController,
|
||||||
Pinetime::Controllers::TouchHandler& touchHandler,
|
Pinetime::Controllers::TouchHandler& touchHandler,
|
||||||
Pinetime::Controllers::FS& filesystem,
|
Pinetime::Controllers::FS& filesystem);
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash);
|
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
void Start(Pinetime::System::BootErrors) {
|
void Start(Pinetime::System::BootErrors) {
|
||||||
|
|||||||
@@ -248,8 +248,6 @@ void LittleVgl::SetNewTouchPoint(int16_t x, int16_t y, bool contact) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel an ongoing tap
|
|
||||||
// Signifies that LVGL should not handle the current tap
|
|
||||||
void LittleVgl::CancelTap() {
|
void LittleVgl::CancelTap() {
|
||||||
if (tapped) {
|
if (tapped) {
|
||||||
isCancelled = true;
|
isCancelled = true;
|
||||||
@@ -257,13 +255,6 @@ void LittleVgl::CancelTap() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the current tapped state
|
|
||||||
// Signifies that touch input processing is suspended
|
|
||||||
void LittleVgl::ClearTouchState() {
|
|
||||||
touchPoint = {-1, -1};
|
|
||||||
tapped = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
|
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
|
||||||
ptr->point.x = touchPoint.x;
|
ptr->point.x = touchPoint.x;
|
||||||
ptr->point.y = touchPoint.y;
|
ptr->point.y = touchPoint.y;
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ namespace Pinetime {
|
|||||||
void SetFullRefresh(FullRefreshDirections direction);
|
void SetFullRefresh(FullRefreshDirections direction);
|
||||||
void SetNewTouchPoint(int16_t x, int16_t y, bool contact);
|
void SetNewTouchPoint(int16_t x, int16_t y, bool contact);
|
||||||
void CancelTap();
|
void CancelTap();
|
||||||
void ClearTouchState();
|
|
||||||
|
|
||||||
bool GetFullRefresh() {
|
bool GetFullRefresh() {
|
||||||
bool returnValue = fullRefresh;
|
bool returnValue = fullRefresh;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ namespace Pinetime {
|
|||||||
namespace Display {
|
namespace Display {
|
||||||
enum class Messages : uint8_t {
|
enum class Messages : uint8_t {
|
||||||
GoToSleep,
|
GoToSleep,
|
||||||
GoToAOD,
|
|
||||||
GoToRunning,
|
GoToRunning,
|
||||||
|
UpdateDateTime,
|
||||||
UpdateBleConnection,
|
UpdateBleConnection,
|
||||||
TouchEvent,
|
TouchEvent,
|
||||||
ButtonPushed,
|
ButtonPushed,
|
||||||
@@ -17,9 +17,8 @@ namespace Pinetime {
|
|||||||
NewNotification,
|
NewNotification,
|
||||||
TimerDone,
|
TimerDone,
|
||||||
BleFirmwareUpdateStarted,
|
BleFirmwareUpdateStarted,
|
||||||
// Resets the screen timeout timer when awake
|
DimScreen,
|
||||||
// Does nothing when asleep
|
RestoreBrightness,
|
||||||
NotifyDeviceActivity,
|
|
||||||
ShowPairingKey,
|
ShowPairingKey,
|
||||||
AlarmTriggered,
|
AlarmTriggered,
|
||||||
Chime,
|
Chime,
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "displayapp/screens/WatchFaceInfineat.h"
|
#include "displayapp/screens/WatchFaceInfineat.h"
|
||||||
#include "displayapp/screens/WatchFacePineTimeStyle.h"
|
#include "displayapp/screens/WatchFacePineTimeStyle.h"
|
||||||
#include "displayapp/screens/WatchFaceTerminal.h"
|
#include "displayapp/screens/WatchFaceTerminal.h"
|
||||||
|
#include "displayapp/screens/WatchFaceFuzzy.h"
|
||||||
|
#include "displayapp/screens/WatchFaceSundial.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Pinetime {
|
|||||||
SettingWakeUp,
|
SettingWakeUp,
|
||||||
SettingSteps,
|
SettingSteps,
|
||||||
SettingSetDateTime,
|
SettingSetDateTime,
|
||||||
|
SettingLocation,
|
||||||
SettingChimes,
|
SettingChimes,
|
||||||
SettingShakeThreshold,
|
SettingShakeThreshold,
|
||||||
SettingBluetooth,
|
SettingBluetooth,
|
||||||
@@ -50,6 +51,8 @@ namespace Pinetime {
|
|||||||
Analog,
|
Analog,
|
||||||
PineTimeStyle,
|
PineTimeStyle,
|
||||||
Terminal,
|
Terminal,
|
||||||
|
Fuzzy,
|
||||||
|
Sundial,
|
||||||
Infineat,
|
Infineat,
|
||||||
CasioStyleG7710,
|
CasioStyleG7710,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,19 +2,11 @@ if(DEFINED ENABLE_USERAPPS)
|
|||||||
set(USERAPP_TYPES ${ENABLE_USERAPPS} CACHE STRING "List of user apps to build into the firmware")
|
set(USERAPP_TYPES ${ENABLE_USERAPPS} CACHE STRING "List of user apps to build into the firmware")
|
||||||
else ()
|
else ()
|
||||||
set(DEFAULT_USER_APP_TYPES "Apps::StopWatch")
|
set(DEFAULT_USER_APP_TYPES "Apps::StopWatch")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Alarm")
|
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Timer")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Timer")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Steps")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Steps")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::HeartRate")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::HeartRate")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Music")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Music")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paint")
|
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paddle")
|
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Twos")
|
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Dice")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Dice")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome")
|
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation")
|
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
|
|
||||||
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion")
|
|
||||||
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
|
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@@ -22,11 +14,8 @@ if(DEFINED ENABLE_WATCHFACES)
|
|||||||
set(WATCHFACE_TYPES ${ENABLE_WATCHFACES} CACHE STRING "List of watch faces to build into the firmware")
|
set(WATCHFACE_TYPES ${ENABLE_WATCHFACES} CACHE STRING "List of watch faces to build into the firmware")
|
||||||
else()
|
else()
|
||||||
set(DEFAULT_WATCHFACE_TYPES "WatchFace::Digital")
|
set(DEFAULT_WATCHFACE_TYPES "WatchFace::Digital")
|
||||||
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Analog")
|
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Fuzzy")
|
||||||
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::PineTimeStyle")
|
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Sundial")
|
||||||
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Terminal")
|
|
||||||
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Infineat")
|
|
||||||
set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::CasioStyleG7710")
|
|
||||||
set(WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}" CACHE STRING "List of watch faces to build into the firmware")
|
set(WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}" CACHE STRING "List of watch faces to build into the firmware")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
set(FONTS jetbrains_mono_42 jetbrains_mono_76 jetbrains_mono_bold_20 jetbrains_mono_14
|
set(FONTS jetbrains_mono_42 jetbrains_mono_76 jetbrains_mono_bold_20 jetbrains_mono_bold_24
|
||||||
jetbrains_mono_extrabold_compressed lv_font_sys_48
|
jetbrains_mono_extrabold_compressed lv_font_sys_48
|
||||||
open_sans_light fontawesome_weathericons)
|
open_sans_light fontawesome_weathericons)
|
||||||
find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED
|
find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED
|
||||||
@@ -11,7 +11,6 @@ configure_file(${CMAKE_CURRENT_LIST_DIR}/jetbrains_mono_bold_20.c_M.patch
|
|||||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
||||||
# FindPython3 module introduces with CMake 3.12
|
# FindPython3 module introduces with CMake 3.12
|
||||||
# https://cmake.org/cmake/help/latest/module/FindPython3.html
|
# https://cmake.org/cmake/help/latest/module/FindPython3.html
|
||||||
set(Python3_FIND_STRATEGY LOCATION) # https://discourse.cmake.org/t/find-package-python3-is-not-finding-the-correct-python/10563
|
|
||||||
find_package(Python3 REQUIRED)
|
find_package(Python3 REQUIRED)
|
||||||
else()
|
else()
|
||||||
set(Python3_EXECUTABLE "python")
|
set(Python3_EXECUTABLE "python")
|
||||||
|
|||||||
@@ -1,23 +1,9 @@
|
|||||||
{
|
{
|
||||||
"jetbrains_mono_14": {
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"file": "JetBrainsMono-Regular.ttf",
|
|
||||||
"range": "0x20-0x7e, 0x410-0x44f, 0xB0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "NotoSerifCJKjp-VF.ttf",
|
|
||||||
"symbols": "大雪"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"bpp": 1,
|
|
||||||
"size": 14
|
|
||||||
},
|
|
||||||
"jetbrains_mono_bold_20": {
|
"jetbrains_mono_bold_20": {
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"file": "JetBrainsMono-Bold.ttf",
|
"file": "Vulf_Mono-Italic.woff",
|
||||||
"range": "0x20-0x7e, 0x410-0x44f, 0xB0"
|
"range": "0x20-0x7e, 0xB0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "FontAwesome5-Solid+Brands+Regular.woff",
|
"file": "FontAwesome5-Solid+Brands+Regular.woff",
|
||||||
@@ -25,13 +11,26 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bpp": 1,
|
"bpp": 1,
|
||||||
"size": 20,
|
"size": 20
|
||||||
"patches": ["jetbrains_mono_bold_20.c_zero.patch", "jetbrains_mono_bold_20.c_M.patch"]
|
},
|
||||||
|
"jetbrains_mono_bold_24": {
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"file": "Vulf_Mono-Italic.woff",
|
||||||
|
"range": "0x20-0x7e, 0xB0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "FontAwesome5-Solid+Brands+Regular.woff",
|
||||||
|
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bpp": 1,
|
||||||
|
"size": 26
|
||||||
},
|
},
|
||||||
"jetbrains_mono_42": {
|
"jetbrains_mono_42": {
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"file": "JetBrainsMono-Regular.ttf",
|
"file": "Vulf Mono Light Italic.ttf",
|
||||||
"range": "0x25, 0x2b, 0x2d, 0x2e, 0x30-0x3a, 0x43, 0x46, 0x4b-0x4d, 0x66, 0x69, 0x6b, 0x6d, 0x74, 0xb0"
|
"range": "0x25, 0x2b, 0x2d, 0x2e, 0x30-0x3a, 0x43, 0x46, 0x4b-0x4d, 0x66, 0x69, 0x6b, 0x6d, 0x74, 0xb0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -68,16 +67,6 @@
|
|||||||
"bpp": 1,
|
"bpp": 1,
|
||||||
"size": 150
|
"size": 150
|
||||||
},
|
},
|
||||||
"noto_serif_cjk_14": {
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"file": "NotoSerifCJKjp-VF.ttf",
|
|
||||||
"symbols": "大雪"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"bpp": 1,
|
|
||||||
"size": 14
|
|
||||||
},
|
|
||||||
"lv_font_sys_48": {
|
"lv_font_sys_48": {
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
|
|||||||
Controllers::Settings::ClockType clockType,
|
Controllers::Settings::ClockType clockType,
|
||||||
System::SystemTask& systemTask,
|
System::SystemTask& systemTask,
|
||||||
Controllers::MotorController& motorController)
|
Controllers::MotorController& motorController)
|
||||||
: alarmController {alarmController}, wakeLock(systemTask), motorController {motorController} {
|
: alarmController {alarmController}, systemTask {systemTask}, motorController {motorController} {
|
||||||
|
|
||||||
hourCounter.Create();
|
hourCounter.Create();
|
||||||
lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||||
@@ -117,7 +117,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
|
|||||||
|
|
||||||
UpdateAlarmTime();
|
UpdateAlarmTime();
|
||||||
|
|
||||||
if (alarmController.IsAlerting()) {
|
if (alarmController.State() == Controllers::AlarmController::AlarmState::Alerting) {
|
||||||
SetAlerting();
|
SetAlerting();
|
||||||
} else {
|
} else {
|
||||||
SetSwitchState(LV_ANIM_OFF);
|
SetSwitchState(LV_ANIM_OFF);
|
||||||
@@ -125,15 +125,14 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Alarm::~Alarm() {
|
Alarm::~Alarm() {
|
||||||
if (alarmController.IsAlerting()) {
|
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
|
||||||
StopAlerting();
|
StopAlerting();
|
||||||
}
|
}
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
alarmController.SaveAlarm();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::DisableAlarm() {
|
void Alarm::DisableAlarm() {
|
||||||
if (alarmController.IsEnabled()) {
|
if (alarmController.State() == AlarmController::AlarmState::Set) {
|
||||||
alarmController.DisableAlarm();
|
alarmController.DisableAlarm();
|
||||||
lv_switch_off(enableSwitch, LV_ANIM_ON);
|
lv_switch_off(enableSwitch, LV_ANIM_ON);
|
||||||
}
|
}
|
||||||
@@ -173,7 +172,7 @@ bool Alarm::OnButtonPushed() {
|
|||||||
HideInfo();
|
HideInfo();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (alarmController.IsAlerting()) {
|
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
|
||||||
StopAlerting();
|
StopAlerting();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -182,7 +181,7 @@ bool Alarm::OnButtonPushed() {
|
|||||||
|
|
||||||
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||||
// Don't allow closing the screen by swiping while the alarm is alerting
|
// Don't allow closing the screen by swiping while the alarm is alerting
|
||||||
return alarmController.IsAlerting() && event == TouchEvents::SwipeDown;
|
return alarmController.State() == AlarmController::AlarmState::Alerting && event == TouchEvents::SwipeDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::OnValueChanged() {
|
void Alarm::OnValueChanged() {
|
||||||
@@ -206,7 +205,7 @@ void Alarm::SetAlerting() {
|
|||||||
lv_obj_set_hidden(btnStop, false);
|
lv_obj_set_hidden(btnStop, false);
|
||||||
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
|
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
|
||||||
motorController.StartRinging();
|
motorController.StartRinging();
|
||||||
wakeLock.Lock();
|
systemTask.PushMessage(System::Messages::DisableSleeping);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::StopAlerting() {
|
void Alarm::StopAlerting() {
|
||||||
@@ -217,16 +216,21 @@ void Alarm::StopAlerting() {
|
|||||||
lv_task_del(taskStopAlarm);
|
lv_task_del(taskStopAlarm);
|
||||||
taskStopAlarm = nullptr;
|
taskStopAlarm = nullptr;
|
||||||
}
|
}
|
||||||
wakeLock.Release();
|
systemTask.PushMessage(System::Messages::EnableSleeping);
|
||||||
lv_obj_set_hidden(enableSwitch, false);
|
lv_obj_set_hidden(enableSwitch, false);
|
||||||
lv_obj_set_hidden(btnStop, true);
|
lv_obj_set_hidden(btnStop, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
|
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
|
||||||
if (alarmController.IsEnabled()) {
|
switch (alarmController.State()) {
|
||||||
|
case AlarmController::AlarmState::Set:
|
||||||
lv_switch_on(enableSwitch, anim);
|
lv_switch_on(enableSwitch, anim);
|
||||||
} else {
|
break;
|
||||||
|
case AlarmController::AlarmState::Not_Set:
|
||||||
lv_switch_off(enableSwitch, anim);
|
lv_switch_off(enableSwitch, anim);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +247,7 @@ void Alarm::ShowInfo() {
|
|||||||
txtMessage = lv_label_create(btnMessage, nullptr);
|
txtMessage = lv_label_create(btnMessage, nullptr);
|
||||||
lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
|
lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
|
||||||
|
|
||||||
if (alarmController.IsEnabled()) {
|
if (alarmController.State() == AlarmController::AlarmState::Set) {
|
||||||
auto timeToAlarm = alarmController.SecondsToAlarm();
|
auto timeToAlarm = alarmController.SecondsToAlarm();
|
||||||
|
|
||||||
auto daysToAlarm = timeToAlarm / 86400;
|
auto daysToAlarm = timeToAlarm / 86400;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "displayapp/widgets/Counter.h"
|
#include "displayapp/widgets/Counter.h"
|
||||||
#include "displayapp/Controllers.h"
|
#include "displayapp/Controllers.h"
|
||||||
#include "systemtask/WakeLock.h"
|
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
@@ -44,7 +43,7 @@ namespace Pinetime {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Controllers::AlarmController& alarmController;
|
Controllers::AlarmController& alarmController;
|
||||||
System::WakeLock wakeLock;
|
System::SystemTask& systemTask;
|
||||||
Controllers::MotorController& motorController;
|
Controllers::MotorController& motorController;
|
||||||
|
|
||||||
lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch;
|
lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch;
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ namespace {
|
|||||||
FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& validator) : validator {validator} {
|
FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& validator) : validator {validator} {
|
||||||
labelVersion = lv_label_create(lv_scr_act(), nullptr);
|
labelVersion = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_label_set_text_fmt(labelVersion,
|
lv_label_set_text_fmt(labelVersion,
|
||||||
"Version: %lu.%lu.%lu\n"
|
"Version : %lu.%lu.%lu\n"
|
||||||
"ShortRef: %s",
|
"ShortRef : %s",
|
||||||
Version::Major(),
|
Version::Major(),
|
||||||
Version::Minor(),
|
Version::Minor(),
|
||||||
Version::Patch(),
|
Version::Patch(),
|
||||||
|
|||||||
@@ -15,9 +15,8 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessController& brightnessController)
|
FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessController& brightnessController)
|
||||||
: wakeLock(systemTask), brightnessController {brightnessController} {
|
: systemTask {systemTask}, brightnessController {brightnessController} {
|
||||||
|
|
||||||
previousBrightnessLevel = brightnessController.Level();
|
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
||||||
|
|
||||||
flashLight = lv_label_create(lv_scr_act(), nullptr);
|
flashLight = lv_label_create(lv_scr_act(), nullptr);
|
||||||
@@ -47,13 +46,13 @@ FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessCo
|
|||||||
backgroundAction->user_data = this;
|
backgroundAction->user_data = this;
|
||||||
lv_obj_set_event_cb(backgroundAction, EventHandler);
|
lv_obj_set_event_cb(backgroundAction, EventHandler);
|
||||||
|
|
||||||
wakeLock.Lock();
|
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlashLight::~FlashLight() {
|
FlashLight::~FlashLight() {
|
||||||
lv_obj_clean(lv_scr_act());
|
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_BLACK);
|
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||||
brightnessController.Set(previousBrightnessLevel);
|
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlashLight::SetColors() {
|
void FlashLight::SetColors() {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "components/brightness/BrightnessController.h"
|
#include "components/brightness/BrightnessController.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "systemtask/WakeLock.h"
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
@@ -24,11 +23,10 @@ namespace Pinetime {
|
|||||||
void SetIndicators();
|
void SetIndicators();
|
||||||
void SetColors();
|
void SetColors();
|
||||||
|
|
||||||
Pinetime::System::WakeLock wakeLock;
|
Pinetime::System::SystemTask& systemTask;
|
||||||
Controllers::BrightnessController& brightnessController;
|
Controllers::BrightnessController& brightnessController;
|
||||||
|
|
||||||
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
|
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
|
||||||
Controllers::BrightnessController::Levels previousBrightnessLevel;
|
|
||||||
|
|
||||||
lv_obj_t* flashLight;
|
lv_obj_t* flashLight;
|
||||||
lv_obj_t* backgroundAction;
|
lv_obj_t* backgroundAction;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask)
|
HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask)
|
||||||
: heartRateController {heartRateController}, wakeLock(systemTask) {
|
: heartRateController {heartRateController}, systemTask {systemTask} {
|
||||||
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
|
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
|
||||||
label_hr = lv_label_create(lv_scr_act(), nullptr);
|
label_hr = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
|
|||||||
label_startStop = lv_label_create(btn_startStop, nullptr);
|
label_startStop = lv_label_create(btn_startStop, nullptr);
|
||||||
UpdateStartStopButton(isHrRunning);
|
UpdateStartStopButton(isHrRunning);
|
||||||
if (isHrRunning) {
|
if (isHrRunning) {
|
||||||
wakeLock.Lock();
|
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
||||||
}
|
}
|
||||||
|
|
||||||
taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this);
|
taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this);
|
||||||
@@ -72,6 +72,7 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
|
|||||||
HeartRate::~HeartRate() {
|
HeartRate::~HeartRate() {
|
||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
|
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeartRate::Refresh() {
|
void HeartRate::Refresh() {
|
||||||
@@ -100,12 +101,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
|
|||||||
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
|
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
|
||||||
heartRateController.Start();
|
heartRateController.Start();
|
||||||
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
||||||
wakeLock.Lock();
|
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
||||||
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
|
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
|
||||||
} else {
|
} else {
|
||||||
heartRateController.Stop();
|
heartRateController.Stop();
|
||||||
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
||||||
wakeLock.Release();
|
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
||||||
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
|
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "systemtask/WakeLock.h"
|
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include <lvgl/src/lv_core/lv_style.h>
|
#include <lvgl/src/lv_core/lv_style.h>
|
||||||
#include <lvgl/src/lv_core/lv_obj.h>
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
@@ -28,7 +27,7 @@ namespace Pinetime {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Controllers::HeartRateController& heartRateController;
|
Controllers::HeartRateController& heartRateController;
|
||||||
Pinetime::System::WakeLock wakeLock;
|
Pinetime::System::SystemTask& systemTask;
|
||||||
void UpdateStartStopButton(bool isRunning);
|
void UpdateStartStopButton(bool isRunning);
|
||||||
lv_obj_t* label_hr;
|
lv_obj_t* label_hr;
|
||||||
lv_obj_t* label_bpm;
|
lv_obj_t* label_bpm;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Metronome::Metronome(Controllers::MotorController& motorController, System::SystemTask& systemTask)
|
Metronome::Metronome(Controllers::MotorController& motorController, System::SystemTask& systemTask)
|
||||||
: motorController {motorController}, wakeLock(systemTask) {
|
: motorController {motorController}, systemTask {systemTask} {
|
||||||
|
|
||||||
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
|
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
|
||||||
bpmArc->user_data = this;
|
bpmArc->user_data = this;
|
||||||
@@ -72,6 +72,7 @@ Metronome::Metronome(Controllers::MotorController& motorController, System::Syst
|
|||||||
|
|
||||||
Metronome::~Metronome() {
|
Metronome::~Metronome() {
|
||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
|
systemTask.PushMessage(System::Messages::EnableSleeping);
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,12 +128,12 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
|
|||||||
metronomeStarted = !metronomeStarted;
|
metronomeStarted = !metronomeStarted;
|
||||||
if (metronomeStarted) {
|
if (metronomeStarted) {
|
||||||
lv_label_set_text_static(lblPlayPause, Symbols::pause);
|
lv_label_set_text_static(lblPlayPause, Symbols::pause);
|
||||||
wakeLock.Lock();
|
systemTask.PushMessage(System::Messages::DisableSleeping);
|
||||||
startTime = xTaskGetTickCount();
|
startTime = xTaskGetTickCount();
|
||||||
counter = 1;
|
counter = 1;
|
||||||
} else {
|
} else {
|
||||||
lv_label_set_text_static(lblPlayPause, Symbols::play);
|
lv_label_set_text_static(lblPlayPause, Symbols::play);
|
||||||
wakeLock.Release();
|
systemTask.PushMessage(System::Messages::EnableSleeping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "systemtask/WakeLock.h"
|
|
||||||
#include "components/motor/MotorController.h"
|
#include "components/motor/MotorController.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
@@ -22,7 +21,7 @@ namespace Pinetime {
|
|||||||
TickType_t startTime = 0;
|
TickType_t startTime = 0;
|
||||||
TickType_t tappedTime = 0;
|
TickType_t tappedTime = 0;
|
||||||
Controllers::MotorController& motorController;
|
Controllers::MotorController& motorController;
|
||||||
System::WakeLock wakeLock;
|
System::SystemTask& systemTask;
|
||||||
int16_t bpm = 120;
|
int16_t bpm = 120;
|
||||||
uint8_t bpb = 4;
|
uint8_t bpb = 4;
|
||||||
uint8_t counter = 1;
|
uint8_t counter = 1;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Notifications::Notifications(DisplayApp* app,
|
|||||||
notificationManager {notificationManager},
|
notificationManager {notificationManager},
|
||||||
alertNotificationService {alertNotificationService},
|
alertNotificationService {alertNotificationService},
|
||||||
motorController {motorController},
|
motorController {motorController},
|
||||||
wakeLock(systemTask),
|
systemTask {systemTask},
|
||||||
mode {mode} {
|
mode {mode} {
|
||||||
|
|
||||||
notificationManager.ClearNewNotificationFlag();
|
notificationManager.ClearNewNotificationFlag();
|
||||||
@@ -40,7 +40,7 @@ Notifications::Notifications(DisplayApp* app,
|
|||||||
validDisplay = false;
|
validDisplay = false;
|
||||||
}
|
}
|
||||||
if (mode == Modes::Preview) {
|
if (mode == Modes::Preview) {
|
||||||
wakeLock.Lock();
|
systemTask.PushMessage(System::Messages::DisableSleeping);
|
||||||
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
|
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
|
||||||
motorController.StartRinging();
|
motorController.StartRinging();
|
||||||
} else {
|
} else {
|
||||||
@@ -65,6 +65,7 @@ Notifications::~Notifications() {
|
|||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
// make sure we stop any vibrations before exiting
|
// make sure we stop any vibrations before exiting
|
||||||
motorController.StopRinging();
|
motorController.StopRinging();
|
||||||
|
systemTask.PushMessage(System::Messages::EnableSleeping);
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ void Notifications::Refresh() {
|
|||||||
|
|
||||||
} else if (mode == Modes::Preview && dismissingNotification) {
|
} else if (mode == Modes::Preview && dismissingNotification) {
|
||||||
running = false;
|
running = false;
|
||||||
|
currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
|
||||||
|
|
||||||
} else if (dismissingNotification) {
|
} else if (dismissingNotification) {
|
||||||
dismissingNotification = false;
|
dismissingNotification = false;
|
||||||
@@ -111,15 +113,15 @@ void Notifications::Refresh() {
|
|||||||
alertNotificationService,
|
alertNotificationService,
|
||||||
motorController);
|
motorController);
|
||||||
} else {
|
} else {
|
||||||
running = false;
|
currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
running = running && currentItem->IsRunning();
|
running = currentItem->IsRunning() && running;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Notifications::OnPreviewInteraction() {
|
void Notifications::OnPreviewInteraction() {
|
||||||
wakeLock.Release();
|
systemTask.PushMessage(System::Messages::EnableSleeping);
|
||||||
motorController.StopRinging();
|
motorController.StopRinging();
|
||||||
if (timeoutLine != nullptr) {
|
if (timeoutLine != nullptr) {
|
||||||
lv_obj_del(timeoutLine);
|
lv_obj_del(timeoutLine);
|
||||||
@@ -171,9 +173,7 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
|||||||
} else if (nextMessage.valid) {
|
} else if (nextMessage.valid) {
|
||||||
currentId = nextMessage.id;
|
currentId = nextMessage.id;
|
||||||
} else {
|
} else {
|
||||||
// don't update id, notification manager will try to fetch
|
// don't update id, won't be found be refresh and try to load latest message or no message box
|
||||||
// but not find it. Refresh will try to load latest message
|
|
||||||
// or dismiss to watchface
|
|
||||||
}
|
}
|
||||||
DismissToBlack();
|
DismissToBlack();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "components/ble/NotificationManager.h"
|
#include "components/ble/NotificationManager.h"
|
||||||
#include "components/motor/MotorController.h"
|
#include "components/motor/MotorController.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "systemtask/WakeLock.h"
|
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
@@ -74,7 +73,7 @@ namespace Pinetime {
|
|||||||
Pinetime::Controllers::NotificationManager& notificationManager;
|
Pinetime::Controllers::NotificationManager& notificationManager;
|
||||||
Pinetime::Controllers::AlertNotificationService& alertNotificationService;
|
Pinetime::Controllers::AlertNotificationService& alertNotificationService;
|
||||||
Pinetime::Controllers::MotorController& motorController;
|
Pinetime::Controllers::MotorController& motorController;
|
||||||
System::WakeLock wakeLock;
|
System::SystemTask& systemTask;
|
||||||
Modes mode = Modes::Normal;
|
Modes mode = Modes::Normal;
|
||||||
std::unique_ptr<NotificationItem> currentItem;
|
std::unique_ptr<NotificationItem> currentItem;
|
||||||
Pinetime::Controllers::NotificationManager::Notification::Id currentId;
|
Pinetime::Controllers::NotificationManager::Notification::Id currentId;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace {
|
|||||||
constexpr TickType_t blinkInterval = pdMS_TO_TICKS(1000);
|
constexpr TickType_t blinkInterval = pdMS_TO_TICKS(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
StopWatch::StopWatch(System::SystemTask& systemTask) : wakeLock(systemTask) {
|
StopWatch::StopWatch(System::SystemTask& systemTask) : systemTask {systemTask} {
|
||||||
static constexpr uint8_t btnWidth = 115;
|
static constexpr uint8_t btnWidth = 115;
|
||||||
static constexpr uint8_t btnHeight = 80;
|
static constexpr uint8_t btnHeight = 80;
|
||||||
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
|
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
|
||||||
@@ -79,6 +79,7 @@ StopWatch::StopWatch(System::SystemTask& systemTask) : wakeLock(systemTask) {
|
|||||||
|
|
||||||
StopWatch::~StopWatch() {
|
StopWatch::~StopWatch() {
|
||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
|
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ void StopWatch::Start() {
|
|||||||
SetInterfaceRunning();
|
SetInterfaceRunning();
|
||||||
startTime = xTaskGetTickCount();
|
startTime = xTaskGetTickCount();
|
||||||
currentState = States::Running;
|
currentState = States::Running;
|
||||||
wakeLock.Lock();
|
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopWatch::Pause() {
|
void StopWatch::Pause() {
|
||||||
@@ -144,7 +145,7 @@ void StopWatch::Pause() {
|
|||||||
oldTimeElapsed = laps[lapsDone];
|
oldTimeElapsed = laps[lapsDone];
|
||||||
blinkTime = xTaskGetTickCount() + blinkInterval;
|
blinkTime = xTaskGetTickCount() + blinkInterval;
|
||||||
currentState = States::Halted;
|
currentState = States::Halted;
|
||||||
wakeLock.Release();
|
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopWatch::Refresh() {
|
void StopWatch::Refresh() {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include "portmacro_cmsis.h"
|
#include "portmacro_cmsis.h"
|
||||||
|
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "systemtask/WakeLock.h"
|
|
||||||
#include "displayapp/apps/Apps.h"
|
#include "displayapp/apps/Apps.h"
|
||||||
#include "displayapp/Controllers.h"
|
#include "displayapp/Controllers.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
@@ -44,7 +43,7 @@ namespace Pinetime {
|
|||||||
void Start();
|
void Start();
|
||||||
void Pause();
|
void Pause();
|
||||||
|
|
||||||
Pinetime::System::WakeLock wakeLock;
|
Pinetime::System::SystemTask& systemTask;
|
||||||
States currentState = States::Init;
|
States currentState = States::Init;
|
||||||
TickType_t startTime;
|
TickType_t startTime;
|
||||||
TickType_t oldTimeElapsed = 0;
|
TickType_t oldTimeElapsed = 0;
|
||||||
|
|||||||
@@ -38,16 +38,15 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app,
|
|||||||
const Pinetime::Controllers::Ble& bleController,
|
const Pinetime::Controllers::Ble& bleController,
|
||||||
const Pinetime::Drivers::Watchdog& watchdog,
|
const Pinetime::Drivers::Watchdog& watchdog,
|
||||||
Pinetime::Controllers::MotionController& motionController,
|
Pinetime::Controllers::MotionController& motionController,
|
||||||
const Pinetime::Drivers::Cst816S& touchPanel,
|
const Pinetime::Drivers::Cst816S& touchPanel)
|
||||||
const Pinetime::Drivers::SpiNorFlash& spiNorFlash)
|
: app {app},
|
||||||
: dateTimeController {dateTimeController},
|
dateTimeController {dateTimeController},
|
||||||
batteryController {batteryController},
|
batteryController {batteryController},
|
||||||
brightnessController {brightnessController},
|
brightnessController {brightnessController},
|
||||||
bleController {bleController},
|
bleController {bleController},
|
||||||
watchdog {watchdog},
|
watchdog {watchdog},
|
||||||
motionController {motionController},
|
motionController {motionController},
|
||||||
touchPanel {touchPanel},
|
touchPanel {touchPanel},
|
||||||
spiNorFlash {spiNorFlash},
|
|
||||||
screens {app,
|
screens {app,
|
||||||
0,
|
0,
|
||||||
{[this]() -> std::unique_ptr<Screen> {
|
{[this]() -> std::unique_ptr<Screen> {
|
||||||
@@ -187,12 +186,10 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
|
|||||||
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
|
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_label_set_recolor(label, true);
|
lv_label_set_recolor(label, true);
|
||||||
const auto& bleAddr = bleController.Address();
|
const auto& bleAddr = bleController.Address();
|
||||||
auto spiFlashId = spiNorFlash.GetIdentification();
|
|
||||||
lv_label_set_text_fmt(label,
|
lv_label_set_text_fmt(label,
|
||||||
"#808080 BLE MAC#\n"
|
"#808080 BLE MAC#\n"
|
||||||
" %02x:%02x:%02x:%02x:%02x:%02x\n"
|
" %02x:%02x:%02x:%02x:%02x:%02x"
|
||||||
"\n"
|
"\n"
|
||||||
"#808080 SPI Flash# %02x-%02x-%02x\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"#808080 Memory heap#\n"
|
"#808080 Memory heap#\n"
|
||||||
" #808080 Free# %d\n"
|
" #808080 Free# %d\n"
|
||||||
@@ -205,9 +202,6 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
|
|||||||
bleAddr[2],
|
bleAddr[2],
|
||||||
bleAddr[1],
|
bleAddr[1],
|
||||||
bleAddr[0],
|
bleAddr[0],
|
||||||
spiFlashId.manufacturer,
|
|
||||||
spiFlashId.type,
|
|
||||||
spiFlashId.density,
|
|
||||||
xPortGetFreeHeapSize(),
|
xPortGetFreeHeapSize(),
|
||||||
xPortGetMinimumEverFreeHeapSize(),
|
xPortGetMinimumEverFreeHeapSize(),
|
||||||
mallocFailedCount,
|
mallocFailedCount,
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ namespace Pinetime {
|
|||||||
const Pinetime::Controllers::Ble& bleController,
|
const Pinetime::Controllers::Ble& bleController,
|
||||||
const Pinetime::Drivers::Watchdog& watchdog,
|
const Pinetime::Drivers::Watchdog& watchdog,
|
||||||
Pinetime::Controllers::MotionController& motionController,
|
Pinetime::Controllers::MotionController& motionController,
|
||||||
const Pinetime::Drivers::Cst816S& touchPanel,
|
const Pinetime::Drivers::Cst816S& touchPanel);
|
||||||
const Pinetime::Drivers::SpiNorFlash& spiNorFlash);
|
|
||||||
~SystemInfo() override;
|
~SystemInfo() override;
|
||||||
bool OnTouchEvent(TouchEvents event) override;
|
bool OnTouchEvent(TouchEvents event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
DisplayApp* app;
|
||||||
Pinetime::Controllers::DateTime& dateTimeController;
|
Pinetime::Controllers::DateTime& dateTimeController;
|
||||||
const Pinetime::Controllers::Battery& batteryController;
|
const Pinetime::Controllers::Battery& batteryController;
|
||||||
Pinetime::Controllers::BrightnessController& brightnessController;
|
Pinetime::Controllers::BrightnessController& brightnessController;
|
||||||
@@ -42,7 +42,6 @@ namespace Pinetime {
|
|||||||
const Pinetime::Drivers::Watchdog& watchdog;
|
const Pinetime::Drivers::Watchdog& watchdog;
|
||||||
Pinetime::Controllers::MotionController& motionController;
|
Pinetime::Controllers::MotionController& motionController;
|
||||||
const Pinetime::Drivers::Cst816S& touchPanel;
|
const Pinetime::Drivers::Cst816S& touchPanel;
|
||||||
const Pinetime::Drivers::SpiNorFlash& spiNorFlash;
|
|
||||||
|
|
||||||
ScreenList<5> screens;
|
ScreenList<5> screens;
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,9 @@ void Timer::UpdateMask() {
|
|||||||
|
|
||||||
void Timer::Refresh() {
|
void Timer::Refresh() {
|
||||||
if (timer.IsRunning()) {
|
if (timer.IsRunning()) {
|
||||||
DisplayTime();
|
auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
|
||||||
|
minuteCounter.SetValue(secondsRemaining.count() / 60);
|
||||||
|
secondCounter.SetValue(secondsRemaining.count() % 60);
|
||||||
} else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) {
|
} else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) {
|
||||||
lv_label_set_text_static(txtPlayPause, "Reset");
|
lv_label_set_text_static(txtPlayPause, "Reset");
|
||||||
maskPosition += 15;
|
maskPosition += 15;
|
||||||
@@ -117,14 +119,6 @@ void Timer::Refresh() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::DisplayTime() {
|
|
||||||
displaySeconds = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
|
|
||||||
if (displaySeconds.IsUpdated()) {
|
|
||||||
minuteCounter.SetValue(displaySeconds.Get().count() / 60);
|
|
||||||
secondCounter.SetValue(displaySeconds.Get().count() % 60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timer::SetTimerRunning() {
|
void Timer::SetTimerRunning() {
|
||||||
minuteCounter.HideControls();
|
minuteCounter.HideControls();
|
||||||
secondCounter.HideControls();
|
secondCounter.HideControls();
|
||||||
@@ -139,7 +133,9 @@ void Timer::SetTimerStopped() {
|
|||||||
|
|
||||||
void Timer::ToggleRunning() {
|
void Timer::ToggleRunning() {
|
||||||
if (timer.IsRunning()) {
|
if (timer.IsRunning()) {
|
||||||
DisplayTime();
|
auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
|
||||||
|
minuteCounter.SetValue(secondsRemaining.count() / 60);
|
||||||
|
secondCounter.SetValue(secondsRemaining.count() % 60);
|
||||||
timer.StopTimer();
|
timer.StopTimer();
|
||||||
SetTimerStopped();
|
SetTimerStopped();
|
||||||
} else if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) {
|
} else if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) {
|
||||||
@@ -151,6 +147,7 @@ void Timer::ToggleRunning() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Timer::Reset() {
|
void Timer::Reset() {
|
||||||
DisplayTime();
|
minuteCounter.SetValue(0);
|
||||||
|
secondCounter.SetValue(0);
|
||||||
SetTimerStopped();
|
SetTimerStopped();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "components/datetime/DateTimeController.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "displayapp/LittleVgl.h"
|
#include "displayapp/LittleVgl.h"
|
||||||
#include "displayapp/widgets/Counter.h"
|
#include "displayapp/widgets/Counter.h"
|
||||||
#include "utility/DirtyValue.h"
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
#include "components/timer/Timer.h"
|
#include "components/timer/Timer.h"
|
||||||
@@ -26,7 +26,6 @@ namespace Pinetime::Applications {
|
|||||||
void SetTimerRunning();
|
void SetTimerRunning();
|
||||||
void SetTimerStopped();
|
void SetTimerStopped();
|
||||||
void UpdateMask();
|
void UpdateMask();
|
||||||
void DisplayTime();
|
|
||||||
Pinetime::Controllers::Timer& timer;
|
Pinetime::Controllers::Timer& timer;
|
||||||
|
|
||||||
lv_obj_t* btnPlayPause;
|
lv_obj_t* btnPlayPause;
|
||||||
@@ -44,7 +43,6 @@ namespace Pinetime::Applications {
|
|||||||
bool buttonPressing = false;
|
bool buttonPressing = false;
|
||||||
lv_coord_t maskPosition = 0;
|
lv_coord_t maskPosition = 0;
|
||||||
TickType_t pressTime = 0;
|
TickType_t pressTime = 0;
|
||||||
Utility::DirtyValue<std::chrono::seconds> displaySeconds;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ namespace Pinetime {
|
|||||||
|
|
||||||
BatteryIcon batteryIcon;
|
BatteryIcon batteryIcon;
|
||||||
|
|
||||||
Controllers::DateTime& dateTimeController;
|
const Controllers::DateTime& dateTimeController;
|
||||||
const Controllers::Battery& batteryController;
|
const Controllers::Battery& batteryController;
|
||||||
const Controllers::Ble& bleController;
|
const Controllers::Ble& bleController;
|
||||||
Controllers::NotificationManager& notificationManager;
|
Controllers::NotificationManager& notificationManager;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "displayapp/screens/NotificationIcon.h"
|
#include "displayapp/screens/NotificationIcon.h"
|
||||||
#include "displayapp/screens/Symbols.h"
|
#include "displayapp/screens/Symbols.h"
|
||||||
#include "displayapp/screens/WeatherSymbols.h"
|
#include "displayapp/screens/WeatherSymbols.h"
|
||||||
@@ -175,12 +174,13 @@ void WatchFaceDigital::Refresh() {
|
|||||||
if (currentWeather.IsUpdated()) {
|
if (currentWeather.IsUpdated()) {
|
||||||
auto optCurrentWeather = currentWeather.Get();
|
auto optCurrentWeather = currentWeather.Get();
|
||||||
if (optCurrentWeather) {
|
if (optCurrentWeather) {
|
||||||
int16_t temp = optCurrentWeather->temperature.Celsius();
|
int16_t temp = optCurrentWeather->temperature;
|
||||||
char tempUnit = 'C';
|
char tempUnit = 'C';
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
temp = optCurrentWeather->temperature.Fahrenheit();
|
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
||||||
tempUnit = 'F';
|
tempUnit = 'F';
|
||||||
}
|
}
|
||||||
|
temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
||||||
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
|
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
|
||||||
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
189
src/displayapp/screens/WatchFaceFuzzy.cpp
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
#include "displayapp/screens/WatchFaceFuzzy.h"
|
||||||
|
|
||||||
|
#include <lvgl/lvgl.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include "displayapp/screens/NotificationIcon.h"
|
||||||
|
#include "displayapp/screens/Symbols.h"
|
||||||
|
#include "displayapp/screens/WeatherSymbols.h"
|
||||||
|
#include "components/ble/NotificationManager.h"
|
||||||
|
#include "components/heartrate/HeartRateController.h"
|
||||||
|
#include "components/motion/MotionController.h"
|
||||||
|
#include "components/ble/SimpleWeatherService.h"
|
||||||
|
#include "components/settings/Settings.h"
|
||||||
|
|
||||||
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
|
WatchFaceFuzzy::WatchFaceFuzzy(Controllers::DateTime& dateTimeController,
|
||||||
|
Controllers::NotificationManager& notificationManager,
|
||||||
|
Controllers::Settings& settingsController,
|
||||||
|
Controllers::HeartRateController& heartRateController,
|
||||||
|
Controllers::MotionController& motionController,
|
||||||
|
Controllers::SimpleWeatherService& weatherService)
|
||||||
|
: currentDateTime {{}},
|
||||||
|
dateTimeController {dateTimeController},
|
||||||
|
notificationManager {notificationManager},
|
||||||
|
settingsController {settingsController},
|
||||||
|
heartRateController {heartRateController},
|
||||||
|
motionController {motionController},
|
||||||
|
weatherService {weatherService}
|
||||||
|
{
|
||||||
|
|
||||||
|
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME);
|
||||||
|
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
|
||||||
|
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||||
|
|
||||||
|
weatherIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
|
||||||
|
lv_obj_set_style_local_text_font(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons);
|
||||||
|
lv_label_set_text(weatherIcon, "");
|
||||||
|
lv_obj_align(weatherIcon, nullptr, LV_ALIGN_IN_TOP_MID, -20, 50);
|
||||||
|
lv_obj_set_auto_realign(weatherIcon, true);
|
||||||
|
|
||||||
|
temperature = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
|
||||||
|
lv_label_set_text(temperature, "");
|
||||||
|
lv_obj_align(temperature, nullptr, LV_ALIGN_IN_TOP_MID, 20, 50);
|
||||||
|
|
||||||
|
label_date = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
|
||||||
|
lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
|
||||||
|
|
||||||
|
label_time = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_font(label_time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_24);
|
||||||
|
lv_label_set_recolor(label_time, true);
|
||||||
|
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 0);
|
||||||
|
|
||||||
|
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
|
||||||
|
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
|
||||||
|
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||||
|
|
||||||
|
heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
|
||||||
|
lv_label_set_text_static(heartbeatValue, "");
|
||||||
|
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
|
||||||
|
|
||||||
|
stepValue = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
|
||||||
|
lv_label_set_text_static(stepValue, "0");
|
||||||
|
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||||
|
|
||||||
|
stepIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
|
||||||
|
lv_label_set_text_static(stepIcon, Symbols::shoe);
|
||||||
|
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
||||||
|
|
||||||
|
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchFaceFuzzy::~WatchFaceFuzzy() {
|
||||||
|
lv_task_del(taskRefresh);
|
||||||
|
lv_obj_clean(lv_scr_act());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchFaceFuzzy::Refresh() {
|
||||||
|
notificationState = notificationManager.AreNewNotificationsAvailable();
|
||||||
|
if (notificationState.IsUpdated()) {
|
||||||
|
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
|
||||||
|
|
||||||
|
if (currentDateTime.IsUpdated()) {
|
||||||
|
uint8_t hour = dateTimeController.Hours();
|
||||||
|
uint8_t minute = dateTimeController.Minutes();
|
||||||
|
|
||||||
|
printTimeWords(hour, minute);
|
||||||
|
|
||||||
|
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
|
||||||
|
if (currentDate.IsUpdated()) {
|
||||||
|
uint16_t year = dateTimeController.Year();
|
||||||
|
uint8_t day = dateTimeController.Day();
|
||||||
|
lv_label_set_text_fmt(label_date,
|
||||||
|
"%s %d %s %d",
|
||||||
|
dateTimeController.DayOfWeekShortToString(),
|
||||||
|
day,
|
||||||
|
dateTimeController.MonthShortToString(),
|
||||||
|
year);
|
||||||
|
lv_obj_realign(label_date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heartbeat = heartRateController.HeartRate();
|
||||||
|
heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
|
||||||
|
if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) {
|
||||||
|
if (heartbeatRunning.Get()) {
|
||||||
|
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
|
||||||
|
lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get());
|
||||||
|
} else {
|
||||||
|
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
|
||||||
|
lv_label_set_text_static(heartbeatValue, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_realign(heartbeatIcon);
|
||||||
|
lv_obj_realign(heartbeatValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
stepCount = motionController.NbSteps();
|
||||||
|
if (stepCount.IsUpdated()) {
|
||||||
|
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
|
||||||
|
lv_obj_realign(stepValue);
|
||||||
|
lv_obj_realign(stepIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentWeather = weatherService.Current();
|
||||||
|
if (currentWeather.IsUpdated()) {
|
||||||
|
auto optCurrentWeather = currentWeather.Get();
|
||||||
|
if (optCurrentWeather) {
|
||||||
|
int16_t temp = optCurrentWeather->temperature;
|
||||||
|
char tempUnit = 'C';
|
||||||
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
|
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
||||||
|
tempUnit = 'F';
|
||||||
|
}
|
||||||
|
temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
||||||
|
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
|
||||||
|
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||||
|
} else {
|
||||||
|
lv_label_set_text_static(temperature, "");
|
||||||
|
lv_label_set_text(weatherIcon, "");
|
||||||
|
}
|
||||||
|
lv_obj_realign(temperature);
|
||||||
|
lv_obj_realign(weatherIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char const* WatchFaceFuzzy::mods[] = {"", "five", "ten", "quarter", "twenty", "twenty five", "half"};
|
||||||
|
char const* WatchFaceFuzzy::nums[] = {"twelve", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"};
|
||||||
|
|
||||||
|
void WatchFaceFuzzy::printTimeWords(int h, int m) {
|
||||||
|
const char* mod;
|
||||||
|
|
||||||
|
if (m <= 30) {
|
||||||
|
mod = mods[m / 5];
|
||||||
|
} else {
|
||||||
|
mod = mods[(60-m) / 5];
|
||||||
|
}
|
||||||
|
h = (h % 12);
|
||||||
|
|
||||||
|
if (m >= 57) {
|
||||||
|
sprintf(timeStr, "#ffffff nearly %s#\n#808080 o' clock#", nums[(h+1) % 12]);
|
||||||
|
}
|
||||||
|
else if (m == 0 || m <= 4) {
|
||||||
|
sprintf(timeStr, "#ffffff %s#\n#808080 o' clock#", nums[h]);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (m <= 32) {
|
||||||
|
sprintf(timeStr, "#ffffff %s#\n#808080 past# #FFFFFF %s#", mod, nums[h]);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (m > 32) {
|
||||||
|
sprintf(timeStr, "#ffffff %s#\n#808080 to# #FFFFFF %s#", mod, nums[(h+1) % 12]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", timeStr);
|
||||||
|
lv_label_set_text(label_time, timeStr);
|
||||||
|
lv_obj_realign(label_time);
|
||||||
|
}
|
||||||
100
src/displayapp/screens/WatchFaceFuzzy.h
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "components/datetime/DateTimeController.h"
|
||||||
|
#include "components/ble/SimpleWeatherService.h"
|
||||||
|
#include "components/ble/BleController.h"
|
||||||
|
#include "displayapp/widgets/StatusIcons.h"
|
||||||
|
#include "utility/DirtyValue.h"
|
||||||
|
#include "displayapp/apps/Apps.h"
|
||||||
|
|
||||||
|
extern lv_font_t jetbrains_mono_bold_24;
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Controllers {
|
||||||
|
class Settings;
|
||||||
|
class Battery;
|
||||||
|
class Ble;
|
||||||
|
class NotificationManager;
|
||||||
|
class HeartRateController;
|
||||||
|
class MotionController;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Applications {
|
||||||
|
namespace Screens {
|
||||||
|
|
||||||
|
class WatchFaceFuzzy : public Screen {
|
||||||
|
public:
|
||||||
|
WatchFaceFuzzy(Controllers::DateTime& dateTimeController,
|
||||||
|
Controllers::NotificationManager& notificationManager,
|
||||||
|
Controllers::Settings& settingsController,
|
||||||
|
Controllers::HeartRateController& heartRateController,
|
||||||
|
Controllers::MotionController& motionController,
|
||||||
|
Controllers::SimpleWeatherService& weather);
|
||||||
|
~WatchFaceFuzzy() override;
|
||||||
|
|
||||||
|
void Refresh() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t displayedHour = -1;
|
||||||
|
uint8_t displayedMinute = -1;
|
||||||
|
|
||||||
|
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
|
||||||
|
Utility::DirtyValue<uint32_t> stepCount {};
|
||||||
|
Utility::DirtyValue<uint8_t> heartbeat {};
|
||||||
|
Utility::DirtyValue<bool> heartbeatRunning {};
|
||||||
|
Utility::DirtyValue<bool> notificationState {};
|
||||||
|
Utility::DirtyValue<std::optional<Pinetime::Controllers::SimpleWeatherService::CurrentWeather>> currentWeather {};
|
||||||
|
|
||||||
|
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
||||||
|
|
||||||
|
lv_obj_t* label_time;
|
||||||
|
lv_obj_t* label_date;
|
||||||
|
lv_obj_t* heartbeatIcon;
|
||||||
|
lv_obj_t* heartbeatValue;
|
||||||
|
lv_obj_t* stepIcon;
|
||||||
|
lv_obj_t* stepValue;
|
||||||
|
lv_obj_t* notificationIcon;
|
||||||
|
lv_obj_t* weatherIcon;
|
||||||
|
lv_obj_t* temperature;
|
||||||
|
|
||||||
|
Controllers::DateTime& dateTimeController;
|
||||||
|
Controllers::NotificationManager& notificationManager;
|
||||||
|
Controllers::Settings& settingsController;
|
||||||
|
Controllers::HeartRateController& heartRateController;
|
||||||
|
Controllers::MotionController& motionController;
|
||||||
|
Controllers::SimpleWeatherService& weatherService;
|
||||||
|
|
||||||
|
lv_task_t* taskRefresh;
|
||||||
|
|
||||||
|
static char const *nums[];
|
||||||
|
static char const *mods[];
|
||||||
|
char timeStr[64];
|
||||||
|
void printTimeWords(int hour, int minute);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct WatchFaceTraits<WatchFace::Fuzzy> {
|
||||||
|
static constexpr WatchFace watchFace = WatchFace::Fuzzy;
|
||||||
|
static constexpr const char* name = "Fuzzy face";
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::WatchFaceFuzzy(controllers.dateTimeController,
|
||||||
|
controllers.notificationManager,
|
||||||
|
controllers.settingsController,
|
||||||
|
controllers.heartRateController,
|
||||||
|
controllers.motionController,
|
||||||
|
*controllers.weatherController);
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
#include "displayapp/screens/WatchFacePineTimeStyle.h"
|
#include "displayapp/screens/WatchFacePineTimeStyle.h"
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include "displayapp/Colors.h"
|
#include <displayapp/Colors.h>
|
||||||
#include "displayapp/screens/BatteryIcon.h"
|
#include "displayapp/screens/BatteryIcon.h"
|
||||||
#include "displayapp/screens/BleIcon.h"
|
#include "displayapp/screens/BleIcon.h"
|
||||||
#include "displayapp/screens/NotificationIcon.h"
|
#include "displayapp/screens/NotificationIcon.h"
|
||||||
@@ -543,10 +543,11 @@ void WatchFacePineTimeStyle::Refresh() {
|
|||||||
if (currentWeather.IsUpdated()) {
|
if (currentWeather.IsUpdated()) {
|
||||||
auto optCurrentWeather = currentWeather.Get();
|
auto optCurrentWeather = currentWeather.Get();
|
||||||
if (optCurrentWeather) {
|
if (optCurrentWeather) {
|
||||||
int16_t temp = optCurrentWeather->temperature.Celsius();
|
int16_t temp = optCurrentWeather->temperature;
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
temp = optCurrentWeather->temperature.Fahrenheit();
|
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
||||||
}
|
}
|
||||||
|
temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
||||||
lv_label_set_text_fmt(temperature, "%d°", temp);
|
lv_label_set_text_fmt(temperature, "%d°", temp);
|
||||||
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
259
src/displayapp/screens/WatchFaceSundial.cpp
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
#include "displayapp/screens/WatchFaceSundial.h"
|
||||||
|
|
||||||
|
#include <lvgl/lvgl.h>
|
||||||
|
#include <cstdio>
|
||||||
|
// #include <cmath>
|
||||||
|
#include "displayapp/screens/NotificationIcon.h"
|
||||||
|
#include "displayapp/screens/Symbols.h"
|
||||||
|
#include "displayapp/screens/WeatherSymbols.h"
|
||||||
|
#include "displayapp/InfiniTimeTheme.h"
|
||||||
|
#include "components/battery/BatteryController.h"
|
||||||
|
#include "components/ble/BleController.h"
|
||||||
|
#include "components/ble/NotificationManager.h"
|
||||||
|
#include "components/heartrate/HeartRateController.h"
|
||||||
|
#include "components/motion/MotionController.h"
|
||||||
|
#include "components/ble/SimpleWeatherService.h"
|
||||||
|
#include "components/settings/Settings.h"
|
||||||
|
#include "sunset/src/sunset.h"
|
||||||
|
|
||||||
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int16_t HourLength = 70;
|
||||||
|
constexpr int16_t MinuteLength = 90;
|
||||||
|
constexpr int16_t SecondLength = 110;
|
||||||
|
constexpr int16_t SunDialVerticalOffset = 40;
|
||||||
|
|
||||||
|
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
|
||||||
|
const auto LV_TRIG_SCALE = _lv_trigo_sin(90);
|
||||||
|
const lv_color_t DARK_GRAY = lv_color_make(48, 48, 48);
|
||||||
|
const lv_color_t DARK_ORANGE = lv_color_make(48, 26, 0);
|
||||||
|
|
||||||
|
int16_t Cosine(int16_t angle) {
|
||||||
|
return _lv_trigo_sin(angle + 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t Sine(int16_t angle) {
|
||||||
|
return _lv_trigo_sin(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t CoordinateXRelocate(int16_t x) {
|
||||||
|
return (x + LV_HOR_RES / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int16_t CoordinateYRelocateSundial(int16_t y) {
|
||||||
|
return std::abs(y - SunDialVerticalOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_point_t CoordinateRelocateSundial(int16_t radius, int16_t angle) {
|
||||||
|
return lv_point_t {.x = CoordinateXRelocate(radius * static_cast<int32_t>(Sine(angle)) / LV_TRIG_SCALE),
|
||||||
|
.y = CoordinateYRelocateSundial(radius * static_cast<int32_t>(Cosine(angle)) / LV_TRIG_SCALE)};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchFaceSundial::WatchFaceSundial(Controllers::DateTime& dateTimeController,
|
||||||
|
Controllers::NotificationManager& notificationManager,
|
||||||
|
Controllers::Settings& settingsController)
|
||||||
|
: currentDateTime {{}},
|
||||||
|
dateTimeController {dateTimeController},
|
||||||
|
notificationManager {notificationManager},
|
||||||
|
settingsController {settingsController}
|
||||||
|
{
|
||||||
|
// minor_scales = lv_linemeter_create(lv_scr_act(), nullptr);
|
||||||
|
// lv_linemeter_set_scale(minor_scales, 300, 51);
|
||||||
|
// lv_linemeter_set_angle_offset(minor_scales, 180);
|
||||||
|
// lv_obj_set_size(minor_scales, 240, 240);
|
||||||
|
// lv_obj_align(minor_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
// lv_obj_set_style_local_bg_opa(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||||
|
// lv_obj_set_style_local_scale_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
|
||||||
|
// lv_obj_set_style_local_scale_end_line_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 1);
|
||||||
|
// lv_obj_set_style_local_scale_end_color(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
|
||||||
|
|
||||||
|
// major_scales = lv_linemeter_create(lv_scr_act(), nullptr);
|
||||||
|
// lv_linemeter_set_scale(major_scales, 300, 11);
|
||||||
|
// lv_linemeter_set_angle_offset(major_scales, 180);
|
||||||
|
// lv_obj_set_size(major_scales, 240, 240);
|
||||||
|
// lv_obj_align(major_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
// lv_obj_set_style_local_bg_opa(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||||
|
// lv_obj_set_style_local_scale_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 6);
|
||||||
|
// lv_obj_set_style_local_scale_end_line_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
|
||||||
|
// lv_obj_set_style_local_scale_end_color(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
|
||||||
|
// large_scales = lv_linemeter_create(lv_scr_act(), nullptr);
|
||||||
|
// lv_linemeter_set_scale(large_scales, 180, 3);
|
||||||
|
// lv_linemeter_set_angle_offset(large_scales, 180);
|
||||||
|
// lv_obj_set_size(large_scales, 240, 240);
|
||||||
|
// lv_obj_align(large_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
// lv_obj_set_style_local_bg_opa(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||||
|
// lv_obj_set_style_local_scale_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||||
|
// lv_obj_set_style_local_scale_end_line_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
|
||||||
|
// lv_obj_set_style_local_scale_end_color(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||||
|
|
||||||
|
twelve = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_align(twelve, LV_LABEL_ALIGN_RIGHT);
|
||||||
|
lv_label_set_text_static(twelve, "XII");
|
||||||
|
lv_obj_align(twelve, NULL, LV_ALIGN_IN_TOP_RIGHT, -20, SunDialVerticalOffset-20);
|
||||||
|
lv_obj_set_style_local_text_color(twelve, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
|
||||||
|
one = lv_label_create(lv_scr_act(), NULL);
|
||||||
|
lv_label_set_align(one, LV_LABEL_ALIGN_LEFT);
|
||||||
|
lv_label_set_text(one, "I");
|
||||||
|
lv_obj_align(one, NULL, LV_ALIGN_IN_TOP_LEFT, 20, SunDialVerticalOffset-20);
|
||||||
|
lv_obj_set_style_local_text_color(one, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
|
||||||
|
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME);
|
||||||
|
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
|
||||||
|
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||||
|
|
||||||
|
// Date - Day / Week day
|
||||||
|
label_date_day = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, DARK_ORANGE);
|
||||||
|
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
|
||||||
|
lv_label_set_align(label_date_day, LV_LABEL_ALIGN_CENTER);
|
||||||
|
lv_obj_align(label_date_day, nullptr, LV_ALIGN_CENTER, 50, 0);
|
||||||
|
|
||||||
|
minute_body = lv_line_create(lv_scr_act(), nullptr);
|
||||||
|
minute_body_trace = lv_line_create(lv_scr_act(), nullptr);
|
||||||
|
hour_body = lv_line_create(lv_scr_act(), nullptr);
|
||||||
|
hour_body_trace = lv_line_create(lv_scr_act(), nullptr);
|
||||||
|
second_body = lv_line_create(lv_scr_act(), nullptr);
|
||||||
|
|
||||||
|
lv_style_init(&second_line_style);
|
||||||
|
lv_style_set_line_width(&second_line_style, LV_STATE_DEFAULT, 3);
|
||||||
|
lv_style_set_line_color(&second_line_style, LV_STATE_DEFAULT, LV_COLOR_RED);
|
||||||
|
lv_style_set_line_rounded(&second_line_style, LV_STATE_DEFAULT, true);
|
||||||
|
lv_obj_add_style(second_body, LV_LINE_PART_MAIN, &second_line_style);
|
||||||
|
|
||||||
|
lv_style_init(&minute_line_style);
|
||||||
|
lv_style_set_line_width(&minute_line_style, LV_STATE_DEFAULT, 7);
|
||||||
|
lv_style_set_line_color(&minute_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
lv_style_set_line_rounded(&minute_line_style, LV_STATE_DEFAULT, true);
|
||||||
|
lv_obj_add_style(minute_body, LV_LINE_PART_MAIN, &minute_line_style);
|
||||||
|
|
||||||
|
lv_style_init(&minute_line_style_trace);
|
||||||
|
lv_style_set_line_width(&minute_line_style_trace, LV_STATE_DEFAULT, 3);
|
||||||
|
lv_style_set_line_color(&minute_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
lv_style_set_line_rounded(&minute_line_style_trace, LV_STATE_DEFAULT, false);
|
||||||
|
lv_obj_add_style(minute_body_trace, LV_LINE_PART_MAIN, &minute_line_style_trace);
|
||||||
|
|
||||||
|
lv_style_init(&hour_line_style);
|
||||||
|
lv_style_set_line_width(&hour_line_style, LV_STATE_DEFAULT, 7);
|
||||||
|
lv_style_set_line_color(&hour_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
lv_style_set_line_rounded(&hour_line_style, LV_STATE_DEFAULT, true);
|
||||||
|
lv_obj_add_style(hour_body, LV_LINE_PART_MAIN, &hour_line_style);
|
||||||
|
|
||||||
|
lv_style_init(&hour_line_style_trace);
|
||||||
|
lv_style_set_line_width(&hour_line_style_trace, LV_STATE_DEFAULT, 3);
|
||||||
|
lv_style_set_line_color(&hour_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
lv_style_set_line_rounded(&hour_line_style_trace, LV_STATE_DEFAULT, false);
|
||||||
|
lv_obj_add_style(hour_body_trace, LV_LINE_PART_MAIN, &hour_line_style_trace);
|
||||||
|
|
||||||
|
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
||||||
|
UpdateClock();
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchFaceSundial::~WatchFaceSundial() {
|
||||||
|
lv_task_del(taskRefresh);
|
||||||
|
|
||||||
|
lv_style_reset(&hour_line_style);
|
||||||
|
lv_style_reset(&hour_line_style_trace);
|
||||||
|
lv_style_reset(&minute_line_style);
|
||||||
|
lv_style_reset(&minute_line_style_trace);
|
||||||
|
lv_style_reset(&second_line_style);
|
||||||
|
|
||||||
|
lv_obj_clean(lv_scr_act());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchFaceSundial::Refresh() {
|
||||||
|
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
|
||||||
|
if (currentDateTime.IsUpdated()) {
|
||||||
|
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
|
||||||
|
UpdateClock();
|
||||||
|
if (currentDate.IsUpdated()) {
|
||||||
|
char const* MonthsString[] = {"--", "IANUARIUS","FEBRUARIUS","MARTIUS","APRILIS","MARTIUSIUNIUS","QUINTILIS","SEXTILIS","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"};
|
||||||
|
char const* DaysString[] = {"--", "LUNAE", "MARTIS", "MERCURII", "IOVIS", "VENERIS", "SATURNI", "SOLIS"};
|
||||||
|
char const* RomanNumeralsString[] = {"--", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"};
|
||||||
|
lv_label_set_text_fmt(label_date_day, "%s\n%s %s",
|
||||||
|
DaysString[static_cast<uint8_t>(dateTimeController.DayOfWeek())],
|
||||||
|
RomanNumeralsString[static_cast<uint8_t>(dateTimeController.Day())],
|
||||||
|
MonthsString[static_cast<uint8_t>(dateTimeController.Month())]);
|
||||||
|
lv_obj_align(label_date_day, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, -20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchFaceSundial::UpdateClock() {
|
||||||
|
uint8_t hour = dateTimeController.Hours();
|
||||||
|
uint8_t minute = dateTimeController.Minutes();
|
||||||
|
location = settingsController.GetLocation();
|
||||||
|
|
||||||
|
if (sHour != hour || sMinute != minute) {
|
||||||
|
// sun.setPosition(settings.lat.toFloat(), settings.lon.toFloat(), settings.gmtOffset / 3600);
|
||||||
|
sun.setPosition((float)location.latitude, (float)location.longitude, location.tzOffset);
|
||||||
|
|
||||||
|
//from minutes past midnight
|
||||||
|
sun.setCurrentDate(dateTimeController.Year(), static_cast<uint8_t>(dateTimeController.Month())+1, dateTimeController.Day());
|
||||||
|
sun.setTZOffset(location.tzOffset);
|
||||||
|
|
||||||
|
minutesSunrise = sun.calcSunrise(); //360;
|
||||||
|
minutesSunset = sun.calcSunset(); //1080;
|
||||||
|
minutesDaytime = (minutesSunset - minutesSunrise);
|
||||||
|
minutesNighttime = (1440 - minutesDaytime);
|
||||||
|
|
||||||
|
minutesBeforeSunset = minutesSunset - (hour * 60 + minute); // i.e.zero degrees
|
||||||
|
HourLength = 90; // sundial hand length
|
||||||
|
|
||||||
|
int16_t hourAngle;
|
||||||
|
|
||||||
|
if(minutesBeforeSunset > 0 && minutesBeforeSunset < minutesDaytime) { // day (after sunrise)
|
||||||
|
hourAngle = 180.0 * minutesBeforeSunset / minutesDaytime + 90;
|
||||||
|
lv_style_set_line_color(&hour_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
lv_style_set_line_color(&hour_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
|
||||||
|
lv_obj_set_style_local_text_color(one, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
lv_obj_set_style_local_text_color(twelve, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||||
|
} else { // night (before sunrise or after sunset)
|
||||||
|
lv_style_set_line_color(&hour_line_style, LV_STATE_DEFAULT, DARK_GRAY);
|
||||||
|
lv_style_set_line_color(&hour_line_style_trace, LV_STATE_DEFAULT, DARK_GRAY);
|
||||||
|
lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, DARK_ORANGE);
|
||||||
|
lv_obj_set_style_local_text_color(one, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, DARK_GRAY);
|
||||||
|
lv_obj_set_style_local_text_color(twelve, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, DARK_GRAY);
|
||||||
|
|
||||||
|
if(minutesBeforeSunset > minutesDaytime) { // before sunrise
|
||||||
|
hourAngle = 180.0 * (minutesBeforeSunset - minutesDaytime) / minutesNighttime + 90;
|
||||||
|
} else { // after sunset
|
||||||
|
hourAngle = 180 + 180.0 * minutesBeforeSunset / minutesNighttime + 90;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*NRF_LOG_INFO("a: %d, la: %f, lo: %f, ri: %d, se: %d, be: %d",
|
||||||
|
hourAngle,
|
||||||
|
(float)location.latitude,
|
||||||
|
(float)location.longitude,
|
||||||
|
minutesSunrise,
|
||||||
|
minutesSunset,
|
||||||
|
minutesBeforeSunset);*/
|
||||||
|
|
||||||
|
sHour = hour;
|
||||||
|
sMinute = minute;
|
||||||
|
|
||||||
|
printf("H%d:%d lat%f lng%f z%d\n", hour, minute, (float)location.latitude, (float)location.longitude, location.tzOffset);
|
||||||
|
printf("%d before sunset, sunrise at %d:%d sunset at %d:%d angle %d\n",
|
||||||
|
minutesBeforeSunset,
|
||||||
|
minutesSunrise/60, minutesSunrise % 60,
|
||||||
|
minutesSunset/60, minutesSunset % 60, hourAngle);
|
||||||
|
|
||||||
|
hour_point_trace[0] = CoordinateRelocateSundial(HourLength*.75, hourAngle);
|
||||||
|
hour_point_trace[1] = CoordinateRelocateSundial(HourLength, hourAngle);
|
||||||
|
|
||||||
|
hour_point[0] = CoordinateRelocateSundial(0, hourAngle);
|
||||||
|
hour_point[1] = CoordinateRelocateSundial(HourLength*.75, hourAngle);
|
||||||
|
|
||||||
|
lv_line_set_points(hour_body, hour_point, 2);
|
||||||
|
lv_line_set_points(hour_body_trace, hour_point_trace, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/displayapp/screens/WatchFaceSundial.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "components/datetime/DateTimeController.h"
|
||||||
|
#include "components/battery/BatteryController.h"
|
||||||
|
#include "components/ble/BleController.h"
|
||||||
|
#include "components/ble/NotificationManager.h"
|
||||||
|
#include "displayapp/screens/BatteryIcon.h"
|
||||||
|
#include "utility/DirtyValue.h"
|
||||||
|
#include "sunset/src/sunset.h"
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Controllers {
|
||||||
|
class Settings;
|
||||||
|
class Battery;
|
||||||
|
class Ble;
|
||||||
|
class NotificationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Applications {
|
||||||
|
namespace Screens {
|
||||||
|
|
||||||
|
class WatchFaceSundial : public Screen {
|
||||||
|
public:
|
||||||
|
WatchFaceSundial(Controllers::DateTime& dateTimeController,
|
||||||
|
Controllers::NotificationManager& notificationManager,
|
||||||
|
Controllers::Settings& settingsController);
|
||||||
|
|
||||||
|
~WatchFaceSundial() override;
|
||||||
|
|
||||||
|
void Refresh() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t sHour, sMinute, sSecond;
|
||||||
|
|
||||||
|
Utility::DirtyValue<uint8_t> batteryPercentRemaining {0};
|
||||||
|
Utility::DirtyValue<bool> isCharging {};
|
||||||
|
Utility::DirtyValue<bool> bleState {};
|
||||||
|
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
|
||||||
|
Utility::DirtyValue<bool> notificationState {false};
|
||||||
|
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||||
|
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||||
|
|
||||||
|
lv_obj_t* major_scales;
|
||||||
|
lv_obj_t* one;
|
||||||
|
lv_obj_t* twelve;
|
||||||
|
|
||||||
|
lv_obj_t* hour_body;
|
||||||
|
lv_obj_t* hour_body_trace;
|
||||||
|
lv_obj_t* minute_body;
|
||||||
|
lv_obj_t* minute_body_trace;
|
||||||
|
lv_obj_t* second_body;
|
||||||
|
|
||||||
|
lv_point_t hour_point[2];
|
||||||
|
lv_point_t hour_point_trace[2];
|
||||||
|
lv_point_t minute_point[2];
|
||||||
|
lv_point_t minute_point_trace[2];
|
||||||
|
lv_point_t second_point[2];
|
||||||
|
|
||||||
|
lv_style_t hour_line_style;
|
||||||
|
lv_style_t hour_line_style_trace;
|
||||||
|
lv_style_t minute_line_style;
|
||||||
|
lv_style_t minute_line_style_trace;
|
||||||
|
lv_style_t second_line_style;
|
||||||
|
|
||||||
|
lv_obj_t* label_date_day;
|
||||||
|
lv_obj_t* plugIcon;
|
||||||
|
lv_obj_t* notificationIcon;
|
||||||
|
lv_obj_t* bleIcon;
|
||||||
|
|
||||||
|
Controllers::Settings::Location location;
|
||||||
|
SunSet sun;
|
||||||
|
int16_t minutesSunrise;
|
||||||
|
int16_t minutesSunset;
|
||||||
|
int16_t minutesDaytime;
|
||||||
|
int16_t minutesNighttime;
|
||||||
|
int16_t minutesBeforeSunset;
|
||||||
|
|
||||||
|
const Controllers::DateTime& dateTimeController;
|
||||||
|
Controllers::NotificationManager& notificationManager;
|
||||||
|
Controllers::Settings& settingsController;
|
||||||
|
|
||||||
|
void drawWatchFaceModeNight();
|
||||||
|
void UpdateClock();
|
||||||
|
void SetBatteryIcon();
|
||||||
|
|
||||||
|
lv_task_t* taskRefresh;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct WatchFaceTraits<WatchFace::Sundial> {
|
||||||
|
static constexpr WatchFace watchFace = WatchFace::Sundial;
|
||||||
|
static constexpr const char* name = "Sundial face";
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::WatchFaceSundial(controllers.dateTimeController,
|
||||||
|
controllers.notificationManager,
|
||||||
|
controllers.settingsController);
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,180 +27,41 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController,
|
|||||||
settingsController {settingsController},
|
settingsController {settingsController},
|
||||||
heartRateController {heartRateController},
|
heartRateController {heartRateController},
|
||||||
motionController {motionController} {
|
motionController {motionController} {
|
||||||
|
batteryValue = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(batteryValue, true);
|
||||||
|
lv_obj_align(batteryValue, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -20);
|
||||||
|
|
||||||
/*Create an array for the points of the line*/
|
connectState = lv_label_create(lv_scr_act(), nullptr);
|
||||||
// progress_points[0] = {0, 0};
|
lv_label_set_recolor(connectState, true);
|
||||||
// progress_points[1] = {0, 0};
|
lv_obj_align(connectState, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 40);
|
||||||
|
|
||||||
static lv_style_t style_now_line;
|
|
||||||
lv_style_init(&style_now_line);
|
|
||||||
lv_style_set_line_width(&style_now_line, LV_STATE_DEFAULT, 1);
|
|
||||||
lv_style_set_line_color(&style_now_line, LV_STATE_DEFAULT, LV_COLOR_RED);
|
|
||||||
// lv_style_set_line_rounded(&style_now_line, LV_STATE_DEFAULT, true);
|
|
||||||
|
|
||||||
/*Create a line and apply the new style*/
|
|
||||||
now_line = lv_line_create(lv_scr_act(), NULL);
|
|
||||||
static lv_point_t line_points[] = { {40, 2}, {40, 50}};
|
|
||||||
lv_line_set_points(now_line, line_points, 2);
|
|
||||||
lv_obj_add_style(now_line, LV_LINE_PART_MAIN, &style_now_line);
|
|
||||||
lv_obj_align(now_line, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
|
||||||
|
|
||||||
static lv_style_t style_event;
|
|
||||||
lv_style_init(&style_event);
|
|
||||||
lv_style_set_radius(&style_event, LV_STATE_DEFAULT, 5);
|
|
||||||
lv_style_set_bg_opa(&style_event, LV_STATE_DEFAULT, LV_OPA_COVER);
|
|
||||||
lv_style_set_bg_color(&style_event, LV_STATE_DEFAULT, LV_COLOR_MAGENTA);
|
|
||||||
lv_style_set_pad_top(&style_event, LV_STATE_DEFAULT, 2);
|
|
||||||
lv_style_set_pad_bottom(&style_event, LV_STATE_DEFAULT, 2);
|
|
||||||
lv_style_set_pad_left(&style_event, LV_STATE_DEFAULT, 5);
|
|
||||||
lv_style_set_pad_right(&style_event, LV_STATE_DEFAULT, 5);
|
|
||||||
lv_style_set_text_font(&style_event, LV_STATE_DEFAULT, &jetbrains_mono_14);
|
|
||||||
|
|
||||||
static lv_style_t style_flex;
|
|
||||||
lv_style_init(&style_flex);
|
|
||||||
lv_style_copy(&style_flex, &style_event);
|
|
||||||
lv_style_set_bg_color(&style_flex, LV_STATE_DEFAULT, LV_COLOR_PURPLE);
|
|
||||||
|
|
||||||
static lv_style_t style_season;
|
|
||||||
lv_style_init(&style_season);
|
|
||||||
lv_style_copy(&style_season, &style_event);
|
|
||||||
lv_style_set_bg_color(&style_season, LV_STATE_DEFAULT, LV_COLOR_GREEN);
|
|
||||||
|
|
||||||
static lv_style_t style_actualization;
|
|
||||||
lv_style_init(&style_actualization);
|
|
||||||
lv_style_copy(&style_actualization, &style_event);
|
|
||||||
lv_style_set_bg_color(&style_actualization, LV_STATE_DEFAULT, LV_COLOR_TEAL);
|
|
||||||
|
|
||||||
static lv_style_t style_community;
|
|
||||||
lv_style_init(&style_community);
|
|
||||||
lv_style_copy(&style_community, &style_event);
|
|
||||||
lv_style_set_bg_color(&style_community, LV_STATE_DEFAULT, LV_COLOR_OLIVE);
|
|
||||||
|
|
||||||
/* Fixed events */
|
|
||||||
// lv_obj_t* lbl_event;
|
|
||||||
// lbl_event = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
// lv_label_set_recolor(lbl_event, true);
|
|
||||||
// lv_obj_add_style(lbl_event, LV_LABEL_PART_MAIN, &style_event);
|
|
||||||
// lv_obj_align(lbl_event, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 10);
|
|
||||||
// lv_label_set_long_mode(lbl_event, LV_LABEL_LONG_BREAK);
|
|
||||||
// lv_obj_set_width(lbl_event, LV_HOR_RES/3);
|
|
||||||
// lv_label_set_text_fmt(lbl_event, "Foo Bar");
|
|
||||||
|
|
||||||
lv_obj_t* lbl_prior_event;
|
|
||||||
|
|
||||||
for(int i=0;i<sizeof(fixedtime)/sizeof(CalendarEntry);i++){
|
|
||||||
lv_obj_t* lbl_event2;
|
|
||||||
lbl_event2 = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
lv_label_set_recolor(lbl_event2, true);
|
|
||||||
lv_obj_add_style(lbl_event2, LV_LABEL_PART_MAIN, &style_event);
|
|
||||||
|
|
||||||
if (i==0) {
|
|
||||||
lv_obj_align(lbl_event2, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 10);
|
|
||||||
} else {
|
|
||||||
lv_obj_align(lbl_event2, lbl_prior_event, LV_ALIGN_OUT_RIGHT_TOP, 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_label_set_long_mode(lbl_event2, LV_LABEL_LONG_BREAK);
|
|
||||||
lv_obj_set_width(lbl_event2, LV_HOR_RES/3);
|
|
||||||
lv_label_set_text_fmt(lbl_event2, fixedtime[i].name);
|
|
||||||
fixedtime_labels[i] = lbl_event2;
|
|
||||||
lbl_prior_event = lbl_event2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0;i<sizeof(flextime)/sizeof(TaskEntry);i++){
|
|
||||||
lv_obj_t* lbl_event2;
|
|
||||||
lbl_event2 = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
lv_label_set_recolor(lbl_event2, true);
|
|
||||||
lv_obj_add_style(lbl_event2, LV_LABEL_PART_MAIN, &style_flex);
|
|
||||||
|
|
||||||
if (i==0) {
|
|
||||||
lv_obj_align(lbl_event2, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 60);
|
|
||||||
} else {
|
|
||||||
lv_obj_align(lbl_event2, lbl_prior_event, LV_ALIGN_OUT_RIGHT_TOP, 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_label_set_long_mode(lbl_event2, LV_LABEL_LONG_BREAK);
|
|
||||||
lv_obj_set_width(lbl_event2, LV_HOR_RES/3);
|
|
||||||
lv_label_set_text_fmt(lbl_event2, flextime[i].name);
|
|
||||||
lbl_prior_event = lbl_event2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0;i<sizeof(season)/sizeof(TaskEntry);i++){
|
|
||||||
lv_obj_t* lbl_event2;
|
|
||||||
lbl_event2 = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
lv_label_set_recolor(lbl_event2, true);
|
|
||||||
lv_obj_add_style(lbl_event2, LV_LABEL_PART_MAIN, &style_season);
|
|
||||||
|
|
||||||
if (i==0) {
|
|
||||||
lv_obj_align(lbl_event2, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 125);
|
|
||||||
} else {
|
|
||||||
lv_obj_align(lbl_event2, lbl_prior_event, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_label_set_long_mode(lbl_event2, LV_LABEL_LONG_BREAK);
|
|
||||||
lv_obj_set_width(lbl_event2, LV_HOR_RES-10);
|
|
||||||
lv_label_set_text_fmt(lbl_event2, season[i].name);
|
|
||||||
lv_label_set_align(lbl_event2, LV_LABEL_ALIGN_CENTER);
|
|
||||||
lbl_prior_event = lbl_event2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0;i<sizeof(actualization)/sizeof(TaskEntry);i++){
|
|
||||||
lv_obj_t* lbl_event2;
|
|
||||||
lbl_event2 = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
lv_label_set_recolor(lbl_event2, true);
|
|
||||||
lv_obj_add_style(lbl_event2, LV_LABEL_PART_MAIN, &style_actualization);
|
|
||||||
|
|
||||||
if (i==0) {
|
|
||||||
lv_obj_align(lbl_event2, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 155);
|
|
||||||
} else {
|
|
||||||
lv_obj_align(lbl_event2, lbl_prior_event, LV_ALIGN_OUT_RIGHT_TOP, 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_label_set_long_mode(lbl_event2, LV_LABEL_LONG_BREAK);
|
|
||||||
lv_obj_set_width(lbl_event2, LV_HOR_RES/2-5);
|
|
||||||
lv_label_set_text_fmt(lbl_event2, actualization[i].name);
|
|
||||||
lbl_prior_event = lbl_event2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0;i<sizeof(community)/sizeof(TaskEntry);i++){
|
|
||||||
lv_obj_t* lbl_event2;
|
|
||||||
lbl_event2 = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
lv_label_set_recolor(lbl_event2, true);
|
|
||||||
lv_obj_add_style(lbl_event2, LV_LABEL_PART_MAIN, &style_community);
|
|
||||||
|
|
||||||
if (i==0) {
|
|
||||||
lv_obj_align(lbl_event2, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 205);
|
|
||||||
} else {
|
|
||||||
lv_obj_align(lbl_event2, lbl_prior_event, LV_ALIGN_OUT_RIGHT_TOP, 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// lv_label_set_long_mode(lbl_event2, LV_LABEL_LONG_BREAK);
|
|
||||||
// lv_obj_set_width(lbl_event2, LV_HOR_RES*0.55);
|
|
||||||
lv_label_set_text_fmt(lbl_event2, community[i].name);
|
|
||||||
lbl_prior_event = lbl_event2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lv_obj_t* lbl_event2;
|
|
||||||
// lbl_event2 = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
// lv_label_set_recolor(lbl_event2, true);
|
|
||||||
// lv_obj_add_style(lbl_event2, LV_LABEL_PART_MAIN, &style_event);
|
|
||||||
// lv_obj_align(lbl_event2, lbl_event, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
|
|
||||||
// // lv_label_set_long_mode(lbl_event2, LV_LABEL_LONG_SROLL);
|
|
||||||
// lv_label_set_text_fmt(lbl_event2, "Baz Bing Boom");
|
|
||||||
// lv_obj_set_width(lbl_event2, LV_HOR_RES-5);
|
|
||||||
// lv_obj_set_height(label_now, 40);
|
|
||||||
|
|
||||||
// batteryValue = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
// lv_label_set_recolor(batteryValue, true);
|
|
||||||
// lv_obj_align(batteryValue, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -20);
|
|
||||||
|
|
||||||
// connectState = lv_label_create(lv_scr_act(), nullptr);
|
|
||||||
// lv_label_set_recolor(connectState, true);
|
|
||||||
// lv_obj_align(connectState, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 40);
|
|
||||||
|
|
||||||
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
|
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_LEFT_MID, 0, -100);
|
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_LEFT_MID, 0, -100);
|
||||||
|
|
||||||
|
label_date = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(label_date, true);
|
||||||
|
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -40);
|
||||||
|
|
||||||
|
label_prompt_1 = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_align(label_prompt_1, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -80);
|
||||||
|
lv_label_set_text_static(label_prompt_1, "user@watch:~ $ now");
|
||||||
|
|
||||||
|
label_prompt_2 = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_align(label_prompt_2, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60);
|
||||||
|
lv_label_set_text_static(label_prompt_2, "user@watch:~ $");
|
||||||
|
|
||||||
|
label_time = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(label_time, true);
|
||||||
|
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -60);
|
||||||
|
|
||||||
|
heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(heartbeatValue, true);
|
||||||
|
lv_obj_align(heartbeatValue, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 20);
|
||||||
|
|
||||||
|
stepValue = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(stepValue, true);
|
||||||
|
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 0);
|
||||||
|
|
||||||
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
@@ -210,41 +71,29 @@ WatchFaceTerminal::~WatchFaceTerminal() {
|
|||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t timeOffset = 0;
|
|
||||||
void WatchFaceTerminal::Refresh() {
|
void WatchFaceTerminal::Refresh() {
|
||||||
// if (currentDuration == 0) {
|
powerPresent = batteryController.IsPowerPresent();
|
||||||
// lv_label_set_text_fmt(label_now, "#007fff Now:# %s",calendar[calIndex].name);
|
batteryPercentRemaining = batteryController.PercentRemaining();
|
||||||
// if (calIndex+1 < sizeof(calendar)/sizeof(CalendarEntry)) {
|
if (batteryPercentRemaining.IsUpdated() || powerPresent.IsUpdated()) {
|
||||||
// lv_label_set_text_fmt(label_next, "#ee3377 Next:# %s", calendar[calIndex+1].name);
|
lv_label_set_text_fmt(batteryValue, "[BATT]#387b54 %d%%", batteryPercentRemaining.Get());
|
||||||
// } else {
|
if (batteryController.IsPowerPresent()) {
|
||||||
// lv_label_set_text_fmt(label_next, "");
|
lv_label_ins_text(batteryValue, LV_LABEL_POS_LAST, " Charging");
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// progress_points[1] = {0,(lv_coord_t)((double)LV_VER_RES*currentDuration/calendar[calIndex].duration)};
|
bleState = bleController.IsConnected();
|
||||||
// lv_line_set_points(progress_line, progress_points, 2); /*Set the points*/
|
bleRadioEnabled = bleController.IsRadioEnabled();
|
||||||
// lv_label_set_text_fmt(batteryValue, "cI %d sO %d",calIndex, sizeof(calendar)/sizeof(CalendarEntry));
|
if (bleState.IsUpdated() || bleRadioEnabled.IsUpdated()) {
|
||||||
int last_width=0;
|
if (!bleRadioEnabled.Get()) {
|
||||||
for(int i=0;i<sizeof(fixedtime_labels)/sizeof(CalendarEntry);i++){
|
lv_label_set_text_static(connectState, "[STAT]#0082fc Disabled#");
|
||||||
lv_obj_set_width(fixedtime_labels[i], pow(log(fixedtime[i].duration),2)*10);
|
|
||||||
if (i > 0) {
|
|
||||||
lv_obj_align(fixedtime_labels[i], fixedtime_labels[i-1], LV_ALIGN_OUT_RIGHT_TOP, 5, 0);
|
|
||||||
} else {
|
} else {
|
||||||
lv_obj_align(fixedtime_labels[i], lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5-timeOffset, 10);
|
if (bleState.Get()) {
|
||||||
|
lv_label_set_text_static(connectState, "[STAT]#0082fc Connected#");
|
||||||
|
} else {
|
||||||
|
lv_label_set_text_static(connectState, "[STAT]#0082fc Disconnected#");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timeOffset++;
|
|
||||||
|
|
||||||
|
|
||||||
// currentDuration++;
|
|
||||||
// if (currentDuration>calendar[calIndex].duration) {
|
|
||||||
// currentDuration = 0;
|
|
||||||
// calIndex++;
|
|
||||||
// if (calIndex > sizeof(calendar)/sizeof(CalendarEntry)-1) {
|
|
||||||
// calIndex = 0;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
notificationState = notificationManager.AreNewNotificationsAvailable();
|
notificationState = notificationManager.AreNewNotificationsAvailable();
|
||||||
if (notificationState.IsUpdated()) {
|
if (notificationState.IsUpdated()) {
|
||||||
@@ -257,8 +106,46 @@ void WatchFaceTerminal::Refresh() {
|
|||||||
|
|
||||||
currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
|
currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
|
||||||
if (currentDateTime.IsUpdated()) {
|
if (currentDateTime.IsUpdated()) {
|
||||||
// uint8_t hour = dateTimeController.Hours();
|
uint8_t hour = dateTimeController.Hours();
|
||||||
// uint8_t minute = dateTimeController.Minutes();
|
uint8_t minute = dateTimeController.Minutes();
|
||||||
// uint8_t second = dateTimeController.Seconds();
|
uint8_t second = dateTimeController.Seconds();
|
||||||
|
|
||||||
|
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
||||||
|
char ampmChar[3] = "AM";
|
||||||
|
if (hour == 0) {
|
||||||
|
hour = 12;
|
||||||
|
} else if (hour == 12) {
|
||||||
|
ampmChar[0] = 'P';
|
||||||
|
} else if (hour > 12) {
|
||||||
|
hour = hour - 12;
|
||||||
|
ampmChar[0] = 'P';
|
||||||
|
}
|
||||||
|
lv_label_set_text_fmt(label_time, "[TIME]#11cc55 %02d:%02d:%02d %s#", hour, minute, second, ampmChar);
|
||||||
|
} else {
|
||||||
|
lv_label_set_text_fmt(label_time, "[TIME]#11cc55 %02d:%02d:%02d", hour, minute, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
|
||||||
|
if (currentDate.IsUpdated()) {
|
||||||
|
uint16_t year = dateTimeController.Year();
|
||||||
|
Controllers::DateTime::Months month = dateTimeController.Month();
|
||||||
|
uint8_t day = dateTimeController.Day();
|
||||||
|
lv_label_set_text_fmt(label_date, "[DATE]#007fff %04d-%02d-%02d#", short(year), char(month), char(day));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heartbeat = heartRateController.HeartRate();
|
||||||
|
heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
|
||||||
|
if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) {
|
||||||
|
if (heartbeatRunning.Get()) {
|
||||||
|
lv_label_set_text_fmt(heartbeatValue, "[L_HR]#ee3311 %d bpm#", heartbeat.Get());
|
||||||
|
} else {
|
||||||
|
lv_label_set_text_static(heartbeatValue, "[L_HR]#ee3311 ---#");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stepCount = motionController.NbSteps();
|
||||||
|
if (stepCount.IsUpdated()) {
|
||||||
|
lv_label_set_text_fmt(stepValue, "[STEP]#ee3377 %lu steps#", stepCount.Get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,6 @@ namespace Pinetime {
|
|||||||
class MotionController;
|
class MotionController;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CalendarEntry {
|
|
||||||
char* name;
|
|
||||||
uint32_t duration;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TaskEntry {
|
|
||||||
char* name;
|
|
||||||
uint32_t priority;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
namespace Screens {
|
namespace Screens {
|
||||||
|
|
||||||
@@ -57,50 +47,15 @@ namespace Pinetime {
|
|||||||
Utility::DirtyValue<bool> notificationState {};
|
Utility::DirtyValue<bool> notificationState {};
|
||||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
||||||
|
|
||||||
CalendarEntry fixedtime[14] = {
|
lv_obj_t* label_time;
|
||||||
{"Present FuckTime", 30},
|
lv_obj_t* label_date;
|
||||||
{"Party @ David's", 3*60},
|
lv_obj_t* label_prompt_1;
|
||||||
{"Catch the L Train", 4*60},
|
lv_obj_t* label_prompt_2;
|
||||||
{"Sleep", 8*60},
|
|
||||||
{"Wake up", 30},
|
|
||||||
{"Work", 4*60},
|
|
||||||
{"Eat Lunch", 60},
|
|
||||||
{"Afternoon Work", 4*60},
|
|
||||||
{"Get groceries", 20},
|
|
||||||
{"Prepare dinner", 30},
|
|
||||||
{"Eat", 60},
|
|
||||||
{"Sleep", 8*60},
|
|
||||||
{"Rave", 30},
|
|
||||||
{"Repeat", 10}
|
|
||||||
};
|
|
||||||
TaskEntry flextime[5] = {
|
|
||||||
{"Enjoy the sunset", 1},
|
|
||||||
// {"Enjoy the crisp night air", 3},
|
|
||||||
// {"Savor the moment", 5},
|
|
||||||
{"Collect leaves", 7},
|
|
||||||
{"Read southern gothic", 9},
|
|
||||||
{"Attend Food Not Bombs", 11},
|
|
||||||
{"Undermine authority", 13},
|
|
||||||
};
|
|
||||||
TaskEntry season[1] = {
|
|
||||||
{"大雪 Winter begins", 0},
|
|
||||||
};
|
|
||||||
TaskEntry actualization[2] = {
|
|
||||||
{"Become a tea monk", 1},
|
|
||||||
{"Organize BuyNothing", 3},
|
|
||||||
};
|
|
||||||
TaskEntry community[2] = {
|
|
||||||
{"Publish zines", 1},
|
|
||||||
{"Cultivate the next", 3},
|
|
||||||
};
|
|
||||||
|
|
||||||
lv_obj_t* fixedtime_labels[sizeof(fixedtime)/sizeof(CalendarEntry)];
|
|
||||||
uint32_t calIndex = 0;
|
|
||||||
uint32_t currentDuration = 0;
|
|
||||||
|
|
||||||
lv_obj_t* now_line;
|
|
||||||
lv_obj_t* batteryValue;
|
lv_obj_t* batteryValue;
|
||||||
|
lv_obj_t* heartbeatValue;
|
||||||
|
lv_obj_t* stepValue;
|
||||||
lv_obj_t* notificationIcon;
|
lv_obj_t* notificationIcon;
|
||||||
|
lv_obj_t* connectState;
|
||||||
|
|
||||||
Controllers::DateTime& dateTimeController;
|
Controllers::DateTime& dateTimeController;
|
||||||
const Controllers::Battery& batteryController;
|
const Controllers::Battery& batteryController;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "displayapp/screens/Weather.h"
|
#include "displayapp/screens/Weather.h"
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
#include "components/ble/SimpleWeatherService.h"
|
#include "components/ble/SimpleWeatherService.h"
|
||||||
#include "components/datetime/DateTimeController.h"
|
#include "components/datetime/DateTimeController.h"
|
||||||
#include "components/settings/Settings.h"
|
#include "components/settings/Settings.h"
|
||||||
@@ -12,27 +10,31 @@
|
|||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
lv_color_t TemperatureColor(Pinetime::Controllers::SimpleWeatherService::Temperature temp) {
|
lv_color_t TemperatureColor(int16_t temperature) {
|
||||||
if (temp.Celsius() <= 0) { // freezing
|
if (temperature <= 0) { // freezing
|
||||||
return Colors::blue;
|
return Colors::blue;
|
||||||
} else if (temp.Celsius() <= 4) { // ice
|
} else if (temperature <= 400) { // ice
|
||||||
return LV_COLOR_CYAN;
|
return LV_COLOR_CYAN;
|
||||||
} else if (temp.Celsius() >= 27) { // hot
|
} else if (temperature >= 2700) { // hot
|
||||||
return Colors::deepOrange;
|
return Colors::deepOrange;
|
||||||
}
|
}
|
||||||
return Colors::orange; // normal
|
return Colors::orange; // normal
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TemperatureStyle(Pinetime::Controllers::SimpleWeatherService::Temperature temp) {
|
uint8_t TemperatureStyle(int16_t temperature) {
|
||||||
if (temp.Celsius() <= 0) { // freezing
|
if (temperature <= 0) { // freezing
|
||||||
return LV_TABLE_PART_CELL3;
|
return LV_TABLE_PART_CELL3;
|
||||||
} else if (temp.Celsius() <= 4) { // ice
|
} else if (temperature <= 400) { // ice
|
||||||
return LV_TABLE_PART_CELL4;
|
return LV_TABLE_PART_CELL4;
|
||||||
} else if (temp.Celsius() >= 27) { // hot
|
} else if (temperature >= 2700) { // hot
|
||||||
return LV_TABLE_PART_CELL6;
|
return LV_TABLE_PART_CELL6;
|
||||||
}
|
}
|
||||||
return LV_TABLE_PART_CELL5; // normal
|
return LV_TABLE_PART_CELL5; // normal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t RoundTemperature(int16_t temp) {
|
||||||
|
return temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Weather::Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService)
|
Weather::Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService)
|
||||||
@@ -118,25 +120,22 @@ void Weather::Refresh() {
|
|||||||
if (currentWeather.IsUpdated()) {
|
if (currentWeather.IsUpdated()) {
|
||||||
auto optCurrentWeather = currentWeather.Get();
|
auto optCurrentWeather = currentWeather.Get();
|
||||||
if (optCurrentWeather) {
|
if (optCurrentWeather) {
|
||||||
int16_t temp = optCurrentWeather->temperature.Celsius();
|
int16_t temp = optCurrentWeather->temperature;
|
||||||
int16_t minTemp = optCurrentWeather->minTemperature.Celsius();
|
int16_t minTemp = optCurrentWeather->minTemperature;
|
||||||
int16_t maxTemp = optCurrentWeather->maxTemperature.Celsius();
|
int16_t maxTemp = optCurrentWeather->maxTemperature;
|
||||||
|
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, TemperatureColor(temp));
|
||||||
char tempUnit = 'C';
|
char tempUnit = 'C';
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
temp = optCurrentWeather->temperature.Fahrenheit();
|
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
||||||
minTemp = optCurrentWeather->minTemperature.Fahrenheit();
|
minTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(minTemp);
|
||||||
maxTemp = optCurrentWeather->maxTemperature.Fahrenheit();
|
maxTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(maxTemp);
|
||||||
tempUnit = 'F';
|
tempUnit = 'F';
|
||||||
}
|
}
|
||||||
lv_obj_set_style_local_text_color(temperature,
|
|
||||||
LV_LABEL_PART_MAIN,
|
|
||||||
LV_STATE_DEFAULT,
|
|
||||||
TemperatureColor(optCurrentWeather->temperature));
|
|
||||||
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||||
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
|
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
|
||||||
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
|
lv_label_set_text_fmt(temperature, "%d°%c", RoundTemperature(temp), tempUnit);
|
||||||
lv_label_set_text_fmt(minTemperature, "%d°", minTemp);
|
lv_label_set_text_fmt(minTemperature, "%d°", RoundTemperature(minTemp));
|
||||||
lv_label_set_text_fmt(maxTemperature, "%d°", maxTemp);
|
lv_label_set_text_fmt(maxTemperature, "%d°", RoundTemperature(maxTemp));
|
||||||
} else {
|
} else {
|
||||||
lv_label_set_text(icon, "");
|
lv_label_set_text(icon, "");
|
||||||
lv_label_set_text(condition, "");
|
lv_label_set_text(condition, "");
|
||||||
@@ -153,22 +152,24 @@ void Weather::Refresh() {
|
|||||||
if (optCurrentForecast) {
|
if (optCurrentForecast) {
|
||||||
std::tm localTime = *std::localtime(reinterpret_cast<const time_t*>(&optCurrentForecast->timestamp));
|
std::tm localTime = *std::localtime(reinterpret_cast<const time_t*>(&optCurrentForecast->timestamp));
|
||||||
|
|
||||||
for (int i = 0; i < optCurrentForecast->nbDays; i++) {
|
for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
|
||||||
int16_t minTemp = optCurrentForecast->days[i]->minTemperature.Celsius();
|
int16_t maxTemp = optCurrentForecast->days[i].maxTemperature;
|
||||||
int16_t maxTemp = optCurrentForecast->days[i]->maxTemperature.Celsius();
|
int16_t minTemp = optCurrentForecast->days[i].minTemperature;
|
||||||
|
lv_table_set_cell_type(forecast, 2, i, TemperatureStyle(maxTemp));
|
||||||
|
lv_table_set_cell_type(forecast, 3, i, TemperatureStyle(minTemp));
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
minTemp = optCurrentForecast->days[i]->maxTemperature.Fahrenheit();
|
maxTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(maxTemp);
|
||||||
maxTemp = optCurrentForecast->days[i]->minTemperature.Fahrenheit();
|
minTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(minTemp);
|
||||||
}
|
}
|
||||||
lv_table_set_cell_type(forecast, 2, i, TemperatureStyle(optCurrentForecast->days[i]->maxTemperature));
|
|
||||||
lv_table_set_cell_type(forecast, 3, i, TemperatureStyle(optCurrentForecast->days[i]->minTemperature));
|
|
||||||
uint8_t wday = localTime.tm_wday + i + 1;
|
uint8_t wday = localTime.tm_wday + i + 1;
|
||||||
if (wday > 7) {
|
if (wday > 7) {
|
||||||
wday -= 7;
|
wday -= 7;
|
||||||
}
|
}
|
||||||
|
maxTemp = RoundTemperature(maxTemp);
|
||||||
|
minTemp = RoundTemperature(minTemp);
|
||||||
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
|
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
|
||||||
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
|
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
|
||||||
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId));
|
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i].iconId));
|
||||||
// Pad cells based on the largest number of digits on each column
|
// Pad cells based on the largest number of digits on each column
|
||||||
char maxPadding[3] = " ";
|
char maxPadding[3] = " ";
|
||||||
char minPadding[3] = " ";
|
char minPadding[3] = " ";
|
||||||
|
|||||||