diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4744eaef..bd24359a 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -9,13 +9,13 @@ name: Build PineTime Firmware
# When to run this Workflow...
on:
- # Run this Workflow when files are updated (Pushed) in the "master" Branch
+ # Run this Workflow when files are updated (Pushed) in the "master" and "develop" Branch
push:
- branches: [ master ]
+ branches: [ master, develop ]
- # Also run this Workflow when a Pull Request is created or updated in the "master" Branch
+ # Also run this Workflow when a Pull Request is created or updated in the "master" and "develop" Branch
pull_request:
- branches: [ master ]
+ branches: [ master, develop ]
# Steps to run for the Workflow
jobs:
diff --git a/.gitignore b/.gitignore
index 6de76a9e..39fb672b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,4 +38,8 @@ Testing/Temporary/
**/.DS_Store
# Windows
-**/thumbs.db
\ No newline at end of file
+**/thumbs.db
+
+#VSCODE
+.vscode/.cortex-debug.registers.state.json
+.vscode/.cortex-debug.peripherals.state.json
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8f0e63f4..f1cc3a81 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,5 +5,55 @@
"-DNRF5_SDK_PATH=${env:NRF5_SDK_PATH}",
],
"cmake.generator": "Unix Makefiles",
- "clang-tidy.buildPath": "build/compile_commands.json"
+ "clang-tidy.buildPath": "build/compile_commands.json",
+ "files.associations": {
+ "array": "cpp",
+ "atomic": "cpp",
+ "bit": "cpp",
+ "*.tcc": "cpp",
+ "bitset": "cpp",
+ "cctype": "cpp",
+ "chrono": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdint": "cpp",
+ "cstdio": "cpp",
+ "cstdlib": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "deque": "cpp",
+ "unordered_map": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "functional": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "optional": "cpp",
+ "random": "cpp",
+ "ratio": "cpp",
+ "string": "cpp",
+ "string_view": "cpp",
+ "system_error": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "utility": "cpp",
+ "fstream": "cpp",
+ "initializer_list": "cpp",
+ "iosfwd": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "new": "cpp",
+ "ostream": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "streambuf": "cpp",
+ "cinttypes": "cpp",
+ "typeinfo": "cpp"
+ }
}
diff --git a/gcc_nrf52-mcuboot.ld b/gcc_nrf52-mcuboot.ld
index 0746f491..81b318c5 100644
--- a/gcc_nrf52-mcuboot.ld
+++ b/gcc_nrf52-mcuboot.ld
@@ -6,12 +6,18 @@ GROUP(-lgcc -lc -lnosys)
MEMORY
{
FLASH (rx) : ORIGIN = 0x08020, LENGTH = 0x78000
- RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000
+ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
-}
+ .noinit(NOLOAD):
+ {
+ PROVIDE(__start_noinit_data = .);
+ *(.noinit)
+ PROVIDE(__stop_noinit_data = .);
+ } > RAM
+} INSERT AFTER .bss
SECTIONS
{
diff --git a/gcc_nrf52.ld b/gcc_nrf52.ld
index 98e133aa..f9bc5b68 100644
--- a/gcc_nrf52.ld
+++ b/gcc_nrf52.ld
@@ -6,12 +6,18 @@ GROUP(-lgcc -lc -lnosys)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000, LENGTH = 0x78000
- RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000
+ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
-}
+ .noinit(NOLOAD):
+ {
+ PROVIDE(__start_noinit_data = .);
+ *(.noinit)
+ PROVIDE(__stop_noinit_data = .);
+ } > RAM
+} INSERT AFTER .bss
SECTIONS
{
@@ -44,6 +50,7 @@ SECTIONS
PROVIDE(__stop_log_filter_data = .);
} > RAM
+
} INSERT AFTER .data;
SECTIONS
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4dd6c8d2..255bd1ac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -422,6 +422,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/Steps.cpp
displayapp/screens/Timer.cpp
displayapp/screens/Error.cpp
+ displayapp/screens/Alarm.cpp
displayapp/Colors.cpp
## Settings
@@ -478,6 +479,7 @@ list(APPEND SOURCE_FILES
components/motor/MotorController.cpp
components/settings/Settings.cpp
components/timer/TimerController.cpp
+ components/alarm/AlarmController.cpp
components/fs/FS.cpp
drivers/Cst816s.cpp
FreeRTOS/port.c
@@ -544,6 +546,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/firmwarevalidator/FirmwareValidator.cpp
components/settings/Settings.cpp
components/timer/TimerController.cpp
+ components/alarm/AlarmController.cpp
drivers/Cst816s.cpp
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
@@ -616,6 +619,7 @@ set(INCLUDE_FILES
displayapp/screens/Metronome.h
displayapp/screens/Motion.h
displayapp/screens/Timer.h
+ displayapp/screens/Alarm.h
displayapp/Colors.h
drivers/St7789.h
drivers/SpiNorFlash.h
@@ -648,6 +652,7 @@ set(INCLUDE_FILES
components/ble/HeartRateService.h
components/settings/Settings.h
components/timer/TimerController.h
+ components/alarm/AlarmController.h
drivers/Cst816s.h
FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h
diff --git a/src/components/alarm/AlarmController.cpp b/src/components/alarm/AlarmController.cpp
new file mode 100644
index 00000000..67ca05a9
--- /dev/null
+++ b/src/components/alarm/AlarmController.cpp
@@ -0,0 +1,114 @@
+/* Copyright (C) 2021 mruss77, Florian
+
+ This file is part of InfiniTime.
+
+ InfiniTime 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ InfiniTime 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 .
+*/
+#include "AlarmController.h"
+#include "systemtask/SystemTask.h"
+#include "app_timer.h"
+#include "task.h"
+#include
+
+using namespace Pinetime::Controllers;
+using namespace std::chrono_literals;
+
+AlarmController::AlarmController(Controllers::DateTime& dateTimeController) : dateTimeController {dateTimeController} {
+}
+
+APP_TIMER_DEF(alarmAppTimer);
+
+namespace {
+ void SetOffAlarm(void* p_context) {
+ auto* controller = static_cast(p_context);
+ if (controller != nullptr) {
+ controller->SetOffAlarmNow();
+ }
+ }
+}
+
+void AlarmController::Init(System::SystemTask* systemTask) {
+ app_timer_create(&alarmAppTimer, APP_TIMER_MODE_SINGLE_SHOT, SetOffAlarm);
+ this->systemTask = systemTask;
+}
+
+void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) {
+ hours = alarmHr;
+ minutes = alarmMin;
+}
+
+void AlarmController::ScheduleAlarm() {
+ // Determine the next time the alarm needs to go off and set the app_timer
+ app_timer_stop(alarmAppTimer);
+
+ auto now = dateTimeController.CurrentDateTime();
+ alarmTime = now;
+ time_t ttAlarmTime = std::chrono::system_clock::to_time_t(alarmTime);
+ tm* tmAlarmTime = std::localtime(&ttAlarmTime);
+
+ // If the time being set has already passed today,the alarm should be set for tomorrow
+ if (hours < dateTimeController.Hours() || (hours == dateTimeController.Hours() && minutes <= dateTimeController.Minutes())) {
+ tmAlarmTime->tm_mday += 1;
+ // tm_wday doesn't update automatically
+ tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
+ }
+
+ tmAlarmTime->tm_hour = hours;
+ tmAlarmTime->tm_min = minutes;
+ tmAlarmTime->tm_sec = 0;
+
+ // if alarm is in weekday-only mode, make sure it shifts to the next weekday
+ if (recurrence == RecurType::Weekdays) {
+ if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day
+ tmAlarmTime->tm_mday += 1;
+ } else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
+ tmAlarmTime->tm_mday += 2;
+ }
+ }
+ tmAlarmTime->tm_isdst = -1; // use system timezone setting to determine DST
+
+ // now can convert back to a time_point
+ alarmTime = std::chrono::system_clock::from_time_t(std::mktime(tmAlarmTime));
+ auto mSecToAlarm = std::chrono::duration_cast(alarmTime - now).count();
+ app_timer_start(alarmAppTimer, APP_TIMER_TICKS(mSecToAlarm), this);
+
+ state = AlarmState::Set;
+}
+
+uint32_t AlarmController::SecondsToAlarm() {
+ return std::chrono::duration_cast(alarmTime - dateTimeController.CurrentDateTime()).count();
+}
+
+void AlarmController::DisableAlarm() {
+ app_timer_stop(alarmAppTimer);
+ state = AlarmState::Not_Set;
+}
+
+void AlarmController::SetOffAlarmNow() {
+ state = AlarmState::Alerting;
+ systemTask->PushMessage(System::Messages::SetOffAlarm);
+}
+
+void AlarmController::StopAlerting() {
+ systemTask->PushMessage(System::Messages::StopRinging);
+
+ // Alarm state is off unless this is a recurring alarm
+ if (recurrence == RecurType::None) {
+ state = AlarmState::Not_Set;
+ } else {
+ state = AlarmState::Set;
+ // set next instance
+ ScheduleAlarm();
+ }
+}
diff --git a/src/components/alarm/AlarmController.h b/src/components/alarm/AlarmController.h
new file mode 100644
index 00000000..bf85d431
--- /dev/null
+++ b/src/components/alarm/AlarmController.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2021 mruss77, Florian
+
+ This file is part of InfiniTime.
+
+ InfiniTime 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ InfiniTime 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 .
+*/
+#pragma once
+
+#include
+#include "app_timer.h"
+#include "components/datetime/DateTimeController.h"
+
+namespace Pinetime {
+ namespace System {
+ class SystemTask;
+ }
+ namespace Controllers {
+ class AlarmController {
+ public:
+ AlarmController(Controllers::DateTime& dateTimeController);
+
+ void Init(System::SystemTask* systemTask);
+ void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
+ void ScheduleAlarm();
+ void DisableAlarm();
+ void SetOffAlarmNow();
+ uint32_t SecondsToAlarm();
+ void StopAlerting();
+ enum class AlarmState { Not_Set, Set, Alerting };
+ enum class RecurType { None, Daily, Weekdays };
+ uint8_t Hours() const {
+ return hours;
+ }
+ uint8_t Minutes() const {
+ return minutes;
+ }
+ AlarmState State() const {
+ return state;
+ }
+ RecurType Recurrence() const {
+ return recurrence;
+ }
+ void SetRecurrence(RecurType recurType) {
+ recurrence = recurType;
+ }
+
+ private:
+ Controllers::DateTime& dateTimeController;
+ System::SystemTask* systemTask = nullptr;
+ uint8_t hours = 7;
+ uint8_t minutes = 0;
+ std::chrono::time_point alarmTime;
+ AlarmState state = AlarmState::Not_Set;
+ RecurType recurrence = RecurType::None;
+ };
+ }
+}
diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp
index d6aa83c8..6e426825 100644
--- a/src/components/datetime/DateTimeController.cpp
+++ b/src/components/datetime/DateTimeController.cpp
@@ -5,6 +5,10 @@
using namespace Pinetime::Controllers;
+void DateTime::SetCurrentTime(std::chrono::time_point t) {
+ this->currentDateTime = t;
+}
+
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 = {
@@ -67,7 +71,7 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
// Notify new day to SystemTask
if (hour == 0 and not isMidnightAlreadyNotified) {
isMidnightAlreadyNotified = true;
- if(systemTask != nullptr)
+ if (systemTask != nullptr)
systemTask->PushMessage(System::Messages::OnNewDay);
} else if (hour != 0) {
isMidnightAlreadyNotified = false;
diff --git a/src/components/datetime/DateTimeController.h b/src/components/datetime/DateTimeController.h
index 265d6e9d..061c303f 100644
--- a/src/components/datetime/DateTimeController.h
+++ b/src/components/datetime/DateTimeController.h
@@ -74,6 +74,7 @@ namespace Pinetime {
}
void Register(System::SystemTask* systemTask);
+ void SetCurrentTime(std::chrono::time_point t);
private:
uint16_t year = 0;
diff --git a/src/components/motor/MotorController.h b/src/components/motor/MotorController.h
index 1d8fcc95..cf78088e 100644
--- a/src/components/motor/MotorController.h
+++ b/src/components/motor/MotorController.h
@@ -1,7 +1,6 @@
#pragma once
#include
-#include "app_timer.h"
#include "components/settings/Settings.h"
namespace Pinetime {
diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h
index 888f7c92..b14fe687 100644
--- a/src/displayapp/Apps.h
+++ b/src/displayapp/Apps.h
@@ -12,6 +12,7 @@ namespace Pinetime {
NotificationsPreview,
Notifications,
Timer,
+ Alarm,
FlashLight,
BatteryInfo,
Music,
diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp
index e058a124..68e24e4b 100644
--- a/src/displayapp/DisplayApp.cpp
+++ b/src/displayapp/DisplayApp.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/datetime/DateTimeController.h"
@@ -91,6 +92,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController,
+ Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::TouchHandler& touchHandler)
: lcd {lcd},
lvgl {lvgl},
@@ -105,6 +107,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
motorController {motorController},
motionController {motionController},
timerController {timerController},
+ alarmController {alarmController},
touchHandler {touchHandler} {
}
@@ -208,6 +211,13 @@ void DisplayApp::Refresh() {
LoadApp(Apps::Timer, DisplayApp::FullRefreshDirections::Down);
}
break;
+ case Messages::AlarmTriggered:
+ if (currentApp == Apps::Alarm) {
+ auto* alarm = static_cast(currentScreen.get());
+ alarm->SetAlerting();
+ } else {
+ LoadApp(Apps::Alarm, DisplayApp::FullRefreshDirections::None);
+ }
case Messages::TouchEvent: {
if (state != States::Running) {
break;
@@ -345,6 +355,9 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
case Apps::Timer:
currentScreen = std::make_unique(this, timerController);
break;
+ case Apps::Alarm:
+ currentScreen = std::make_unique(this, alarmController);
+ break;
// Settings
case Apps::QuickSettings:
diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h
index f9d75690..93164869 100644
--- a/src/displayapp/DisplayApp.h
+++ b/src/displayapp/DisplayApp.h
@@ -14,7 +14,9 @@
#include "components/settings/Settings.h"
#include "displayapp/screens/Screen.h"
#include "components/timer/TimerController.h"
+#include "components/alarm/AlarmController.h"
#include "touchhandler/TouchHandler.h"
+
#include "Messages.h"
#include "BootErrors.h"
@@ -58,6 +60,7 @@ namespace Pinetime {
Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController,
+ Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::TouchHandler& touchHandler);
void Start(System::BootErrors error);
void PushMessage(Display::Messages msg);
@@ -83,6 +86,7 @@ namespace Pinetime {
Pinetime::Controllers::MotorController& motorController;
Pinetime::Controllers::MotionController& motionController;
Pinetime::Controllers::TimerController& timerController;
+ Pinetime::Controllers::AlarmController& alarmController;
Pinetime::Controllers::TouchHandler& touchHandler;
Pinetime::Controllers::FirmwareValidator validator;
diff --git a/src/displayapp/DisplayAppRecovery.cpp b/src/displayapp/DisplayAppRecovery.cpp
index 7a202629..a42d81a2 100644
--- a/src/displayapp/DisplayAppRecovery.cpp
+++ b/src/displayapp/DisplayAppRecovery.cpp
@@ -22,6 +22,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController,
+ Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::TouchHandler& touchHandler)
: lcd {lcd}, bleController {bleController} {
diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h
index 4184ea49..425e0aca 100644
--- a/src/displayapp/DisplayAppRecovery.h
+++ b/src/displayapp/DisplayAppRecovery.h
@@ -32,6 +32,7 @@ namespace Pinetime {
class TouchHandler;
class MotorController;
class TimerController;
+ class AlarmController;
}
namespace System {
@@ -54,6 +55,7 @@ namespace Pinetime {
Pinetime::Controllers::MotorController& motorController,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::TimerController& timerController,
+ Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::TouchHandler& touchHandler);
void Start();
void PushMessage(Pinetime::Applications::Display::Messages msg);
diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h
index 8e4884db..d48b646f 100644
--- a/src/displayapp/Messages.h
+++ b/src/displayapp/Messages.h
@@ -14,7 +14,8 @@ namespace Pinetime {
BleFirmwareUpdateStarted,
UpdateTimeOut,
DimScreen,
- RestoreBrightness
+ RestoreBrightness,
+ AlarmTriggered
};
}
}
diff --git a/src/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp
new file mode 100644
index 00000000..959cb0b2
--- /dev/null
+++ b/src/displayapp/screens/Alarm.cpp
@@ -0,0 +1,254 @@
+/* Copyright (C) 2021 mruss77, Florian
+
+ This file is part of InfiniTime.
+
+ InfiniTime 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ InfiniTime 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 .
+*/
+#include "Alarm.h"
+#include "Screen.h"
+#include "Symbols.h"
+
+using namespace Pinetime::Applications::Screens;
+using Pinetime::Controllers::AlarmController;
+
+static void btnEventHandler(lv_obj_t* obj, lv_event_t event) {
+ Alarm* screen = static_cast(obj->user_data);
+ screen->OnButtonEvent(obj, event);
+}
+
+Alarm::Alarm(DisplayApp* app, Controllers::AlarmController& alarmController)
+ : Screen(app), running {true}, alarmController {alarmController} {
+
+ 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);
+ lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
+
+ alarmHours = alarmController.Hours();
+ alarmMinutes = alarmController.Minutes();
+ lv_label_set_text_fmt(time, "%02lu:%02lu", alarmHours, alarmMinutes);
+
+ lv_obj_align(time, lv_scr_act(), LV_ALIGN_CENTER, 0, -25);
+
+ btnHoursUp = lv_btn_create(lv_scr_act(), nullptr);
+ btnHoursUp->user_data = this;
+ lv_obj_set_event_cb(btnHoursUp, btnEventHandler);
+ lv_obj_set_size(btnHoursUp, 60, 40);
+ lv_obj_align(btnHoursUp, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 20, -85);
+ txtHrUp = lv_label_create(btnHoursUp, nullptr);
+ lv_label_set_text_static(txtHrUp, "+");
+
+ btnHoursDown = lv_btn_create(lv_scr_act(), nullptr);
+ btnHoursDown->user_data = this;
+ lv_obj_set_event_cb(btnHoursDown, btnEventHandler);
+ lv_obj_set_size(btnHoursDown, 60, 40);
+ lv_obj_align(btnHoursDown, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 20, 35);
+ txtHrDown = lv_label_create(btnHoursDown, nullptr);
+ lv_label_set_text_static(txtHrDown, "-");
+
+ btnMinutesUp = lv_btn_create(lv_scr_act(), nullptr);
+ btnMinutesUp->user_data = this;
+ lv_obj_set_event_cb(btnMinutesUp, btnEventHandler);
+ lv_obj_set_size(btnMinutesUp, 60, 40);
+ lv_obj_align(btnMinutesUp, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -20, -85);
+ txtMinUp = lv_label_create(btnMinutesUp, nullptr);
+ lv_label_set_text_static(txtMinUp, "+");
+
+ btnMinutesDown = lv_btn_create(lv_scr_act(), nullptr);
+ btnMinutesDown->user_data = this;
+ lv_obj_set_event_cb(btnMinutesDown, btnEventHandler);
+ lv_obj_set_size(btnMinutesDown, 60, 40);
+ lv_obj_align(btnMinutesDown, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -20, 35);
+ txtMinDown = lv_label_create(btnMinutesDown, nullptr);
+ lv_label_set_text_static(txtMinDown, "-");
+
+ btnEnable = lv_btn_create(lv_scr_act(), nullptr);
+ btnEnable->user_data = this;
+ lv_obj_set_event_cb(btnEnable, btnEventHandler);
+ lv_obj_set_size(btnEnable, 115, 50);
+ lv_obj_align(btnEnable, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+ txtEnable = lv_label_create(btnEnable, nullptr);
+ SetEnableButtonState();
+
+ btnRecur = lv_btn_create(lv_scr_act(), nullptr);
+ btnRecur->user_data = this;
+ lv_obj_set_event_cb(btnRecur, btnEventHandler);
+ lv_obj_set_size(btnRecur, 115, 50);
+ lv_obj_align(btnRecur, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
+ txtRecur = lv_label_create(btnRecur, nullptr);
+ SetRecurButtonState();
+
+ btnInfo = lv_btn_create(lv_scr_act(), nullptr);
+ btnInfo->user_data = this;
+ lv_obj_set_event_cb(btnInfo, btnEventHandler);
+ lv_obj_set_size(btnInfo, 50, 40);
+ lv_obj_align(btnInfo, lv_scr_act(), LV_ALIGN_CENTER, 0, -85);
+ txtInfo = lv_label_create(btnInfo, nullptr);
+ lv_label_set_text_static(txtInfo, "i");
+}
+
+Alarm::~Alarm() {
+ lv_obj_clean(lv_scr_act());
+}
+
+void Alarm::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
+ using Pinetime::Controllers::AlarmController;
+ if (event == LV_EVENT_CLICKED) {
+ if (obj == btnEnable) {
+ if (alarmController.State() == AlarmController::AlarmState::Alerting) {
+ alarmController.StopAlerting();
+ } else if (alarmController.State() == AlarmController::AlarmState::Set) {
+ alarmController.DisableAlarm();
+ } else {
+ alarmController.ScheduleAlarm();
+ }
+ SetEnableButtonState();
+ return;
+ }
+ if (obj == btnInfo) {
+ ShowInfo();
+ return;
+ }
+ if (obj == btnMessage) {
+ lv_obj_del(txtMessage);
+ lv_obj_del(btnMessage);
+ txtMessage = nullptr;
+ btnMessage = nullptr;
+ return;
+ }
+ // If any other button was pressed, disable the alarm
+ // this is to make it clear that the alarm won't be set until it is turned back on
+ if (alarmController.State() == AlarmController::AlarmState::Set) {
+ alarmController.DisableAlarm();
+ SetEnableButtonState();
+ }
+ if (obj == btnMinutesUp) {
+ if (alarmMinutes >= 59) {
+ alarmMinutes = 0;
+ } else {
+ alarmMinutes++;
+ }
+ UpdateAlarmTime();
+ return;
+ }
+ if (obj == btnMinutesDown) {
+ if (alarmMinutes == 0) {
+ alarmMinutes = 59;
+ } else {
+ alarmMinutes--;
+ }
+ UpdateAlarmTime();
+ return;
+ }
+ if (obj == btnHoursUp) {
+ if (alarmHours >= 23) {
+ alarmHours = 0;
+ } else {
+ alarmHours++;
+ }
+ UpdateAlarmTime();
+ return;
+ }
+ if (obj == btnHoursDown) {
+ if (alarmHours == 0) {
+ alarmHours = 23;
+ } else {
+ alarmHours--;
+ }
+ UpdateAlarmTime();
+ return;
+ }
+ if (obj == btnRecur) {
+ ToggleRecurrence();
+ }
+ }
+}
+
+void Alarm::UpdateAlarmTime() {
+ lv_label_set_text_fmt(time, "%02d:%02d", alarmHours, alarmMinutes);
+ alarmController.SetAlarmTime(alarmHours, alarmMinutes);
+}
+
+void Alarm::SetAlerting() {
+ SetEnableButtonState();
+}
+
+void Alarm::SetEnableButtonState() {
+ switch (alarmController.State()) {
+ case AlarmController::AlarmState::Set:
+ lv_label_set_text(txtEnable, "ON");
+ lv_obj_set_style_local_bg_color(btnEnable, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
+ break;
+ case AlarmController::AlarmState::Not_Set:
+ lv_label_set_text(txtEnable, "OFF");
+ lv_obj_set_style_local_bg_color(btnEnable, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
+ break;
+ case AlarmController::AlarmState::Alerting:
+ lv_label_set_text(txtEnable, Symbols::stop);
+ lv_obj_set_style_local_bg_color(btnEnable, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
+ }
+}
+
+void Alarm::ShowInfo() {
+ btnMessage = lv_btn_create(lv_scr_act(), nullptr);
+ btnMessage->user_data = this;
+ lv_obj_set_event_cb(btnMessage, btnEventHandler);
+ lv_obj_set_height(btnMessage, 200);
+ lv_obj_set_width(btnMessage, 150);
+ lv_obj_align(btnMessage, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
+ txtMessage = lv_label_create(btnMessage, nullptr);
+ lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
+
+ if (alarmController.State() == AlarmController::AlarmState::Set) {
+ auto timeToAlarm = alarmController.SecondsToAlarm();
+
+ auto daysToAlarm = timeToAlarm / 86400;
+ auto hrsToAlarm = (timeToAlarm % 86400) / 3600;
+ auto minToAlarm = (timeToAlarm % 3600) / 60;
+ auto secToAlarm = timeToAlarm % 60;
+
+ lv_label_set_text_fmt(
+ txtMessage, "Time to\nalarm:\n%2d Days\n%2d Hours\n%2d Minutes\n%2d Seconds", daysToAlarm, hrsToAlarm, minToAlarm, secToAlarm);
+ } else {
+ lv_label_set_text(txtMessage, "Alarm\nis not\nset.");
+ }
+}
+
+void Alarm::SetRecurButtonState() {
+ using Pinetime::Controllers::AlarmController;
+ switch (alarmController.Recurrence()) {
+ case AlarmController::RecurType::None:
+ lv_label_set_text(txtRecur, "ONCE");
+ break;
+ case AlarmController::RecurType::Daily:
+ lv_label_set_text(txtRecur, "DAILY");
+ break;
+ case AlarmController::RecurType::Weekdays:
+ lv_label_set_text(txtRecur, "MON-FRI");
+ }
+}
+
+void Alarm::ToggleRecurrence() {
+ using Pinetime::Controllers::AlarmController;
+ switch (alarmController.Recurrence()) {
+ case AlarmController::RecurType::None:
+ alarmController.SetRecurrence(AlarmController::RecurType::Daily);
+ break;
+ case AlarmController::RecurType::Daily:
+ alarmController.SetRecurrence(AlarmController::RecurType::Weekdays);
+ break;
+ case AlarmController::RecurType::Weekdays:
+ alarmController.SetRecurrence(AlarmController::RecurType::None);
+ }
+ SetRecurButtonState();
+}
diff --git a/src/displayapp/screens/Alarm.h b/src/displayapp/screens/Alarm.h
new file mode 100644
index 00000000..abf97eba
--- /dev/null
+++ b/src/displayapp/screens/Alarm.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2021 mruss77, Florian
+
+ This file is part of InfiniTime.
+
+ InfiniTime 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ InfiniTime 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 .
+*/
+#pragma once
+
+#include "Screen.h"
+#include "systemtask/SystemTask.h"
+#include "../LittleVgl.h"
+#include "components/alarm/AlarmController.h"
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class Alarm : public Screen {
+ public:
+ Alarm(DisplayApp* app, Controllers::AlarmController& alarmController);
+ ~Alarm() override;
+ void SetAlerting();
+ void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
+
+ private:
+ bool running;
+ uint8_t alarmHours;
+ uint8_t alarmMinutes;
+ Controllers::AlarmController& alarmController;
+
+ lv_obj_t *time, *btnEnable, *txtEnable, *btnMinutesUp, *btnMinutesDown, *btnHoursUp, *btnHoursDown, *txtMinUp, *txtMinDown,
+ *txtHrUp, *txtHrDown, *btnRecur, *txtRecur, *btnMessage, *txtMessage, *btnInfo, *txtInfo;
+
+ enum class EnableButtonState { On, Off, Alerting };
+ void SetEnableButtonState();
+ void SetRecurButtonState();
+ void SetAlarm();
+ void ShowInfo();
+ void ToggleRecurrence();
+ void UpdateAlarmTime();
+ };
+ };
+ };
+}
diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp
index 6e7bbb74..5c582f60 100644
--- a/src/displayapp/screens/ApplicationList.cpp
+++ b/src/displayapp/screens/ApplicationList.cpp
@@ -58,7 +58,7 @@ std::unique_ptr ApplicationList::CreateScreen2() {
{"2", Apps::Twos},
{Symbols::chartLine, Apps::Motion},
{Symbols::drum, Apps::Metronome},
- {"", Apps::None},
+ {Symbols::clock, Apps::Alarm},
}};
return std::make_unique(1, 2, app, settingsController, batteryController, dateTimeController, applications);
diff --git a/src/main.cpp b/src/main.cpp
index 0d4ba42f..e14d03a2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -108,13 +108,13 @@ Pinetime::Drivers::WatchdogView watchdogView(watchdog);
Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::TimerController timerController;
+Pinetime::Controllers::AlarmController alarmController {dateTimeController};
Pinetime::Controllers::TouchHandler touchHandler(touchPanel, lvgl);
Pinetime::Controllers::FS fs {spiNorFlash};
Pinetime::Controllers::Settings settingsController {fs};
Pinetime::Controllers::MotorController motorController {settingsController};
-
Pinetime::Applications::DisplayApp displayApp(lcd,
lvgl,
touchPanel,
@@ -128,6 +128,7 @@ Pinetime::Applications::DisplayApp displayApp(lcd,
motorController,
motionController,
timerController,
+ alarmController,
touchHandler);
Pinetime::System::SystemTask systemTask(spi,
@@ -140,6 +141,7 @@ Pinetime::System::SystemTask systemTask(spi,
bleController,
dateTimeController,
timerController,
+ alarmController,
watchdog,
notificationManager,
motorController,
@@ -153,6 +155,16 @@ Pinetime::System::SystemTask systemTask(spi,
fs,
touchHandler);
+/* Variable Declarations for variables in noinit SRAM
+ Increment NoInit_MagicValue upon adding variables to this area
+*/
+extern uint32_t __start_noinit_data;
+extern uint32_t __stop_noinit_data;
+static constexpr uint32_t NoInit_MagicValue = 0xDEAD0000;
+uint32_t NoInit_MagicWord __attribute__((section(".noinit")));
+std::chrono::time_point NoInit_BackUpTime __attribute__((section(".noinit")));
+
+
void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
if (pin == Pinetime::PinMap::Cst816sIrq) {
systemTask.OnTouchEvent();
@@ -313,6 +325,15 @@ int main(void) {
// retrieve version stored by bootloader
Pinetime::BootloaderVersion::SetVersion(NRF_TIMER2->CC[0]);
+
+ if (NoInit_MagicWord == NoInit_MagicValue) {
+ dateTimeController.SetCurrentTime(NoInit_BackUpTime);
+ } else {
+ //Clear Memory to known state
+ memset(&__start_noinit_data,0,(uintptr_t)&__stop_noinit_data-(uintptr_t)&__start_noinit_data);
+ NoInit_MagicWord = NoInit_MagicValue;
+ }
+
lvgl.Init();
systemTask.Start();
diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h
index ebe6a2b1..bd1de234 100644
--- a/src/systemtask/Messages.h
+++ b/src/systemtask/Messages.h
@@ -21,6 +21,8 @@ namespace Pinetime {
DisableSleeping,
OnNewDay,
OnChargingEvent,
+ SetOffAlarm,
+ StopRinging,
MeasureBatteryTimerExpired,
BatteryMeasurementDone,
};
diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp
index a2c92b47..1bfff911 100644
--- a/src/systemtask/SystemTask.cpp
+++ b/src/systemtask/SystemTask.cpp
@@ -65,6 +65,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Controllers::TimerController& timerController,
+ Controllers::AlarmController& alarmController,
Drivers::Watchdog& watchdog,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::MotorController& motorController,
@@ -87,6 +88,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
bleController {bleController},
dateTimeController {dateTimeController},
timerController {timerController},
+ alarmController {alarmController},
watchdog {watchdog},
notificationManager {notificationManager},
motorController {motorController},
@@ -144,6 +146,7 @@ void SystemTask::Work() {
motionSensor.SoftReset();
timerController.Register(this);
timerController.Init();
+ alarmController.Init(this);
// Reset the TWI device because the motion sensor chip most probably crashed it...
twiMaster.Sleep();
@@ -283,6 +286,16 @@ void SystemTask::Work() {
motorController.RunForDuration(35);
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TimerDone);
break;
+ case Messages::SetOffAlarm:
+ if (isSleeping && !isWakingUp) {
+ GoToRunning();
+ }
+ motorController.StartRinging();
+ displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
+ break;
+ case Messages::StopRinging:
+ motorController.StopRinging();
+ break;
case Messages::BleConnected:
ReloadIdleTimer();
isBleDiscoveryTimerRunning = true;
@@ -368,6 +381,7 @@ void SystemTask::Work() {
monitor.Process();
uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG);
dateTimeController.UpdateTime(systick_counter);
+ NoInit_BackUpTime = dateTimeController.CurrentDateTime();
if (!nrf_gpio_pin_read(PinMap::Button))
watchdog.Kick();
}
diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h
index 1f8be75f..1fcfeb8a 100644
--- a/src/systemtask/SystemTask.h
+++ b/src/systemtask/SystemTask.h
@@ -17,6 +17,7 @@
#include "components/ble/NotificationManager.h"
#include "components/motor/MotorController.h"
#include "components/timer/TimerController.h"
+#include "components/alarm/AlarmController.h"
#include "components/fs/FS.h"
#include "touchhandler/TouchHandler.h"
@@ -32,6 +33,7 @@
#include "drivers/Watchdog.h"
#include "Messages.h"
+extern std::chrono::time_point NoInit_BackUpTime;
namespace Pinetime {
namespace Drivers {
class Cst816S;
@@ -57,6 +59,7 @@ namespace Pinetime {
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Controllers::TimerController& timerController,
+ Controllers::AlarmController& alarmController,
Drivers::Watchdog& watchdog,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::MotorController& motorController,
@@ -101,6 +104,7 @@ namespace Pinetime {
Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Controllers::TimerController& timerController;
+ Pinetime::Controllers::AlarmController& alarmController;
QueueHandle_t systemTasksMsgQueue;
std::atomic isSleeping {false};
std::atomic isGoingToSleep {false};