From 90c66be5150f973e54e58fdcd82df86ad56a2ff6 Mon Sep 17 00:00:00 2001 From: Lewis Jackson <> Date: Thu, 1 Jun 2023 03:07:52 +0300 Subject: [PATCH] WIP refactoring --- .gitignore | 1 + src/Main.cpp | 2 +- src/WatchFace.cpp | 107 +++++-------------- src/WatchFace.h | 11 +- src/WatchFacePages/Clock.cpp | 81 ++++++++++++++ src/WatchFacePages/Clock.h | 25 +++++ src/WatchFacePages/Page.h | 15 +++ src/WatchFeatures/Battery.cpp | 29 +++++ src/WatchFeatures/Battery.h | 16 +++ src/{WatchyRTC.cpp => WatchFeatures/RTC.cpp} | 28 ++--- src/{WatchyRTC.h => WatchFeatures/RTC.h} | 12 +-- src/WatchFeatures/StepCounter.cpp | 16 +++ src/WatchFeatures/StepCounter.h | 20 ++++ src/WatchFeatures/Storage.cpp | 58 ++++++++++ src/WatchFeatures/Storage.h | 16 +++ src/WatchFeatures/WatchFeatures.h | 24 +++++ src/Watchy.cpp | 76 +++++++------ src/Watchy.h | 17 ++- src/config.h | 21 +++- 19 files changed, 413 insertions(+), 162 deletions(-) create mode 100644 src/WatchFacePages/Clock.cpp create mode 100644 src/WatchFacePages/Clock.h create mode 100644 src/WatchFacePages/Page.h create mode 100644 src/WatchFeatures/Battery.cpp create mode 100644 src/WatchFeatures/Battery.h rename src/{WatchyRTC.cpp => WatchFeatures/RTC.cpp} (87%) rename src/{WatchyRTC.h => WatchFeatures/RTC.h} (77%) create mode 100644 src/WatchFeatures/StepCounter.cpp create mode 100644 src/WatchFeatures/StepCounter.h create mode 100644 src/WatchFeatures/Storage.cpp create mode 100644 src/WatchFeatures/Storage.h create mode 100644 src/WatchFeatures/WatchFeatures.h diff --git a/.gitignore b/.gitignore index 8da6879..9be5a91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .pio .vscode/** +src/secrets.h diff --git a/src/Main.cpp b/src/Main.cpp index 4f911c7..7e1c1a9 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -5,7 +5,7 @@ WatchFace watchy; void setup() { Serial.begin(9600); - watchy.Init(); + watchy.Wake(); } void loop() diff --git a/src/WatchFace.cpp b/src/WatchFace.cpp index ed31646..2f04b4e 100644 --- a/src/WatchFace.cpp +++ b/src/WatchFace.cpp @@ -1,28 +1,34 @@ #include "WatchFace.h" -#include "SevenSegment.h" -#include "Icons.h" +#include "WatchFacePages/Clock.h" + #include -#include -#include -#include -RTC_DATA_ATTR bool WatchFace::m_menuSetup = false; RTC_DATA_ATTR bool WatchFace::m_inMenu = false; -RTC_DATA_ATTR int WatchFace::m_tzOffset = TZ_OFFSET; -Menu WatchFace::m_menu; +RTC_DATA_ATTR Menu WatchFace::m_menu; +RTC_DATA_ATTR uint8_t WatchFace::m_watchFacePage; -void WatchFace::Setup() // Called after hardware is set up +void WatchFace::InitBoot() { - if (!m_menuSetup) { - m_menu.Init(m_display); - } + m_menu.Init(m_display); + SetupVolatileMenuStuff(); + m_menu.Reset(); + ConnectWiFi(); + SyncNTPTime(); + DisconnectWiFi(); + + for (auto & page : m_pages) { + page->InitBoot(); + } +} + +void WatchFace::InitWake() +{ SetupVolatileMenuStuff(); - if (!m_menuSetup) { - m_menu.Reset(); - m_menuSetup = true; - } + for (auto & page : m_pages) { + page->InitWake(); + } } void WatchFace::HandleButtonPress(uint64_t buttonMask) @@ -86,68 +92,7 @@ void WatchFace::DrawWatchFace(bool partialRefresh) return; } - m_display.setFullWindow(); - - m_display.fillScreen(GxEPD_WHITE); - DrawBatteryIcon(); - - tmElements_t currentTime; - m_RTC.Get(currentTime, m_tzOffset); - SevenSegment sevenSegment(30, 60, 6, 5, 5); - - if (currentTime.Hour < 10) { - sevenSegment.DrawDigit(m_display, 0, 15, 75, GxEPD_BLACK); - } else { - sevenSegment.DrawDigit(m_display, currentTime.Hour / 10, 20, 75, GxEPD_BLACK); - } - - sevenSegment.DrawDigit(m_display, currentTime.Hour % 10, 60, 75, GxEPD_BLACK); - - if (currentTime.Minute < 10) { - sevenSegment.DrawDigit(m_display, 0, 110, 75, GxEPD_BLACK); - } else { - sevenSegment.DrawDigit(m_display, currentTime.Minute / 10, 110, 75, GxEPD_BLACK); - } - - sevenSegment.DrawDigit(m_display, currentTime.Minute % 10, 150, 75, GxEPD_BLACK); - - m_display.fillRect(97, 90, 5, 5, GxEPD_BLACK); - m_display.fillRect(97, 110, 5, 5, GxEPD_BLACK); - - m_display.display(partialRefresh); -} - -void WatchFace::DrawBatteryIcon() -{ - m_display.fillRect(200 - 48, 5, 43, 23, GxEPD_BLACK); - m_display.fillRect(200 - 46, 7, 39, 19, GxEPD_WHITE); - float VBAT = GetBatteryVoltage(); - float level = (VBAT - 3.6f) / 0.6f; - if (level > 1.0f) { - level = 1.0f; - } else if (level < 0.0f) { - level = 0.0f; - } - level = 0.5f - sin(asin(1.0f - 2.0f * level) / 3.0f); - - m_display.fillRect(200 - 44, 9, (int)std::round(35.0f * level), 15, GxEPD_BLACK); - - int x = 200 - 85; - int intLevel = (int)std::round(100.0f * level); - if (intLevel == 100) { - x -= 13; - } - - m_display.setFont(&FreeMonoBold9pt7b); - m_display.setCursor(x, 22); - m_display.setTextColor(GxEPD_BLACK); - m_display.print(intLevel); - m_display.print("%"); - - m_display.setFont(&FreeMonoBold12pt7b); - m_display.drawBitmap(10, 177, Icons::steps, 19, 23, GxEPD_BLACK); - m_display.setCursor(40, 195); - m_display.print(GetSteps()); + m_pages[0]->DrawPage(partialRefresh); } void WatchFace::SetupVolatileMenuStuff() @@ -269,7 +214,7 @@ void WatchFace::MenuNTPSyncSelected() ConnectWiFi(); SyncNTPTime(); DisconnectWiFi(); - m_RTC.Resync(); + m_features.rtc.Resync(); if (m_inMenu) { m_inMenu = false; @@ -280,7 +225,7 @@ void WatchFace::MenuNTPSyncSelected() void WatchFace::MenuTimeZoneSelected(int tzOffset) { - m_tzOffset = tzOffset; + m_features.storage.SetTzOffset(tzOffset); if (m_inMenu) { m_inMenu = false; @@ -291,7 +236,7 @@ void WatchFace::MenuTimeZoneSelected(int tzOffset) void WatchFace::MenuConfirmResetSteps() { - ResetSteps(); + m_features.stepCounter.ResetSteps(); if (m_inMenu) { m_inMenu = false; diff --git a/src/WatchFace.h b/src/WatchFace.h index dc4b5c5..96c331c 100644 --- a/src/WatchFace.h +++ b/src/WatchFace.h @@ -2,21 +2,26 @@ #include "Watchy.h" #include "Menu.h" +#include "WatchFacePages/Clock.h" +#include class WatchFace : public Watchy { public: - void Setup() override; // Called after hardware is set up + void InitBoot() override; // Called once when the watch starts up + void InitWake() override; // Called every time the watch wakes from sleep void HandleButtonPress(uint64_t buttonMask) override; void HandleDoubleTap() override; void HandleTilt() override; void DrawWatchFace(bool partialRefresh = false) override; private: - RTC_DATA_ATTR static bool m_menuSetup; RTC_DATA_ATTR static bool m_inMenu; RTC_DATA_ATTR static Menu m_menu; - RTC_DATA_ATTR static int m_tzOffset; + RTC_DATA_ATTR static uint8_t m_watchFacePage; + std::vector> m_pages = { + std::make_shared(m_display, m_features) + }; void SetupVolatileMenuStuff(); void DrawBatteryIcon(); diff --git a/src/WatchFacePages/Clock.cpp b/src/WatchFacePages/Clock.cpp new file mode 100644 index 0000000..4cbe910 --- /dev/null +++ b/src/WatchFacePages/Clock.cpp @@ -0,0 +1,81 @@ +#include "Clock.h" +#include "../SevenSegment.h" +#include "../Icons.h" +#include +#include +#include + +WatchFacePages::Clock::Clock(WatchyDisplay & display, WatchFeatures::WatchFeatures & features) + : m_display(display), m_features(features) +{ +} + +void WatchFacePages::Clock::InitBoot() +{ +} + +void WatchFacePages::Clock::InitWake() +{ + +} + +void WatchFacePages::Clock::DrawPage(bool partialRefresh) +{ + m_display.setFullWindow(); + + m_display.fillScreen(GxEPD_WHITE); + DrawBatteryIcon(); + + tmElements_t currentTime; + m_features.rtc.Get(currentTime); + int tzOffset = m_features.storage.GetTzOffset(); + m_features.rtc.OffsetTime(currentTime, tzOffset); + SevenSegment sevenSegment(30, 60, 6, 5, 5); + + if (currentTime.Hour < 10) { + sevenSegment.DrawDigit(m_display, 0, 15, 75, GxEPD_BLACK); + } else { + sevenSegment.DrawDigit(m_display, currentTime.Hour / 10, 20, 75, GxEPD_BLACK); + } + + sevenSegment.DrawDigit(m_display, currentTime.Hour % 10, 60, 75, GxEPD_BLACK); + + if (currentTime.Minute < 10) { + sevenSegment.DrawDigit(m_display, 0, 110, 75, GxEPD_BLACK); + } else { + sevenSegment.DrawDigit(m_display, currentTime.Minute / 10, 110, 75, GxEPD_BLACK); + } + + sevenSegment.DrawDigit(m_display, currentTime.Minute % 10, 150, 75, GxEPD_BLACK); + + m_display.fillRect(97, 90, 5, 5, GxEPD_BLACK); + m_display.fillRect(97, 110, 5, 5, GxEPD_BLACK); + + m_display.display(partialRefresh); +} + +void WatchFacePages::Clock::DrawBatteryIcon() +{ + m_display.fillRect(200 - 48, 5, 43, 23, GxEPD_BLACK); + m_display.fillRect(200 - 46, 7, 39, 19, GxEPD_WHITE); + int intLevel = m_features.battery.GetPercentage(); + uint8_t level = intLevel / 100.0f; + + m_display.fillRect(200 - 44, 9, (int)std::round(35.0f * level), 15, GxEPD_BLACK); + + int x = 200 - 85; + if (intLevel == 100) { + x -= 13; + } + + m_display.setFont(&FreeMonoBold9pt7b); + m_display.setCursor(x, 22); + m_display.setTextColor(GxEPD_BLACK); + m_display.print(intLevel); + m_display.print("%"); + + m_display.setFont(&FreeMonoBold12pt7b); + m_display.drawBitmap(10, 177, Icons::steps, 19, 23, GxEPD_BLACK); + m_display.setCursor(40, 195); + m_display.print(m_features.stepCounter.GetSteps()); +} \ No newline at end of file diff --git a/src/WatchFacePages/Clock.h b/src/WatchFacePages/Clock.h new file mode 100644 index 0000000..dcf8c5c --- /dev/null +++ b/src/WatchFacePages/Clock.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Page.h" +#include "../WatchyDisplay.h" +#include "../WatchFeatures/WatchFeatures.h" + +namespace WatchFacePages +{ + class Clock; +} + +class WatchFacePages::Clock : public WatchFacePages::Page +{ +public: + Clock(WatchyDisplay & display, WatchFeatures::WatchFeatures & features); + void InitBoot() override; + void InitWake() override; + void DrawPage(bool partialRefresh = false) override; + +private: + void DrawBatteryIcon(); + + WatchyDisplay & m_display; + WatchFeatures::WatchFeatures & m_features; +}; \ No newline at end of file diff --git a/src/WatchFacePages/Page.h b/src/WatchFacePages/Page.h new file mode 100644 index 0000000..19fba66 --- /dev/null +++ b/src/WatchFacePages/Page.h @@ -0,0 +1,15 @@ +#pragma once + +namespace WatchFacePages +{ + class Page; +} + +class WatchFacePages::Page +{ +public: + Page() {}; + virtual void InitBoot() {}; + virtual void InitWake() {}; + virtual void DrawPage(bool partialRefresh = false) = 0; +}; \ No newline at end of file diff --git a/src/WatchFeatures/Battery.cpp b/src/WatchFeatures/Battery.cpp new file mode 100644 index 0000000..2bc0813 --- /dev/null +++ b/src/WatchFeatures/Battery.cpp @@ -0,0 +1,29 @@ +#include "../config.h" +#include "Battery.h" +#include +#include + +WatchFeatures::Battery::Battery() +{ +} + +float WatchFeatures::Battery::GetVoltage() +{ + return analogReadMilliVolts(BATT_ADC_PIN) / 1000.0f * 2.0f; +} + +uint8_t WatchFeatures::Battery::GetPercentage() +{ + float voltage = GetVoltage(); + + float level = (GetVoltage() - 3.6f) / 0.6f; + if (level < 0.0f) { + level = 0.0f; + } else if (level > 1.0f) { + level = 1.0f; + } + + level = 0.5f - sin(asin(1.0f - 2.0f * level) / 3.0f); + + return std::round(level * 100.0f); +} diff --git a/src/WatchFeatures/Battery.h b/src/WatchFeatures/Battery.h new file mode 100644 index 0000000..5a6ad1f --- /dev/null +++ b/src/WatchFeatures/Battery.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace WatchFeatures +{ + class Battery; +} + +class WatchFeatures::Battery +{ +public: + Battery(); + float GetVoltage(); + uint8_t GetPercentage(); +}; \ No newline at end of file diff --git a/src/WatchyRTC.cpp b/src/WatchFeatures/RTC.cpp similarity index 87% rename from src/WatchyRTC.cpp rename to src/WatchFeatures/RTC.cpp index 8fee1b3..1834755 100644 --- a/src/WatchyRTC.cpp +++ b/src/WatchFeatures/RTC.cpp @@ -1,21 +1,14 @@ -#include "WatchyRTC.h" +#include "RTC.h" +#include #if (UPDATE_INTERVAL > 255) #error "UPDATE_INTERVAL must be either a multiple of 60, or less than 256 seconds" #endif -RTC_DATA_ATTR bool WatchyRTC::m_timerSet = false; -RTC_DATA_ATTR bool WatchyRTC::m_initialTimer = true; +RTC_DATA_ATTR bool WatchFeatures::RTC::m_timerSet = false; +RTC_DATA_ATTR bool WatchFeatures::RTC::m_initialTimer = true; -WatchyRTC::WatchyRTC() -{ -} - -void WatchyRTC::Init() -{ -} - -void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds) +void WatchFeatures::RTC::Get(tmElements_t & tm) { rtc_pcf.getDateTime(); tm.Year = y2kYearToTm(rtc_pcf.getYear()); @@ -25,10 +18,9 @@ void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds) tm.Hour = rtc_pcf.getHour(); tm.Minute = rtc_pcf.getMinute(); tm.Second = rtc_pcf.getSecond(); - OffsetTime(tm, offsetInSeconds); } -void WatchyRTC::Set(tmElements_t tm) +void WatchFeatures::RTC::Set(tmElements_t tm) { time_t t = makeTime(tm); // make and break to calculate tm.Wday breakTime(t, tm); @@ -36,14 +28,14 @@ void WatchyRTC::Set(tmElements_t tm) rtc_pcf.setDateTime(tm.Day, tm.Wday - 1, tm.Month, 0, tmYearToY2k(tm.Year), tm.Hour, tm.Minute, tm.Second); } -void WatchyRTC::SetTimer() +void WatchFeatures::RTC::SetTimer() { if (!m_timerSet) { Resync(); } } -void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) +void WatchFeatures::RTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) { int year = tm.Year; int month = tm.Month; @@ -108,7 +100,7 @@ void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) } // TODO: implement more advanced wakeup logic, i.e. > 255 seconds that are not a multiple of 60 -bool WatchyRTC::CheckWakeup() +bool WatchFeatures::RTC::CheckWakeup() { if(m_initialTimer) { m_initialTimer = false; @@ -132,7 +124,7 @@ bool WatchyRTC::CheckWakeup() return true; } -void WatchyRTC::Resync() +void WatchFeatures::RTC::Resync() { rtc_pcf.getDateTime(); // Sleep just long enough to get to a multiple of the update interval, makes updates happen on exact turn of the minute diff --git a/src/WatchyRTC.h b/src/WatchFeatures/RTC.h similarity index 77% rename from src/WatchyRTC.h rename to src/WatchFeatures/RTC.h index c745929..9abf79c 100644 --- a/src/WatchyRTC.h +++ b/src/WatchFeatures/RTC.h @@ -6,17 +6,17 @@ #include #include -#define RTC_PCF_ADDR 0x51 -#define YEAR_OFFSET_PCF 2000 +namespace WatchFeatures +{ + class RTC; +} -class WatchyRTC { +class WatchFeatures::RTC { public: Rtc_Pcf8563 rtc_pcf; public: - WatchyRTC(); - void Init(); - void Get(tmElements_t & tm, int offsetInSeconds = 0); + void Get(tmElements_t & tm); void Set(tmElements_t tm); void SetTimer(); bool CheckWakeup(); // Checks to really wake up or not, also resets the timer after the initial sleep diff --git a/src/WatchFeatures/StepCounter.cpp b/src/WatchFeatures/StepCounter.cpp new file mode 100644 index 0000000..e1e867f --- /dev/null +++ b/src/WatchFeatures/StepCounter.cpp @@ -0,0 +1,16 @@ +#include "StepCounter.h" + +WatchFeatures::StepCounter::StepCounter(BMA423 & sensor) : + m_sensor(sensor) +{ +} + +uint64_t WatchFeatures::StepCounter::GetSteps() +{ + return m_sensor.getCounter(); +} + +void WatchFeatures::StepCounter::ResetSteps() +{ + m_sensor.resetStepCounter(); +} \ No newline at end of file diff --git a/src/WatchFeatures/StepCounter.h b/src/WatchFeatures/StepCounter.h new file mode 100644 index 0000000..baa6d0e --- /dev/null +++ b/src/WatchFeatures/StepCounter.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "../bma.h" + +namespace WatchFeatures +{ + class StepCounter; +} + +class WatchFeatures::StepCounter +{ +public: + StepCounter(BMA423 & sensor); + uint64_t GetSteps(); + void ResetSteps(); + +private: + BMA423 & m_sensor; +}; \ No newline at end of file diff --git a/src/WatchFeatures/Storage.cpp b/src/WatchFeatures/Storage.cpp new file mode 100644 index 0000000..5de2fb7 --- /dev/null +++ b/src/WatchFeatures/Storage.cpp @@ -0,0 +1,58 @@ +#include "../config.h" +#include "Storage.h" +#include + +WatchFeatures::Storage::Storage() +{ + +} + +void WatchFeatures::Storage::InitBoot() +{ + EEPROM.begin(512); + if (EEPROM.read(EEPROM_LOCATION_MAGIC) != EEPROM_MAGIC1 + || EEPROM.read(EEPROM_LOCATION_MAGIC + 1) != EEPROM_MAGIC2) + { + Serial.println("Initializing EEPROM"); + EEPROM.write(EEPROM_LOCATION_MAGIC, EEPROM_MAGIC1); + EEPROM.write(EEPROM_LOCATION_MAGIC + 1, EEPROM_MAGIC2); + uint16_t version = EEPROM_VERSION; + EEPROM.write(EEPROM_LOCATION_VERSION, version & 0xFF); + EEPROM.write(EEPROM_LOCATION_VERSION + 1, version >> 8); + + int offset = DEFAULT_TZ_OFFSET; + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET, offset & 0xFF); + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET + 1, (offset >> 8) & 0xFF); + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET + 2, (offset >> 16) & 0xFF); + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET + 3, (offset >> 24) & 0xFF); + EEPROM.commit(); + } + + uint16_t version = EEPROM.read(EEPROM_LOCATION_VERSION) | (EEPROM.read(EEPROM_LOCATION_VERSION + 1) << 8); + if (version != EEPROM_VERSION) + { + // TODO: Handle version mismatch + } +} + +void WatchFeatures::Storage::InitWake() +{ + EEPROM.begin(512); +} + +int32_t WatchFeatures::Storage::GetTzOffset() +{ + return EEPROM.read(EEPROM_LOCATION_TZ_OFFSET) + | (EEPROM.read(EEPROM_LOCATION_TZ_OFFSET + 1) << 8) + | (EEPROM.read(EEPROM_LOCATION_TZ_OFFSET + 2) << 16) + | (EEPROM.read(EEPROM_LOCATION_TZ_OFFSET + 3) << 24); +} + +void WatchFeatures::Storage::SetTzOffset(int offset) +{ + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET, offset & 0xFF); + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET + 1, (offset >> 8) & 0xFF); + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET + 2, (offset >> 16) & 0xFF); + EEPROM.write(EEPROM_LOCATION_TZ_OFFSET + 3, (offset >> 24) & 0xFF); + EEPROM.commit(); +} \ No newline at end of file diff --git a/src/WatchFeatures/Storage.h b/src/WatchFeatures/Storage.h new file mode 100644 index 0000000..b0e26c2 --- /dev/null +++ b/src/WatchFeatures/Storage.h @@ -0,0 +1,16 @@ +#pragma once + +namespace WatchFeatures +{ + class Storage; +} + +class WatchFeatures::Storage +{ +public: + Storage(); + void InitBoot(); + void InitWake(); + int GetTzOffset(); + void SetTzOffset(int offset); +}; \ No newline at end of file diff --git a/src/WatchFeatures/WatchFeatures.h b/src/WatchFeatures/WatchFeatures.h new file mode 100644 index 0000000..e5049ed --- /dev/null +++ b/src/WatchFeatures/WatchFeatures.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Battery.h" +#include "StepCounter.h" +#include "RTC.h" +#include "Storage.h" + +namespace WatchFeatures +{ + struct WatchFeatures; +} + +struct WatchFeatures::WatchFeatures +{ + WatchFeatures(BMA423 & sensor) + : battery(), stepCounter(sensor), rtc() + { + } + + Battery battery; + StepCounter stepCounter; + RTC rtc; + Storage storage; +}; \ No newline at end of file diff --git a/src/Watchy.cpp b/src/Watchy.cpp index 183e6ff..7e49b8b 100644 --- a/src/Watchy.cpp +++ b/src/Watchy.cpp @@ -3,44 +3,41 @@ WatchyDisplayBase Watchy::m_displayBase; WatchyDisplay Watchy::m_display(Watchy::m_displayBase); -WatchyRTC Watchy::m_RTC; RTC_DATA_ATTR BMA423 Watchy::m_sensor; RTC_DATA_ATTR bool g_displayFullInit = true; Watchy::Watchy() + : m_features(m_sensor) { } -void Watchy::Init() +void Watchy::Wake() { esp_sleep_wakeup_cause_t wakeup_reason; wakeup_reason = esp_sleep_get_wakeup_cause(); if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) { - if(!m_RTC.CheckWakeup()) { + if(!m_features.rtc.CheckWakeup()) { DeepSleep(); } } - Wire.begin(SDA, SCL); - m_display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); - m_display.init(0, g_displayFullInit, 10, true); - SetBusyCallback(); - - Setup(); - switch(wakeup_reason) { case ESP_SLEEP_WAKEUP_EXT0: { // RTC interrupt + InitWakeInternal(); ShowWatchFace(true); break; } case ESP_SLEEP_WAKEUP_EXT1: { + // Button press or accelerometer interrupt + InitWakeInternal(); uint64_t wakeupBit = esp_sleep_get_ext1_wakeup_status(); if (wakeupBit & ACC_INT_MASK) { + // Accelerometer interrupt m_sensor.getINT(); uint8_t irqMask = m_sensor.getIRQMASK(); @@ -54,24 +51,14 @@ void Watchy::Init() m_sensor.getINT(); } else if (wakeupBit & BTN_PIN_MASK) { - // Button press HandleButtonPress(wakeupBit); - break; } - - // Button press - HandleButtonPress(wakeupBit); break; } default: { - BmaConfig(); - m_RTC.Init(); - ConnectWiFi(); - SyncNTPTime(); - DisconnectWiFi(); - + InitBootInternal(); ShowWatchFace(false); break; } @@ -102,7 +89,7 @@ void Watchy::DeepSleep() BTN_PIN_MASK | ACC_INT_MASK, ESP_EXT1_WAKEUP_ANY_HIGH); - m_RTC.SetTimer(); + m_features.rtc.SetTimer(); esp_deep_sleep_start(); } @@ -125,21 +112,6 @@ void Watchy::VibeMotor(uint8_t intervalMs, uint8_t length) } } -float Watchy::GetBatteryVoltage() -{ - return analogReadMilliVolts(BATT_ADC_PIN) / 1000.0f * 2.0f; -} - -uint64_t Watchy::GetSteps() -{ - return m_sensor.getCounter(); -} - -void Watchy::ResetSteps() -{ - m_sensor.resetStepCounter(); -} - void Watchy::ConnectWiFi() { if(WiFi.begin(WIFI_SSID, WIFI_PASS) == WL_CONNECT_FAILED) { @@ -158,14 +130,14 @@ void Watchy::ConnectWiFi() void Watchy::SyncNTPTime() { WiFiUDP ntpUDP; - // GMT offset should be 0, RTC class will adjust to local time + // GMT offset should be 0, since RTC is set to UTC NTPClient timeClient(ntpUDP, NTP_SERVER, 0); timeClient.begin(); bool success = timeClient.forceUpdate(); if (success) { tmElements_t tm; breakTime((time_t)timeClient.getEpochTime(), tm); - m_RTC.Set(tm); + m_features.rtc.Set(tm); } else { Serial.begin(9600); Serial.println("Failed to get NTP time"); @@ -187,6 +159,29 @@ void Watchy::ClearBusyCallback() m_display.epd2.setBusyCallback(nullptr); } +void Watchy::InitBootInternal() +{ + Wire.begin(SDA, SCL); + m_display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); + m_display.init(0, g_displayFullInit, 10, true); + SetBusyCallback(); + + BmaConfig(); + m_features.storage.InitBoot(); + InitBoot(); +} + +void Watchy::InitWakeInternal() +{ + Wire.begin(SDA, SCL); + m_display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); + m_display.init(0, g_displayFullInit, 10, true); + SetBusyCallback(); + + m_features.storage.InitWake(); + InitWake(); +} + void Watchy::SetBusyCallback() { m_display.epd2.setBusyCallback(DisplayBusyCallback); @@ -308,4 +303,5 @@ void Watchy::BmaConfig() m_sensor.enableTiltInterrupt(); m_sensor.enableWakeupInterrupt(); } -} \ No newline at end of file +} + diff --git a/src/Watchy.h b/src/Watchy.h index 4c075ec..59137d1 100644 --- a/src/Watchy.h +++ b/src/Watchy.h @@ -2,7 +2,7 @@ #include "config.h" #include "WatchyDisplay.h" -#include "WatchyRTC.h" +#include "WatchFeatures/WatchFeatures.h" #include #include #include @@ -12,12 +12,10 @@ class Watchy { public: Watchy(); - void Init(); + void Wake(); void DeepSleep(); void VibeMotor(uint8_t intervalMs = 100, uint8_t length = 20); float GetBatteryVoltage(); - uint64_t GetSteps(); - void ResetSteps(); void ConnectWiFi(); void SyncNTPTime(); void DisconnectWiFi(); @@ -26,9 +24,8 @@ public: void ClearBusyCallback(); void SetBusyCallback(); - // Called after hardware is setup - virtual void Setup() = 0; - + virtual void InitBoot() {}; // Called on first boot + virtual void InitWake() {}; // Called every time the watch wakes from sleep virtual void HandleButtonPress(uint64_t buttonMask) = 0; virtual void HandleDoubleTap() {} virtual void HandleTilt() {} @@ -36,13 +33,15 @@ public: protected: + void InitBootInternal(); + void InitWakeInternal(); + static void DisplayBusyCallback(const void *); static WatchyDisplayBase m_displayBase; static WatchyDisplay m_display; - static WatchyRTC m_RTC; - static RTC_DATA_ATTR BMA423 m_sensor; + WatchFeatures::WatchFeatures m_features; private: void BmaConfig(); diff --git a/src/config.h b/src/config.h index 98847c1..a2b5b30 100644 --- a/src/config.h +++ b/src/config.h @@ -13,6 +13,8 @@ #define ACC_INT_2_PIN 12 #define VIB_MOTOR_PIN 13 #define RTC_INT_PIN 27 +#define RTC_PCF_ADDR 0x51 +#define YEAR_OFFSET_PCF 2000 #define MENU_BTN_MASK GPIO_SEL_26 #define BACK_BTN_MASK GPIO_SEL_25 @@ -24,11 +26,22 @@ #define DISPLAY_WIDTH 200 #define DISPLAY_HEIGHT 200 -#define WIFI_SSID "" -#define WIFI_PASS "" -#define TZ_OFFSET 3600 * 3 +#define EEPROM_LOCATION_MAGIC 0 +#define EEPROM_LOCATION_VERSION 2 +#define EEPROM_LOCATION_TZ_OFFSET 4 + +#define EEPROM_MAGIC1 0xf0 +#define EEPROM_MAGIC2 0x0d +#define EEPROM_VERSION 1 + +#include "secrets.h" +#if !defined(WIFI_SSID) || !defined(WIFI_PASS) +#error "Please define WIFI_SSID and WIFI_PASS in secrets.h" +#endif + +#define DEFAULT_TZ_OFFSET 3600 * 3 #define NTP_SERVER "pool.ntp.org" #define UPDATE_INTERVAL 60 // seconds -#define WAKE_ON_ACCEL_EVENTS false // useful if saving battery by not updating every minute \ No newline at end of file +#define WAKE_ON_ACCEL_EVENTS false // useful if saving battery by not updating every minute