summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/battery/BatteryController.cpp33
-rw-r--r--src/components/battery/BatteryController.h24
-rw-r--r--src/components/ble/AlertNotificationClient.cpp2
-rw-r--r--src/components/ble/AlertNotificationService.cpp2
-rw-r--r--src/components/ble/BatteryInformationService.cpp8
-rw-r--r--src/components/ble/BatteryInformationService.h2
-rw-r--r--src/components/ble/DfuService.cpp18
-rw-r--r--src/components/ble/ImmediateAlertService.cpp2
-rw-r--r--src/components/ble/MusicService.cpp234
-rw-r--r--src/components/ble/MusicService.h74
-rw-r--r--src/components/ble/NimbleController.cpp8
-rw-r--r--src/components/ble/NimbleController.h3
-rw-r--r--src/components/datetime/DateTimeController.cpp10
-rw-r--r--src/components/datetime/DateTimeController.h6
-rw-r--r--src/components/fs/FS.cpp197
-rw-r--r--src/components/fs/FS.h71
-rw-r--r--src/components/heartrate/HeartRateController.cpp3
-rw-r--r--src/components/heartrate/HeartRateController.h4
-rw-r--r--src/components/heartrate/Ppg.cpp11
-rw-r--r--src/components/heartrate/Ppg.h3
-rw-r--r--src/components/motion/MotionController.cpp7
-rw-r--r--src/components/motion/MotionController.h14
-rw-r--r--src/components/motor/MotorController.cpp4
-rw-r--r--src/components/rle/RleDecoder.h2
-rw-r--r--src/components/settings/Settings.cpp100
-rw-r--r--src/components/settings/Settings.h95
-rw-r--r--src/components/timer/TimerController.cpp69
-rw-r--r--src/components/timer/TimerController.h37
28 files changed, 668 insertions, 375 deletions
diff --git a/src/components/battery/BatteryController.cpp b/src/components/battery/BatteryController.cpp
index b39efefb..fa476ea3 100644
--- a/src/components/battery/BatteryController.cpp
+++ b/src/components/battery/BatteryController.cpp
@@ -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,17 +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(powerPresentPin, (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;
@@ -33,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,
@@ -55,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) {
diff --git a/src/components/battery/BatteryController.h b/src/components/battery/BatteryController.h
index 04bcf6b8..1333ad0e 100644
--- a/src/components/battery/BatteryController.h
+++ b/src/components/battery/BatteryController.h
@@ -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;
};
}
-} \ No newline at end of file
+}
diff --git a/src/components/ble/AlertNotificationClient.cpp b/src/components/ble/AlertNotificationClient.cpp
index 6043a129..c3d1d69a 100644
--- a/src/components/ble/AlertNotificationClient.cpp
+++ b/src/components/ble/AlertNotificationClient.cpp
@@ -159,7 +159,7 @@ void AlertNotificationClient::OnNotification(ble_gap_event* event) {
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif));
- systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
+ systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
}
}
diff --git a/src/components/ble/AlertNotificationService.cpp b/src/components/ble/AlertNotificationService.cpp
index e9f5941e..d5fc7f65 100644
--- a/src/components/ble/AlertNotificationService.cpp
+++ b/src/components/ble/AlertNotificationService.cpp
@@ -79,7 +79,7 @@ int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle
break;
}
- auto event = Pinetime::System::SystemTask::Messages::OnNewNotification;
+ auto event = Pinetime::System::Messages::OnNewNotification;
notificationManager.Push(std::move(notif));
systemTask.PushMessage(event);
}
diff --git a/src/components/ble/BatteryInformationService.cpp b/src/components/ble/BatteryInformationService.cpp
index 10a78d67..7f176904 100644
--- a/src/components/ble/BatteryInformationService.cpp
+++ b/src/components/ble/BatteryInformationService.cpp
@@ -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;
-} \ No newline at end of file
+}
+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);
+}
diff --git a/src/components/ble/BatteryInformationService.h b/src/components/ble/BatteryInformationService.h
index 7d060909..1303fd6c 100644
--- a/src/components/ble/BatteryInformationService.h
+++ b/src/components/ble/BatteryInformationService.h
@@ -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};
diff --git a/src/components/ble/DfuService.cpp b/src/components/ble/DfuService.cpp
index 2031668e..4179994d 100644
--- a/src/components/ble/DfuService.cpp
+++ b/src/components/ble/DfuService.cpp
@@ -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};
@@ -205,7 +210,7 @@ int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf* om) {
bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Running);
bleController.FirmwareUpdateTotalBytes(0xffffffffu);
bleController.FirmwareUpdateCurrentBytes(0);
- systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateStarted);
+ systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateStarted);
return 0;
} else {
NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType);
@@ -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::SystemTask::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,9 +307,8 @@ void DfuService::Reset() {
applicationSize = 0;
expectedCrc = 0;
notificationManager.Reset();
- bleController.State(Pinetime::Controllers::Ble::FirmwareUpdateStates::Error);
bleController.StopFirmwareUpdate();
- systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished);
+ systemTask.PushMessage(Pinetime::System::Messages::BleFirmwareUpdateFinished);
}
DfuService::NotificationManager::NotificationManager() {
diff --git a/src/components/ble/ImmediateAlertService.cpp b/src/components/ble/ImmediateAlertService.cpp
index fd6430af..820d3b6e 100644
--- a/src/components/ble/ImmediateAlertService.cpp
+++ b/src/components/ble/ImmediateAlertService.cpp
@@ -67,7 +67,7 @@ int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif));
- systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
+ systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
}
}
diff --git a/src/components/ble/MusicService.cpp b/src/components/ble/MusicService.cpp
index 36bf2709..74fe9522 100644
--- a/src/components/ble/MusicService.cpp
+++ b/src/components/ble/MusicService.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 JF, Adam Pigg, Avamander
+/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
@@ -18,132 +18,103 @@
#include "MusicService.h"
#include "systemtask/SystemTask.h"
-int MSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
- auto musicService = static_cast<Pinetime::Controllers::MusicService*>(arg);
- return musicService->OnCommand(conn_handle, attr_handle, ctxt);
+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) {
- msUuid.value[14] = msId[0];
- msUuid.value[15] = msId[1];
-
- msEventCharUuid.value[12] = msEventCharId[0];
- msEventCharUuid.value[13] = msEventCharId[1];
- msEventCharUuid.value[14] = msId[0];
- msEventCharUuid.value[15] = msId[1];
-
- msStatusCharUuid.value[12] = msStatusCharId[0];
- msStatusCharUuid.value[13] = msStatusCharId[1];
- msStatusCharUuid.value[14] = msId[0];
- msStatusCharUuid.value[15] = msId[1];
-
- msTrackCharUuid.value[12] = msTrackCharId[0];
- msTrackCharUuid.value[13] = msTrackCharId[1];
- msTrackCharUuid.value[14] = msId[0];
- msTrackCharUuid.value[15] = msId[1];
-
- msArtistCharUuid.value[12] = msArtistCharId[0];
- msArtistCharUuid.value[13] = msArtistCharId[1];
- msArtistCharUuid.value[14] = msId[0];
- msArtistCharUuid.value[15] = msId[1];
-
- msAlbumCharUuid.value[12] = msAlbumCharId[0];
- msAlbumCharUuid.value[13] = msAlbumCharId[1];
- msAlbumCharUuid.value[14] = msId[0];
- msAlbumCharUuid.value[15] = msId[1];
-
- msPositionCharUuid.value[12] = msPositionCharId[0];
- msPositionCharUuid.value[13] = msPositionCharId[1];
- msPositionCharUuid.value[14] = msId[0];
- msPositionCharUuid.value[15] = msId[1];
-
- msTotalLengthCharUuid.value[12] = msTotalLengthCharId[0];
- msTotalLengthCharUuid.value[13] = msTotalLengthCharId[1];
- msTotalLengthCharUuid.value[14] = msId[0];
- msTotalLengthCharUuid.value[15] = msId[1];
-
- msTrackNumberCharUuid.value[12] = msTrackNumberCharId[0];
- msTrackNumberCharUuid.value[13] = msTrackNumberCharId[1];
- msTrackNumberCharUuid.value[14] = msId[0];
- msTrackNumberCharUuid.value[15] = msId[1];
-
- msTrackTotalCharUuid.value[12] = msTrackTotalCharId[0];
- msTrackTotalCharUuid.value[13] = msTrackTotalCharId[1];
- msTrackTotalCharUuid.value[14] = msId[0];
- msTrackTotalCharUuid.value[15] = msId[1];
-
- msPlaybackSpeedCharUuid.value[12] = msPlaybackSpeedCharId[0];
- msPlaybackSpeedCharUuid.value[13] = msPlaybackSpeedCharId[1];
- msPlaybackSpeedCharUuid.value[14] = msId[0];
- msPlaybackSpeedCharUuid.value[15] = msId[1];
-
- msRepeatCharUuid.value[12] = msRepeatCharId[0];
- msRepeatCharUuid.value[13] = msRepeatCharId[1];
- msRepeatCharUuid.value[14] = msId[0];
- msRepeatCharUuid.value[15] = msId[1];
-
- msShuffleCharUuid.value[12] = msShuffleCharId[0];
- msShuffleCharUuid.value[13] = msShuffleCharId[1];
- msShuffleCharUuid.value[14] = msId[0];
- msShuffleCharUuid.value[15] = msId[1];
-
- characteristicDefinition[0] = {.uuid = (ble_uuid_t*) (&msEventCharUuid),
- .access_cb = MSCallback,
+ characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
+ .access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY,
.val_handle = &eventHandle};
- characteristicDefinition[1] = {
- .uuid = (ble_uuid_t*) (&msStatusCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[2] = {
- .uuid = (ble_uuid_t*) (&msTrackCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[3] = {
- .uuid = (ble_uuid_t*) (&msArtistCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[4] = {
- .uuid = (ble_uuid_t*) (&msAlbumCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[5] = {
- .uuid = (ble_uuid_t*) (&msPositionCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[6] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid),
- .access_cb = MSCallback,
+ characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
+ .access_cb = MusicCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
+ characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
+ .access_cb = MusicCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
+ characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
+ .access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[7] = {.uuid = (ble_uuid_t*) (&msTotalLengthCharUuid),
- .access_cb = MSCallback,
+ characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
+ .access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[8] = {.uuid = (ble_uuid_t*) (&msTrackNumberCharUuid),
- .access_cb = MSCallback,
+ characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
+ .access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[9] = {.uuid = (ble_uuid_t*) (&msTrackTotalCharUuid),
- .access_cb = MSCallback,
+ characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
+ .access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[10] = {.uuid = (ble_uuid_t*) (&msPlaybackSpeedCharUuid),
- .access_cb = MSCallback,
+ characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
+ .access_cb = MusicCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
+ characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
+ .access_cb = MusicCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
+ characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
+ .access_cb = MusicCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
+ characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
+ .access_cb = MusicCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
+ characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
+ .access_cb = MusicCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
+ characteristicDefinition[12] = {.uuid = &msShuffleCharUuid.u,
+ .access_cb = MusicCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[11] = {
- .uuid = (ble_uuid_t*) (&msRepeatCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
- characteristicDefinition[12] = {
- .uuid = (ble_uuid_t*) (&msShuffleCharUuid), .access_cb = MSCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[13] = {0};
- serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = (ble_uuid_t*) &msUuid, .characteristics = characteristicDefinition};
+ serviceDefinition[0] = {
+ .type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &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() {
- int res = 0;
+ uint8_t res = 0;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);
@@ -152,60 +123,67 @@ void Pinetime::Controllers::MusicService::Init() {
}
int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
-
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
- uint8_t data[notifSize + 1];
+ char data[notifSize + 1];
data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data);
- char* s = (char*) &data[0];
- if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &msArtistCharUuid) == 0) {
+ char* s = &data[0];
+ if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
artistName = s;
- } else if (ble_uuid_cmp(ctxt->chr->uuid, (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, (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, (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, (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, (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, (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, (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, (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, (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, (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;
}
}
return 0;
}
-std::string Pinetime::Controllers::MusicService::getAlbum() {
+std::string Pinetime::Controllers::MusicService::getAlbum() const {
return albumName;
}
-std::string Pinetime::Controllers::MusicService::getArtist() {
+std::string Pinetime::Controllers::MusicService::getArtist() const {
return artistName;
}
-std::string Pinetime::Controllers::MusicService::getTrack() {
+std::string Pinetime::Controllers::MusicService::getTrack() const {
return trackName;
}
-bool Pinetime::Controllers::MusicService::isPlaying() {
+bool Pinetime::Controllers::MusicService::isPlaying() const {
return playing;
}
-float Pinetime::Controllers::MusicService::getPlaybackSpeed() {
+float Pinetime::Controllers::MusicService::getPlaybackSpeed() const {
return playbackSpeed;
}
+int Pinetime::Controllers::MusicService::getProgress() const {
+ return trackProgress;
+}
+
+int Pinetime::Controllers::MusicService::getTrackLength() const {
+ return trackLength;
+}
+
void Pinetime::Controllers::MusicService::event(char event) {
auto* om = ble_hs_mbuf_from_flat(&event, 1);
@@ -217,11 +195,3 @@ void Pinetime::Controllers::MusicService::event(char event) {
ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
-
-int Pinetime::Controllers::MusicService::getProgress() {
- return trackProgress;
-}
-
-int Pinetime::Controllers::MusicService::getTrackLength() {
- return trackLength;
-}
diff --git a/src/components/ble/MusicService.h b/src/components/ble/MusicService.h
index 5f5343e4..1ad9a420 100644
--- a/src/components/ble/MusicService.h
+++ b/src/components/ble/MusicService.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 JF, Adam Pigg, Avamander
+/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
@@ -26,16 +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 }
-
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
-
class MusicService {
public:
explicit MusicService(Pinetime::System::SystemTask& system);
@@ -46,19 +41,19 @@ namespace Pinetime {
void event(char event);
- std::string getArtist();
+ std::string getArtist() const;
- std::string getTrack();
+ std::string getTrack() const;
- std::string getAlbum();
+ std::string getAlbum() const;
- int getProgress();
+ int getProgress() const;
- int getTrackLength();
+ int getTrackLength() const;
- float getPlaybackSpeed();
+ float getPlaybackSpeed() const;
- bool isPlaying();
+ bool isPlaying() const;
static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00;
@@ -71,55 +66,26 @@ namespace Pinetime {
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private:
- static constexpr uint8_t msId[2] = {0x00, 0x00};
- static constexpr uint8_t msEventCharId[2] = {0x01, 0x00};
- static constexpr uint8_t msStatusCharId[2] = {0x02, 0x00};
- static constexpr uint8_t msArtistCharId[2] = {0x03, 0x00};
- static constexpr uint8_t msTrackCharId[2] = {0x04, 0x00};
- static constexpr uint8_t msAlbumCharId[2] = {0x05, 0x00};
- static constexpr uint8_t msPositionCharId[2] = {0x06, 0x00};
- static constexpr uint8_t msTotalLengthCharId[2] = {0x07, 0x00};
- static constexpr uint8_t msTrackNumberCharId[2] = {0x08, 0x00};
- static constexpr uint8_t msTrackTotalCharId[2] = {0x09, 0x00};
- static constexpr uint8_t msPlaybackSpeedCharId[2] = {0x0a, 0x00};
- static constexpr uint8_t msRepeatCharId[2] = {0x0b, 0x00};
- static constexpr uint8_t msShuffleCharId[2] = {0x0c, 0x00};
-
- ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
-
- ble_uuid128_t msEventCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msStatusCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msArtistCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msTrackCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msAlbumCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msPositionCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msTotalLengthCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msTrackNumberCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msTrackTotalCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msPlaybackSpeedCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msRepeatCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
- ble_uuid128_t msShuffleCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = MUSIC_SERVICE_UUID_BASE};
-
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;
};
diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp
index 67a6d691..5eb227bf 100644
--- a/src/components/ble/NimbleController.cpp
+++ b/src/components/ble/NimbleController.cpp
@@ -149,7 +149,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
bleController.Disconnect();
} else {
bleController.Connect();
- systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleConnected);
+ systemTask.PushMessage(Pinetime::System::Messages::BleConnected);
connectionHandle = event->connect.conn_handle;
// Service discovery is deffered via systemtask
}
@@ -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);
+ }
+}
diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h
index 5dd01e42..0cfe983c 100644
--- a/src/components/ble/NimbleController.h
+++ b/src/components/ble/NimbleController.h
@@ -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},
diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp
index 4f9302eb..28a70abc 100644
--- a/src/components/datetime/DateTimeController.cpp
+++ b/src/components/datetime/DateTimeController.cpp
@@ -5,9 +5,6 @@
using namespace Pinetime::Controllers;
-DateTime::DateTime(System::SystemTask& systemTask) : systemTask {systemTask} {
-}
-
void DateTime::SetTime(
uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter) {
std::tm tm = {
@@ -70,7 +67,8 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
// Notify new day to SystemTask
if (hour == 0 and not isMidnightAlreadyNotified) {
isMidnightAlreadyNotified = true;
- systemTask.PushMessage(System::SystemTask::Messages::OnNewDay);
+ if(systemTask != nullptr)
+ systemTask->PushMessage(System::Messages::OnNewDay);
} else if (hour != 0) {
isMidnightAlreadyNotified = false;
}
@@ -104,6 +102,10 @@ const char* DateTime::DayOfWeekShortToStringLow() {
return DateTime::DaysStringShortLow[(uint8_t) dayOfWeek];
}
+void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
+ this->systemTask = systemTask;
+}
+
char const* DateTime::DaysStringLow[] = {"--", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
char const* DateTime::DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
diff --git a/src/components/datetime/DateTimeController.h b/src/components/datetime/DateTimeController.h
index d0ae727e..265d6e9d 100644
--- a/src/components/datetime/DateTimeController.h
+++ b/src/components/datetime/DateTimeController.h
@@ -27,8 +27,6 @@ namespace Pinetime {
December
};
- DateTime(System::SystemTask& systemTask);
-
void SetTime(uint16_t year,
uint8_t month,
uint8_t day,
@@ -75,8 +73,9 @@ namespace Pinetime {
return uptime;
}
+ void Register(System::SystemTask* systemTask);
+
private:
- System::SystemTask& systemTask;
uint16_t year = 0;
Months month = Months::Unknown;
uint8_t day = 0;
@@ -90,6 +89,7 @@ namespace Pinetime {
std::chrono::seconds uptime {0};
bool isMidnightAlreadyNotified = false;
+ System::SystemTask* systemTask = nullptr;
static char const* DaysString[];
static char const* DaysStringShort[];
diff --git a/src/components/fs/FS.cpp b/src/components/fs/FS.cpp
new file mode 100644
index 00000000..857e6bf9
--- /dev/null
+++ b/src/components/fs/FS.cpp
@@ -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);
+
+} \ No newline at end of file
diff --git a/src/components/fs/FS.h b/src/components/fs/FS.h
new file mode 100644
index 00000000..1f2eb7e0
--- /dev/null
+++ b/src/components/fs/FS.h
@@ -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);
+
+ };
+ }
+}
diff --git a/src/components/heartrate/HeartRateController.cpp b/src/components/heartrate/HeartRateController.cpp
index e84d665a..716813b3 100644
--- a/src/components/heartrate/HeartRateController.cpp
+++ b/src/components/heartrate/HeartRateController.cpp
@@ -4,9 +4,6 @@
using namespace Pinetime::Controllers;
-HeartRateController::HeartRateController(Pinetime::System::SystemTask& systemTask) : systemTask {systemTask} {
-}
-
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
this->state = newState;
if (this->heartRate != heartRate) {
diff --git a/src/components/heartrate/HeartRateController.h b/src/components/heartrate/HeartRateController.h
index d3a8460d..a63f1a70 100644
--- a/src/components/heartrate/HeartRateController.h
+++ b/src/components/heartrate/HeartRateController.h
@@ -15,8 +15,7 @@ namespace Pinetime {
public:
enum class States { Stopped, NotEnoughData, NoTouch, Running };
- explicit HeartRateController(System::SystemTask& systemTask);
-
+ HeartRateController() = default;
void Start();
void Stop();
void Update(States newState, uint8_t heartRate);
@@ -32,7 +31,6 @@ namespace Pinetime {
void SetService(Pinetime::Controllers::HeartRateService* service);
private:
- System::SystemTask& systemTask;
Applications::HeartRateTask* task = nullptr;
States state = States::Stopped;
uint8_t heartRate = 0;
diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp
index da0789e0..fcba3815 100644
--- a/src/components/heartrate/Ppg.cpp
+++ b/src/components/heartrate/Ppg.cpp
@@ -38,9 +38,8 @@ namespace {
}
}
-Ppg::Ppg(float spl)
- : offset {spl},
- hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
+Ppg::Ppg()
+ : hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694},
agc {20, 0.971, 2},
lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} {
}
@@ -67,13 +66,7 @@ float Ppg::HeartRate() {
dataIndex = 0;
return hr;
}
-
-int cccount = 0;
float Ppg::ProcessHeartRate() {
-
- if (cccount > 2)
- asm("nop");
- cccount++;
auto t0 = Trough(data.data(), dataIndex, 7, 48);
if (t0 < 0)
return 0;
diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h
index ee07dfcf..00014162 100644
--- a/src/components/heartrate/Ppg.h
+++ b/src/components/heartrate/Ppg.h
@@ -8,8 +8,7 @@ namespace Pinetime {
namespace Controllers {
class Ppg {
public:
- explicit Ppg(float spl);
-
+ Ppg();
int8_t Preprocess(float spl);
float HeartRate();
diff --git a/src/components/motion/MotionController.cpp b/src/components/motion/MotionController.cpp
index e9ee314b..b0dbada4 100644
--- a/src/components/motion/MotionController.cpp
+++ b/src/components/motion/MotionController.cpp
@@ -34,3 +34,10 @@ bool MotionController::ShouldWakeUp(bool isSleeping) {
void MotionController::IsSensorOk(bool isOk) {
isSensorOk = isOk;
}
+void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) {
+ switch(types){
+ case Drivers::Bma421::DeviceTypes::BMA421: this->deviceType = DeviceTypes::BMA421; break;
+ case Drivers::Bma421::DeviceTypes::BMA425: this->deviceType = DeviceTypes::BMA425; break;
+ default: this->deviceType = DeviceTypes::Unknown; break;
+ }
+}
diff --git a/src/components/motion/MotionController.h b/src/components/motion/MotionController.h
index 3a238262..ff715093 100644
--- a/src/components/motion/MotionController.h
+++ b/src/components/motion/MotionController.h
@@ -1,11 +1,18 @@
#pragma once
#include <cstdint>
+#include <drivers/Bma421.h>
namespace Pinetime {
namespace Controllers {
class MotionController {
public:
+ enum class DeviceTypes{
+ Unknown,
+ BMA421,
+ BMA425,
+ };
+
void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
int16_t X() const {
@@ -27,6 +34,12 @@ namespace Pinetime {
return isSensorOk;
}
+ DeviceTypes DeviceType() const {
+ return deviceType;
+ }
+
+ void Init(Pinetime::Drivers::Bma421::DeviceTypes types);
+
private:
uint32_t nbSteps;
int16_t x;
@@ -34,6 +47,7 @@ namespace Pinetime {
int16_t z;
int16_t lastYForWakeUp = 0;
bool isSensorOk = false;
+ DeviceTypes deviceType = DeviceTypes::Unknown;
};
}
} \ No newline at end of file
diff --git a/src/components/motor/MotorController.cpp b/src/components/motor/MotorController.cpp
index 6f02a583..80883025 100644
--- a/src/components/motor/MotorController.cpp
+++ b/src/components/motor/MotorController.cpp
@@ -15,7 +15,7 @@ void MotorController::Init() {
nrf_gpio_cfg_output(pinMotor);
nrf_gpio_pin_set(pinMotor);
app_timer_init();
-
+
app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate);
app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, vibrate);
isBusy = false;
@@ -53,4 +53,4 @@ void MotorController::vibrate(void* p_context) {
} else {
nrf_gpio_pin_clear(pinMotor);
}
-} \ No newline at end of file
+}
diff --git a/src/components/rle/RleDecoder.h b/src/components/rle/RleDecoder.h
index 0f607fb8..31c1ed10 100644
--- a/src/components/rle/RleDecoder.h
+++ b/src/components/rle/RleDecoder.h
@@ -21,7 +21,7 @@ namespace Pinetime {
const uint8_t* buffer;
size_t size;
- int encodedBufferIndex = 0;
+ size_t encodedBufferIndex = 0;
int y = 0;
uint16_t bp = 0;
uint16_t foregroundColor = 0xffff;
diff --git a/src/components/settings/Settings.cpp b/src/components/settings/Settings.cpp
index 071940b8..37c09f91 100644
--- a/src/components/settings/Settings.cpp
+++ b/src/components/settings/Settings.cpp
@@ -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)];
-
- for (uint8_t block = 0; block < 10; block++) {
+void Settings::LoadSettingsFromFile() {
+ SettingsData bufferSettings;
+ lfs_file_t settingsFile;
- 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() {
-
- // 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
+void Settings::SaveSettingsToFile() {
+ lfs_file_t settingsFile;
- 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);
}
diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h
index 4409425b..93d6d217 100644
--- a/src/components/settings/Settings.h
+++ b/src/components/settings/Settings.h
@@ -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 {
+ }
+ 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();
};
}
-} \ No newline at end of file
+}
diff --git a/src/components/timer/TimerController.cpp b/src/components/timer/TimerController.cpp
new file mode 100644
index 00000000..8d5f5c33
--- /dev/null
+++ b/src/components/timer/TimerController.cpp
@@ -0,0 +1,69 @@
+//
+// Created by florian on 16.05.21.
+//
+
+#include "TimerController.h"
+#include "systemtask/SystemTask.h"
+#include "app_timer.h"
+#include "task.h"
+
+using namespace Pinetime::Controllers;
+
+
+APP_TIMER_DEF(timerAppTimer);
+
+namespace {
+ void TimerEnd(void* p_context) {
+ auto* controller = static_cast<Pinetime::Controllers::TimerController*> (p_context);
+ if(controller != nullptr)
+ controller->OnTimerEnd();
+ }
+}
+
+
+void TimerController::Init() {
+ app_timer_create(&timerAppTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEnd);
+}
+
+void TimerController::StartTimer(uint32_t duration) {
+ app_timer_stop(timerAppTimer);
+ auto currentTicks = xTaskGetTickCount();
+ app_timer_start(timerAppTimer, APP_TIMER_TICKS(duration), this);
+ endTicks = currentTicks + APP_TIMER_TICKS(duration);
+ timerRunning = true;
+}
+
+uint32_t TimerController::GetTimeRemaining() {
+ if (!timerRunning) {
+ return 0;
+ }
+ auto currentTicks = xTaskGetTickCount();
+
+ TickType_t deltaTicks = 0;
+ if (currentTicks > endTicks) {
+ deltaTicks = 0xffffffff - currentTicks;
+ deltaTicks += (endTicks + 1);
+ } else {
+ deltaTicks = endTicks - currentTicks;
+ }
+
+ return (static_cast<TickType_t>(deltaTicks) / static_cast<TickType_t>(configTICK_RATE_HZ)) * 1000;
+}
+
+void TimerController::StopTimer() {
+ app_timer_stop(timerAppTimer);
+ timerRunning = false;
+}
+
+bool TimerController::IsRunning() {
+ return timerRunning;
+}
+void TimerController::OnTimerEnd() {
+ timerRunning = false;
+ if(systemTask != nullptr)
+ systemTask->PushMessage(System::Messages::OnTimerDone);
+}
+
+void TimerController::Register(Pinetime::System::SystemTask* systemTask) {
+ this->systemTask = systemTask;
+}
diff --git a/src/components/timer/TimerController.h b/src/components/timer/TimerController.h
new file mode 100644
index 00000000..fa7bc90d
--- /dev/null
+++ b/src/components/timer/TimerController.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <cstdint>
+#include "app_timer.h"
+#include "portmacro_cmsis.h"
+
+namespace Pinetime {
+ namespace System {
+ class SystemTask;
+ }
+ namespace Controllers {
+
+ class TimerController {
+ public:
+ TimerController() = default;
+
+ void Init();
+
+ void StartTimer(uint32_t duration);
+
+ void StopTimer();
+
+ uint32_t GetTimeRemaining();
+
+ bool IsRunning();
+
+ void OnTimerEnd();
+
+ void Register(System::SystemTask* systemTask);
+
+ private:
+ System::SystemTask* systemTask = nullptr;
+ TickType_t endTicks;
+ bool timerRunning = false;
+ };
+ }
+} \ No newline at end of file