WIP refactoring
Some checks failed
Compile / Compile (push) Failing after 1m5s

This commit is contained in:
Lewis Jackson 2023-06-01 03:07:52 +03:00
parent 1b843ce4b4
commit 90c66be515
19 changed files with 413 additions and 162 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.pio .pio
.vscode/** .vscode/**
src/secrets.h

View file

@ -5,7 +5,7 @@ WatchFace watchy;
void setup() void setup()
{ {
Serial.begin(9600); Serial.begin(9600);
watchy.Init(); watchy.Wake();
} }
void loop() void loop()

View file

@ -1,28 +1,34 @@
#include "WatchFace.h" #include "WatchFace.h"
#include "SevenSegment.h" #include "WatchFacePages/Clock.h"
#include "Icons.h"
#include <cmath> #include <cmath>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
RTC_DATA_ATTR bool WatchFace::m_menuSetup = false;
RTC_DATA_ATTR bool WatchFace::m_inMenu = false; RTC_DATA_ATTR bool WatchFace::m_inMenu = false;
RTC_DATA_ATTR int WatchFace::m_tzOffset = TZ_OFFSET; RTC_DATA_ATTR Menu WatchFace::m_menu;
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(); SetupVolatileMenuStuff();
if (!m_menuSetup) { for (auto & page : m_pages) {
m_menu.Reset(); page->InitWake();
m_menuSetup = true; }
}
} }
void WatchFace::HandleButtonPress(uint64_t buttonMask) void WatchFace::HandleButtonPress(uint64_t buttonMask)
@ -86,68 +92,7 @@ void WatchFace::DrawWatchFace(bool partialRefresh)
return; return;
} }
m_display.setFullWindow(); m_pages[0]->DrawPage(partialRefresh);
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());
} }
void WatchFace::SetupVolatileMenuStuff() void WatchFace::SetupVolatileMenuStuff()
@ -269,7 +214,7 @@ void WatchFace::MenuNTPSyncSelected()
ConnectWiFi(); ConnectWiFi();
SyncNTPTime(); SyncNTPTime();
DisconnectWiFi(); DisconnectWiFi();
m_RTC.Resync(); m_features.rtc.Resync();
if (m_inMenu) { if (m_inMenu) {
m_inMenu = false; m_inMenu = false;
@ -280,7 +225,7 @@ void WatchFace::MenuNTPSyncSelected()
void WatchFace::MenuTimeZoneSelected(int tzOffset) void WatchFace::MenuTimeZoneSelected(int tzOffset)
{ {
m_tzOffset = tzOffset; m_features.storage.SetTzOffset(tzOffset);
if (m_inMenu) { if (m_inMenu) {
m_inMenu = false; m_inMenu = false;
@ -291,7 +236,7 @@ void WatchFace::MenuTimeZoneSelected(int tzOffset)
void WatchFace::MenuConfirmResetSteps() void WatchFace::MenuConfirmResetSteps()
{ {
ResetSteps(); m_features.stepCounter.ResetSteps();
if (m_inMenu) { if (m_inMenu) {
m_inMenu = false; m_inMenu = false;

View file

@ -2,21 +2,26 @@
#include "Watchy.h" #include "Watchy.h"
#include "Menu.h" #include "Menu.h"
#include "WatchFacePages/Clock.h"
#include <memory>
class WatchFace : public Watchy class WatchFace : public Watchy
{ {
public: 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 HandleButtonPress(uint64_t buttonMask) override;
void HandleDoubleTap() override; void HandleDoubleTap() override;
void HandleTilt() override; void HandleTilt() override;
void DrawWatchFace(bool partialRefresh = false) override; void DrawWatchFace(bool partialRefresh = false) override;
private: private:
RTC_DATA_ATTR static bool m_menuSetup;
RTC_DATA_ATTR static bool m_inMenu; RTC_DATA_ATTR static bool m_inMenu;
RTC_DATA_ATTR static Menu m_menu; RTC_DATA_ATTR static Menu m_menu;
RTC_DATA_ATTR static int m_tzOffset; RTC_DATA_ATTR static uint8_t m_watchFacePage;
std::vector<std::shared_ptr<WatchFacePages::Page>> m_pages = {
std::make_shared<WatchFacePages::Clock>(m_display, m_features)
};
void SetupVolatileMenuStuff(); void SetupVolatileMenuStuff();
void DrawBatteryIcon(); void DrawBatteryIcon();

View file

@ -0,0 +1,81 @@
#include "Clock.h"
#include "../SevenSegment.h"
#include "../Icons.h"
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
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());
}

View file

@ -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;
};

15
src/WatchFacePages/Page.h Normal file
View file

@ -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;
};

View file

@ -0,0 +1,29 @@
#include "../config.h"
#include "Battery.h"
#include <Wire.h>
#include <Arduino.h>
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);
}

View file

@ -0,0 +1,16 @@
#pragma once
#include <stdint.h>
namespace WatchFeatures
{
class Battery;
}
class WatchFeatures::Battery
{
public:
Battery();
float GetVoltage();
uint8_t GetPercentage();
};

View file

@ -1,21 +1,14 @@
#include "WatchyRTC.h" #include "RTC.h"
#include <EEPROM.h>
#if (UPDATE_INTERVAL > 255) #if (UPDATE_INTERVAL > 255)
#error "UPDATE_INTERVAL must be either a multiple of 60, or less than 256 seconds" #error "UPDATE_INTERVAL must be either a multiple of 60, or less than 256 seconds"
#endif #endif
RTC_DATA_ATTR bool WatchyRTC::m_timerSet = false; RTC_DATA_ATTR bool WatchFeatures::RTC::m_timerSet = false;
RTC_DATA_ATTR bool WatchyRTC::m_initialTimer = true; RTC_DATA_ATTR bool WatchFeatures::RTC::m_initialTimer = true;
WatchyRTC::WatchyRTC() void WatchFeatures::RTC::Get(tmElements_t & tm)
{
}
void WatchyRTC::Init()
{
}
void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds)
{ {
rtc_pcf.getDateTime(); rtc_pcf.getDateTime();
tm.Year = y2kYearToTm(rtc_pcf.getYear()); tm.Year = y2kYearToTm(rtc_pcf.getYear());
@ -25,10 +18,9 @@ void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds)
tm.Hour = rtc_pcf.getHour(); tm.Hour = rtc_pcf.getHour();
tm.Minute = rtc_pcf.getMinute(); tm.Minute = rtc_pcf.getMinute();
tm.Second = rtc_pcf.getSecond(); 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 time_t t = makeTime(tm); // make and break to calculate tm.Wday
breakTime(t, tm); 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); 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) { if (!m_timerSet) {
Resync(); Resync();
} }
} }
void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) void WatchFeatures::RTC::OffsetTime(tmElements_t & tm, int offsetInSeconds)
{ {
int year = tm.Year; int year = tm.Year;
int month = tm.Month; 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 // 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) { if(m_initialTimer) {
m_initialTimer = false; m_initialTimer = false;
@ -132,7 +124,7 @@ bool WatchyRTC::CheckWakeup()
return true; return true;
} }
void WatchyRTC::Resync() void WatchFeatures::RTC::Resync()
{ {
rtc_pcf.getDateTime(); 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 // Sleep just long enough to get to a multiple of the update interval, makes updates happen on exact turn of the minute

View file

@ -6,17 +6,17 @@
#include <TimeLib.h> #include <TimeLib.h>
#include <Rtc_Pcf8563.h> #include <Rtc_Pcf8563.h>
#define RTC_PCF_ADDR 0x51 namespace WatchFeatures
#define YEAR_OFFSET_PCF 2000 {
class RTC;
}
class WatchyRTC { class WatchFeatures::RTC {
public: public:
Rtc_Pcf8563 rtc_pcf; Rtc_Pcf8563 rtc_pcf;
public: public:
WatchyRTC(); void Get(tmElements_t & tm);
void Init();
void Get(tmElements_t & tm, int offsetInSeconds = 0);
void Set(tmElements_t tm); void Set(tmElements_t tm);
void SetTimer(); void SetTimer();
bool CheckWakeup(); // Checks to really wake up or not, also resets the timer after the initial sleep bool CheckWakeup(); // Checks to really wake up or not, also resets the timer after the initial sleep

View file

@ -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();
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
#include "../bma.h"
namespace WatchFeatures
{
class StepCounter;
}
class WatchFeatures::StepCounter
{
public:
StepCounter(BMA423 & sensor);
uint64_t GetSteps();
void ResetSteps();
private:
BMA423 & m_sensor;
};

View file

@ -0,0 +1,58 @@
#include "../config.h"
#include "Storage.h"
#include <EEPROM.h>
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();
}

View file

@ -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);
};

View file

@ -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;
};

View file

@ -3,44 +3,41 @@
WatchyDisplayBase Watchy::m_displayBase; WatchyDisplayBase Watchy::m_displayBase;
WatchyDisplay Watchy::m_display(Watchy::m_displayBase); WatchyDisplay Watchy::m_display(Watchy::m_displayBase);
WatchyRTC Watchy::m_RTC;
RTC_DATA_ATTR BMA423 Watchy::m_sensor; RTC_DATA_ATTR BMA423 Watchy::m_sensor;
RTC_DATA_ATTR bool g_displayFullInit = true; RTC_DATA_ATTR bool g_displayFullInit = true;
Watchy::Watchy() Watchy::Watchy()
: m_features(m_sensor)
{ {
} }
void Watchy::Init() void Watchy::Wake()
{ {
esp_sleep_wakeup_cause_t wakeup_reason; esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause(); wakeup_reason = esp_sleep_get_wakeup_cause();
if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) { if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
if(!m_RTC.CheckWakeup()) { if(!m_features.rtc.CheckWakeup()) {
DeepSleep(); 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) { switch(wakeup_reason) {
case ESP_SLEEP_WAKEUP_EXT0: case ESP_SLEEP_WAKEUP_EXT0:
{ {
// RTC interrupt // RTC interrupt
InitWakeInternal();
ShowWatchFace(true); ShowWatchFace(true);
break; break;
} }
case ESP_SLEEP_WAKEUP_EXT1: case ESP_SLEEP_WAKEUP_EXT1:
{ {
// Button press or accelerometer interrupt
InitWakeInternal();
uint64_t wakeupBit = esp_sleep_get_ext1_wakeup_status(); uint64_t wakeupBit = esp_sleep_get_ext1_wakeup_status();
if (wakeupBit & ACC_INT_MASK) { if (wakeupBit & ACC_INT_MASK) {
// Accelerometer interrupt
m_sensor.getINT(); m_sensor.getINT();
uint8_t irqMask = m_sensor.getIRQMASK(); uint8_t irqMask = m_sensor.getIRQMASK();
@ -54,24 +51,14 @@ void Watchy::Init()
m_sensor.getINT(); m_sensor.getINT();
} else if (wakeupBit & BTN_PIN_MASK) { } else if (wakeupBit & BTN_PIN_MASK) {
// Button press // Button press
HandleButtonPress(wakeupBit); HandleButtonPress(wakeupBit);
break;
} }
// Button press
HandleButtonPress(wakeupBit);
break; break;
} }
default: default:
{ {
BmaConfig(); InitBootInternal();
m_RTC.Init();
ConnectWiFi();
SyncNTPTime();
DisconnectWiFi();
ShowWatchFace(false); ShowWatchFace(false);
break; break;
} }
@ -102,7 +89,7 @@ void Watchy::DeepSleep()
BTN_PIN_MASK | ACC_INT_MASK, BTN_PIN_MASK | ACC_INT_MASK,
ESP_EXT1_WAKEUP_ANY_HIGH); ESP_EXT1_WAKEUP_ANY_HIGH);
m_RTC.SetTimer(); m_features.rtc.SetTimer();
esp_deep_sleep_start(); 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() void Watchy::ConnectWiFi()
{ {
if(WiFi.begin(WIFI_SSID, WIFI_PASS) == WL_CONNECT_FAILED) { if(WiFi.begin(WIFI_SSID, WIFI_PASS) == WL_CONNECT_FAILED) {
@ -158,14 +130,14 @@ void Watchy::ConnectWiFi()
void Watchy::SyncNTPTime() void Watchy::SyncNTPTime()
{ {
WiFiUDP ntpUDP; 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); NTPClient timeClient(ntpUDP, NTP_SERVER, 0);
timeClient.begin(); timeClient.begin();
bool success = timeClient.forceUpdate(); bool success = timeClient.forceUpdate();
if (success) { if (success) {
tmElements_t tm; tmElements_t tm;
breakTime((time_t)timeClient.getEpochTime(), tm); breakTime((time_t)timeClient.getEpochTime(), tm);
m_RTC.Set(tm); m_features.rtc.Set(tm);
} else { } else {
Serial.begin(9600); Serial.begin(9600);
Serial.println("Failed to get NTP time"); Serial.println("Failed to get NTP time");
@ -187,6 +159,29 @@ void Watchy::ClearBusyCallback()
m_display.epd2.setBusyCallback(nullptr); 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() void Watchy::SetBusyCallback()
{ {
m_display.epd2.setBusyCallback(DisplayBusyCallback); m_display.epd2.setBusyCallback(DisplayBusyCallback);
@ -308,4 +303,5 @@ void Watchy::BmaConfig()
m_sensor.enableTiltInterrupt(); m_sensor.enableTiltInterrupt();
m_sensor.enableWakeupInterrupt(); m_sensor.enableWakeupInterrupt();
} }
} }

View file

@ -2,7 +2,7 @@
#include "config.h" #include "config.h"
#include "WatchyDisplay.h" #include "WatchyDisplay.h"
#include "WatchyRTC.h" #include "WatchFeatures/WatchFeatures.h"
#include <WiFi.h> #include <WiFi.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <NTPClient.h> #include <NTPClient.h>
@ -12,12 +12,10 @@ class Watchy
{ {
public: public:
Watchy(); Watchy();
void Init(); void Wake();
void DeepSleep(); void DeepSleep();
void VibeMotor(uint8_t intervalMs = 100, uint8_t length = 20); void VibeMotor(uint8_t intervalMs = 100, uint8_t length = 20);
float GetBatteryVoltage(); float GetBatteryVoltage();
uint64_t GetSteps();
void ResetSteps();
void ConnectWiFi(); void ConnectWiFi();
void SyncNTPTime(); void SyncNTPTime();
void DisconnectWiFi(); void DisconnectWiFi();
@ -26,9 +24,8 @@ public:
void ClearBusyCallback(); void ClearBusyCallback();
void SetBusyCallback(); void SetBusyCallback();
// Called after hardware is setup virtual void InitBoot() {}; // Called on first boot
virtual void Setup() = 0; virtual void InitWake() {}; // Called every time the watch wakes from sleep
virtual void HandleButtonPress(uint64_t buttonMask) = 0; virtual void HandleButtonPress(uint64_t buttonMask) = 0;
virtual void HandleDoubleTap() {} virtual void HandleDoubleTap() {}
virtual void HandleTilt() {} virtual void HandleTilt() {}
@ -36,13 +33,15 @@ public:
protected: protected:
void InitBootInternal();
void InitWakeInternal();
static void DisplayBusyCallback(const void *); static void DisplayBusyCallback(const void *);
static WatchyDisplayBase m_displayBase; static WatchyDisplayBase m_displayBase;
static WatchyDisplay m_display; static WatchyDisplay m_display;
static WatchyRTC m_RTC;
static RTC_DATA_ATTR BMA423 m_sensor; static RTC_DATA_ATTR BMA423 m_sensor;
WatchFeatures::WatchFeatures m_features;
private: private:
void BmaConfig(); void BmaConfig();

View file

@ -13,6 +13,8 @@
#define ACC_INT_2_PIN 12 #define ACC_INT_2_PIN 12
#define VIB_MOTOR_PIN 13 #define VIB_MOTOR_PIN 13
#define RTC_INT_PIN 27 #define RTC_INT_PIN 27
#define RTC_PCF_ADDR 0x51
#define YEAR_OFFSET_PCF 2000
#define MENU_BTN_MASK GPIO_SEL_26 #define MENU_BTN_MASK GPIO_SEL_26
#define BACK_BTN_MASK GPIO_SEL_25 #define BACK_BTN_MASK GPIO_SEL_25
@ -24,11 +26,22 @@
#define DISPLAY_WIDTH 200 #define DISPLAY_WIDTH 200
#define DISPLAY_HEIGHT 200 #define DISPLAY_HEIGHT 200
#define WIFI_SSID "<ssid>" #define EEPROM_LOCATION_MAGIC 0
#define WIFI_PASS "<pass>" #define EEPROM_LOCATION_VERSION 2
#define TZ_OFFSET 3600 * 3 #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 NTP_SERVER "pool.ntp.org"
#define UPDATE_INTERVAL 60 // seconds #define UPDATE_INTERVAL 60 // seconds
#define WAKE_ON_ACCEL_EVENTS false // useful if saving battery by not updating every minute #define WAKE_ON_ACCEL_EVENTS false // useful if saving battery by not updating every minute