Merge branch 'develop' of JF/PineTime into master

This commit is contained in:
JF 2021-07-23 11:59:08 +02:00 committed by Gitea
commit db6a701644
67 changed files with 2951 additions and 995 deletions

View File

@ -1,4 +1,5 @@
Checks: '*,
-altera-unroll-loops,
-llvmlibc-callee-namespace,
-llvm-header-guard,
-llvm-namespace-comment,
@ -14,17 +15,20 @@ Checks: '*,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-special-member-functions,
-readability-magic-numbers,
-readability-uppercase-literal-suffix,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
-hicpp-signed-bitwise,
-hicpp-no-assembler,
-hicpp-avoid-c-arrays,
-hicpp-uppercase-literal-suffix,
-hicpp-vararg,
-hicpp-no-assembler,
-hicpp-no-array-decay,
-hicpp-signed-bitwise,
-hicpp-special-member-functions,
-cert-err58-cpp,
-cert-err60-cpp'
CheckOptions:
- key: readability-function-cognitive-complexity.Threshold
value: 100
value: 100

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "src/libs/lvgl"]
path = src/libs/lvgl
url = https://github.com/joaquimorg/lvgl.git
[submodule "src/libs/littlefs"]
path = src/libs/littlefs
url = https://github.com/littlefs-project/littlefs.git

View File

@ -12,6 +12,29 @@
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
</Objective-C>
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
<codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="140" />
<option name="IF_BRACE_FORCE" value="3" />

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(pinetime VERSION 1.2.0 LANGUAGES C CXX ASM)
project(pinetime VERSION 1.3.0 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 14)

View File

@ -53,6 +53,7 @@ As of now, here is the list of achievements of this project:
* Two (2048 clone game)
* Stopwatch (with all the necessary functions such as play, pause, lap, stop)
* Motion sensor and step counter (displays the number of steps and the state of the motion sensor in real-time)
* Metronome (vibrates to a given bpm with a customizable beats per bar)
- User settings:
* Display timeout
* Wake-up condition

View File

