Skip to content

Commit a563443

Browse files
authored
Merge branch 'main' into main
2 parents 93dc66e + 6572b50 commit a563443

23 files changed

+325
-114
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
cmake_minimum_required(VERSION 3.10)
22

33
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
4+
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
45

5-
project(pinetime VERSION 1.15.0 LANGUAGES C CXX ASM)
6+
project(pinetime VERSION 1.16.0 LANGUAGES C CXX ASM)
67

78
set(CMAKE_C_STANDARD 99)
89
set(CMAKE_CXX_STANDARD 20)

doc/SimpleWeatherService.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,25 @@ The host uses this characteristic to update the current weather information and
1717

1818
This characteristics accepts a byte array with the following 2-Bytes header:
1919

20-
- [0] Message Type :
20+
- [0] Message Type :
2121
- `0` : Current weather
2222
- `1` : Forecast
23-
- [1] Message Version : Version `0` is currently supported. Other versions might be added in future releases
23+
- [1] Message Version :
24+
- `0` : Currently supported
25+
- `1` : Adds support for sunrise and sunset
2426

25-
### Current Weather
27+
### Current Weather
2628

2729
The byte array must contain the following data:
2830

2931
- [0] : Message type = `0`
30-
- [1] : Message version = `0`
32+
- [1] : Message version = `1`
3133
- [2][3][4][5][6][7][8][9] : Timestamp (64 bits UNIX timestamp, number of seconds elapsed since 1 JAN 1970) in local time (the same timezone as the one used to set the time)
3234
- [10, 11] : Current temperature (°C * 100)
3335
- [12, 13] : Minimum temperature (°C * 100)
3436
- [14, 15] : Maximum temperature (°C * 100)
3537
- [16]..[47] : location (string, unused characters should be set to `0`)
36-
- [48] : icon ID
38+
- [48] : icon ID
3739
- 0 = Sun, clear sky
3840
- 1 = Few clouds
3941
- 2 = Clouds
@@ -43,6 +45,13 @@ The byte array must contain the following data:
4345
- 6 = Thunderstorm
4446
- 7 = Snow
4547
- 8 = Mist, smog
48+
- [49, 50] : Sunrise (number of minutes elapsed since midnight)
49+
- `0` sun already up when day starts
50+
- `-1` unknown
51+
- `-2` no sunrise (e.g. polar night)
52+
- [51, 52] : Sunset (number of minutes elapsed since midnight)
53+
- `-1` unknown
54+
- `-2` no sunset (e.g. polar day)
4655

4756
### Forecast
4857

doc/buildWithDocker.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,9 @@ Here's an example for `pinetime-app`:
8787
```sh
8888
docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime-build /opt/build.sh pinetime-app
8989
```
90+
91+
If you want to change the apps and/or watchfaces built in the project, you can pass `ENABLE_USERAPPS` and `ENABLE_WATCHFACES` as environment variables like so:
92+
93+
```sh
94+
docker run --rm -it -v ${PWD}:/sources -e ENABLE_USERAPPS="Apps::Alarm,Apps::Timer,Apps::Steps,Apps::HeartRate,Apps::Music,Apps::Navigation" infinitime-build
95+
```

