summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorJF <jf@codingfield.com>2022-04-02 14:34:53 (GMT)
committerGitea <gitea@fake.local>2022-04-02 14:34:53 (GMT)
commit187ea0f06d93c7f7df5779cb321a28ad040234ee (patch)
tree3d6d1b2f60573045734153d975e9b0aa1b327394 /src/components
parentadc7909c9823c5cd9fc9888a84e84f9182b9088f (diff)
parentb498e1d633522eed975d78b04508834b7a79befe (diff)
Merge branch 'develop' of JF/PineTime into master
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ble/AlertNotificationClient.cpp1
-rw-r--r--src/components/ble/BleController.cpp16
-rw-r--r--src/components/ble/BleController.h9
-rw-r--r--src/components/ble/DfuService.cpp1
-rw-r--r--src/components/ble/HeartRateService.cpp1
-rw-r--r--src/components/ble/MotionService.cpp1
-rw-r--r--src/components/ble/MusicService.cpp21
-rw-r--r--src/components/ble/NimbleController.cpp66
-rw-r--r--src/components/ble/NimbleController.h37
-rw-r--r--src/components/datetime/DateTimeController.cpp30
-rw-r--r--src/components/datetime/DateTimeController.h9
-rw-r--r--src/components/heartrate/Ppg.h2
-rw-r--r--src/components/motor/MotorController.h2
-rw-r--r--src/components/settings/Settings.cpp4
-rw-r--r--src/components/settings/Settings.h40
15 files changed, 189 insertions, 51 deletions
diff --git a/src/components/ble/AlertNotificationClient.cpp b/src/components/ble/AlertNotificationClient.cpp
index 0f85087..335845e 100644
--- a/src/components/ble/AlertNotificationClient.cpp
+++ b/src/components/ble/AlertNotificationClient.cpp
@@ -2,6 +2,7 @@
#include <algorithm>
#include "components/ble/NotificationManager.h"
#include "systemtask/SystemTask.h"
+#include <nrf_log.h>
using namespace Pinetime::Controllers;
constexpr ble_uuid16_t AlertNotificationClient::ansServiceUuid;
diff --git a/src/components/ble/BleController.cpp b/src/components/ble/BleController.cpp
index a80c971..b6b7383 100644
--- a/src/components/ble/BleController.cpp
+++ b/src/components/ble/BleController.cpp
@@ -2,6 +2,10 @@
using namespace Pinetime::Controllers;
+bool Ble::IsConnected() const {
+ return isConnected;
+}
+
void Ble::Connect() {
isConnected = true;
}
@@ -10,6 +14,18 @@ void Ble::Disconnect() {
isConnected = false;
}
+bool Ble::IsRadioEnabled() const {
+ return isRadioEnabled;
+}
+
+void Ble::EnableRadio() {
+ isRadioEnabled = true;
+}
+
+void Ble::DisableRadio() {
+ isRadioEnabled = false;
+}
+
void Ble::StartFirmwareUpdate() {
isFirmwareUpdating = true;
}
diff --git a/src/components/ble/BleController.h b/src/components/ble/BleController.h
index 72b8766..675ede2 100644
--- a/src/components/ble/BleController.h
+++ b/src/components/ble/BleController.h
@@ -12,12 +12,14 @@ namespace Pinetime {
enum class AddressTypes { Public, Random, RPA_Public, RPA_Random };
Ble() = default;
- bool IsConnected() const {
- return isConnected;
- }
+ bool IsConnected() const;
void Connect();
void Disconnect();
+ bool IsRadioEnabled() const;
+ void EnableRadio();
+ void DisableRadio();
+
void StartFirmwareUpdate();
void StopFirmwareUpdate();
void FirmwareUpdateTotalBytes(uint32_t totalBytes);
@@ -57,6 +59,7 @@ namespace Pinetime {
private:
bool isConnected = false;
+ bool isRadioEnabled = true;
bool isFirmwareUpdating = false;
uint32_t firmwareUpdateTotalBytes = 0;
uint32_t firmwareUpdateCurrentBytes = 0;
diff --git a/src/components/ble/DfuService.cpp b/src/components/ble/DfuService.cpp
index 71dcc7e..cf99f01 100644
--- a/src/components/ble/DfuService.cpp
+++ b/src/components/ble/DfuService.cpp
@@ -3,6 +3,7 @@
#include "components/ble/BleController.h"
#include "drivers/SpiNorFlash.h"
#include "systemtask/SystemTask.h"
+#include <nrf_log.h>
using namespace Pinetime::Controllers;
diff --git a/src/components/ble/HeartRateService.cpp b/src/components/ble/HeartRateService.cpp
index f178af7..4824a6b 100644
--- a/src/components/ble/HeartRateService.cpp
+++ b/src/components/ble/HeartRateService.cpp
@@ -1,6 +1,7 @@
#include "components/ble/HeartRateService.h"
#include "components/heartrate/HeartRateController.h"
#include "systemtask/SystemTask.h"
+#include <nrf_log.h>
using namespace Pinetime::Controllers;
diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp
index 6381915..87923c2 100644
--- a/src/components/ble/MotionService.cpp
+++ b/src/components/ble/MotionService.cpp
@@ -1,6 +1,7 @@
#include "components/ble/MotionService.h"
#include "components/motion/MotionController.h"
#include "systemtask/SystemTask.h"
+#include <nrf_log.h>
using namespace Pinetime::Controllers;
diff --git a/src/components/ble/MusicService.cpp b/src/components/ble/MusicService.cpp
index 3457ce4..c99aa1e 100644
--- a/src/components/ble/MusicService.cpp
+++ b/src/components/ble/MusicService.cpp
@@ -17,6 +17,7 @@
*/
#include "components/ble/MusicService.h"
#include "systemtask/SystemTask.h"
+#include <cstring>
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
@@ -47,6 +48,8 @@ namespace {
constexpr ble_uuid128_t msRepeatCharUuid {CharUuid(0x0b, 0x00)};
constexpr ble_uuid128_t msShuffleCharUuid {CharUuid(0x0c, 0x00)};
+ constexpr uint8_t MaxStringSize {40};
+
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);
}
@@ -125,9 +128,21 @@ 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);
- char data[notifSize + 1];
- data[notifSize] = '\0';
- os_mbuf_copydata(ctxt->om, 0, notifSize, data);
+ size_t bufferSize = notifSize;
+ if (notifSize > MaxStringSize) {
+ bufferSize = MaxStringSize;
+ }
+
+ char data[bufferSize + 1];
+ os_mbuf_copydata(ctxt->om, 0, bufferSize, data);
+
+ if (notifSize > bufferSize) {
+ data[bufferSize-1] = '.';
+ data[bufferSize-2] = '.';
+ data[bufferSize-3] = '.';
+ }
+ data[bufferSize] = '\0';
+
char* s = &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
artistName = s;
diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp
index d8510bd..10eb429 100644
--- a/src/components/ble/NimbleController.cpp
+++ b/src/components/ble/NimbleController.cpp
@@ -2,6 +2,7 @@
#include <cstring>
#include <hal/nrf_rtc.h>
+#include <nrf_log.h>
#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
@@ -23,14 +24,14 @@
using namespace Pinetime::Controllers;
NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
- Pinetime::Controllers::Ble& bleController,
+ Ble& bleController,
DateTime& dateTimeController,
- Pinetime::Controllers::NotificationManager& notificationManager,
- Controllers::Battery& batteryController,
+ NotificationManager& notificationManager,
+ Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
- Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController,
- Controllers::FS& fs)
+ HeartRateController& heartRateController,
+ MotionController& motionController,
+ FS& fs)
: systemTask {systemTask},
bleController {bleController},
dateTimeController {dateTimeController},
@@ -76,6 +77,7 @@ int GAPEventCallback(struct ble_gap_event* event, void* arg) {
void NimbleController::Init() {
while (!ble_hs_synced()) {
+ vTaskDelay(10);
}
nptr = this;
@@ -184,7 +186,9 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
case BLE_GAP_EVENT_ADV_COMPLETE:
NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
NRF_LOG_INFO("reason=%d; status=%0X", event->adv_complete.reason, event->connect.status);
- StartAdvertising();
+ if (bleController.IsRadioEnabled() && !bleController.IsConnected()) {
+ StartAdvertising();
+ }
break;
case BLE_GAP_EVENT_CONNECT:
@@ -220,9 +224,11 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
currentTimeClient.Reset();
alertNotificationClient.Reset();
connectionHandle = BLE_HS_CONN_HANDLE_NONE;
- bleController.Disconnect();
- fastAdvCount = 0;
- StartAdvertising();
+ if(bleController.IsConnected()) {
+ bleController.Disconnect();
+ fastAdvCount = 0;
+ StartAdvertising();
+ }
break;
case BLE_GAP_EVENT_CONN_UPDATE:
@@ -278,7 +284,28 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
struct ble_sm_io pkey = {0};
pkey.action = event->passkey.params.action;
- pkey.passkey = ble_ll_rand() % 1000000;
+
+ /*
+ * Passkey is a 6 digits code (1'000'000 possibilities).
+ * It is important every possible value has an equal probability
+ * of getting generated. Simply applying a modulo creates a bias
+ * since 2^32 is not a multiple of 1'000'000.
+ * To prevent that, we can reject values greater than 999'999.
+ *
+ * Rejecting values would happen a lot since 2^32-1 is way greater
+ * than 1'000'000. An optimisation is to use a multiple of 1'000'000.
+ * The greatest multiple of 1'000'000 lesser than 2^32-1 is
+ * 4'294'000'000.
+ *
+ * Great explanation at:
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
+ */
+ uint32_t passkey_rand;
+ do {
+ passkey_rand = ble_ll_rand();
+ } while (passkey_rand > 4293999999);
+ pkey.passkey = passkey_rand % 1000000;
+
bleController.SetPairingKey(pkey.passkey);
systemTask.PushMessage(Pinetime::System::Messages::OnPairing);
ble_sm_inject_io(event->passkey.conn_handle, &pkey);
@@ -376,6 +403,23 @@ void NimbleController::NotifyBatteryLevel(uint8_t level) {
}
}
+void NimbleController::EnableRadio() {
+ bleController.EnableRadio();
+ bleController.Disconnect();
+ fastAdvCount = 0;
+ StartAdvertising();
+}
+
+void NimbleController::DisableRadio() {
+ bleController.DisableRadio();
+ if (bleController.IsConnected()) {
+ ble_gap_terminate(connectionHandle, BLE_ERR_REM_USER_CONN_TERM);
+ bleController.Disconnect();
+ } else {
+ ble_gap_adv_stop();
+ }
+}
+
void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
union ble_store_key key;
union ble_store_value our_sec, peer_sec, peer_cccd_set[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)] = {0};
diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h
index 2b300e6..ad19421 100644
--- a/src/components/ble/NimbleController.h
+++ b/src/components/ble/NimbleController.h
@@ -14,6 +14,7 @@
#include "components/ble/CurrentTimeService.h"
#include "components/ble/DeviceInformationService.h"
#include "components/ble/DfuService.h"
+#include "components/ble/FSService.h"
#include "components/ble/HeartRateService.h"
#include "components/ble/ImmediateAlertService.h"
#include "components/ble/MusicService.h"
@@ -22,7 +23,6 @@
#include "components/ble/MotionService.h"
#include "components/ble/weather/WeatherService.h"
#include "components/fs/FS.h"
-#include "components/ble/FSService.h"
namespace Pinetime {
namespace Drivers {
@@ -42,27 +42,17 @@ namespace Pinetime {
public:
NimbleController(Pinetime::System::SystemTask& systemTask,
- Pinetime::Controllers::Ble& bleController,
+ Ble& bleController,
DateTime& dateTimeController,
- Pinetime::Controllers::NotificationManager& notificationManager,
- Controllers::Battery& batteryController,
+ NotificationManager& notificationManager,
+ Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
- Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController,
- Pinetime::Controllers::FS& fs);
+ HeartRateController& heartRateController,
+ MotionController& motionController,
+ FS& fs);
void Init();
void StartAdvertising();
int OnGAPEvent(ble_gap_event* event);
-
- int OnDiscoveryEvent(uint16_t i, const ble_gatt_error* pError, const ble_gatt_svc* pSvc);
- int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
- int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
- int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
- int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
- const ble_gatt_error* error,
- uint16_t characteristicValueHandle,
- const ble_gatt_dsc* descriptor);
-
void StartDiscovery();
Pinetime::Controllers::MusicService& music() {
@@ -83,7 +73,10 @@ namespace Pinetime {
void RestartFastAdv() {
fastAdvCount = 0;
- }
+ };
+
+ void EnableRadio();
+ void DisableRadio();
private:
void PersistBond(struct ble_gap_conn_desc& desc);
@@ -91,12 +84,12 @@ namespace Pinetime {
static constexpr const char* deviceName = "InfiniTime";
Pinetime::System::SystemTask& systemTask;
- Pinetime::Controllers::Ble& bleController;
+ Ble& bleController;
DateTime& dateTimeController;
- Pinetime::Controllers::NotificationManager& notificationManager;
+ NotificationManager& notificationManager;
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
- Pinetime::Controllers::FS& fs;
- Pinetime::Controllers::DfuService dfuService;
+ FS& fs;
+ DfuService dfuService;
DeviceInformationService deviceInformationService;
CurrentTimeClient currentTimeClient;
diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp
index 673903c..3bfbdc7 100644
--- a/src/components/datetime/DateTimeController.cpp
+++ b/src/components/datetime/DateTimeController.cpp
@@ -11,6 +11,9 @@ namespace {
char const* MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
}
+DateTime::DateTime(Controllers::Settings& settingsController) : settingsController {settingsController} {
+}
+
void DateTime::SetCurrentTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> t) {
this->currentDateTime = t;
UpdateTime(previousSystickCounter); // Update internal state without updating the time
@@ -36,6 +39,8 @@ void DateTime::SetTime(
UpdateTime(systickCounter);
NRF_LOG_INFO("* %d %d %d ", this->hour, this->minute, this->second);
NRF_LOG_INFO("* %d %d %d ", this->day, this->month, this->year);
+
+ systemTask->PushMessage(System::Messages::OnNewTime);
}
void DateTime::UpdateTime(uint32_t systickCounter) {
@@ -103,11 +108,11 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
}
}
-const char* DateTime::MonthShortToString() {
+const char* DateTime::MonthShortToString() const {
return MonthsString[static_cast<uint8_t>(month)];
}
-const char* DateTime::DayOfWeekShortToString() {
+const char* DateTime::DayOfWeekShortToString() const {
return DaysStringShort[static_cast<uint8_t>(dayOfWeek)];
}
@@ -118,3 +123,24 @@ const char* DateTime::MonthShortToStringLow(Months month) {
void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}
+
+using ClockType = Pinetime::Controllers::Settings::ClockType;
+std::string DateTime::FormattedTime() {
+ // Return time as a string in 12- or 24-hour format
+ char buff[9];
+ if (settingsController.GetClockType() == ClockType::H12) {
+ uint8_t hour12;
+ const char* amPmStr;
+ if (hour < 12) {
+ hour12 = (hour == 0) ? 12 : hour;
+ amPmStr = "AM";
+ } else {
+ hour12 = (hour == 12) ? 12 : hour - 12;
+ amPmStr = "PM";
+ }
+ sprintf(buff, "%i:%02i %s", hour12, minute, amPmStr);
+ } else {
+ sprintf(buff, "%02i:%02i", hour, minute);
+ }
+ return std::string(buff);
+}
diff --git a/src/components/datetime/DateTimeController.h b/src/components/datetime/DateTimeController.h
index cbc8044..00bbc2e 100644
--- a/src/components/datetime/DateTimeController.h
+++ b/src/components/datetime/DateTimeController.h
@@ -2,6 +2,8 @@
#include <cstdint>
#include <chrono>
+#include <string>
+#include "components/settings/Settings.h"
namespace Pinetime {
namespace System {
@@ -10,6 +12,7 @@ namespace Pinetime {
namespace Controllers {
class DateTime {
public:
+ DateTime(Controllers::Settings& settingsController);
enum class Days : uint8_t { Unknown, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
enum class Months : uint8_t {
Unknown,
@@ -58,8 +61,8 @@ namespace Pinetime {
return second;
}
- const char* MonthShortToString();
- const char* DayOfWeekShortToString();
+ const char* MonthShortToString() const;
+ const char* DayOfWeekShortToString() const;
static const char* MonthShortToStringLow(Months month);
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
@@ -71,6 +74,7 @@ namespace Pinetime {
void Register(System::SystemTask* systemTask);
void SetCurrentTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> t);
+ std::string FormattedTime();
private:
uint16_t year = 0;
@@ -89,6 +93,7 @@ namespace Pinetime {
bool isHourAlreadyNotified = true;
bool isHalfHourAlreadyNotified = true;
System::SystemTask* systemTask = nullptr;
+ Controllers::Settings& settingsController;
};
}
}
diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h
index ed79b08..7000c87 100644
--- a/src/components/heartrate/Ppg.h
+++ b/src/components/heartrate/Ppg.h
@@ -1,6 +1,8 @@
#pragma once
#include <array>
+#include <cstddef>
+#include <cstdint>
#include "components/heartrate/Biquad.h"
#include "components/heartrate/Ptagc.h"
diff --git a/src/components/motor/MotorController.h b/src/components/motor/MotorController.h
index c9326d5..b5a592b 100644
--- a/src/components/motor/MotorController.h
+++ b/src/components/motor/MotorController.h
@@ -12,7 +12,7 @@ namespace Pinetime {
void Init();
void RunForDuration(uint8_t motorDuration);
void StartRinging();
- static void StopRinging();
+ void StopRinging();
private:
static void Ring(void* p_context);
diff --git a/src/components/settings/Settings.cpp b/src/components/settings/Settings.cpp
index ef73ad1..fee62da 100644
--- a/src/components/settings/Settings.cpp
+++ b/src/components/settings/Settings.cpp
@@ -26,7 +26,7 @@ void Settings::LoadSettingsFromFile() {
SettingsData bufferSettings;
lfs_file_t settingsFile;
- if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
+ if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDONLY) != LFS_ERR_OK) {
return;
}
fs.FileRead(&settingsFile, reinterpret_cast<uint8_t*>(&bufferSettings), sizeof(settings));
@@ -39,7 +39,7 @@ void Settings::LoadSettingsFromFile() {
void Settings::SaveSettingsToFile() {
lfs_file_t settingsFile;
- if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
+ if ( fs.FileOpen(&settingsFile, "/settings.dat", LFS_O_WRONLY | LFS_O_CREAT) != LFS_ERR_OK) {
return;
}
fs.FileWrite(&settingsFile, reinterpret_cast<uint8_t*>(&settings), sizeof(settings));
diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h
index 6de44aa..44a1a85 100644
--- a/src/components/settings/Settings.h
+++ b/src/components/settings/Settings.h
@@ -1,7 +1,6 @@
#pragma once
#include <cstdint>
#include <bitset>
-#include "components/datetime/DateTimeController.h"
#include "components/brightness/BrightnessController.h"
#include "components/fs/FS.h"
@@ -19,7 +18,23 @@ namespace Pinetime {
Shake = 3,
};
enum class Colors : uint8_t {
- White, Silver, Gray, Black, Red, Maroon, Yellow, Olive, Lime, Green, Cyan, Teal, Blue, Navy, Magenta, Purple, Orange
+ White,
+ Silver,
+ Gray,
+ Black,
+ Red,
+ Maroon,
+ Yellow,
+ Olive,
+ Lime,
+ Green,
+ Cyan,
+ Teal,
+ Blue,
+ Navy,
+ Magenta,
+ Purple,
+ Orange
};
struct PineTimeStyle {
Colors ColorTime = Colors::Teal;
@@ -171,18 +186,29 @@ namespace Pinetime {
}
settings.brightLevel = level;
};
+
Controllers::BrightnessController::Levels GetBrightness() const {
return settings.brightLevel;
};
- void SetStepsGoal( uint32_t goal ) {
- if ( goal != settings.stepsGoal ) {
+ void SetStepsGoal(uint32_t goal) {
+ if (goal != settings.stepsGoal) {
settingsChanged = true;
}
settings.stepsGoal = goal;
};
- uint32_t GetStepsGoal() const { return settings.stepsGoal; };
+ uint32_t GetStepsGoal() const {
+ return settings.stepsGoal;
+ };
+
+ void SetBleRadioEnabled(bool enabled) {
+ bleRadioEnabled = enabled;
+ };
+
+ bool GetBleRadioEnabled() const {
+ return bleRadioEnabled;
+ };
private:
Pinetime::Controllers::FS& fs;
@@ -211,6 +237,10 @@ namespace Pinetime {
uint8_t appMenu = 0;
uint8_t settingsMenu = 0;
+ /* ble state is intentionally not saved with the other watch settings and initialized
+ * to off (false) on every boot because we always want ble to be enabled on startup
+ */
+ bool bleRadioEnabled = true;
void LoadSettingsFromFile();
void SaveSettingsToFile();