@ -27,7 +27,7 @@ CMake configures the project according to variables you specify the command line
**NRFJPROG**|Path to the NRFJProg executable. Used only if `USE_JLINK` is 1.|`-DNRFJPROG=/opt/nrfjprog/nrfjprog`
**GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb`
**GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0`
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-BUILD_DFU=1`
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
####(**) Note about **CMAKE_BUILD_TYPE**:
By default, this variable is set to *Release*. It compiles the code with size and speed optimizations. We use this value for all the binaries we publish when we [release](https://github.com/JF002/InfiniTime/releases) new versions of InfiniTime.

View File

@ -1,26 +1,36 @@
#include <cstdint>
#include <cstdio>
#include "BootloaderVersion.h"
using namespace Pinetime;
// NOTE : current bootloader does not export its version to the application firmware.
// NOTE : version < 1.0.0 of bootloader does not export its version to the application firmware.
uint32_t BootloaderVersion::Major() {
return 0;
uint32_t BootloaderVersion::version = 0;
char BootloaderVersion::versionString[BootloaderVersion::VERSION_STR_LEN] = "0.0.0";
const uint32_t BootloaderVersion::Major() {
return (BootloaderVersion::version >> 16u) & 0xff;
}
uint32_t BootloaderVersion::Minor() {
return 0;
const uint32_t BootloaderVersion::Minor() {
return (BootloaderVersion::version >> 8u) & 0xff;
}
uint32_t BootloaderVersion::Patch() {
return 0;
const uint32_t BootloaderVersion::Patch() {
return BootloaderVersion::version & 0xff;
}
const char* BootloaderVersion::VersionString() {
return "0.0.0";
return BootloaderVersion::versionString;
}
bool BootloaderVersion::IsValid() {
return false;
const bool BootloaderVersion::IsValid() {
return BootloaderVersion::version >= 0x00010000;
}
void BootloaderVersion::SetVersion(uint32_t v) {
BootloaderVersion::version = v;
snprintf(BootloaderVersion::versionString, BootloaderVersion::VERSION_STR_LEN, "%ld.%ld.%ld",
BootloaderVersion::Major(), BootloaderVersion::Minor(), BootloaderVersion::Patch());
}

View File

@ -3,10 +3,15 @@
namespace Pinetime {
class BootloaderVersion {
public:
static uint32_t Major();
static uint32_t Minor();
static uint32_t Patch();
static const uint32_t Major();
static const uint32_t Minor();
static const uint32_t Patch();
static const char* VersionString();
static bool IsValid();
static const bool IsValid();
static void SetVersion(uint32_t v);
private:
static uint32_t version;
static constexpr size_t VERSION_STR_LEN = 12;
static char versionString[VERSION_STR_LEN];
};
}
}

View File

@ -166,6 +166,13 @@ set(NIMBLE_SRC
libs/mynewt-nimble/nimble/host/util/src/addr.c
)
set(LITTLEFS_SRC
libs/littlefs/lfs_util.h
libs/littlefs/lfs.h
libs/littlefs/lfs_util.c
libs/littlefs/lfs.c
)
set(LVGL_SRC
libs/lv_conf.h
libs/lvgl/lvgl.h
@ -235,6 +242,7 @@ set(LVGL_SRC
libs/lvgl/src/lv_widgets/lv_cont.h
libs/lvgl/src/lv_widgets/lv_cpicker.h
libs/lvgl/src/lv_widgets/lv_dropdown.h
libs/lvgl/src/lv_widgets/lv_gauge.h
libs/lvgl/src/lv_widgets/lv_img.h
libs/lvgl/src/lv_widgets/lv_imgbtn.h
libs/lvgl/src/lv_widgets/lv_keyboard.h
@ -321,6 +329,7 @@ set(LVGL_SRC
libs/lvgl/src/lv_widgets/lv_cont.c
libs/lvgl/src/lv_widgets/lv_cpicker.c
libs/lvgl/src/lv_widgets/lv_dropdown.c
libs/lvgl/src/lv_widgets/lv_gauge.c
libs/lvgl/src/lv_widgets/lv_img.c
libs/lvgl/src/lv_widgets/lv_imgbtn.c
libs/lvgl/src/lv_widgets/lv_keyboard.c
@ -423,6 +432,7 @@ list(APPEND SOURCE_FILES
displayapp/icons/bg_clock.c
displayapp/screens/WatchFaceAnalog.cpp
displayapp/screens/WatchFaceDigital.cpp
displayapp/screens/PineTimeStyle.cpp
##
@ -462,6 +472,7 @@ list(APPEND SOURCE_FILES
components/motor/MotorController.cpp
components/settings/Settings.cpp
components/timer/TimerController.cpp
components/fs/FS.cpp
drivers/Cst816s.cpp
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
@ -473,6 +484,7 @@ list(APPEND SOURCE_FILES
displayapp/fonts/jetbrains_mono_76.c
displayapp/fonts/jetbrains_mono_42.c
displayapp/fonts/lv_font_sys_48.c
displayapp/fonts/open_sans_light.c
displayapp/lv_pinetime_theme.c
systemtask/SystemTask.cpp
@ -539,6 +551,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/heartrate/Biquad.cpp
components/heartrate/Ptagc.cpp
components/motor/MotorController.cpp
components/fs/FS.cpp
)
list(APPEND RECOVERYLOADER_SOURCE_FILES
@ -797,13 +810,25 @@ target_compile_options(lvgl PRIVATE
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
)
# LITTLEFS_SRC
add_library(littlefs STATIC ${LITTLEFS_SRC})
target_include_directories(littlefs SYSTEM PUBLIC . ../)
target_include_directories(littlefs SYSTEM PUBLIC ${INCLUDES_FROM_LIBS})
target_compile_options(littlefs PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wno-unused-function -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wno-unused-function -Os>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Wno-unused-function -Og -g3 -fno-rtti>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Wno-unused-function -Os -fno-rtti>
$<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
)
# Build autonomous binary (without support for bootloader)
set(EXECUTABLE_NAME "pinetime-app")
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")
add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME})
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl)
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs)
target_compile_options(${EXECUTABLE_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -Os>
@ -832,7 +857,7 @@ set(IMAGE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERSION_
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")
add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES})
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl)
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs)
set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME})
target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC
$<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -Og -g3>
@ -868,7 +893,7 @@ endif()
set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery")
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})
target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk)
target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs)
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_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC
@ -898,7 +923,7 @@ set(EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-${
set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.bin)
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})
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk)
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs)
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_options(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC

View File

@ -77,7 +77,7 @@
#define configENABLE_BACKWARD_COMPATIBILITY 1
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0

View File

@ -1,9 +1,7 @@
#include "BatteryController.h"
#include <hal/nrf_gpio.h>
#include <nrfx_saadc.h>
#include <libraries/log/nrf_log.h>
#include <algorithm>
#include <math.h>
using namespace Pinetime::Controllers;
@ -14,16 +12,16 @@ Battery::Battery() {
}
void Battery::Init() {
nrf_gpio_cfg_input(chargingPin, (nrf_gpio_pin_pull_t) GPIO_PIN_CNF_PULL_Pullup);
nrf_gpio_cfg_input(chargingPin, static_cast<nrf_gpio_pin_pull_t> GPIO_PIN_CNF_PULL_Pullup);
}
void Battery::Update() {
isCharging = !nrf_gpio_pin_read(chargingPin);
isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
if (isReading)
if (isReading) {
return;
}
// Non blocking read
samples = 0;
isReading = true;
@ -32,13 +30,13 @@ void Battery::Update() {
nrfx_saadc_sample();
}
void Battery::adcCallbackStatic(nrfx_saadc_evt_t const* event) {
void Battery::AdcCallbackStatic(nrfx_saadc_evt_t const* event) {
instance->SaadcEventHandler(event);
}
void Battery::SaadcInit() {
nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, adcCallbackStatic));
APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, AdcCallbackStatic));
nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
@ -54,23 +52,23 @@ void Battery::SaadcInit() {
}
void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
const float battery_max = 4.18; // maximum voltage of battery ( max charging voltage is 4.21 )
const float battery_min = 3.20; // minimum voltage of battery before shutdown ( depends on the battery )
const uint16_t battery_max = 4180; // maximum voltage of battery ( max charging voltage is 4.21 )
const uint16_t battery_min = 3200; // minimum voltage of battery before shutdown ( depends on the battery )
if (p_event->type == NRFX_SAADC_EVT_DONE) {
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1));
voltage = (static_cast<float>(p_event->data.done.p_buffer[0]) * 2.04f) / (1024 / 3.0f);
voltage = roundf(voltage * 100) / 100;
percentRemaining = static_cast<int>(((voltage - battery_min) / (battery_max - battery_min)) * 100);
// A hardware voltage divider divides the battery voltage by 2
// ADC gain is 1/5
// thus adc_voltage = battery_voltage / 2 * gain = battery_voltage / 10
// reference_voltage is 0.6V
// p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
voltage = p_event->data.done.p_buffer[0] * 6000 / 1024;
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
percentRemaining = std::max(percentRemaining, 0);
percentRemaining = std::min(percentRemaining, 100);
percentRemainingBuffer.insert(percentRemaining);
percentRemainingBuffer.Insert(percentRemaining);
samples++;
if (samples > percentRemainingSamples) {

View File

@ -19,7 +19,7 @@ namespace Pinetime {
insert member function overwrites the next data to the current
HEAD and moves the HEAD to the newly inserted value.
*/
void insert(const int num) {
void Insert(const uint8_t num) {
head %= cap;
arr[head++] = num;
if (sz != cap) {
@ -27,13 +27,13 @@ namespace Pinetime {
}
}
int GetAverage() const {
uint8_t GetAverage() const {
int sum = std::accumulate(arr.begin(), arr.end(), 0);
return (sum / sz);
return static_cast<uint8_t>(sum / sz);
}
private:
std::array<int, N> arr; /**< internal array used to store the values*/
std::array<uint8_t, N> arr; /**< internal array used to store the values*/
uint8_t sz; /**< The current size of the array.*/
uint8_t cap; /**< Total capacity of the CircBuffer.*/
uint8_t head; /**< The current head of the CircBuffer*/
@ -46,17 +46,21 @@ namespace Pinetime {
void Init();
void Update();
int PercentRemaining() const {
return percentRemainingBuffer.GetAverage();
uint8_t PercentRemaining() const {
auto avg = percentRemainingBuffer.GetAverage();
avg = std::min(avg, static_cast<uint8_t>(100));
avg = std::max(avg, static_cast<uint8_t>(0));
return avg;
}
float Voltage() const {
uint16_t Voltage() const {
return voltage;
}
bool IsCharging() const {
return isCharging;
}
bool IsPowerPresent() const {
return isPowerPresent;
}
@ -71,7 +75,7 @@ namespace Pinetime {
static constexpr uint32_t chargingPin = 12;
static constexpr uint32_t powerPresentPin = 19;
static constexpr nrf_saadc_input_t batteryVoltageAdcInput = NRF_SAADC_INPUT_AIN7;
float voltage = 0.0f;
uint16_t voltage = 0;
int percentRemaining = -1;
bool isCharging = false;
@ -80,10 +84,10 @@ namespace Pinetime {
void SaadcInit();
void SaadcEventHandler(nrfx_saadc_evt_t const* p_event);
static void adcCallbackStatic(nrfx_saadc_evt_t const* event);
static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
bool isReading = false;
uint8_t samples = 0;
};
}
}
}

View File

@ -17,7 +17,7 @@ BatteryInformationService::BatteryInformationService(Controllers::Battery& batte
characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid,
.access_cb = BatteryInformationServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &batteryLevelHandle},
{0}},
serviceDefinition {
@ -48,4 +48,8 @@ int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHand
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
return 0;
}
}
void BatteryInformationService::NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level) {
auto* om = ble_hs_mbuf_from_flat(&level, 1);
ble_gattc_notify_custom(connectionHandle, batteryLevelHandle, om);
}

View File

@ -17,7 +17,7 @@ namespace Pinetime {
void Init();
int OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level);
private:
Controllers::Battery& batteryController;
static constexpr uint16_t batteryInformationServiceId {0x180F};

View File

@ -121,6 +121,11 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
NRF_LOG_INFO(
"[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, bootloaderSize, applicationSize);
// wait until SystemTask has finished waking up all devices
while (systemTask.IsSleeping()) {
vTaskDelay(50); // 50ms
}
dfuImage.Erase();
uint8_t data[] {16, 1, 1};
@ -261,13 +266,14 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
static_cast<uint8_t>(ErrorCodes::NoError)};
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
} else {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
NRF_LOG_INFO("Image Error : bad CRC");
uint8_t data[3] {static_cast<uint8_t>(Opcodes::Response),
static_cast<uint8_t>(Opcodes::ValidateFirmware),
static_cast<uint8_t>(ErrorCodes::CrcError)};
notificationManager.AsyncSend(connectionHandle, controlPointCharacteristicHandle, data, 3);
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
Reset();
}
return 0;
@ -278,10 +284,8 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
return 0;
}
NRF_LOG_INFO("[DFU] -> Activate image and reset!");
bleController.StopFirmwareUpdate();
systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateFinished);
Reset();
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated);
Reset();
return 0;
default:
return 0;
@ -289,6 +293,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
}
void DfuService::OnTimeout() {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
Reset();
}
@ -302,7 +307,6 @@ void DfuService::Reset() {
applicationSize = 0;
expectedCrc = 0;
notificationManager.Reset();
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
bleController.StopFirmwareUpdate();
systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateFinished);
}

View File

@ -18,79 +18,99 @@
#include "MusicService.h"
#include "systemtask/SystemTask.h"
int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::MusicService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
return ble_uuid128_t{
.u = {.type = BLE_UUID_TYPE_128},
.value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
};
}
// 00000000-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t BaseUuid() {
return CharUuid(0x00, 0x00);
}
constexpr ble_uuid128_t msUuid {BaseUuid()};
constexpr ble_uuid128_t msEventCharUuid {CharUuid(0x01, 0x00)};
constexpr ble_uuid128_t msStatusCharUuid {CharUuid(0x02, 0x00)};
constexpr ble_uuid128_t msArtistCharUuid {CharUuid(0x03, 0x00)};
constexpr ble_uuid128_t msTrackCharUuid {CharUuid(0x04, 0x00)};
constexpr ble_uuid128_t msAlbumCharUuid {CharUuid(0x05, 0x00)};
constexpr ble_uuid128_t msPositionCharUuid {CharUuid(0x06, 0x00)};
constexpr ble_uuid128_t msTotalLengthCharUuid {CharUuid(0x07, 0x00)};
constexpr ble_uuid128_t msTrackNumberCharUuid {CharUuid(0x08, 0x00)};
constexpr ble_uuid128_t msTrackTotalCharUuid {CharUuid(0x09, 0x00)};
constexpr ble_uuid128_t msPlaybackSpeedCharUuid {CharUuid(0x0a, 0x00)};
constexpr ble_uuid128_t msRepeatCharUuid {CharUuid(0x0b, 0x00)};
constexpr ble_uuid128_t msShuffleCharUuid {CharUuid(0x0c, 0x00)};
int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::MusicService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
}
}
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask& system) : m_system(system) {
characteristicDefinition[0] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msEventCharUuid),
characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &eventHandle};
characteristicDefinition[1] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msStatusCharUuid),
characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[2] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackCharUuid),
characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[3] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msArtistCharUuid),
characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[4] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msAlbumCharUuid),
characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[5] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msPositionCharUuid),
characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[6] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid),
characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[7] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid),
characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[8] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackNumberCharUuid),
characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[9] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msTrackTotalCharUuid),
characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[10] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msPlaybackSpeedCharUuid),
characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[11] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msRepeatCharUuid),
characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[12] = {.uuid = reinterpret_cast<ble_uuid_t*>(&msShuffleCharUuid),
characteristicDefinition[12] = {.uuid = &msShuffleCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[13] = {0};
serviceDefinition[0] = {
.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = reinterpret_cast<ble_uuid_t*>(&msUuid), .characteristics = characteristicDefinition};
.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &msUuid.u, .characteristics = characteristicDefinition};
serviceDefinition[1] = {0};
artistName = "Waiting for";
albumName = "";
trackName = "track information..";
playing = false;
repeat = false;
shuffle = false;
playbackSpeed = 1.0f;
trackProgress = 0;
trackLength = 0;
}
void Pinetime::Controllers::MusicService::Init() {
@ -109,27 +129,27 @@ int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_
data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char* s = &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msArtistCharUuid)) == 0) {
if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
artistName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackCharUuid.u) == 0) {
trackName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msAlbumCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msAlbumCharUuid.u) == 0) {
albumName = s;
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msStatusCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msStatusCharUuid.u) == 0) {
playing = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msRepeatCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msRepeatCharUuid.u) == 0) {
repeat = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msShuffleCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msShuffleCharUuid.u) == 0) {
shuffle = s[0];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msPositionCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msPositionCharUuid.u) == 0) {
trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTotalLengthCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTotalLengthCharUuid.u) == 0) {
trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackNumberCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackNumberCharUuid.u) == 0) {
trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msTrackTotalCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackTotalCharUuid.u) == 0) {
tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
} else if (ble_uuid_cmp(ctxt->chr->uuid, reinterpret_cast<ble_uuid_t*>(&msPlaybackSpeedCharUuid)) == 0) {
} else if (ble_uuid_cmp(ctxt->chr->uuid, &msPlaybackSpeedCharUuid.u) == 0) {
playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f;
}
}
@ -174,4 +194,4 @@ void Pinetime::Controllers::MusicService::event(char event) {
}
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
}

View File

@ -26,18 +26,11 @@
#undef max
#undef min
// 00000000-78fc-48fe-8e23-433b3a1942d0
#define MUSIC_SERVICE_UUID_BASE \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 }
#define MUSIC_SERVICE_CHAR_UUID(x, y) \
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class MusicService {
public:
explicit MusicService(Pinetime::System::SystemTask& system);
@ -73,41 +66,26 @@ namespace Pinetime {
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private:
ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
ble_uuid128_t msEventCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x01, 0x00)};
ble_uuid128_t msStatusCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x02, 0x00)};
ble_uuid128_t msArtistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x03, 0x00)};
ble_uuid128_t msTrackCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x04, 0x00)};
ble_uuid128_t msAlbumCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x05, 0x00)};
ble_uuid128_t msPositionCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x06, 0x00)};
ble_uuid128_t msTotalLengthCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x07, 0x00)};
ble_uuid128_t msTrackNumberCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x08, 0x00)};
ble_uuid128_t msTrackTotalCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x09, 0x00)};
ble_uuid128_t msPlaybackSpeedCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0a, 0x00)};
ble_uuid128_t msRepeatCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0b, 0x00)};
ble_uuid128_t msShuffleCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_CHAR_UUID(0x0c, 0x00)};
struct ble_gatt_chr_def characteristicDefinition[14];
struct ble_gatt_svc_def serviceDefinition[2];
uint16_t eventHandle;
uint16_t eventHandle {};
std::string artistName;
std::string albumName;
std::string trackName;
std::string artistName {"Waiting for"};
std::string albumName {};
std::string trackName {"track information.."};
bool playing;
bool playing {false};
int trackProgress;
int trackLength;
int trackNumber;
int tracksTotal;
int trackProgress {0};
int trackLength {0};
int trackNumber {};
int tracksTotal {};
float playbackSpeed;
float playbackSpeed {1.0f};
bool repeat;
bool shuffle;
bool repeat {false};
bool shuffle {false};
Pinetime::System::SystemTask& m_system;
};

View File

@ -235,3 +235,9 @@ void NimbleController::StartDiscovery() {
uint16_t NimbleController::connHandle() {
return connectionHandle;
}
void NimbleController::NotifyBatteryLevel(uint8_t level) {
if(connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
batteryInformationService.NotifyBatteryLevel(connectionHandle, level);
}
}

View File

@ -70,6 +70,7 @@ namespace Pinetime {
};
uint16_t connHandle();
void NotifyBatteryLevel(uint8_t level);
private:
static constexpr const char* deviceName = "InfiniTime";
@ -92,7 +93,7 @@ namespace Pinetime {
HeartRateService heartRateService;
uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle = 0;
uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
ble_uuid128_t dfuServiceUuid {
.u {.type = BLE_UUID_TYPE_128},

197
src/components/fs/FS.cpp Normal file
View File

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

71
src/components/fs/FS.h Normal file
View File

@ -0,0 +1,71 @@
#pragma once
#include <cstdint>
#include "drivers/SpiNorFlash.h"
#include <littlefs/lfs.h>
namespace Pinetime {
namespace Controllers {
class FS {
public:
FS(Pinetime::Drivers::SpiNorFlash&);
void Init();
void LVGLFileSystemInit();
int FileOpen(lfs_file_t* file_p, const char* fileName, const int flags);
int FileClose(lfs_file_t* file_p);
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
int FileSeek(lfs_file_t* file_p, uint32_t pos);
int FileDelete(const char* fileName);
int DirCreate(const char* path);
int DirDelete(const char* path);
void VerifyResource();
private:
Pinetime::Drivers::SpiNorFlash& flashDriver;
/*
* External Flash MAP (4 MBytes)
*
* 0x000000 +---------------------------------------+
* | Bootloader Assets |
* | 256 KBytes |
* | |
* 0x040000 +---------------------------------------+
* | OTA |
* | 464 KBytes |
* | |
* | |
* | |
* 0x0B4000 +---------------------------------------+
* | File System |
* | |
* | |
* | |
* | |
* 0x400000 +---------------------------------------+
*
*/
static constexpr size_t startAddress = 0x0B4000;
static constexpr size_t size = 0x3C0000;
static constexpr size_t blockSize = 4096;
bool resourcesValid = false;
const struct lfs_config lfsConfig;
lfs_t lfs;
static int SectorSync(const struct lfs_config* c);
static int SectorErase(const struct lfs_config* c, lfs_block_t block);
static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
};
}
}

View File

@ -4,108 +4,44 @@
using namespace Pinetime::Controllers;
struct SettingsHeader {
uint8_t isActive; // 0xF1 = Block is active, 0xF0 = Block is inactive
uint16_t version; // Current version, to verify if the saved data is for the current Version
};
#define HEADER_SIZE sizeof(SettingsHeader)
Settings::Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash {spiNorFlash} {
Settings::Settings(Pinetime::Controllers::FS& fs) : fs {fs} {
}
void Settings::Init() {
// Load default settings from Flash
LoadSettingsFromFlash();
LoadSettingsFromFile();
}
void Settings::SaveSettings() {
// verify if is necessary to save
if (settingsChanged) {
SaveSettingsToFlash();
SaveSettingsToFile();
}
settingsChanged = false;
}
bool Settings::FindHeader() {
SettingsHeader settingsHeader;
uint8_t bufferHead[sizeof(settingsHeader)];
void Settings::LoadSettingsFromFile() {
SettingsData bufferSettings;
lfs_file_t settingsFile;
for (uint8_t block = 0; block < 10; block++) {
spiNorFlash.Read(settingsBaseAddr + (block * 0x1000), bufferHead, sizeof(settingsHeader));
std::memcpy(&settingsHeader, bufferHead, sizeof(settingsHeader));
if (settingsHeader.isActive == 0xF1 && settingsHeader.version == settingsVersion) {
settingsFlashBlock = block;
return true;
}
if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
return;
}
return false;
}
void Settings::ReadSettingsData() {
uint8_t bufferSettings[sizeof(settings)];
spiNorFlash.Read(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
std::memcpy(&settings, bufferSettings, sizeof(settings));
}
void Settings::EraseBlock() {
spiNorFlash.SectorErase(settingsBaseAddr + (settingsFlashBlock * 0x1000));
}
void Settings::SetHeader(bool state) {
SettingsHeader settingsHeader;
uint8_t bufferHead[sizeof(settingsHeader)];
settingsHeader.isActive = state ? 0xF1 : 0xF0;
settingsHeader.version = settingsVersion;
std::memcpy(bufferHead, &settingsHeader, sizeof(settingsHeader));
spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000), bufferHead, sizeof(settingsHeader));
}
void Settings::SaveSettingsData() {
uint8_t bufferSettings[sizeof(settings)];
std::memcpy(bufferSettings, &settings, sizeof(settings));
spiNorFlash.Write(settingsBaseAddr + (settingsFlashBlock * 0x1000) + HEADER_SIZE, bufferSettings, sizeof(settings));
}
void Settings::LoadSettingsFromFlash() {
if (settingsFlashBlock == 99) {
// Find current Block, if can't find use default settings and set block to 0 ans save !
if (FindHeader()) {
ReadSettingsData();
} else {
SaveSettingsToFlash();
}
} else {
// Read Settings from flash...
// never used :)
ReadSettingsData();
fs.FileRead(&settingsFile, reinterpret_cast<uint8_t*>(&bufferSettings), sizeof(settings));
fs.FileClose(&settingsFile);
if ( bufferSettings.version == settingsVersion ) {
settings = bufferSettings;
}
}
void Settings::SaveSettingsToFlash() {
void Settings::SaveSettingsToFile() {
lfs_file_t settingsFile;
// calculate where to save...
// mark current to inactive
// erase the new location and save
// set settingsFlashBlock
// if first time hever, only saves to block 0 and set settingsFlashBlock
if (settingsFlashBlock != 99) {
SetHeader(false);
if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
return;
}
settingsFlashBlock++;
if (settingsFlashBlock > 9)
settingsFlashBlock = 0;
EraseBlock();
SetHeader(true);
SaveSettingsData();
fs.FileWrite(&settingsFile, reinterpret_cast<uint8_t*>(&settings), sizeof(settings));
fs.FileClose(&settingsFile);
}

View File

@ -1,26 +1,32 @@
#pragma once
#include <cstdint>
#include <bitset>
#include "components/datetime/DateTimeController.h"
#include "components/brightness/BrightnessController.h"
#include "drivers/SpiNorFlash.h"
#include "components/fs/FS.h"
#include "drivers/Cst816s.h"
namespace Pinetime {
namespace Controllers {
class Settings {
public:
enum class ClockType { H24, H12 };
enum class Vibration { ON, OFF };
enum class WakeUpMode { None, SingleTap, DoubleTap, RaiseWrist };
enum class ClockType : uint8_t { H24, H12 };
enum class Vibration : uint8_t { ON, OFF };
enum class WakeUpMode : uint8_t {
SingleTap = 0,
DoubleTap = 1,
RaiseWrist = 2,
};
Settings(Pinetime::Drivers::SpiNorFlash& spiNorFlash);
Settings(Pinetime::Controllers::FS& fs);
void Init();
void SaveSettings();
void SetClockFace(uint8_t face) {
if (face != settings.clockFace)
if (face != settings.clockFace) {
settingsChanged = true;
}
settings.clockFace = face;
};
uint8_t GetClockFace() const {
@ -42,8 +48,9 @@ namespace Pinetime {
};
void SetClockType(ClockType clocktype) {
if (clocktype != settings.clockType)
if (clocktype != settings.clockType) {
settingsChanged = true;
}
settings.clockType = clocktype;
};
ClockType GetClockType() const {
@ -51,8 +58,9 @@ namespace Pinetime {
};
void SetVibrationStatus(Vibration status) {
if (status != settings.vibrationStatus)
if (status != settings.vibrationStatus) {
settingsChanged = true;
}
settings.vibrationStatus = status;
};
Vibration GetVibrationStatus() const {
@ -60,26 +68,47 @@ namespace Pinetime {
};
void SetScreenTimeOut(uint32_t timeout) {
if (timeout != settings.screenTimeOut)
if (timeout != settings.screenTimeOut) {
settingsChanged = true;
}
settings.screenTimeOut = timeout;
};
uint32_t GetScreenTimeOut() const {
return settings.screenTimeOut;
};
void setWakeUpMode(WakeUpMode wakeUp) {
if (wakeUp != settings.wakeUpMode)
void setWakeUpMode(WakeUpMode wakeUp, bool enabled) {
if (!isWakeUpModeOn(wakeUp)) {
settingsChanged = true;
settings.wakeUpMode = wakeUp;
};
WakeUpMode getWakeUpMode() const {
return settings.wakeUpMode;
}
settings.wakeUpMode.set(static_cast<size_t>(wakeUp), enabled);
// Handle special behavior
if (enabled) {
switch (wakeUp) {
case WakeUpMode::SingleTap:
settings.wakeUpMode.set(static_cast<size_t>(WakeUpMode::DoubleTap), false);
break;
case WakeUpMode::DoubleTap:
settings.wakeUpMode.set(static_cast<size_t>(WakeUpMode::SingleTap), false);
break;
case WakeUpMode::RaiseWrist:
break;
}
}
};
std::bitset<3> getWakeUpModes() const {
return settings.wakeUpMode;
}
bool isWakeUpModeOn(const WakeUpMode mode) const {
return getWakeUpModes()[static_cast<size_t>(mode)];
}
void SetBrightness(Controllers::BrightnessController::Levels level) {
if (level != settings.brightLevel)
if (level != settings.brightLevel) {
settingsChanged = true;
}
settings.brightLevel = level;
};
Controllers::BrightnessController::Levels GetBrightness() const {
@ -87,26 +116,30 @@ namespace Pinetime {
};
void SetStepsGoal( uint32_t goal ) {
if ( goal != settings.stepsGoal )
if ( goal != settings.stepsGoal ) {
settingsChanged = true;
}
settings.stepsGoal = goal;
};
uint32_t GetStepsGoal() const { return settings.stepsGoal; };
private:
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
Pinetime::Controllers::FS& fs;
static constexpr uint32_t settingsVersion = 0x0001;
struct SettingsData {
uint32_t version = settingsVersion;
uint32_t stepsGoal = 10000;
uint32_t screenTimeOut = 15000;
ClockType clockType = ClockType::H24;
Vibration vibrationStatus = Vibration::ON;
uint8_t clockFace = 0;
uint32_t stepsGoal = 10000;
uint32_t screenTimeOut = 15000;
WakeUpMode wakeUpMode = WakeUpMode::None;
std::bitset<3> wakeUpMode {0};
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
};
@ -117,20 +150,8 @@ namespace Pinetime {
uint8_t appMenu = 0;
uint8_t settingsMenu = 0;
// There are 10 blocks of reserved flash to save settings
// to minimize wear, the recording is done in a rotating way by the 10 blocks
uint8_t settingsFlashBlock = 99; // default to indicate it needs to find the active block
static constexpr uint32_t settingsBaseAddr = 0x3F6000; // Flash Settings Location
static constexpr uint16_t settingsVersion = 0x0100; // Flash Settings Version
bool FindHeader();
void ReadSettingsData();
void EraseBlock();
void SetHeader(bool state);
void SaveSettingsData();
void LoadSettingsFromFlash();
void SaveSettingsToFlash();
void LoadSettingsFromFile();
void SaveSettingsToFile();
};
}
}
}

View File

@ -43,6 +43,8 @@
#include "displayapp/screens/settings/SettingDisplay.h"
#include "displayapp/screens/settings/SettingSteps.h"
#include "libs/lv_conf.h"
using namespace Pinetime::Applications;
using namespace Pinetime::Applications::Display;
@ -114,6 +116,7 @@ uint32_t count = 0;
bool toggle = true;
void DisplayApp::Refresh() {
TickType_t queueTimeout;
TickType_t delta;
switch (state) {
case States::Idle:
IdleState();
@ -121,7 +124,11 @@ void DisplayApp::Refresh() {
break;
case States::Running:
RunningState();
queueTimeout = 20;
delta = xTaskGetTickCount() - lastWakeTime;
if (delta > LV_DISP_DEF_REFR_PERIOD) {
delta = LV_DISP_DEF_REFR_PERIOD;
}
queueTimeout = LV_DISP_DEF_REFR_PERIOD - delta;
break;
default:
queueTimeout = portMAX_DELAY;
@ -129,7 +136,9 @@ void DisplayApp::Refresh() {
}
Messages msg;
if (xQueueReceive(msgQueue, &msg, queueTimeout)) {
bool messageReceived = xQueueReceive(msgQueue, &msg, queueTimeout);
lastWakeTime = xTaskGetTickCount();
if (messageReceived) {
switch (msg) {
case Messages::GoToSleep:
brightnessController.Backup();
@ -168,9 +177,13 @@ void DisplayApp::Refresh() {
}
break;
case Messages::TouchEvent: {
if (state != States::Running)
if (state != States::Running) {
break;
}
auto gesture = OnTouchEvent();
if (gesture == TouchEvents::None) {
break;
}
if (!currentScreen->OnTouchEvent(gesture)) {
if (currentApp == Apps::Clock) {
switch (gesture) {
@ -277,6 +290,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
break;
case Apps::FirmwareUpdate:
currentScreen = std::make_unique<Screens::FirmwareUpdate>(this, bleController);
ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None);
break;
case Apps::Notifications:
@ -337,7 +351,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None);
break;
case Apps::StopWatch:
currentScreen = std::make_unique<Screens::StopWatch>(this);
currentScreen = std::make_unique<Screens::StopWatch>(this, *systemTask);
break;
case Apps::Twos:
currentScreen = std::make_unique<Screens::Twos>(this);

View File

@ -114,6 +114,7 @@ namespace Pinetime {
Apps nextApp = Apps::None;
DisplayApp::FullRefreshDirections nextDirection;
TickType_t lastWakeTime;
};
}
}

Binary file not shown.

View File

@ -2,6 +2,7 @@
* [Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)
* [Awesome font from LVGL](https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff)
* [Open Sans Light from Google](https://fonts.google.com/specimen/Open+Sans)
## Generate the fonts:
@ -10,10 +11,12 @@
* Size : 20
* Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting
* Load the file `JetBrainsMono-Bold.tff` and specify the following range : `0x20-0x7f, 0x410-0x44f`
* Load the file `JetBrainsMono-Bold.tff` (use the file in this repo to ensure the version matches) and specify the following range : `0x20-0x7f, 0x410-0x44f`
* Add a 2nd font, load the file `FontAwesome5-Solid+Brands+Regular.woff` and specify the following
range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf069, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf029, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252`
range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf069, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf029, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569`
* Click on Convert, and download the file `jetbrains_mono_bold_20.c` and copy it in `src/DisplayApp/Fonts`
* Add the font .c file path to src/CMakeLists.txt
* Add an LV_FONT_DECLARE line in src/libs/lv_conf.h
Add new symbols:
@ -28,6 +31,21 @@ Add new symbols:
static constexpr const char* newSymbol = "\xEF\x86\x85";
```
## Simple method to generate a font
If you want to generate a basic font containing only numbers and letters, you can use the above settings but instead of specifying a range, simply list the characters you need in the Symbols field and leave the range blank. This is the approach used for the PineTimeStyle watchface.
This works well for fonts which will only be used to display numbers, but will fail if you try to add a colon or other punctuation.
* Open the [LVGL font converter](https://lvgl.io/tools/fontconverter)
* Name : open_sans_light
* Size : 150
* Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting
* Load the file `open_sans_light.tff` (use the file in this repo to ensure the version matches) and specify the following symbols : `0123456789`
* Click on Convert, and download the file `open_sans_light.c` and copy it in `src/DisplayApp/Fonts`
* Add the font .c file path to src/CMakeLists.txt (search for jetbrains to find the appropriate location/format)
* Add an LV_FONT_DECLARE line in src/libs/lv_conf.h (as above)
#### Navigation font
To create the navigtion.ttf I use the web app [icomoon](https://icomoon.io/app)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

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

View File

@ -1,9 +1,10 @@
#include <cstdint>
#include "BatteryIcon.h"
#include "Symbols.h"
using namespace Pinetime::Applications::Screens;
const char* BatteryIcon::GetBatteryIcon(int batteryPercent) {
const char* BatteryIcon::GetBatteryIcon(uint8_t batteryPercent) {
if (batteryPercent > 90)
return Symbols::batteryFull;
if (batteryPercent > 75)

View File

@ -6,7 +6,7 @@ namespace Pinetime {
class BatteryIcon {
public:
static const char* GetUnknownIcon();
static const char* GetBatteryIcon(int batteryPercent);
static const char* GetBatteryIcon(uint8_t batteryPercent);
static const char* GetPlugIcon(bool isCharging);
};
}

View File

@ -9,11 +9,6 @@ static void lv_update_task(struct _lv_task_t* task) {
user_data->UpdateScreen();
}
static void lv_anim_task(struct _lv_task_t* task) {
auto user_data = static_cast<BatteryInfo*>(task->user_data);
user_data->UpdateAnim();
}
BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Battery& batteryController)
: Screen(app), batteryController {batteryController} {
@ -24,12 +19,12 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
lv_obj_set_size(charging_bar, 200, 15);
lv_bar_set_range(charging_bar, 0, 100);
lv_obj_align(charging_bar, nullptr, LV_ALIGN_CENTER, 0, 10);
lv_bar_set_anim_time(charging_bar, 2000);
lv_bar_set_anim_time(charging_bar, 1000);
lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, lv_color_hex(0x222222));
lv_obj_set_style_local_bg_opa(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_100);
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0xFF0000));
lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_OFF);
lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON);
status = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(status, "Reading Battery status");
@ -38,24 +33,13 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
percent = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
if (batteryPercent >= 0) {
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
} else {
lv_label_set_text(percent, "--%");
}
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT);
lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60);
// hack to not use the flot functions from printf
uint8_t batteryVoltageBytes[2];
batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); // truncate whole numbers
batteryVoltageBytes[0] =
static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over
//
voltage = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xC6A600));
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltageBytes[1], batteryVoltageBytes[0]);
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER);
lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95);
@ -65,40 +49,15 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, "");
taskUpdate = lv_task_create(lv_update_task, 500000, LV_TASK_PRIO_LOW, this);
taskAnim = lv_task_create(lv_anim_task, 1000, LV_TASK_PRIO_LOW, this);
taskUpdate = lv_task_create(lv_update_task, 5000, LV_TASK_PRIO_LOW, this);
UpdateScreen();
}
BatteryInfo::~BatteryInfo() {
lv_task_del(taskUpdate);
lv_task_del(taskAnim);
lv_obj_clean(lv_scr_act());
}
void BatteryInfo::UpdateAnim() {
batteryPercent = batteryController.PercentRemaining();
if (batteryPercent >= 0) {
if (batteryController.IsCharging() and batteryPercent < 100) {
animation += 1;
if (animation >= 100) {
animation = 0;
}
} else {
if (animation > batteryPercent) {
animation--;
}
if (animation < batteryPercent) {
animation++;
}
}
lv_bar_set_value(charging_bar, animation, LV_ANIM_OFF);
}
}
void BatteryInfo::UpdateScreen() {
batteryController.Update();
@ -106,39 +65,27 @@ void BatteryInfo::UpdateScreen() {
batteryPercent = batteryController.PercentRemaining();
batteryVoltage = batteryController.Voltage();
if (batteryPercent >= 0) {
if (batteryController.IsCharging() and batteryPercent < 100) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_label_set_text_static(status, "Battery charging");
} else if (batteryPercent == 100) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE);
lv_label_set_text_static(status, "Battery is fully charged");
} else if (batteryPercent < 10) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_label_set_text_static(status, "Battery is low");
} else {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_label_set_text_static(status, "Battery discharging");
}
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
if (batteryController.IsCharging() and batteryPercent < 100) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_label_set_text_static(status, "Charging");
} else if (batteryPercent == 100) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE);
lv_label_set_text_static(status, "Fully charged");
} else if (batteryPercent < 10) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_label_set_text_static(status, "Battery low");
} else {
lv_label_set_text_static(status, "Reading Battery status");
lv_label_set_text(percent, "--%");
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_label_set_text_static(status, "Discharging");
}
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
// hack to not use the flot functions from printf
uint8_t batteryVoltageBytes[2];
batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); // truncate whole numbers
batteryVoltageBytes[0] =
static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over
//
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltageBytes[1], batteryVoltageBytes[0]);
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON);
}
bool BatteryInfo::Refresh() {
return running;
}