doc/code/Intro.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This page is meant to guide you through the source code, so you can find the rel
77
Infinitime is based on FreeRTOS, a real-time operating system.
88
FreeRTOS provides several quality of life abstractions (for example easy software timers)
99
and most importantly supports multiple tasks.
10-
If you want to read up on real-time operating systems, you can look [here](https://www.freertos.org/implementation/a00002.html) and [here](https://www.freertos.org/features.html).
10+
If you want to read up on real-time operating systems, you can look [here](https://www.freertos.org/Documentation/01-FreeRTOS-quick-start/01-Beginners-guide/01-RTOS-fundamentals) and [here](https://www.freertos.org/features.html).
1111
The main "process" creates at least one task and then starts the FreeRTOS task scheduler.
1212
This main "process" is the standard main() function inside [main.cpp](/src/main.cpp).
1313
The task scheduler is responsible for giving every task enough cpu time.

docker/build.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,19 @@ GetNrfSdk() {
7777
}
7878

7979
CmakeGenerate() {
80+
CMAKE_ARGS=()
81+
[ -n "$ENABLE_USERAPPS" ] && CMAKE_ARGS+=("-DENABLE_USERAPPS=$ENABLE_USERAPPS")
82+
[ -n "$ENABLE_WATCHFACES" ] && CMAKE_ARGS+=("-DENABLE_WATCHFACES=$ENABLE_WATCHFACES")
83+
8084
cmake -G "Unix Makefiles" \
8185
-S "$SOURCES_DIR" \
8286
-B "$BUILD_DIR" \
8387
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
8488
-DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_PATH" \
8589
-DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \
8690
-DBUILD_DFU=1 \
87-
-DBUILD_RESOURCES=1
91+
-DBUILD_RESOURCES=1 \
92+
${CMAKE_ARGS[@]}
8893
}
8994

9095
CmakeBuild() {

src/components/ble/SimpleWeatherService.cpp

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,48 @@ namespace {
4141
SimpleWeatherService::Location cityName;
4242
std::memcpy(cityName.data(), &dataBuffer[16], 32);
4343
cityName[32] = '\0';
44+
int16_t sunrise = -1;
45+
int16_t sunset = -1;
46+
if (dataBuffer[1] > 0) {
47+
int16_t bufferSunrise = ToInt16(&dataBuffer[49]);
48+
int16_t bufferSunset = ToInt16(&dataBuffer[51]);
49+
50+
// Sunrise/sunset format
51+
52+
// Assume sun is down at minute 0 / midnight
53+
54+
// 0<=x<1440 sunrise happens this many minutes into the day
55+
// (0 day starts with sun up)
56+
// -1 unknown
57+
// -2 sun not rising today
58+
59+
// 0<x<1440 sunset happens this many minutes into the day
60+
// -1 unknown
61+
// -2 sun not setting today
62+
63+
// Check if the weather data is well formed
64+
// Disable boolean simplification suggestion, as simplifying it makes it unreadable
65+
if (!( // NOLINT(readability-simplify-boolean-expr)
66+
// Fail if either unknown
67+
(bufferSunrise == -1 || bufferSunset == -1)
68+
// Cannot be out of range
69+
|| (bufferSunrise < -2 || bufferSunrise > 1439 || bufferSunset < -2 || bufferSunset > 1439)
70+
// Cannot have sunset without sunrise
71+
|| (bufferSunrise == -2 && bufferSunset != -2)
72+
// Cannot have sunset before sunrise
73+
|| (bufferSunrise >= bufferSunset && bufferSunrise >= 0 && bufferSunset >= 0))) {
74+
sunrise = bufferSunrise;
75+
sunset = bufferSunset;
76+
}
77+
}
4478
return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
4579
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[10])),
4680
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[12])),
4781
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[14])),
4882
SimpleWeatherService::Icons {dataBuffer[16 + 32]},
49-
std::move(cityName));
83+
std::move(cityName),
84+
sunrise,
85+
sunset);
5086
}
5187

5288
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
@@ -94,7 +130,7 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
94130