View File

@ -22,7 +22,6 @@ namespace Pinetime {
bool Refresh() override;
void UpdateScreen();
void UpdateAnim();
private:
Pinetime::Controllers::Battery& batteryController;
@ -33,11 +32,9 @@ namespace Pinetime {
lv_obj_t* status;
lv_task_t* taskUpdate;
lv_task_t* taskAnim;
int8_t animation = 0;
int8_t batteryPercent = -1;
float batteryVoltage = 0.0f;
uint8_t batteryPercent = 0;
uint16_t batteryVoltage = 0;
};
}
}

View File

@ -14,6 +14,7 @@
#include "../DisplayApp.h"
#include "WatchFaceDigital.h"
#include "WatchFaceAnalog.h"
#include "PineTimeStyle.h"
using namespace Pinetime::Applications::Screens;
@ -41,6 +42,9 @@ Clock::Clock(DisplayApp* app,
case 1:
return WatchFaceAnalogScreen();
break;
case 2:
return PineTimeStyleScreen();
break;
}
return WatchFaceDigitalScreen();
}()} {
@ -76,6 +80,16 @@ std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {
app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
}
std::unique_ptr<Screen> Clock::PineTimeStyleScreen() {
return std::make_unique<Screens::PineTimeStyle>(app,
dateTimeController,
batteryController,
bleController,
notificatioManager,
settingsController,
motionController);
}
/*
// Examples for more watch faces
std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() {

View File

@ -50,6 +50,7 @@ namespace Pinetime {
std::unique_ptr<Screen> screen;
std::unique_ptr<Screen> WatchFaceDigitalScreen();
std::unique_ptr<Screen> WatchFaceAnalogScreen();
std::unique_ptr<Screen> PineTimeStyleScreen();
// Examples for more watch faces
// std::unique_ptr<Screen> WatchFaceMinimalScreen();

View File

@ -27,9 +27,10 @@ FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp* app, Pinetime
lv_bar_set_value(bar1, 0, LV_ANIM_OFF);
percentLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text(percentLabel, "");
lv_label_set_text(percentLabel, "Waiting...");
lv_obj_set_auto_realign(percentLabel, true);
lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60);
startTime = xTaskGetTickCount();
}
FirmwareUpdate::~FirmwareUpdate() {
@ -40,26 +41,42 @@ bool FirmwareUpdate::Refresh() {
switch (bleController.State()) {
default:
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Idle:
// This condition makes sure that the app is exited if somehow it got
// launched without a firmware update. This should never happen.
if (state != States::Error) {
if (xTaskGetTickCount() - startTime > (60 * 1024)) {
UpdateError();
state = States::Error;
}
} else if (xTaskGetTickCount() - startTime > (5 * 1024)) {
running = false;
}
break;
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Running:
if (state != States::Running)
state = States::Running;
return DisplayProgression();
DisplayProgression();
break;
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated:
if (state != States::Validated) {
UpdateValidated();
state = States::Validated;
}
return running;
break;
case Pinetime::Controllers::Ble::FirmwareUpdateStates::Error:
if (state != States::Error) {
UpdateError();
state = States::Error;
}
return running;
if (xTaskGetTickCount() - startTime > (5 * 1024)) {
running = false;
}
break;
}
return running;
}
bool FirmwareUpdate::DisplayProgression() const {
void FirmwareUpdate::DisplayProgression() const {
float current = bleController.FirmwareUpdateCurrentBytes() / 1024.0f;
float total = bleController.FirmwareUpdateTotalBytes() / 1024.0f;
int16_t pc = (current / total) * 100.0f;
@ -67,7 +84,6 @@ bool FirmwareUpdate::DisplayProgression() const {
lv_label_set_text(percentLabel, percentStr);
lv_bar_set_value(bar1, pc, LV_ANIM_OFF);
return running;
}
void FirmwareUpdate::UpdateValidated() {
@ -78,4 +94,9 @@ void FirmwareUpdate::UpdateValidated() {
void FirmwareUpdate::UpdateError() {
lv_label_set_recolor(percentLabel, true);
lv_label_set_text(percentLabel, "#ff0000 Error!#");
startTime = xTaskGetTickCount();
}
bool FirmwareUpdate::OnButtonPushed() {
return true;
}

View File

@ -2,6 +2,7 @@
#include "Screen.h"
#include <lvgl/src/lv_core/lv_obj.h>
#include "FreeRTOS.h"
namespace Pinetime {
namespace Controllers {
@ -25,13 +26,17 @@ namespace Pinetime {
lv_obj_t* titleLabel;
mutable char percentStr[10];
States state;
States state = States::Idle;
bool DisplayProgression() const;
void DisplayProgression() const;
bool OnButtonPushed() override;
void UpdateValidated();
void UpdateError();
TickType_t startTime;
};
}
}

View File

@ -16,30 +16,18 @@ namespace {
FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator)
: Screen {app}, validator {validator} {
labelVersionInfo = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelVersionInfo, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_label_set_text(labelVersionInfo, "Version : ");
lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT);
labelVersionValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_label_set_recolor(labelVersionValue, true);
sprintf(version, "%ld.%ld.%ld", Version::Major(), Version::Minor(), Version::Patch());
lv_label_set_text(labelVersionValue, version);
labelShortRefInfo = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelShortRefInfo, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 25);
lv_label_set_text(labelShortRefInfo, "ShortRef : ");
lv_label_set_align(labelShortRefInfo, LV_LABEL_ALIGN_LEFT);
labelShortRefValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelShortRefValue, labelShortRefInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_label_set_recolor(labelShortRefValue, true);
sprintf(shortref, "%s", Version::GitCommitHash());
lv_label_set_text(labelShortRefValue, shortref);
labelVersion = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_fmt(labelVersion,
"Version : %d.%d.%d\n"
"ShortRef : %s",
Version::Major(),
Version::Minor(),
Version::Patch(),
Version::GitCommitHash());
lv_obj_align(labelVersion, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
labelIsValidated = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(labelIsValidated, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 50);
lv_obj_align(labelIsValidated, labelVersion, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
lv_label_set_recolor(labelIsValidated, true);
lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK);
lv_obj_set_width(labelIsValidated, 240);

View File

@ -23,12 +23,7 @@ namespace Pinetime {
private:
Pinetime::Controllers::FirmwareValidator& validator;
lv_obj_t* labelVersionInfo;
lv_obj_t* labelVersionValue;
lv_obj_t* labelShortRefInfo;
lv_obj_t* labelShortRefValue;
char version[9];
char shortref[9];
lv_obj_t* labelVersion;
lv_obj_t* labelIsValidated;
lv_obj_t* buttonValidate;
lv_obj_t* labelButtonValidate;

View File

@ -163,10 +163,10 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_pos(container1, 0, 50);
lv_obj_set_width(container1, 240);
lv_obj_set_height(container1, 190);
lv_obj_set_size(container1, LV_HOR_RES, 190);
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
lv_cont_set_fit(container1, LV_FIT_NONE);
lv_obj_t* alert_count = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_fmt(alert_count, "%i/%i", notifNr, notifNb);
@ -198,6 +198,7 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_label_set_text(alert_subject, msg);
} break;
case Controllers::NotificationManager::Categories::IncomingCall: {
lv_obj_set_height(container1, 108);
lv_obj_t* alert_subject = lv_label_create(container1, nullptr);
lv_obj_set_style_local_text_color(alert_subject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK);
@ -210,38 +211,29 @@ Notifications::NotificationItem::NotificationItem(const char* title,
lv_obj_set_width(alert_caller, LV_HOR_RES - 20);
lv_label_set_text(alert_caller, msg);
lv_obj_t* callBtnContainer = lv_cont_create(container1, NULL);
lv_obj_set_width(callBtnContainer, 240);
lv_obj_set_height(callBtnContainer, 90);
lv_cont_set_layout(callBtnContainer, LV_LAYOUT_ROW_MID);
lv_obj_set_style_local_bg_color(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x222222));
lv_obj_set_style_local_pad_all(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_margin_top(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 40);
lv_obj_set_style_local_margin_left(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, -8);
lv_obj_set_style_local_pad_inner(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
lv_obj_set_style_local_border_width(callBtnContainer, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
bt_accept = lv_btn_create(callBtnContainer, nullptr);
bt_accept = lv_btn_create(lv_scr_act(), nullptr);
bt_accept->user_data = this;
lv_obj_set_event_cb(bt_accept, AcceptIncomingCallEventHandler);
lv_obj_set_size(bt_accept, (LV_HOR_RES / 3) - 5, 80);
lv_obj_set_size(bt_accept, 76, 76);
lv_obj_align(bt_accept, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
label_accept = lv_label_create(bt_accept, nullptr);
lv_label_set_text(label_accept, Symbols::phone);
lv_obj_set_style_local_bg_color(bt_accept, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
bt_reject = lv_btn_create(callBtnContainer, nullptr);
bt_reject = lv_btn_create(lv_scr_act(), nullptr);
bt_reject->user_data = this;
lv_obj_set_event_cb(bt_reject, RejectIncomingCallEventHandler);
lv_obj_set_size(bt_reject, (LV_HOR_RES / 3) - 5, 80);
lv_obj_set_size(bt_reject, 76, 76);
lv_obj_align(bt_reject, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
label_reject = lv_label_create(bt_reject, nullptr);
lv_label_set_text(label_reject, Symbols::phoneSlash);
lv_obj_set_style_local_bg_color(bt_reject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
bt_mute = lv_btn_create(callBtnContainer, nullptr);
bt_mute = lv_btn_create(lv_scr_act(), nullptr);
bt_mute->user_data = this;
lv_obj_set_event_cb(bt_mute, MuteIncomingCallEventHandler);
lv_obj_set_size(bt_mute, (LV_HOR_RES / 3) - 5, 80);
lv_obj_set_size(bt_mute, 76, 76);
lv_obj_align(bt_mute, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
label_mute = lv_label_create(bt_mute, nullptr);
lv_label_set_text(label_mute, Symbols::volumMute);
lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);

View File

@ -4,98 +4,31 @@
using namespace Pinetime::Applications::Screens;
namespace {
const uint8_t paddle_map[] = {
0xfc, 0xfe, 0xfc, 0xff, /*Color of index 0*/
0xff, 0xff, 0xff, 0xff, /*Color of index 1*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t ball_map[] = {
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed, 0x6f, 0xed,
};
}
Paddle::Paddle(Pinetime::Applications::DisplayApp* app, Pinetime::Components::LittleVgl& lvgl) : Screen(app), lvgl {lvgl} {
app->SetTouchMode(DisplayApp::TouchModes::Polling);
background = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_size(background, LV_HOR_RES + 1, LV_VER_RES);
lv_obj_set_pos(background, -1, 0);
lv_obj_set_style_local_radius(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_bg_color(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_set_style_local_border_color(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_border_width(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 1);
points = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(points, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_label_set_text(points, "0000");
lv_obj_set_style_local_text_color(points, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x444444));
lv_obj_align(points, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0);
lv_obj_align(points, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 10);
paddle.header.always_zero = 0;
paddle.header.w = 4;
paddle.header.h = 60;
paddle.data_size = 68;
paddle.header.cf = LV_IMG_CF_INDEXED_1BIT;
paddle.data = paddle_map;
paddle_image = lv_img_create(lv_scr_act(), nullptr);
lv_img_set_src(paddle_image, &paddle);
paddle = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(paddle, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_radius(paddle, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(paddle, 4, 60);
ball.header.always_zero = 0;
ball.header.w = 24;
ball.header.h = 24;
ball.data_size = 24 * 24 * LV_COLOR_SIZE / 8;
ball.header.cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
ball.data = ball_map;
ball_image = lv_img_create(lv_scr_act(), nullptr);
lv_img_set_src(ball_image, &ball);
ball = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(ball, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_radius(ball, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_obj_set_size(ball, ballSize, ballSize);
}
Paddle::~Paddle() {
@ -105,41 +38,37 @@ Paddle::~Paddle() {
}
bool Paddle::Refresh() {
if ((counter++ % 5) == 0) {
counter = 0;
ballX += dx;
ballY += dy;
ballX += dx;
ballY += dy;
lv_obj_set_pos(ball, ballX, ballY);
lv_obj_set_pos(ball_image, ballX, ballY);
// checks if it has touched the sides (floor and ceiling)
if (ballY <= 1 || ballY >= LV_VER_RES - ballSize - 2) {
dy *= -1;
}
// checks if it has touched the sides (floor and ceiling)
if (ballY <= 0 || ballY >= 215) {
dy *= -1;
}
// checks if it has touched the side (left side)
if (ballX >= LV_VER_RES - ballSize - 1) {
dx *= -1;
}
// checks if it has touched the side (left side)
if (ballX >= 215) {
dx *= -1;
}
// checks if it is in the position of the paddle
if (ballY <= (paddleBottomY + 16) && ballY >= (paddleTopY - 8)) {
if (ballX >= 0 && ballX < 4) {
lv_obj_set_pos(ball_image, 5, ballY);
// checks if it is in the position of the paddle
if (dx < 0 && ballX <= 4) {
if (ballX >= -ballSize / 4) {
if (ballY <= (paddlePos + 30 - ballSize / 4) && ballY >= (paddlePos - 30 - ballSize + ballSize / 4)) {
dx *= -1;
score++;
}
}
// checks if it has gone behind the paddle
else if (ballX <= -40) {
ballX = 107;
ballY = 107;
else if (ballX <= -ballSize * 2) {
ballX = (LV_HOR_RES - ballSize) / 2;
ballY = (LV_VER_RES - ballSize) / 2;
score = 0;
}
lv_label_set_text_fmt(points, "%04d", score);
}
lv_label_set_text_fmt(points, "%04d", score);
return running;
}
@ -148,11 +77,8 @@ bool Paddle::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
}
bool Paddle::OnTouchEvent(uint16_t x, uint16_t y) {
lv_obj_set_pos(
paddle_image,
0,
y - 30); // sets the center paddle pos. (30px offset) with the the y_coordinate of the finger and defaults the x_coordinate to 0
paddleTopY = y - 30; // refreshes the upper extreme of the paddle
paddleBottomY = y + 30; // refreshes the lower extreme of the paddle
// sets the center paddle pos. (30px offset) with the the y_coordinate of the finger
lv_obj_set_pos(paddle, 0, y - 30);
paddlePos = y;
return true;
}

View File

@ -24,24 +24,22 @@ namespace Pinetime {
private:
Pinetime::Components::LittleVgl& lvgl;
int paddleBottomY = 90; // bottom extreme of the paddle
int paddleTopY = 150; // top extreme of the paddle
const uint8_t ballSize = 16;
int ballX = 107; // Initial x_coordinate for the ball (12px offset from the center to counteract the ball's 24px size)
int ballY = 107; // Initial y_coordinate for the ball
uint16_t paddlePos = 30; // Paddle center
int dx = 2; // Velocity of the ball in the x_coordinate
int dy = 3; // Velocity of the ball in the y_coordinate
int16_t ballX = (LV_HOR_RES - ballSize) / 2;
int16_t ballY = (LV_VER_RES - ballSize) / 2;
int counter = 0; // init Frame refresh limit counter
int score = 0;
int8_t dx = 2; // Velocity of the ball in the x_coordinate
int8_t dy = 3; // Velocity of the ball in the y_coordinate
lv_img_dsc_t paddle;
lv_img_dsc_t ball;
uint16_t score = 0;
lv_obj_t* points;
lv_obj_t* paddle_image; // pointer to paddle image
lv_obj_t* ball_image; // pointer to ball image
lv_obj_t* paddle;
lv_obj_t* ball;
lv_obj_t* background;
};
}
}

View File

@ -0,0 +1,340 @@
/*
* This file is part of the Infinitime distribution (https://github.com/JF002/Infinitime).
* Copyright (c) 2021 Kieran Cawthray.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* PineTimeStyle watchface for Infinitime created by Kieran Cawthray
* Based on WatchFaceDigital
* Style/layout copied from TimeStyle for Pebble by Dan Tilden (github.com/tilden)
*/
#include "PineTimeStyle.h"
#include <date/date.h>
#include <lvgl/lvgl.h>
#include <cstdio>
#include "BatteryIcon.h"
#include "BleIcon.h"
#include "NotificationIcon.h"
#include "Symbols.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/motion/MotionController.h"
#include "components/settings/Settings.h"
#include "../DisplayApp.h"
using namespace Pinetime::Applications::Screens;
PineTimeStyle::PineTimeStyle(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController,
Controllers::MotionController& motionController)
: Screen(app),
currentDateTime {{}},
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
notificatioManager {notificatioManager},
settingsController {settingsController},
motionController {motionController} {
/* This sets the watchface number to return to after leaving the menu */
settingsController.SetClockFace(2);
displayedChar[0] = 0;
displayedChar[1] = 0;
displayedChar[2] = 0;
displayedChar[3] = 0;
displayedChar[4] = 0;
/* Create a 200px wide background rectangle */
timebar = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(timebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(timebar, 200, 240);
lv_obj_align(timebar, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 0);
/* Display the time */
timeDD1 = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
lv_obj_set_style_local_text_color(timeDD1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_label_set_text(timeDD1, "12");
lv_obj_align(timeDD1, timebar, LV_ALIGN_IN_TOP_MID, 5, 5);
timeDD2 = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
lv_obj_set_style_local_text_color(timeDD2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_label_set_text(timeDD2, "34");
lv_obj_align(timeDD2, timebar, LV_ALIGN_IN_BOTTOM_MID, 5, -5);
timeAMPM = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_obj_set_style_local_text_line_space(timeAMPM, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, -3);
lv_label_set_text(timeAMPM, "");
lv_obj_align(timeAMPM, timebar, LV_ALIGN_IN_BOTTOM_LEFT, 2, -20);
/* Create a 40px wide bar down the right side of the screen */
sidebar = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x008080));
lv_obj_set_style_local_radius(sidebar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(sidebar, 40, 240);
lv_obj_align(sidebar, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
/* Display icons */
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(batteryIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(batteryIcon, Symbols::batteryFull);
lv_obj_align(batteryIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 2);
batteryPlug = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_align(batteryPlug, sidebar, LV_ALIGN_IN_TOP_MID, 0, 2);
bleIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25);
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_hex(0x000000));
lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 40);
/* Calendar icon */
calendarOuter = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarOuter, 34, 34);
lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 0);
calendarInner = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xffffff));
lv_obj_set_style_local_radius(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarInner, 27, 27);
lv_obj_align(calendarInner, calendarOuter, LV_ALIGN_CENTER, 0, 0);
calendarBar1 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarBar1, 3, 12);
lv_obj_align(calendarBar1, calendarOuter, LV_ALIGN_IN_TOP_MID, -6, -3);
calendarBar2 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarBar2, 3, 12);
lv_obj_align(calendarBar2, calendarOuter, LV_ALIGN_IN_TOP_MID, 6, -3);
calendarCrossBar1 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarCrossBar1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarCrossBar1, 8, 3);
lv_obj_align(calendarCrossBar1, calendarBar1, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
calendarCrossBar2 = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_obj_set_style_local_radius(calendarCrossBar2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarCrossBar2, 8, 3);
lv_obj_align(calendarCrossBar2, calendarBar2, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
/* Display date */
dateDayOfWeek = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(dateDayOfWeek, "THU");
lv_obj_align(dateDayOfWeek, sidebar, LV_ALIGN_CENTER, 0, -34);
dateDay = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateDay, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(dateDay, "25");
lv_obj_align(dateDay, sidebar, LV_ALIGN_CENTER, 0, 3);
dateMonth = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateMonth, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x000000));
lv_label_set_text(dateMonth, "MAR");
lv_obj_align(dateMonth, sidebar, LV_ALIGN_CENTER, 0, 32);
// Step count gauge
needle_colors[0] = LV_COLOR_WHITE;
stepGauge = lv_gauge_create(lv_scr_act(), nullptr);
lv_gauge_set_needle_count(stepGauge, 1, needle_colors);
lv_obj_set_size(stepGauge, 40, 40);
lv_obj_align(stepGauge, sidebar, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_gauge_set_scale(stepGauge, 360, 11, 0);
lv_gauge_set_angle_offset(stepGauge, 180);
lv_gauge_set_critical_value(stepGauge, 100);
lv_gauge_set_range(stepGauge, 0, 100);
lv_gauge_set_value(stepGauge, 0, 0);
lv_obj_set_style_local_pad_right(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_pad_left(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_pad_bottom(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_opa(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_obj_set_style_local_scale_width(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_line_width(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_line_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_set_style_local_line_opa(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_obj_set_style_local_line_width(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_pad_inner(stepGauge, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, 4);
backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_click(backgroundLabel, true);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, 240, 240);
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text(backgroundLabel, "");
}
PineTimeStyle::~PineTimeStyle() {
lv_obj_clean(lv_scr_act());
}
bool PineTimeStyle::Refresh() {
batteryPercentRemaining = batteryController.PercentRemaining();
if (batteryPercentRemaining.IsUpdated()) {
auto batteryPercent = batteryPercentRemaining.Get();
if (batteryController.IsCharging()) {
auto isCharging = batteryController.IsCharging() || batteryController.IsPowerPresent();
lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging));
lv_obj_realign(batteryPlug);
lv_label_set_text(batteryIcon, "");
} else {
lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent));
lv_label_set_text(batteryPlug, "");
}
}
bleState = bleController.IsConnected();
if (bleState.IsUpdated()) {
if (bleState.Get() == true) {
lv_label_set_text(bleIcon, BleIcon::GetIcon(true));
lv_obj_realign(bleIcon);
} else {
lv_label_set_text(bleIcon, BleIcon::GetIcon(false));
}
}
notificationState = notificatioManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
if (notificationState.Get() == true) {
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true));
lv_obj_realign(notificationIcon);
} else {
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
}
}
currentDateTime = dateTimeController.CurrentDateTime();
if (currentDateTime.IsUpdated()) {
auto newDateTime = currentDateTime.Get();
auto dp = date::floor<date::days>(newDateTime);
auto time = date::make_time(newDateTime - dp);
auto yearMonthDay = date::year_month_day(dp);
auto year = (int) yearMonthDay.year();
auto month = static_cast<Pinetime::Controllers::DateTime::Months>((unsigned) yearMonthDay.month());
auto day = (unsigned) yearMonthDay.day();
auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
int hour = time.hours().count();
auto minute = time.minutes().count();
char minutesChar[3];
sprintf(minutesChar, "%02d", static_cast<int>(minute));
char hoursChar[3];
char ampmChar[5];
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
sprintf(hoursChar, "%02d", hour);
} else {
if (hour == 0 && hour != 12) {
hour = 12;
sprintf(ampmChar, "A\nM");
} else if (hour == 12 && hour != 0) {
hour = 12;
sprintf(ampmChar, "P\nM");
} else if (hour < 12 && hour != 0) {
sprintf(ampmChar, "A\nM");
} else if (hour > 12 && hour != 0) {
hour = hour - 12;
sprintf(ampmChar, "P\nM");
}
sprintf(hoursChar, "%02d", hour);
}
if (hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] ||
minutesChar[1] != displayedChar[3]) {
displayedChar[0] = hoursChar[0];
displayedChar[1] = hoursChar[1];
displayedChar[2] = minutesChar[0];
displayedChar[3] = minutesChar[1];
char hourStr[3];
char minStr[3];
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
lv_label_set_text(timeAMPM, ampmChar);
}
/* Display the time as 2 pairs of digits */
sprintf(hourStr, "%c%c", hoursChar[0], hoursChar[1]);
lv_label_set_text(timeDD1, hourStr);
sprintf(minStr, "%c%c", minutesChar[0], minutesChar[1]);
lv_label_set_text(timeDD2, minStr);
}
if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
char dayOfWeekStr[4];
char dayStr[3];
char monthStr[4];
sprintf(dayOfWeekStr, "%s", dateTimeController.DayOfWeekShortToString());
sprintf(dayStr, "%d", day);
sprintf(monthStr, "%s", dateTimeController.MonthShortToString());
lv_label_set_text(dateDayOfWeek, dayOfWeekStr);
lv_label_set_text(dateDay, dayStr);
lv_obj_realign(dateDay);
lv_label_set_text(dateMonth, monthStr);
currentYear = year;
currentMonth = month;
currentDayOfWeek = dayOfWeek;
currentDay = day;
}
}
stepCount = motionController.NbSteps();
motionSensorOk = motionController.IsSensorOk();
if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
lv_gauge_set_value(stepGauge, 0, (stepCount.Get() / (settingsController.GetStepsGoal() / 100)));
lv_obj_realign(stepGauge);
if (stepCount.Get() > settingsController.GetStepsGoal()) {
lv_obj_set_style_local_line_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_scale_grad_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
}
}
return running;
}

View File

@ -0,0 +1,86 @@
#pragma once
#include <lvgl/src/lv_core/lv_obj.h>
#include <chrono>
#include <cstdint>
#include <memory>
#include "Screen.h"
#include "ScreenList.h"
#include "components/datetime/DateTimeController.h"
namespace Pinetime {
namespace Controllers {
class Settings;
class Battery;
class Ble;
class NotificationManager;
class HeartRateController;
}
namespace Applications {
namespace Screens {
class PineTimeStyle : public Screen {
public:
PineTimeStyle(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController,
Controllers::MotionController& motionController);
~PineTimeStyle() override;
bool Refresh() override;
void OnObjectEvent(lv_obj_t* pObj, lv_event_t i);
private:
char displayedChar[5];
uint16_t currentYear = 1970;
Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0;
DirtyValue<uint8_t> batteryPercentRemaining {};
DirtyValue<bool> bleState {};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
DirtyValue<bool> motionSensorOk {};
DirtyValue<uint32_t> stepCount {};
DirtyValue<bool> notificationState {};
lv_obj_t* timebar;
lv_obj_t* sidebar;
lv_obj_t* timeDD1;
lv_obj_t* timeDD2;
lv_obj_t* timeAMPM;
lv_obj_t* dateDayOfWeek;
lv_obj_t* dateDay;
lv_obj_t* dateMonth;
lv_obj_t* backgroundLabel;
lv_obj_t* batteryIcon;
lv_obj_t* bleIcon;
lv_obj_t* batteryPlug;
lv_obj_t* calendarOuter;
lv_obj_t* calendarInner;
lv_obj_t* calendarBar1;
lv_obj_t* calendarBar2;
lv_obj_t* calendarCrossBar1;
lv_obj_t* calendarCrossBar2;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* heartbeatBpm;
lv_obj_t* notificationIcon;
lv_obj_t* stepGauge;
lv_color_t needle_colors[1];
Controllers::DateTime& dateTimeController;
Controllers::Battery& batteryController;
Controllers::Ble& bleController;
Controllers::NotificationManager& notificatioManager;
Controllers::Settings& settingsController;
Controllers::MotionController& motionController;
};
}
}
}