95131
switch (GetMessageType(dataBuffer)) {
96132
case MessageType::CurrentWeather:
97-
if (GetVersion(dataBuffer) == 0) {
133+
if (GetVersion(dataBuffer) <= 1) {
98134
currentWeather = CreateCurrentWeather(dataBuffer);
99135
NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
100136
currentWeather->timestamp,
@@ -103,6 +139,9 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
103139
currentWeather->maxTemperature.PreciseCelsius(),
104140
currentWeather->iconId,
105141
currentWeather->location.data());
142+
if (GetVersion(dataBuffer) == 1) {
143+
NRF_LOG_INFO("Sunrise: %d\n\tSunset: %d", currentWeather->sunrise, currentWeather->sunset);
144+
}
106145
}
107146
break;
108147
case MessageType::Forecast:
@@ -153,10 +192,36 @@ std::optional<SimpleWeatherService::Forecast> SimpleWeatherService::GetForecast(
153192
return {};
154193
}
155194

195+
bool SimpleWeatherService::IsNight() const {
196+
if (currentWeather && currentWeather->sunrise != -1 && currentWeather->sunset != -1) {
197+
auto currentTime = dateTimeController.CurrentDateTime().time_since_epoch();
198+
199+
// Get timestamp for last midnight
200+
auto midnight = std::chrono::floor<std::chrono::days>(currentTime);
201+
202+
// Calculate minutes since midnight
203+
auto currentMinutes = std::chrono::duration_cast<std::chrono::minutes>(currentTime - midnight).count();
204+
205+
// Sun not rising today => night all hours
206+
if (currentWeather->sunrise == -2) {
207+
return true;
208+
}
209+
// Sun not setting today => check before sunrise
210+
if (currentWeather->sunset == -2) {
211+
return currentMinutes < currentWeather->sunrise;
212+
}
213+
214+
// Before sunrise or after sunset
215+
return currentMinutes < currentWeather->sunrise || currentMinutes >= currentWeather->sunset;
216+
}
217+
218+
return false;
219+
}
220+
156221
bool SimpleWeatherService::CurrentWeather::operator==(const SimpleWeatherService::CurrentWeather& other) const {
157222
return this->iconId == other.iconId && this->temperature == other.temperature && this->timestamp == other.timestamp &&
158223
this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature &&
159-
std::strcmp(this->location.data(), other.location.data()) == 0;
224+
std::strcmp(this->location.data(), other.location.data()) == 0 && this->sunrise == other.sunrise && this->sunset == other.sunset;
160225
}
161226

162227
bool SimpleWeatherService::Forecast::Day::operator==(const SimpleWeatherService::Forecast::Day& other) const {

src/components/ble/SimpleWeatherService.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "components/datetime/DateTimeController.h"
3535
#include <lvgl/lvgl.h>
3636
#include "displayapp/InfiniTimeTheme.h"
37+
#include "utility/Math.h"
3738

3839
int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg);
3940

@@ -77,11 +78,11 @@ namespace Pinetime {
7778
}
7879

7980
[[nodiscard]] int16_t Celsius() const {
80-
return (PreciseCelsius() + 50) / 100;
81+
return Utility::RoundedDiv(PreciseCelsius(), static_cast<int16_t>(100));
8182
}
8283

8384
[[nodiscard]] int16_t Fahrenheit() const {
84-
return (PreciseFahrenheit() + 50) / 100;
85+
return Utility::RoundedDiv(PreciseFahrenheit(), static_cast<int16_t>(100));
8586
}
8687

8788
[[nodiscard]] lv_color_t Color() const {
@@ -112,13 +113,17 @@ namespace Pinetime {
112113
Temperature minTemperature,
113114
Temperature maxTemperature,
114115
Icons iconId,
115-
Location&& location)
116+
Location&& location,
117+
int16_t sunrise,
118+
int16_t sunset)
116119
: timestamp {timestamp},
117120
temperature {temperature},
118121
minTemperature {minTemperature},
119122
maxTemperature {maxTemperature},
120123
iconId {iconId},
121-
location {std::move(location)} {
124+
location {std::move(location)},
125+
sunrise {sunrise},
126+
sunset {sunset} {
122127
}
123128

124129
uint64_t timestamp;
@@ -127,6 +132,8 @@ namespace Pinetime {
127132
Temperature maxTemperature;
128133
Icons iconId;
129134
Location location;
135+
int16_t sunrise;
136+
int16_t sunset;
130137

131138
bool operator==(const CurrentWeather& other) const;
132139
};
@@ -151,6 +158,8 @@ namespace Pinetime {
151158
std::optional<CurrentWeather> Current() const;
152159
std::optional<Forecast> GetForecast() const;
153160

161+
[[nodiscard]] bool IsNight() const;
162+
154163
private:
155164
// 00050000-78fc-48fe-8e23-433b3a1942d0
156165
static constexpr ble_uuid128_t BaseUuid() {

src/displayapp/DisplayApp.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,14 @@
5454
#include "displayapp/screens/settings/SettingOTA.h"
5555
#include "displayapp/screens/settings/SettingTheme.h"
5656

57+
#include "utility/Math.h"
58+
5759
#include "libs/lv_conf.h"
5860
#include "UserApps.h"
5961
#include "displayapp/InfiniTimeTheme.h"
6062

6163
#include <algorithm>
64+
#include <limits>
6265

6366
using namespace Pinetime::Applications;
6467
using namespace Pinetime::Applications::Display;
@@ -169,18 +172,15 @@ TickType_t DisplayApp::CalculateSleepTime() {
169172
// Calculates how many system ticks DisplayApp should sleep before rendering the next AOD frame
170173
// Next frame time is frame count * refresh period (ms) * tick rate
171174

172-
auto RoundedDiv = [](uint32_t a, uint32_t b) {
173-
return ((a + (b / 2)) / b);
174-
};
175-
// RoundedDiv overflows when numerator + (denominator floordiv 2) > uint32 max
176-
// in this case around 9 hours (=overflow frame count / always on refresh period)
177-
constexpr TickType_t overflowFrameCount = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod);
175+
// Avoid overflow when numerator would be > uint32 max
176+
// in this case around 18 hours (=overflow frame count / always on refresh period)
177+
constexpr TickType_t overflowFrameCount = std::numeric_limits<TickType_t>::max() / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod);
178178

179179
TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime;
180180
// Divide both the numerator and denominator by 8 (=GCD(1000,1024))
181181
// to increase the number of ticks (frames) before the overflow tick is reached
182-
TickType_t targetRenderTick = RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnFrameCount * alwaysOnRefreshPeriod, 1000 / 8);
183-
182+
TickType_t targetRenderTick =
183+
Utility::RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnFrameCount * alwaysOnRefreshPeriod, static_cast<uint32_t>(1000 / 8));
184184
// Assumptions
185185

186186
// Tick rate is multiple of 8
@@ -190,7 +190,8 @@ TickType_t DisplayApp::CalculateSleepTime() {
190190
// Frame count must always wraparound more often than the system tick count does
191191
// Always on overflow time (ms) < system tick overflow time (ms)
192192
// Using 64bit ints here to avoid overflow
193-
static_assert((uint64_t) overflowFrameCount * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ);
193+
static_assert((uint64_t) overflowFrameCount * (uint64_t) alwaysOnRefreshPeriod <
194+
(uint64_t) std::numeric_limits<TickType_t>::max() * 1000ULL / configTICK_RATE_HZ);
194195

195196
if (alwaysOnFrameCount == overflowFrameCount) {
196197
alwaysOnFrameCount = 0;

src/displayapp/InfiniTimeTheme.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace Colors {
1515
static constexpr lv_color_t green = LV_COLOR_MAKE(0x0, 0xb0, 0x0);
1616
static constexpr lv_color_t blue = LV_COLOR_MAKE(0x0, 0x50, 0xff);
1717
static constexpr lv_color_t lightGray = LV_COLOR_MAKE(0xb0, 0xb0, 0xb0);
18+
static constexpr lv_color_t gray = LV_COLOR_MAKE(0x50, 0x50, 0x50);
1819

1920
// Configurable colors that can be loaded from storage
2021
inline lv_color_t accent_light = LV_COLOR_MAKE(0x5d, 0x69, 0x7e);

src/displayapp/fonts/fonts.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"sources": [
6969
{
7070
"file": "FontAwesome5-Solid+Brands+Regular.woff",
71-
"range": "0xf185, 0xf6c4, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e, 0xf73b, 0xf0e7, 0xf2dc"
71+
"range": "0xf185, 0xf186, 0xf6c3, 0xf6c4, 0xf73c, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e, 0xf73b, 0xf0e7, 0xf2dc"
7272
}
7373
],
7474
"bpp": 1,

0 commit comments

Comments
 (0)