View File

@ -6,19 +6,19 @@
using namespace Pinetime::Applications::Screens;
Steps::Steps(
Pinetime::Applications::DisplayApp *app,
Pinetime::Applications::DisplayApp *app,
Controllers::MotionController& motionController,
Controllers::Settings &settingsController)
: Screen(app),
Controllers::Settings &settingsController)
: Screen(app),
motionController{motionController},
settingsController{settingsController} {
stepsArc = lv_arc_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_opa(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
lv_obj_set_style_local_border_width(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 2);
lv_obj_set_style_local_radius(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_line_color(stepsArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0x0000FF));
lv_obj_set_style_local_bg_opa(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
lv_obj_set_style_local_border_width(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 2);
lv_obj_set_style_local_radius(stepsArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 0);
lv_obj_set_style_local_line_color(stepsArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, lv_color_hex(0x0000FF));
lv_arc_set_end_angle(stepsArc, 200);
lv_obj_set_size(stepsArc, 220, 220);
lv_arc_set_range(stepsArc, 0, 500);
@ -30,27 +30,26 @@ Steps::Steps(
lSteps = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00));
lv_obj_set_style_local_text_font(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_label_set_text_fmt(lSteps, "%li", stepsCount);
lv_obj_set_style_local_text_font(lSteps, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_label_set_text_fmt(lSteps, "%li", stepsCount);
lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -20);
lv_obj_t * lstepsL = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(lstepsL, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
lv_label_set_text_static(lstepsL, "Steps");
lv_label_set_text_static(lstepsL, "Steps");
lv_obj_align(lstepsL, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
lv_obj_t * lstepsGoal = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(lstepsGoal, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN);
lv_label_set_text_fmt(lstepsGoal,"Goal\n%i", settingsController.GetStepsGoal());
lv_label_set_text_fmt(lstepsGoal, "Goal\n%lu", settingsController.GetStepsGoal());
lv_label_set_align(lstepsGoal, LV_LABEL_ALIGN_CENTER);
lv_obj_align(lstepsGoal, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 60);
lv_obj_t * backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, 240, 240);
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, "");
}
Steps::~Steps() {
@ -58,15 +57,13 @@ Steps::~Steps() {
}
bool Steps::Refresh() {
stepsCount = motionController.NbSteps();
lv_label_set_text_fmt(lSteps,"%li", stepsCount);
stepsCount = motionController.NbSteps();
lv_label_set_text_fmt(lSteps, "%li", stepsCount);
lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -20);
lv_arc_set_value(stepsArc, int16_t(500 * stepsCount / settingsController.GetStepsGoal()));
return running;
}

View File

@ -45,17 +45,16 @@ static void stop_lap_event_handler(lv_obj_t* obj, lv_event_t event) {
stopWatch->stopLapBtnEventHandler(event);
}
StopWatch::StopWatch(DisplayApp* app)
StopWatch::StopWatch(DisplayApp* app, System::SystemTask& systemTask)
: Screen(app),
systemTask {systemTask},
running {true},
currentState {States::Init},
currentEvent {Events::Stop},
startTime {},
oldTimeElapsed {},
currentTimeSeparated {},
lapBuffer {},
lapNr {},
lapPressed {false} {
lapNr {} {
time = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
@ -105,128 +104,100 @@ StopWatch::StopWatch(DisplayApp* app)
}
StopWatch::~StopWatch() {
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
void StopWatch::reset() {
currentState = States::Init;
oldTimeElapsed = 0;
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_label_set_text(time, "00:00");
lv_label_set_text(msecTime, "00");
lv_label_set_text(lapOneText, "");
lv_label_set_text(lapTwoText, "");
lapBuffer.clearBuffer();
lapNr = 0;
lv_obj_set_state(btnStopLap, LV_STATE_DISABLED);
lv_obj_set_state(txtStopLap, LV_STATE_DISABLED);
}
void StopWatch::start() {
lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT);
lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT);
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_label_set_text(txtPlayPause, Symbols::pause);
lv_label_set_text(txtStopLap, Symbols::lapsFlag);
startTime = xTaskGetTickCount();
currentState = States::Running;
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
}
void StopWatch::pause() {
startTime = 0;
// Store the current time elapsed in cache
oldTimeElapsed += timeElapsed;
currentState = States::Halted;
lv_label_set_text(txtPlayPause, Symbols::play);
lv_label_set_text(txtStopLap, Symbols::stop);
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
}
bool StopWatch::Refresh() {
// @startuml CHIP8_state
// State "Init" as init
// State "Running" as run
// State "Halted" as halt
if (currentState == States::Running) {
timeElapsed = calculateDelta(startTime, xTaskGetTickCount());
currentTimeSeparated = convertTicksToTimeSegments((oldTimeElapsed + timeElapsed));
// [*] --> init
// init -> run : press play
// run -> run : press lap
// run --> halt : press pause
// halt --> run : press play
// halt --> init : press stop
// @enduml
// Copy paste the above plantuml text to visualize the state diagram
switch (currentState) {
// Init state when an user first opens the app
// and when a stop/reset button is pressed
case States::Init: {
// The initial default value
lv_label_set_text(time, "00:00");
lv_label_set_text(msecTime, "00");
lv_label_set_text(lapOneText, "");
lv_label_set_text(lapTwoText, "");
lapBuffer.clearBuffer();
lapNr = 0;
if (currentEvent == Events::Play) {
lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT);
lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT);
startTime = xTaskGetTickCount();
currentState = States::Running;
} else {
lv_obj_set_state(btnStopLap, LV_STATE_DISABLED);
lv_obj_set_state(txtStopLap, LV_STATE_DISABLED);
}
break;
}
case States::Running: {
lv_label_set_text(txtPlayPause, Symbols::pause);
lv_label_set_text(txtStopLap, Symbols::lapsFlag);
const auto timeElapsed = calculateDelta(startTime, xTaskGetTickCount());
currentTimeSeparated = convertTicksToTimeSegments((oldTimeElapsed + timeElapsed));
lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths);
if (lapPressed == true) {
if (lapBuffer[1]) {
lv_label_set_text_fmt(
lapOneText, "#%2d %2d:%02d.%02d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->hundredths);
}
if (lapBuffer[0]) {
lv_label_set_text_fmt(
lapTwoText, "#%2d %2d:%02d.%02d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->hundredths);
}
// Reset the bool to avoid setting the text in each cycle until there is a change
lapPressed = false;
}
if (currentEvent == Events::Pause) {
// Reset the start time
startTime = 0;
// Store the current time elapsed in cache
oldTimeElapsed += timeElapsed;
currentState = States::Halted;
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
} else {
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
}
break;
}
case States::Halted: {
lv_label_set_text(txtPlayPause, Symbols::play);
lv_label_set_text(txtStopLap, Symbols::stop);
if (currentEvent == Events::Play) {
startTime = xTaskGetTickCount();
currentState = States::Running;
}
if (currentEvent == Events::Stop) {
currentState = States::Init;
oldTimeElapsed = 0;
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
}
break;
}
lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths);
}
return running;
}
void StopWatch::playPauseBtnEventHandler(lv_event_t event) {
if (event == LV_EVENT_CLICKED) {
if (currentState == States::Init) {
currentEvent = Events::Play;
} else {
// Simple Toggle for play/pause
currentEvent = (currentEvent == Events::Play ? Events::Pause : Events::Play);
}
if (event != LV_EVENT_PRESSED) {
return;
}
if (currentState == States::Init) {
start();
} else if (currentState == States::Running) {
pause();
} else if (currentState == States::Halted) {
start();
}
}
void StopWatch::stopLapBtnEventHandler(lv_event_t event) {
if (event == LV_EVENT_CLICKED) {
// If running, then this button is used to save laps
if (currentState == States::Running) {
lapBuffer.addLaps(currentTimeSeparated);
lapNr++;
lapPressed = true;
} else if (currentState == States::Halted) {
currentEvent = Events::Stop;
} else {
// Not possible to reach here. Do nothing.
if (event != LV_EVENT_PRESSED) {
return;
}
// If running, then this button is used to save laps
if (currentState == States::Running) {
lapBuffer.addLaps(currentTimeSeparated);
lapNr++;
if (lapBuffer[1]) {
lv_label_set_text_fmt(
lapOneText, "#%2d %2d:%02d.%02d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->hundredths);
}
if (lapBuffer[0]) {
lv_label_set_text_fmt(lapTwoText, "#%2d %2d:%02d.%02d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->hundredths);
}
} else if (currentState == States::Halted) {
reset();
}
}
bool StopWatch::OnButtonPushed() {
if (currentState == States::Running) {
pause();
} else {
running = false;
}
return true;
}

View File

@ -8,13 +8,12 @@
#include "portmacro_cmsis.h"
#include <array>
#include "systemtask/SystemTask.h"
namespace Pinetime::Applications::Screens {
enum class States { Init, Running, Halted };
enum class Events { Play, Pause, Stop };
struct TimeSeparated_t {
int mins;
int secs;
@ -63,23 +62,28 @@ namespace Pinetime::Applications::Screens {
class StopWatch : public Screen {
public:
StopWatch(DisplayApp* app);
StopWatch(DisplayApp* app, System::SystemTask& systemTask);
~StopWatch() override;
bool Refresh() override;
void playPauseBtnEventHandler(lv_event_t event);
void stopLapBtnEventHandler(lv_event_t event);
bool OnButtonPushed() override;
void reset();
void start();
void pause();
private:
Pinetime::System::SystemTask& systemTask;
TickType_t timeElapsed;
bool running;
States currentState;
Events currentEvent;
TickType_t startTime;
TickType_t oldTimeElapsed;
TimeSeparated_t currentTimeSeparated; // Holds Mins, Secs, millisecs
LapTextBuffer_t<2> lapBuffer;
int lapNr;
bool lapPressed;
int lapNr = 0;
lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
lv_obj_t *lapOneText, *lapTwoText;
};

View File

@ -40,6 +40,7 @@ namespace Pinetime {
static constexpr const char* stopWatch = "\xEF\x8B\xB2";
static constexpr const char* hourGlass = "\xEF\x89\x92";
static constexpr const char* lapsFlag = "\xEF\x80\xA4";
static constexpr const char* drum = "\xEF\x95\xA9";
// lv_font_sys_48.c
static constexpr const char* settings = "\xEE\xA4\x82"; // e902

View File

@ -3,6 +3,7 @@
#include "../DisplayApp.h"
#include "Label.h"
#include "Version.h"
#include "BootloaderVersion.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/brightness/BrightnessController.h"
@ -83,26 +84,26 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
lv_label_set_recolor(label, true);
lv_label_set_text_fmt(label,
"#FFFF00 InfiniTime#\n\n"
"#444444 Version# %ld.%ld.%ld\n\n"
"#444444 Short Ref# %s\n\n"
"#444444 Version# %ld.%ld.%ld\n"
"#444444 Short Ref# %s\n"
"#444444 Build date#\n"
"%s\n"
"%s\n",
"%s\n\n"
"#444444 Bootloader# %s",
Version::Major(),
Version::Minor(),
Version::Patch(),
Version::GitCommitHash(),
__DATE__,
__TIME__);
__TIME__,
BootloaderVersion::VersionString());
lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
return std::make_unique<Screens::Label>(0, 5, app, label);
}
std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
auto batteryPercent = static_cast<uint8_t>(batteryController.PercentRemaining());
float batteryVoltage = batteryController.Voltage();
auto batteryPercent = batteryController.PercentRemaining();
auto resetReason = [this]() {
switch (watchdog.ResetReason()) {
case Drivers::Watchdog::ResetReasons::Watchdog:
@ -141,21 +142,13 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
uptimeSeconds = uptimeSeconds % secondsInAMinute;
// TODO handle more than 100 days of uptime
if (batteryPercent == -1)
batteryPercent = 0;
// hack to not use the flot functions from printf
uint8_t batteryVoltageBytes[2];
batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); // truncate whole numbers
batteryVoltageBytes[0] = static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_recolor(label, true);
lv_label_set_text_fmt(label,
"#444444 Date# %02d/%02d/%04d\n"
"#444444 Time# %02d:%02d:%02d\n"
"#444444 Uptime#\n %02lud %02lu:%02lu:%02lu\n"
"#444444 Battery# %d%%/%1i.%02iv\n"
"#444444 Battery# %d%%/%03imV\n"
"#444444 Backlight# %s\n"
"#444444 Last reset# %s\n"
"#444444 Accel.# %s\n",
@ -170,8 +163,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
uptimeMinutes,
uptimeSeconds,
batteryPercent,
batteryVoltageBytes[1],
batteryVoltageBytes[0],
batteryController.Voltage(),
brightnessController.ToString(),
resetReason,
ToString(motionController.DeviceType()));
@ -192,22 +184,22 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
"\n"
"#444444 LVGL Memory#\n"
" #444444 used# %d (%d%%)\n"
" #444444 max used# %d\n"
" #444444 max used# %lu\n"
" #444444 frag# %d%%\n"
" #444444 free# %d"
"\n"
"#444444 Steps# %li",
"#444444 Steps# %i",
bleAddr[5],
bleAddr[4],
bleAddr[3],
bleAddr[2],
bleAddr[1],
bleAddr[0],
(int) mon.total_size - mon.free_size,
static_cast<int>(mon.total_size - mon.free_size),
mon.used_pct,
mon.max_used,
mon.frag_pct,
(int) mon.free_biggest_size,
static_cast<int>(mon.free_biggest_size),
0);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
return std::make_unique<Screens::Label>(2, 5, app, label);

View File

@ -107,7 +107,7 @@ Tile::Tile(uint8_t screenID,
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, "");
taskUpdate = lv_task_create(lv_update_task, 500000, LV_TASK_PRIO_MID, this);
taskUpdate = lv_task_create(lv_update_task, 5000, LV_TASK_PRIO_MID, this);
}
Tile::~Tile() {

View File

@ -63,8 +63,8 @@ Timer::Timer(DisplayApp* app, Controllers::TimerController& timerController)
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
uint32_t seconds = timerController.GetTimeRemaining() / 1000;
lv_label_set_text_fmt(time, "%02d:%02d", seconds / 60, seconds % 60);
lv_label_set_text_fmt(time, "%02lu:%02lu", seconds / 60, seconds % 60);
lv_obj_align(time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -20);
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
@ -90,7 +90,7 @@ Timer::~Timer() {
bool Timer::Refresh() {
if (timerController.IsRunning()) {
uint32_t seconds = timerController.GetTimeRemaining() / 1000;
lv_label_set_text_fmt(time, "%02d:%02d", seconds / 60, seconds % 60);
lv_label_set_text_fmt(time, "%02lu:%02lu", seconds / 60, seconds % 60);
}
return running;
}

View File

@ -5,39 +5,56 @@
#include "Symbols.h"
#include "NotificationIcon.h"
#include <cmath>
LV_IMG_DECLARE(bg_clock);
using namespace Pinetime::Applications::Screens;
#define HOUR_LENGTH 70
#define MINUTE_LENGTH 90
#define SECOND_LENGTH 110
#define PI 3.14159265358979323846
namespace {
// ##
static int16_t coordinate_x_relocate(int16_t x) {
return ((x) + LV_HOR_RES / 2);
constexpr auto HOUR_LENGTH = 70;
constexpr auto MINUTE_LENGTH = 90;
constexpr auto SECOND_LENGTH = 110;
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
const auto LV_TRIG_SCALE = _lv_trigo_sin(90);
int16_t cosine(int16_t angle) {
return _lv_trigo_sin(angle + 90);
}
// ##
static int16_t coordinate_y_relocate(int16_t y) {
return (((y) -LV_HOR_RES / 2) < 0) ? (0 - ((y) -LV_HOR_RES / 2)) : ((y) -LV_HOR_RES / 2);
int16_t sine(int16_t angle) {
return _lv_trigo_sin(angle);
}
int16_t coordinate_x_relocate(int16_t x) {
return (x + LV_HOR_RES / 2);
}
int16_t coordinate_y_relocate(int16_t y) {
return std::abs(y - LV_HOR_RES / 2);
}
lv_point_t coordinate_relocate(int16_t radius, int16_t angle) {
return lv_point_t{
.x = coordinate_x_relocate(radius * static_cast<int32_t>(sine(angle)) / LV_TRIG_SCALE),
.y = coordinate_y_relocate(radius * static_cast<int32_t>(cosine(angle)) / LV_TRIG_SCALE)
};
}
} // namespace
WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController)
: Screen(app),
currentDateTime {{}},
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
notificatioManager {notificatioManager},
notificationManager {notificationManager},
settingsController {settingsController} {
settingsController.SetClockFace(1);
@ -123,15 +140,12 @@ void WatchFaceAnalog::UpdateClock() {
second = dateTimeController.Seconds();
if (sMinute != minute) {
minute_point[0].x = coordinate_x_relocate(30 * sin(minute * 6 * PI / 180));
minute_point[0].y = coordinate_y_relocate(30 * cos(minute * 6 * PI / 180));
minute_point[1].x = coordinate_x_relocate(MINUTE_LENGTH * sin(minute * 6 * PI / 180));
minute_point[1].y = coordinate_y_relocate(MINUTE_LENGTH * cos(minute * 6 * PI / 180));
auto const angle = minute * 6;
minute_point[0] = coordinate_relocate(30, angle);
minute_point[1] = coordinate_relocate(MINUTE_LENGTH, angle);
minute_point_trace[0].x = coordinate_x_relocate(5 * sin(minute * 6 * PI / 180));
minute_point_trace[0].y = coordinate_y_relocate(5 * cos(minute * 6 * PI / 180));
minute_point_trace[1].x = coordinate_x_relocate(31 * sin(minute * 6 * PI / 180));
minute_point_trace[1].y = coordinate_y_relocate(31 * cos(minute * 6 * PI / 180));
minute_point_trace[0] = coordinate_relocate(5, angle);
minute_point_trace[1] = coordinate_relocate(31, angle);
lv_line_set_points(minute_body, minute_point, 2);
lv_line_set_points(minute_body_trace, minute_point_trace, 2);
@ -140,15 +154,13 @@ void WatchFaceAnalog::UpdateClock() {
if (sHour != hour || sMinute != minute) {
sHour = hour;
sMinute = minute;
hour_point[0].x = coordinate_x_relocate(30 * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
hour_point[0].y = coordinate_y_relocate(30 * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
hour_point[1].x = coordinate_x_relocate(HOUR_LENGTH * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
hour_point[1].y = coordinate_y_relocate(HOUR_LENGTH * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
auto const angle = (hour * 30 + minute / 2);
hour_point_trace[0].x = coordinate_x_relocate(5 * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
hour_point_trace[0].y = coordinate_y_relocate(5 * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
hour_point_trace[1].x = coordinate_x_relocate(31 * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
hour_point_trace[1].y = coordinate_y_relocate(31 * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180));
hour_point[0] = coordinate_relocate(30, angle);
hour_point[1] = coordinate_relocate(HOUR_LENGTH, angle);
hour_point_trace[0] = coordinate_relocate(5, angle);
hour_point_trace[1] = coordinate_relocate(31, angle);
lv_line_set_points(hour_body, hour_point, 2);
lv_line_set_points(hour_body_trace, hour_point_trace, 2);
@ -156,23 +168,22 @@ void WatchFaceAnalog::UpdateClock() {
if (sSecond != second) {
sSecond = second;
second_point[0].x = coordinate_x_relocate(20 * sin((180 + second * 6) * PI / 180));
second_point[0].y = coordinate_y_relocate(20 * cos((180 + second * 6) * PI / 180));
second_point[1].x = coordinate_x_relocate(SECOND_LENGTH * sin(second * 6 * PI / 180));
second_point[1].y = coordinate_y_relocate(SECOND_LENGTH * cos(second * 6 * PI / 180));
auto const angle = second * 6;
second_point[0] = coordinate_relocate(-20, angle);
second_point[1] = coordinate_relocate(SECOND_LENGTH, angle);
lv_line_set_points(second_body, second_point, 2);
}
}
bool WatchFaceAnalog::Refresh() {
batteryPercentRemaining = batteryController.PercentRemaining();
if (batteryPercentRemaining.IsUpdated()) {
auto batteryPercent = batteryPercentRemaining.Get();
lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent));
}
notificationState = notificatioManager.AreNewNotificationsAvailable();
notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
if (notificationState.Get() == true)
@ -202,4 +213,4 @@ bool WatchFaceAnalog::Refresh() {
}
return true;
}
}

View File

@ -27,7 +27,7 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController);
~WatchFaceAnalog() override;
@ -48,7 +48,7 @@ namespace Pinetime {
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0;
DirtyValue<float> batteryPercentRemaining {0};
DirtyValue<uint8_t> batteryPercentRemaining {0};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
DirtyValue<bool> notificationState {false};
@ -79,11 +79,11 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController;
Controllers::Battery& batteryController;
Controllers::Ble& bleController;
Controllers::NotificationManager& notificatioManager;
Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
void UpdateClock();
};
}
}
}
}

View File

@ -45,7 +45,7 @@ namespace Pinetime {
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0;
DirtyValue<int> batteryPercentRemaining {};
DirtyValue<uint8_t> batteryPercentRemaining {};
DirtyValue<bool> bleState {};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
DirtyValue<bool> motionSensorOk {};

View File

@ -110,7 +110,7 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app,
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, "");
taskUpdate = lv_task_create(lv_update_task, 500000, LV_TASK_PRIO_MID, this);
taskUpdate = lv_task_create(lv_update_task, 5000, LV_TASK_PRIO_MID, this);
}
QuickSettings::~QuickSettings() {

View File

@ -45,7 +45,7 @@ SettingSteps::SettingSteps(
stepValue = lv_label_create(lv_scr_act(), NULL);
lv_obj_set_style_local_text_font(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_label_set_text_fmt(stepValue,"%i", settingsController.GetStepsGoal());
lv_label_set_text_fmt(stepValue, "%lu", settingsController.GetStepsGoal());
lv_label_set_align(stepValue, LV_LABEL_ALIGN_CENTER);
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_CENTER, 0, -10);
@ -81,7 +81,7 @@ void SettingSteps::UpdateSelected(lv_obj_t *object, lv_event_t event) {
value += 1000;
if ( value <= 500000 ) {
settingsController.SetStepsGoal(value);
lv_label_set_text_fmt(stepValue,"%i", settingsController.GetStepsGoal());
lv_label_set_text_fmt(stepValue, "%lu", settingsController.GetStepsGoal());
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_CENTER, 0, -10);
}
}
@ -90,7 +90,7 @@ void SettingSteps::UpdateSelected(lv_obj_t *object, lv_event_t event) {
value -= 1000;
if ( value >= 1000 ) {
settingsController.SetStepsGoal(value);
lv_label_set_text_fmt(stepValue,"%i", settingsController.GetStepsGoal());
lv_label_set_text_fmt(stepValue, "%lu", settingsController.GetStepsGoal());
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_CENTER, 0, -10);
}
}

View File

@ -16,7 +16,7 @@ namespace {
SettingWakeUp::SettingWakeUp(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
: Screen(app), settingsController {settingsController} {
ignoringEvents = false;
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
@ -42,18 +42,10 @@ SettingWakeUp::SettingWakeUp(Pinetime::Applications::DisplayApp* app, Pinetime::
optionsTotal = 0;
cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr);
lv_checkbox_set_text_static(cbOption[optionsTotal], " None");
cbOption[optionsTotal]->user_data = this;
lv_obj_set_event_cb(cbOption[optionsTotal], event_handler);
if (settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::None) {
lv_checkbox_set_checked(cbOption[optionsTotal], true);
}
optionsTotal++;
cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr);
lv_checkbox_set_text_static(cbOption[optionsTotal], " Single Tap");
cbOption[optionsTotal]->user_data = this;
lv_obj_set_event_cb(cbOption[optionsTotal], event_handler);
if (settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::SingleTap) {
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap)) {
lv_checkbox_set_checked(cbOption[optionsTotal], true);
}
optionsTotal++;
@ -61,7 +53,7 @@ SettingWakeUp::SettingWakeUp(Pinetime::Applications::DisplayApp* app, Pinetime::
lv_checkbox_set_text_static(cbOption[optionsTotal], " Double Tap");
cbOption[optionsTotal]->user_data = this;
lv_obj_set_event_cb(cbOption[optionsTotal], event_handler);
if (settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::DoubleTap) {
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
lv_checkbox_set_checked(cbOption[optionsTotal], true);
}
optionsTotal++;
@ -69,7 +61,7 @@ SettingWakeUp::SettingWakeUp(Pinetime::Applications::DisplayApp* app, Pinetime::
lv_checkbox_set_text_static(cbOption[optionsTotal], " Raise Wrist");
cbOption[optionsTotal]->user_data = this;
lv_obj_set_event_cb(cbOption[optionsTotal], event_handler);
if (settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) {
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist)) {
lv_checkbox_set_checked(cbOption[optionsTotal], true);
}
optionsTotal++;
@ -85,27 +77,31 @@ bool SettingWakeUp::Refresh() {
}
void SettingWakeUp::UpdateSelected(lv_obj_t* object, lv_event_t event) {
if (event == LV_EVENT_VALUE_CHANGED) {
for (int i = 0; i < optionsTotal; i++) {
if (object == cbOption[i]) {
lv_checkbox_set_checked(cbOption[i], true);
using WakeUpMode = Pinetime::Controllers::Settings::WakeUpMode;
if (event == LV_EVENT_VALUE_CHANGED && !ignoringEvents) {
ignoringEvents = true;
if (i == 0) {
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::None);
};
if (i == 1) {
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::SingleTap);
};
if (i == 2) {
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap);
};
if (i == 3) {
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist);
};
} else {
lv_checkbox_set_checked(cbOption[i], false);
// Find the index of the checkbox that triggered the event
int index = 0;
for (; index < optionsTotal; ++index) {
if (cbOption[index] == object) {
break;
}
}
// Toggle needed wakeup mode
auto mode = static_cast<WakeUpMode>(index);
auto currentState = settingsController.isWakeUpModeOn(mode);
settingsController.setWakeUpMode(mode, !currentState);
// Update checkbox according to current wakeup modes.
// This is needed because we can have extra logic when setting or unsetting wakeup modes,
// for example, when setting SingleTap, DoubleTap is unset and vice versa.
auto modes = settingsController.getWakeUpModes();
for (int i = 0; i < optionsTotal; ++i) {
lv_checkbox_set_checked(cbOption[i], modes[i]);
}
ignoringEvents = false;
}
}
}

View File

@ -22,6 +22,11 @@ namespace Pinetime {
Controllers::Settings& settingsController;
uint8_t optionsTotal;
lv_obj_t* cbOption[4];
// When UpdateSelected is called, it uses lv_checkbox_set_checked,
// which can cause extra events to be fired,
// which might trigger UpdateSelected again, causing a loop.
// This variable is used as a mutex to prevent that.
bool ignoringEvents;
};
}
}

View File

@ -58,6 +58,15 @@ SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app, Pine
lv_checkbox_set_checked(cbOption[optionsTotal], true);
}
optionsTotal++;
cbOption[optionsTotal] = lv_checkbox_create(container1, nullptr);
lv_checkbox_set_text_static(cbOption[optionsTotal], " PineTimeStyle");
cbOption[optionsTotal]->user_data = this;
lv_obj_set_event_cb(cbOption[optionsTotal], event_handler);
if (settingsController.GetClockFace() == 2) {
lv_checkbox_set_checked(cbOption[optionsTotal], true);
}
optionsTotal++;
}

View File

@ -140,8 +140,9 @@ void St7789::Uninit() {
}
void St7789::DrawPixel(uint16_t x, uint16_t y, uint32_t color) {
if ((x < 0) || (x >= Width) || (y < 0) || (y >= Height))
if (x >= Width || y >= Height) {
return;
}
SetAddrWindow(x, y, x + 1, y + 1);

1
src/libs/littlefs Submodule

@ -0,0 +1 @@
Subproject commit ead50807f1ca3fdf2da00b77a0ce02651ded2d13

View File

@ -42,7 +42,7 @@
/* Default display refresh period.
* Can be changed in the display driver (`lv_disp_drv_t`).*/
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
#define LV_DISP_DEF_REFR_PERIOD 20 /*[ms]*/
/* Dot Per Inch: used to initialize default sizes.
* E.g. a button with width = LV_DPI / 2 -> half inch wide
@ -112,7 +112,7 @@ typedef int16_t lv_coord_t;
* Can be changed in the Input device driver (`lv_indev_drv_t`)*/
/* Input device read period in milliseconds */
#define LV_INDEV_DEF_READ_PERIOD 30
#define LV_INDEV_DEF_READ_PERIOD 20
/* Drag threshold in pixels */
#define LV_INDEV_DEF_DRAG_LIMIT 10
@ -128,7 +128,6 @@ typedef int16_t lv_coord_t;
* Time between `LV_EVENT_LONG_PRESSED_REPEAT */
#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100
/* Gesture threshold in pixels */
#define LV_INDEV_DEF_GESTURE_LIMIT 50
@ -204,7 +203,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h" */
/* 1: Enable file system (might be required for images */
// TODO: Enable FS
#define LV_USE_FILESYSTEM 0
#define LV_USE_FILESYSTEM 1
#if LV_USE_FILESYSTEM
/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/
typedef void * lv_fs_drv_user_data_t;
@ -236,7 +235,7 @@ typedef void * lv_fs_drv_user_data_t;
* With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
* However the opened images might consume additional RAM.
* LV_IMG_CACHE_DEF_SIZE must be >= 1 */
#define LV_IMG_CACHE_DEF_SIZE 1
#define LV_IMG_CACHE_DEF_SIZE 6
/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/
typedef void* lv_img_decoder_user_data_t;
@ -293,10 +292,10 @@ typedef void* lv_img_decoder_user_data_t;
/* 1: use a custom tick source.
* It removes the need to manually update the tick with `lv_tick_inc`) */
#define LV_TICK_CUSTOM 0
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM == 1
#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
#define LV_TICK_CUSTOM_INCLUDE "FreeRTOS.h" /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (xTaskGetTickCount()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/
typedef void* lv_disp_drv_user_data_t; /*Type of user data in the display driver*/
@ -417,6 +416,7 @@ typedef void* lv_indev_drv_user_data_t; /*Type of user data in the in
LV_FONT_DECLARE(jetbrains_mono_extrabold_compressed) \
LV_FONT_DECLARE(jetbrains_mono_42) \
LV_FONT_DECLARE(jetbrains_mono_76) \
LV_FONT_DECLARE(open_sans_light) \
LV_FONT_DECLARE(lv_font_sys_48)
/* Enable it if you have fonts with a lot of characters.
@ -758,4 +758,4 @@ typedef void* lv_obj_user_data_t;
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/
#endif /*LV_CONF_H*/

View File

@ -28,12 +28,14 @@
#include <drivers/Hrs3300.h>
#include <drivers/Bma421.h>
#include "BootloaderVersion.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/motor/MotorController.h"
#include "components/datetime/DateTimeController.h"
#include "components/heartrate/HeartRateController.h"
#include "components/fs/FS.h"
#include "drivers/Spi.h"
#include "drivers/SpiMaster.h"
#include "drivers/SpiNorFlash.h"
@ -107,10 +109,6 @@ void ble_manager_set_ble_disconnection_callback(void (*disconnection)());
static constexpr uint8_t pinTouchIrq = 28;
static constexpr uint8_t pinPowerPresentIrq = 19;
Pinetime::Controllers::Settings settingsController {spiNorFlash};
Pinetime::Controllers::MotorController motorController {settingsController};
Pinetime::Controllers::HeartRateController heartRateController;
Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController);
@ -121,6 +119,11 @@ Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::TimerController timerController;
Pinetime::Controllers::FS fs {spiNorFlash};
Pinetime::Controllers::Settings settingsController {fs};
Pinetime::Controllers::MotorController motorController {settingsController};
Pinetime::Applications::DisplayApp displayApp(lcd,
lvgl,
touchPanel,
@ -154,7 +157,8 @@ Pinetime::System::SystemTask systemTask(spi,
settingsController,
heartRateController,
displayApp,
heartRateApp);
heartRateApp,
fs);
void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
if (pin == pinTouchIrq) {
@ -174,13 +178,6 @@ void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
extern "C" {
void vApplicationIdleHook(void) {
if (!isFactory)
lv_tick_inc(1);
}
}
void DebounceTimerChargeCallback(TimerHandle_t xTimer) {
xTimerStop(xTimer, 0);
systemTask.PushMessage(Pinetime::System::Messages::OnChargingEvent);
@ -306,6 +303,9 @@ int main(void) {
debounceTimer = xTimerCreate("debounceTimer", 200, pdFALSE, (void*) 0, DebounceTimerCallback);
debounceChargeTimer = xTimerCreate("debounceTimerCharge", 200, pdFALSE, (void*) 0, DebounceTimerChargeCallback);
// retrieve version stored by bootloader
Pinetime::BootloaderVersion::SetVersion(NRF_TIMER2->CC[0]);
lvgl.Init();
systemTask.Start();

View File

@ -59,7 +59,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
Controllers::Settings& settingsController,
Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Applications::DisplayApp& displayApp,
Pinetime::Applications::HeartRateTask& heartRateApp)
Pinetime::Applications::HeartRateTask& heartRateApp,
Pinetime::Controllers::FS& fs)
: spi {spi},
lcd {lcd},
spiNorFlash {spiNorFlash},
@ -77,10 +78,11 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
motionSensor {motionSensor},
settingsController {settingsController},
heartRateController{heartRateController},
nimbleController(*this, bleController, dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController),
motionController{motionController},
displayApp{displayApp},
heartRateApp(heartRateApp) {
heartRateApp(heartRateApp),
fs{fs},
nimbleController(*this, bleController, dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController) {
}
@ -107,6 +109,9 @@ void SystemTask::Work() {
spi.Init();
spiNorFlash.Init();
spiNorFlash.Wakeup();
fs.Init();
nimbleController.Init();
nimbleController.StartAdvertising();
brightnessController.Init();
@ -193,7 +198,11 @@ void SystemTask::Work() {
Messages message = static_cast<Messages>(msg);
switch (message) {
case Messages::EnableSleeping:
doNotGoToSleep = false;
// Make sure that exiting an app doesn't enable sleeping,
// if the exiting was caused by a firmware update
if (!bleController.IsFirmwareUpdating()) {
doNotGoToSleep = false;
}
break;
case Messages::DisableSleeping:
doNotGoToSleep = true;
@ -206,7 +215,7 @@ void SystemTask::Work() {
twiMaster.Wakeup();
// Double Tap needs the touch screen to be in normal mode
if (settingsController.getWakeUpMode() != Pinetime::Controllers::Settings::WakeUpMode::DoubleTap) {
if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
touchPanel.Wakeup();
}
@ -227,9 +236,9 @@ void SystemTask::Work() {
auto touchInfo = touchPanel.GetTouchInfo();
twiMaster.Sleep();
if (touchInfo.isTouch and ((touchInfo.gesture == Pinetime::Drivers::Cst816S::Gestures::DoubleTap and
settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::DoubleTap) or
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) or
(touchInfo.gesture == Pinetime::Drivers::Cst816S::Gestures::SingleTap and
settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::SingleTap))) {
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap)))) {
GoToRunning();
}
} break;
@ -270,10 +279,11 @@ void SystemTask::Work() {
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
break;
case Messages::BleFirmwareUpdateFinished:
if (bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated) {
NVIC_SystemReset();
}
doNotGoToSleep = false;
xTimerStart(idleTimer, 0);
if (bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated)
NVIC_SystemReset();
break;
case Messages::OnTouchEvent:
ReloadIdleTimer();
@ -291,7 +301,7 @@ void SystemTask::Work() {
spi.Sleep();
// Double Tap needs the touch screen to be in normal mode
if (settingsController.getWakeUpMode() != Pinetime::Controllers::Settings::WakeUpMode::DoubleTap) {
if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
touchPanel.Sleep();
}
twiMaster.Sleep();
@ -325,6 +335,11 @@ void SystemTask::Work() {
}
}
if (xTaskGetTickCount() - batteryNotificationTick > batteryNotificationPeriod) {
nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining());
batteryNotificationTick = xTaskGetTickCount();
}
monitor.Process();
uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG);
dateTimeController.UpdateTime(systick_counter);
@ -338,7 +353,7 @@ void SystemTask::UpdateMotion() {
if (isGoingToSleep or isWakingUp)
return;
if (isSleeping && settingsController.getWakeUpMode() != Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist)
if (isSleeping && !settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist))
return;
if (isSleeping)
@ -389,10 +404,10 @@ void SystemTask::OnTouchEvent() {
PushMessage(Messages::OnTouchEvent);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent);
} else if (!isWakingUp) {
if (settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::None or
settingsController.getWakeUpMode() == Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist)
return;
PushMessage(Messages::TouchWakeUp);
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap) or
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
PushMessage(Messages::TouchWakeUp);
}
}
}

View File

@ -16,13 +16,15 @@
#include "components/ble/NotificationManager.h"
#include "components/motor/MotorController.h"
#include "components/timer/TimerController.h"
#include "components/fs/FS.h"
#ifdef PINETIME_IS_RECOVERY
#include "displayapp/DisplayAppRecovery.h"
#include "displayapp/DummyLittleVgl.h"
#else
#include "components/settings/Settings.h"
#include "displayapp/DisplayApp.h"
#include "displayapp/LittleVgl.h"
#include "displayapp/LittleVgl.h"
#endif
#include "drivers/Watchdog.h"
@ -59,7 +61,8 @@ namespace Pinetime {
Controllers::Settings& settingsController,
Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Applications::DisplayApp& displayApp,
Pinetime::Applications::HeartRateTask& heartRateApp);
Pinetime::Applications::HeartRateTask& heartRateApp,
Pinetime::Controllers::FS& fs);
void Start();
void PushMessage(Messages msg);
@ -73,6 +76,10 @@ namespace Pinetime {
return nimbleController;
};
bool IsSleeping() const {
return isSleeping;
}
private:
TaskHandle_t taskHandle;
@ -99,13 +106,14 @@ namespace Pinetime {
Pinetime::Drivers::Bma421& motionSensor;
Pinetime::Controllers::Settings& settingsController;
Pinetime::Controllers::HeartRateController& heartRateController;
Pinetime::Controllers::NimbleController nimbleController;
Controllers::BrightnessController brightnessController;
Pinetime::Controllers::MotionController& motionController;
Pinetime::Applications::DisplayApp& displayApp;
Pinetime::Applications::HeartRateTask& heartRateApp;
Pinetime::Controllers::FS& fs;
Pinetime::Controllers::NimbleController nimbleController;
static constexpr uint8_t pinSpiSck = 2;
static constexpr uint8_t pinSpiMosi = 3;
@ -127,6 +135,8 @@ namespace Pinetime {
void GoToRunning();
void UpdateMotion();
bool stepCounterMustBeReset = false;
static constexpr TickType_t batteryNotificationPeriod = 1000 * 60 * 10; // 1 tick ~= 1ms. 1ms * 60 * 10 = 10 minutes
TickType_t batteryNotificationTick = 0;
#if configUSE_TRACE_FACILITY == 1
SystemMonitor<FreeRtosMonitor> monitor;