2021-12-12 17:12:31 -05:00
|
|
|
#include "Watchy.h"
|
|
|
|
|
2021-12-30 23:16:03 -05:00
|
|
|
WatchyRTC Watchy::RTC;
|
2022-08-03 12:08:12 -04:00
|
|
|
GxEPD2_BW<WatchyDisplay, WatchyDisplay::HEIGHT> Watchy::display(
|
|
|
|
WatchyDisplay(DISPLAY_CS, DISPLAY_DC, DISPLAY_RES, DISPLAY_BUSY));
|
2021-12-12 17:12:31 -05:00
|
|
|
|
|
|
|
RTC_DATA_ATTR int guiState;
|
|
|
|
RTC_DATA_ATTR int menuIndex;
|
|
|
|
RTC_DATA_ATTR BMA423 sensor;
|
|
|
|
RTC_DATA_ATTR bool WIFI_CONFIGURED;
|
|
|
|
RTC_DATA_ATTR bool BLE_CONFIGURED;
|
|
|
|
RTC_DATA_ATTR weatherData currentWeather;
|
2021-12-30 22:47:38 -05:00
|
|
|
RTC_DATA_ATTR int weatherIntervalCounter = -1;
|
2022-04-28 21:17:00 -04:00
|
|
|
RTC_DATA_ATTR bool displayFullInit = true;
|
2022-10-15 08:09:06 -04:00
|
|
|
RTC_DATA_ATTR long gmtOffset = 0;
|
2022-10-16 07:56:42 -04:00
|
|
|
RTC_DATA_ATTR bool alreadyInMenu = true;
|
2023-01-12 07:40:27 -05:00
|
|
|
RTC_DATA_ATTR tmElements_t bootTime;
|
2022-04-28 21:17:00 -04:00
|
|
|
|
|
|
|
void Watchy::init(String datetime) {
|
|
|
|
esp_sleep_wakeup_cause_t wakeup_reason;
|
|
|
|
wakeup_reason = esp_sleep_get_wakeup_cause(); // get wake up reason
|
|
|
|
Wire.begin(SDA, SCL); // init i2c
|
|
|
|
RTC.init();
|
|
|
|
|
|
|
|
// Init the display here for all cases, if unused, it will do nothing
|
2022-08-03 06:51:13 -04:00
|
|
|
display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); // Set SPI to 20Mhz (default is 4Mhz)
|
2022-04-28 21:17:00 -04:00
|
|
|
display.init(0, displayFullInit, 10,
|
|
|
|
true); // 10ms by spec, and fast pulldown reset
|
|
|
|
display.epd2.setBusyCallback(displayBusyCallback);
|
|
|
|
|
|
|
|
switch (wakeup_reason) {
|
|
|
|
case ESP_SLEEP_WAKEUP_EXT0: // RTC Alarm
|
2022-10-16 07:56:42 -04:00
|
|
|
RTC.read(currentTime);
|
|
|
|
switch (guiState) {
|
|
|
|
case WATCHFACE_STATE:
|
2022-04-28 21:17:00 -04:00
|
|
|
showWatchFace(true); // partial updates on tick
|
2022-10-15 09:34:52 -04:00
|
|
|
if (settings.vibrateOClock) {
|
2022-10-26 08:31:36 -04:00
|
|
|
if (currentTime.Minute == 0) {
|
|
|
|
// The RTC wakes us up once per minute
|
2022-10-15 09:34:52 -04:00
|
|
|
vibMotor(75, 4);
|
|
|
|
}
|
|
|
|
}
|
2022-10-16 07:56:42 -04:00
|
|
|
break;
|
|
|
|
case MAIN_MENU_STATE:
|
|
|
|
// Return to watchface if in menu for more than one tick
|
|
|
|
if (alreadyInMenu) {
|
|
|
|
guiState = WATCHFACE_STATE;
|
|
|
|
showWatchFace(false);
|
|
|
|
} else {
|
|
|
|
alreadyInMenu = true;
|
|
|
|
}
|
|
|
|
break;
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ESP_SLEEP_WAKEUP_EXT1: // button Press
|
|
|
|
handleButtonPress();
|
|
|
|
break;
|
|
|
|
default: // reset
|
|
|
|
RTC.config(datetime);
|
|
|
|
_bmaConfig();
|
2022-10-15 08:09:06 -04:00
|
|
|
gmtOffset = settings.gmtOffset;
|
2022-04-28 21:17:00 -04:00
|
|
|
RTC.read(currentTime);
|
2023-01-12 07:40:27 -05:00
|
|
|
RTC.read(bootTime);
|
2022-04-28 21:17:00 -04:00
|
|
|
showWatchFace(false); // full update on reset
|
2022-10-15 09:34:52 -04:00
|
|
|
vibMotor(75, 4);
|
2022-04-28 21:17:00 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
deepSleep();
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::displayBusyCallback(const void *) {
|
|
|
|
gpio_wakeup_enable((gpio_num_t)DISPLAY_BUSY, GPIO_INTR_LOW_LEVEL);
|
|
|
|
esp_sleep_enable_gpio_wakeup();
|
|
|
|
esp_light_sleep_start();
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::deepSleep() {
|
|
|
|
display.hibernate();
|
2022-08-03 13:20:44 -04:00
|
|
|
if (displayFullInit) // For some reason, seems to be enabled on first boot
|
2022-10-14 22:23:06 -04:00
|
|
|
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
|
2022-04-28 21:17:00 -04:00
|
|
|
displayFullInit = false; // Notify not to init it again
|
|
|
|
RTC.clearAlarm(); // resets the alarm flag in the RTC
|
2022-08-03 13:20:44 -04:00
|
|
|
|
|
|
|
// Set GPIOs 0-39 to input to avoid power leaking out
|
|
|
|
const uint64_t ignore = 0b11110001000000110000100111000010; // Ignore some GPIOs due to resets
|
|
|
|
for (int i = 0; i < GPIO_NUM_MAX; i++) {
|
|
|
|
if ((ignore >> i) & 0b1)
|
|
|
|
continue;
|
2022-04-28 21:17:00 -04:00
|
|
|
pinMode(i, INPUT);
|
|
|
|
}
|
|
|
|
esp_sleep_enable_ext0_wakeup((gpio_num_t)RTC_INT_PIN,
|
|
|
|
0); // enable deep sleep wake on RTC interrupt
|
|
|
|
esp_sleep_enable_ext1_wakeup(
|
|
|
|
BTN_PIN_MASK,
|
|
|
|
ESP_EXT1_WAKEUP_ANY_HIGH); // enable deep sleep wake on button press
|
|
|
|
esp_deep_sleep_start();
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::handleButtonPress() {
|
2021-12-12 17:12:31 -05:00
|
|
|
uint64_t wakeupBit = esp_sleep_get_ext1_wakeup_status();
|
2022-04-28 21:17:00 -04:00
|
|
|
// Menu Button
|
|
|
|
if (wakeupBit & MENU_BTN_MASK) {
|
|
|
|
if (guiState ==
|
|
|
|
WATCHFACE_STATE) { // enter menu state if coming from watch face
|
2021-12-12 17:12:31 -05:00
|
|
|
showMenu(menuIndex, false);
|
2022-04-28 21:17:00 -04:00
|
|
|
} else if (guiState ==
|
|
|
|
MAIN_MENU_STATE) { // if already in menu, then select menu item
|
|
|
|
switch (menuIndex) {
|
|
|
|
case 0:
|
|
|
|
showAbout();
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
showBuzz();
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
showAccelerometer();
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
setTime();
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
setupWifi();
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
showUpdateFW();
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
showSyncNTP();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
} else if (guiState == FW_UPDATE_STATE) {
|
2021-12-12 17:12:31 -05:00
|
|
|
updateFWBegin();
|
|
|
|
}
|
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
// Back Button
|
|
|
|
else if (wakeupBit & BACK_BTN_MASK) {
|
|
|
|
if (guiState == MAIN_MENU_STATE) { // exit to watch face if already in menu
|
|
|
|
RTC.read(currentTime);
|
|
|
|
showWatchFace(false);
|
|
|
|
} else if (guiState == APP_STATE) {
|
|
|
|
showMenu(menuIndex, false); // exit to menu if already in app
|
|
|
|
} else if (guiState == FW_UPDATE_STATE) {
|
|
|
|
showMenu(menuIndex, false); // exit to menu if already in app
|
|
|
|
} else if (guiState == WATCHFACE_STATE) {
|
|
|
|
return;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
// Up Button
|
|
|
|
else if (wakeupBit & UP_BTN_MASK) {
|
|
|
|
if (guiState == MAIN_MENU_STATE) { // increment menu index
|
2021-12-12 17:12:31 -05:00
|
|
|
menuIndex--;
|
2022-04-28 21:17:00 -04:00
|
|
|
if (menuIndex < 0) {
|
2021-12-12 17:12:31 -05:00
|
|
|
menuIndex = MENU_LENGTH - 1;
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
showMenu(menuIndex, true);
|
2022-04-28 21:17:00 -04:00
|
|
|
} else if (guiState == WATCHFACE_STATE) {
|
|
|
|
return;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
// Down Button
|
|
|
|
else if (wakeupBit & DOWN_BTN_MASK) {
|
|
|
|
if (guiState == MAIN_MENU_STATE) { // decrement menu index
|
2021-12-12 17:12:31 -05:00
|
|
|
menuIndex++;
|
2022-04-28 21:17:00 -04:00
|
|
|
if (menuIndex > MENU_LENGTH - 1) {
|
2021-12-12 17:12:31 -05:00
|
|
|
menuIndex = 0;
|
|
|
|
}
|
|
|
|
showMenu(menuIndex, true);
|
2022-04-28 21:17:00 -04:00
|
|
|
} else if (guiState == WATCHFACE_STATE) {
|
|
|
|
return;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
}
|
2021-12-30 23:16:03 -05:00
|
|
|
|
2021-12-12 17:12:31 -05:00
|
|
|
/***************** fast menu *****************/
|
2022-04-28 21:17:00 -04:00
|
|
|
bool timeout = false;
|
2021-12-12 17:12:31 -05:00
|
|
|
long lastTimeout = millis();
|
|
|
|
pinMode(MENU_BTN_PIN, INPUT);
|
|
|
|
pinMode(BACK_BTN_PIN, INPUT);
|
|
|
|
pinMode(UP_BTN_PIN, INPUT);
|
|
|
|
pinMode(DOWN_BTN_PIN, INPUT);
|
2022-04-28 21:17:00 -04:00
|
|
|
while (!timeout) {
|
|
|
|
if (millis() - lastTimeout > 5000) {
|
|
|
|
timeout = true;
|
|
|
|
} else {
|
|
|
|
if (digitalRead(MENU_BTN_PIN) == 1) {
|
|
|
|
lastTimeout = millis();
|
|
|
|
if (guiState ==
|
|
|
|
MAIN_MENU_STATE) { // if already in menu, then select menu item
|
|
|
|
switch (menuIndex) {
|
|
|
|
case 0:
|
|
|
|
showAbout();
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
showBuzz();
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
showAccelerometer();
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
setTime();
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
setupWifi();
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
showUpdateFW();
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
showSyncNTP();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (guiState == FW_UPDATE_STATE) {
|
|
|
|
updateFWBegin();
|
|
|
|
}
|
|
|
|
} else if (digitalRead(BACK_BTN_PIN) == 1) {
|
|
|
|
lastTimeout = millis();
|
|
|
|
if (guiState ==
|
|
|
|
MAIN_MENU_STATE) { // exit to watch face if already in menu
|
|
|
|
RTC.read(currentTime);
|
|
|
|
showWatchFace(false);
|
|
|
|
break; // leave loop
|
|
|
|
} else if (guiState == APP_STATE) {
|
|
|
|
showMenu(menuIndex, false); // exit to menu if already in app
|
|
|
|
} else if (guiState == FW_UPDATE_STATE) {
|
|
|
|
showMenu(menuIndex, false); // exit to menu if already in app
|
|
|
|
}
|
|
|
|
} else if (digitalRead(UP_BTN_PIN) == 1) {
|
|
|
|
lastTimeout = millis();
|
|
|
|
if (guiState == MAIN_MENU_STATE) { // increment menu index
|
|
|
|
menuIndex--;
|
|
|
|
if (menuIndex < 0) {
|
|
|
|
menuIndex = MENU_LENGTH - 1;
|
|
|
|
}
|
|
|
|
showFastMenu(menuIndex);
|
|
|
|
}
|
|
|
|
} else if (digitalRead(DOWN_BTN_PIN) == 1) {
|
|
|
|
lastTimeout = millis();
|
|
|
|
if (guiState == MAIN_MENU_STATE) { // decrement menu index
|
|
|
|
menuIndex++;
|
|
|
|
if (menuIndex > MENU_LENGTH - 1) {
|
|
|
|
menuIndex = 0;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
showFastMenu(menuIndex);
|
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showMenu(byte menuIndex, bool partialRefresh) {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
|
|
|
|
int16_t x1, y1;
|
|
|
|
uint16_t w, h;
|
|
|
|
int16_t yPos;
|
|
|
|
|
|
|
|
const char *menuItems[] = {
|
|
|
|
"About Watchy", "Vibrate Motor", "Show Accelerometer",
|
|
|
|
"Set Time", "Setup WiFi", "Update Firmware",
|
|
|
|
"Sync NTP"};
|
|
|
|
for (int i = 0; i < MENU_LENGTH; i++) {
|
|
|
|
yPos = MENU_HEIGHT + (MENU_HEIGHT * i);
|
2021-12-12 17:12:31 -05:00
|
|
|
display.setCursor(0, yPos);
|
2022-04-28 21:17:00 -04:00
|
|
|
if (i == menuIndex) {
|
|
|
|
display.getTextBounds(menuItems[i], 0, yPos, &x1, &y1, &w, &h);
|
|
|
|
display.fillRect(x1 - 1, y1 - 10, 200, h + 15, GxEPD_WHITE);
|
|
|
|
display.setTextColor(GxEPD_BLACK);
|
|
|
|
display.println(menuItems[i]);
|
|
|
|
} else {
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.println(menuItems[i]);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(partialRefresh);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
guiState = MAIN_MENU_STATE;
|
2022-10-16 07:56:42 -04:00
|
|
|
alreadyInMenu = false;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showFastMenu(byte menuIndex) {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
|
|
|
|
int16_t x1, y1;
|
|
|
|
uint16_t w, h;
|
|
|
|
int16_t yPos;
|
|
|
|
|
|
|
|
const char *menuItems[] = {
|
|
|
|
"About Watchy", "Vibrate Motor", "Show Accelerometer",
|
|
|
|
"Set Time", "Setup WiFi", "Update Firmware",
|
|
|
|
"Sync NTP"};
|
|
|
|
for (int i = 0; i < MENU_LENGTH; i++) {
|
|
|
|
yPos = MENU_HEIGHT + (MENU_HEIGHT * i);
|
2021-12-12 17:12:31 -05:00
|
|
|
display.setCursor(0, yPos);
|
2022-04-28 21:17:00 -04:00
|
|
|
if (i == menuIndex) {
|
|
|
|
display.getTextBounds(menuItems[i], 0, yPos, &x1, &y1, &w, &h);
|
|
|
|
display.fillRect(x1 - 1, y1 - 10, 200, h + 15, GxEPD_WHITE);
|
|
|
|
display.setTextColor(GxEPD_BLACK);
|
|
|
|
display.println(menuItems[i]);
|
|
|
|
} else {
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.println(menuItems[i]);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(true);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
guiState = MAIN_MENU_STATE;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showAbout() {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 20);
|
2022-01-05 03:25:22 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
display.print("LibVer: ");
|
|
|
|
display.println(WATCHY_LIB_VER);
|
2022-01-05 03:25:22 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
const char *RTC_HW[3] = {"<UNKNOWN>", "DS3231", "PCF8563"};
|
|
|
|
display.print("RTC: ");
|
|
|
|
display.println(RTC_HW[RTC.rtcType]); // 0 = UNKNOWN, 1 = DS3231, 2 = PCF8563
|
2022-01-05 03:25:22 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
display.print("Batt: ");
|
|
|
|
float voltage = getBatteryVoltage();
|
|
|
|
display.print(voltage);
|
|
|
|
display.println("V");
|
2022-01-05 03:25:22 -05:00
|
|
|
|
2023-01-12 07:40:27 -05:00
|
|
|
display.print("Uptime: ");
|
|
|
|
RTC.read(currentTime);
|
|
|
|
time_t b = makeTime(bootTime);
|
|
|
|
time_t c = makeTime(currentTime);
|
|
|
|
int totalSeconds = c-b;
|
|
|
|
//int seconds = (totalSeconds % 60);
|
|
|
|
int minutes = (totalSeconds % 3600) / 60;
|
|
|
|
int hours = (totalSeconds % 86400) / 3600;
|
|
|
|
int days = (totalSeconds % (86400 * 30)) / 86400;
|
|
|
|
display.print(days);
|
|
|
|
display.print("d");
|
|
|
|
display.print(hours);
|
|
|
|
display.print("h");
|
|
|
|
display.print(minutes);
|
|
|
|
display.print("m");
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(false); // full refresh
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
guiState = APP_STATE;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showBuzz() {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(70, 80);
|
|
|
|
display.println("Buzz!");
|
|
|
|
display.display(false); // full refresh
|
|
|
|
vibMotor();
|
|
|
|
showMenu(menuIndex, false);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::vibMotor(uint8_t intervalMs, uint8_t length) {
|
|
|
|
pinMode(VIB_MOTOR_PIN, OUTPUT);
|
|
|
|
bool motorOn = false;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
motorOn = !motorOn;
|
|
|
|
digitalWrite(VIB_MOTOR_PIN, motorOn);
|
|
|
|
delay(intervalMs);
|
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::setTime() {
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
guiState = APP_STATE;
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
RTC.read(currentTime);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
int8_t minute = currentTime.Minute;
|
|
|
|
int8_t hour = currentTime.Hour;
|
|
|
|
int8_t day = currentTime.Day;
|
|
|
|
int8_t month = currentTime.Month;
|
|
|
|
int8_t year = tmYearToY2k(currentTime.Year);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
int8_t setIndex = SET_HOUR;
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
int8_t blink = 0;
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
pinMode(DOWN_BTN_PIN, INPUT);
|
|
|
|
pinMode(UP_BTN_PIN, INPUT);
|
|
|
|
pinMode(MENU_BTN_PIN, INPUT);
|
|
|
|
pinMode(BACK_BTN_PIN, INPUT);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
display.setFullWindow();
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
while (1) {
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
if (digitalRead(MENU_BTN_PIN) == 1) {
|
|
|
|
setIndex++;
|
|
|
|
if (setIndex > SET_DAY) {
|
2021-12-12 17:12:31 -05:00
|
|
|
break;
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
if (digitalRead(BACK_BTN_PIN) == 1) {
|
|
|
|
if (setIndex != SET_HOUR) {
|
2021-12-12 17:12:31 -05:00
|
|
|
setIndex--;
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
|
|
|
blink = 1 - blink;
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
if (digitalRead(DOWN_BTN_PIN) == 1) {
|
|
|
|
blink = 1;
|
|
|
|
switch (setIndex) {
|
|
|
|
case SET_HOUR:
|
|
|
|
hour == 23 ? (hour = 0) : hour++;
|
|
|
|
break;
|
|
|
|
case SET_MINUTE:
|
|
|
|
minute == 59 ? (minute = 0) : minute++;
|
|
|
|
break;
|
|
|
|
case SET_YEAR:
|
|
|
|
year == 99 ? (year = 0) : year++;
|
|
|
|
break;
|
|
|
|
case SET_MONTH:
|
|
|
|
month == 12 ? (month = 1) : month++;
|
|
|
|
break;
|
|
|
|
case SET_DAY:
|
|
|
|
day == 31 ? (day = 1) : day++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
if (digitalRead(UP_BTN_PIN) == 1) {
|
|
|
|
blink = 1;
|
|
|
|
switch (setIndex) {
|
|
|
|
case SET_HOUR:
|
|
|
|
hour == 0 ? (hour = 23) : hour--;
|
|
|
|
break;
|
|
|
|
case SET_MINUTE:
|
|
|
|
minute == 0 ? (minute = 59) : minute--;
|
|
|
|
break;
|
|
|
|
case SET_YEAR:
|
|
|
|
year == 0 ? (year = 99) : year--;
|
|
|
|
break;
|
|
|
|
case SET_MONTH:
|
|
|
|
month == 1 ? (month = 12) : month--;
|
|
|
|
break;
|
|
|
|
case SET_DAY:
|
|
|
|
day == 1 ? (day = 31) : day--;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setFont(&DSEG7_Classic_Bold_53);
|
|
|
|
|
|
|
|
display.setCursor(5, 80);
|
2022-04-28 21:17:00 -04:00
|
|
|
if (setIndex == SET_HOUR) { // blink hour digits
|
|
|
|
display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
if (hour < 10) {
|
|
|
|
display.print("0");
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
display.print(hour);
|
|
|
|
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.print(":");
|
|
|
|
|
|
|
|
display.setCursor(108, 80);
|
2022-04-28 21:17:00 -04:00
|
|
|
if (setIndex == SET_MINUTE) { // blink minute digits
|
|
|
|
display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
if (minute < 10) {
|
|
|
|
display.print("0");
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
display.print(minute);
|
|
|
|
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setCursor(45, 150);
|
2022-04-28 21:17:00 -04:00
|
|
|
if (setIndex == SET_YEAR) { // blink minute digits
|
|
|
|
display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK);
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
display.print(2000 + year);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.print("/");
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
if (setIndex == SET_MONTH) { // blink minute digits
|
|
|
|
display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK);
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
if (month < 10) {
|
|
|
|
display.print("0");
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
display.print(month);
|
|
|
|
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.print("/");
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
if (setIndex == SET_DAY) { // blink minute digits
|
|
|
|
display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK);
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
if (day < 10) {
|
|
|
|
display.print("0");
|
2021-12-30 23:16:03 -05:00
|
|
|
}
|
|
|
|
display.print(day);
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(true); // partial refresh
|
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
tmElements_t tm;
|
|
|
|
tm.Month = month;
|
|
|
|
tm.Day = day;
|
|
|
|
tm.Year = y2kYearToTm(year);
|
|
|
|
tm.Hour = hour;
|
|
|
|
tm.Minute = minute;
|
|
|
|
tm.Second = 0;
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
RTC.set(tm);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
showMenu(menuIndex, false);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showAccelerometer() {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
Accel acc;
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
long previousMillis = 0;
|
|
|
|
long interval = 200;
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
guiState = APP_STATE;
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
pinMode(BACK_BTN_PIN, INPUT);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
while (1) {
|
2021-12-12 17:12:31 -05:00
|
|
|
|
|
|
|
unsigned long currentMillis = millis();
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
if (digitalRead(BACK_BTN_PIN) == 1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentMillis - previousMillis > interval) {
|
|
|
|
previousMillis = currentMillis;
|
|
|
|
// Get acceleration data
|
|
|
|
bool res = sensor.getAccel(acc);
|
|
|
|
uint8_t direction = sensor.getDirection();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
if (res == false) {
|
|
|
|
display.println("getAccel FAIL");
|
|
|
|
} else {
|
|
|
|
display.print(" X:");
|
|
|
|
display.println(acc.x);
|
|
|
|
display.print(" Y:");
|
|
|
|
display.println(acc.y);
|
|
|
|
display.print(" Z:");
|
|
|
|
display.println(acc.z);
|
2021-12-12 17:12:31 -05:00
|
|
|
|
|
|
|
display.setCursor(30, 130);
|
2022-04-28 21:17:00 -04:00
|
|
|
switch (direction) {
|
|
|
|
case DIRECTION_DISP_DOWN:
|
|
|
|
display.println("FACE DOWN");
|
|
|
|
break;
|
|
|
|
case DIRECTION_DISP_UP:
|
|
|
|
display.println("FACE UP");
|
|
|
|
break;
|
|
|
|
case DIRECTION_BOTTOM_EDGE:
|
|
|
|
display.println("BOTTOM EDGE");
|
|
|
|
break;
|
|
|
|
case DIRECTION_TOP_EDGE:
|
|
|
|
display.println("TOP EDGE");
|
|
|
|
break;
|
|
|
|
case DIRECTION_RIGHT_EDGE:
|
|
|
|
display.println("RIGHT EDGE");
|
|
|
|
break;
|
|
|
|
case DIRECTION_LEFT_EDGE:
|
|
|
|
display.println("LEFT EDGE");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
display.println("ERROR!!!");
|
|
|
|
break;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
|
|
|
display.display(true); // full refresh
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
showMenu(menuIndex, false);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showWatchFace(bool partialRefresh) {
|
2021-12-12 17:12:31 -05:00
|
|
|
display.setFullWindow();
|
|
|
|
drawWatchFace();
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(partialRefresh); // partial refresh
|
2021-12-12 17:12:31 -05:00
|
|
|
guiState = WATCHFACE_STATE;
|
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::drawWatchFace() {
|
|
|
|
display.setFont(&DSEG7_Classic_Bold_53);
|
|
|
|
display.setCursor(5, 53 + 60);
|
|
|
|
if (currentTime.Hour < 10) {
|
|
|
|
display.print("0");
|
|
|
|
}
|
|
|
|
display.print(currentTime.Hour);
|
|
|
|
display.print(":");
|
|
|
|
if (currentTime.Minute < 10) {
|
|
|
|
display.print("0");
|
|
|
|
}
|
|
|
|
display.println(currentTime.Minute);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
weatherData Watchy::getWeatherData() {
|
|
|
|
return getWeatherData(settings.cityID, settings.weatherUnit,
|
|
|
|
settings.weatherLang, settings.weatherURL,
|
|
|
|
settings.weatherAPIKey, settings.weatherUpdateInterval);
|
2021-12-30 22:47:38 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
weatherData Watchy::getWeatherData(String cityID, String units, String lang,
|
|
|
|
String url, String apiKey,
|
|
|
|
uint8_t updateInterval) {
|
|
|
|
currentWeather.isMetric = units == String("metric");
|
|
|
|
if (weatherIntervalCounter < 0) { //-1 on first run, set to updateInterval
|
|
|
|
weatherIntervalCounter = updateInterval;
|
|
|
|
}
|
|
|
|
if (weatherIntervalCounter >=
|
|
|
|
updateInterval) { // only update if WEATHER_UPDATE_INTERVAL has elapsed
|
|
|
|
// i.e. 30 minutes
|
|
|
|
if (connectWiFi()) {
|
|
|
|
HTTPClient http; // Use Weather API for live data if WiFi is connected
|
|
|
|
http.setConnectTimeout(3000); // 3 second max timeout
|
|
|
|
String weatherQueryURL = url + cityID + String("&units=") + units +
|
|
|
|
String("&lang=") + lang + String("&appid=") +
|
|
|
|
apiKey;
|
|
|
|
http.begin(weatherQueryURL.c_str());
|
|
|
|
int httpResponseCode = http.GET();
|
|
|
|
if (httpResponseCode == 200) {
|
|
|
|
String payload = http.getString();
|
|
|
|
JSONVar responseObject = JSON.parse(payload);
|
|
|
|
currentWeather.temperature = int(responseObject["main"]["temp"]);
|
|
|
|
currentWeather.weatherConditionCode =
|
|
|
|
int(responseObject["weather"][0]["id"]);
|
|
|
|
currentWeather.weatherDescription =
|
2022-11-26 16:07:29 -05:00
|
|
|
JSONVar::stringify(responseObject["weather"][0]["main"]);
|
2022-12-29 08:08:24 -05:00
|
|
|
currentWeather.external = true;
|
2022-10-14 22:23:06 -04:00
|
|
|
// sync NTP during weather API call and use timezone of city
|
2022-10-15 08:09:06 -04:00
|
|
|
gmtOffset = int(responseObject["timezone"]);
|
2022-10-24 07:20:12 -04:00
|
|
|
syncNTP(gmtOffset);
|
2022-04-28 21:17:00 -04:00
|
|
|
} else {
|
|
|
|
// http error
|
|
|
|
}
|
|
|
|
http.end();
|
|
|
|
// turn off radios
|
|
|
|
WiFi.mode(WIFI_OFF);
|
|
|
|
btStop();
|
|
|
|
} else { // No WiFi, use internal temperature sensor
|
|
|
|
uint8_t temperature = sensor.readTemperature(); // celsius
|
|
|
|
if (!currentWeather.isMetric) {
|
|
|
|
temperature = temperature * 9. / 5. + 32.; // fahrenheit
|
|
|
|
}
|
|
|
|
currentWeather.temperature = temperature;
|
|
|
|
currentWeather.weatherConditionCode = 800;
|
2022-12-29 08:08:24 -05:00
|
|
|
currentWeather.external = false;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
weatherIntervalCounter = 0;
|
|
|
|
} else {
|
|
|
|
weatherIntervalCounter++;
|
|
|
|
}
|
|
|
|
return currentWeather;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
float Watchy::getBatteryVoltage() {
|
|
|
|
if (RTC.rtcType == DS3231) {
|
|
|
|
return analogReadMilliVolts(BATT_ADC_PIN) / 1000.0f *
|
|
|
|
2.0f; // Battery voltage goes through a 1/2 divider.
|
|
|
|
} else {
|
|
|
|
return analogReadMilliVolts(BATT_ADC_PIN) / 1000.0f * 2.0f;
|
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
uint16_t Watchy::_readRegister(uint8_t address, uint8_t reg, uint8_t *data,
|
|
|
|
uint16_t len) {
|
|
|
|
Wire.beginTransmission(address);
|
|
|
|
Wire.write(reg);
|
|
|
|
Wire.endTransmission();
|
|
|
|
Wire.requestFrom((uint8_t)address, (uint8_t)len);
|
|
|
|
uint8_t i = 0;
|
|
|
|
while (Wire.available()) {
|
|
|
|
data[i++] = Wire.read();
|
|
|
|
}
|
|
|
|
return 0;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
uint16_t Watchy::_writeRegister(uint8_t address, uint8_t reg, uint8_t *data,
|
|
|
|
uint16_t len) {
|
|
|
|
Wire.beginTransmission(address);
|
|
|
|
Wire.write(reg);
|
|
|
|
Wire.write(data, len);
|
|
|
|
return (0 != Wire.endTransmission());
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::_bmaConfig() {
|
2021-12-30 23:16:03 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
if (sensor.begin(_readRegister, _writeRegister, delay) == false) {
|
|
|
|
// fail to init BMA
|
|
|
|
return;
|
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
// Accel parameter structure
|
|
|
|
Acfg cfg;
|
|
|
|
/*!
|
|
|
|
Output data rate in Hz, Optional parameters:
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_0_78HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_1_56HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_3_12HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_6_25HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_12_5HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_25HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_50HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_100HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_200HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_400HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_800HZ
|
|
|
|
- BMA4_OUTPUT_DATA_RATE_1600HZ
|
|
|
|
*/
|
|
|
|
cfg.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
|
|
|
|
/*!
|
|
|
|
G-range, Optional parameters:
|
|
|
|
- BMA4_ACCEL_RANGE_2G
|
|
|
|
- BMA4_ACCEL_RANGE_4G
|
|
|
|
- BMA4_ACCEL_RANGE_8G
|
|
|
|
- BMA4_ACCEL_RANGE_16G
|
|
|
|
*/
|
|
|
|
cfg.range = BMA4_ACCEL_RANGE_2G;
|
|
|
|
/*!
|
|
|
|
Bandwidth parameter, determines filter configuration, Optional parameters:
|
|
|
|
- BMA4_ACCEL_OSR4_AVG1
|
|
|
|
- BMA4_ACCEL_OSR2_AVG2
|
|
|
|
- BMA4_ACCEL_NORMAL_AVG4
|
|
|
|
- BMA4_ACCEL_CIC_AVG8
|
|
|
|
- BMA4_ACCEL_RES_AVG16
|
|
|
|
- BMA4_ACCEL_RES_AVG32
|
|
|
|
- BMA4_ACCEL_RES_AVG64
|
|
|
|
- BMA4_ACCEL_RES_AVG128
|
|
|
|
*/
|
|
|
|
cfg.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
|
|
|
|
|
|
|
|
/*! Filter performance mode , Optional parameters:
|
|
|
|
- BMA4_CIC_AVG_MODE
|
|
|
|
- BMA4_CONTINUOUS_MODE
|
|
|
|
*/
|
|
|
|
cfg.perf_mode = BMA4_CONTINUOUS_MODE;
|
|
|
|
|
|
|
|
// Configure the BMA423 accelerometer
|
|
|
|
sensor.setAccelConfig(cfg);
|
|
|
|
|
|
|
|
// Enable BMA423 accelerometer
|
|
|
|
// Warning : Need to use feature, you must first enable the accelerometer
|
|
|
|
// Warning : Need to use feature, you must first enable the accelerometer
|
|
|
|
sensor.enableAccel();
|
|
|
|
|
|
|
|
struct bma4_int_pin_config config;
|
|
|
|
config.edge_ctrl = BMA4_LEVEL_TRIGGER;
|
|
|
|
config.lvl = BMA4_ACTIVE_HIGH;
|
|
|
|
config.od = BMA4_PUSH_PULL;
|
|
|
|
config.output_en = BMA4_OUTPUT_ENABLE;
|
|
|
|
config.input_en = BMA4_INPUT_DISABLE;
|
|
|
|
// The correct trigger interrupt needs to be configured as needed
|
|
|
|
sensor.setINTPinConfig(config, BMA4_INTR1_MAP);
|
|
|
|
|
|
|
|
struct bma423_axes_remap remap_data;
|
|
|
|
remap_data.x_axis = 1;
|
|
|
|
remap_data.x_axis_sign = 0xFF;
|
|
|
|
remap_data.y_axis = 0;
|
|
|
|
remap_data.y_axis_sign = 0xFF;
|
|
|
|
remap_data.z_axis = 2;
|
|
|
|
remap_data.z_axis_sign = 0xFF;
|
|
|
|
// Need to raise the wrist function, need to set the correct axis
|
|
|
|
sensor.setRemapAxes(&remap_data);
|
|
|
|
|
|
|
|
// Enable BMA423 isStepCounter feature
|
|
|
|
sensor.enableFeature(BMA423_STEP_CNTR, true);
|
|
|
|
// Enable BMA423 isTilt feature
|
|
|
|
sensor.enableFeature(BMA423_TILT, true);
|
|
|
|
// Enable BMA423 isDoubleClick feature
|
|
|
|
sensor.enableFeature(BMA423_WAKEUP, true);
|
|
|
|
|
|
|
|
// Reset steps
|
|
|
|
sensor.resetStepCounter();
|
|
|
|
|
|
|
|
// Turn on feature interrupt
|
|
|
|
sensor.enableStepCountInterrupt();
|
|
|
|
sensor.enableTiltInterrupt();
|
|
|
|
// It corresponds to isDoubleClick interrupt
|
|
|
|
sensor.enableWakeupInterrupt();
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::setupWifi() {
|
|
|
|
display.epd2.setBusyCallback(0); // temporarily disable lightsleep on busy
|
|
|
|
WiFiManager wifiManager;
|
|
|
|
wifiManager.resetSettings();
|
|
|
|
wifiManager.setTimeout(WIFI_AP_TIMEOUT);
|
|
|
|
wifiManager.setAPCallback(_configModeCallback);
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
if (!wifiManager.autoConnect(WIFI_AP_SSID)) { // WiFi setup failed
|
|
|
|
display.println("Setup failed &");
|
|
|
|
display.println("timed out!");
|
|
|
|
} else {
|
|
|
|
display.println("Connected to");
|
|
|
|
display.println(WiFi.SSID());
|
2022-12-27 09:54:15 -05:00
|
|
|
display.println("Local IP:");
|
|
|
|
display.println(WiFi.localIP());
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
|
|
|
display.display(false); // full refresh
|
|
|
|
// turn off radios
|
|
|
|
WiFi.mode(WIFI_OFF);
|
|
|
|
btStop();
|
|
|
|
display.epd2.setBusyCallback(displayBusyCallback); // enable lightsleep on
|
|
|
|
// busy
|
|
|
|
guiState = APP_STATE;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::_configModeCallback(WiFiManager *myWiFiManager) {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("Connect to");
|
|
|
|
display.print("SSID: ");
|
|
|
|
display.println(WIFI_AP_SSID);
|
|
|
|
display.print("IP: ");
|
|
|
|
display.println(WiFi.softAPIP());
|
2022-12-27 09:54:15 -05:00
|
|
|
display.println("MAC address:");
|
|
|
|
display.println(WiFi.softAPmacAddress().c_str());
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(false); // full refresh
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
bool Watchy::connectWiFi() {
|
|
|
|
if (WL_CONNECT_FAILED ==
|
|
|
|
WiFi.begin()) { // WiFi not setup, you can also use hard coded credentials
|
|
|
|
// with WiFi.begin(SSID,PASS);
|
|
|
|
WIFI_CONFIGURED = false;
|
|
|
|
} else {
|
|
|
|
if (WL_CONNECTED ==
|
|
|
|
WiFi.waitForConnectResult()) { // attempt to connect for 10s
|
|
|
|
WIFI_CONFIGURED = true;
|
|
|
|
} else { // connection failed, time out
|
|
|
|
WIFI_CONFIGURED = false;
|
|
|
|
// turn off radios
|
|
|
|
WiFi.mode(WIFI_OFF);
|
|
|
|
btStop();
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
|
|
|
return WIFI_CONFIGURED;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showUpdateFW() {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("Please visit");
|
|
|
|
display.println("watchy.sqfmi.com");
|
|
|
|
display.println("with a Bluetooth");
|
|
|
|
display.println("enabled device");
|
|
|
|
display.println(" ");
|
|
|
|
display.println("Press menu button");
|
|
|
|
display.println("again when ready");
|
|
|
|
display.println(" ");
|
|
|
|
display.println("Keep USB powered");
|
|
|
|
display.display(false); // full refresh
|
|
|
|
|
|
|
|
guiState = FW_UPDATE_STATE;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::updateFWBegin() {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("Bluetooth Started");
|
|
|
|
display.println(" ");
|
|
|
|
display.println("Watchy BLE OTA");
|
|
|
|
display.println(" ");
|
|
|
|
display.println("Waiting for");
|
|
|
|
display.println("connection...");
|
|
|
|
display.display(false); // full refresh
|
|
|
|
|
|
|
|
BLE BT;
|
|
|
|
BT.begin("Watchy BLE OTA");
|
|
|
|
int prevStatus = -1;
|
|
|
|
int currentStatus;
|
|
|
|
|
|
|
|
while (1) {
|
2021-12-12 17:12:31 -05:00
|
|
|
currentStatus = BT.updateStatus();
|
2022-04-28 21:17:00 -04:00
|
|
|
if (prevStatus != currentStatus || prevStatus == 1) {
|
|
|
|
if (currentStatus == 0) {
|
2021-12-12 17:12:31 -05:00
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("BLE Connected!");
|
|
|
|
display.println(" ");
|
|
|
|
display.println("Waiting for");
|
|
|
|
display.println("upload...");
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(false); // full refresh
|
|
|
|
}
|
|
|
|
if (currentStatus == 1) {
|
2021-12-12 17:12:31 -05:00
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("Downloading");
|
|
|
|
display.println("firmware:");
|
|
|
|
display.println(" ");
|
|
|
|
display.print(BT.howManyBytes());
|
|
|
|
display.println(" bytes");
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(true); // partial refresh
|
|
|
|
}
|
|
|
|
if (currentStatus == 2) {
|
2021-12-12 17:12:31 -05:00
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("Download");
|
|
|
|
display.println("completed!");
|
|
|
|
display.println(" ");
|
|
|
|
display.println("Rebooting...");
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(false); // full refresh
|
2021-12-12 17:12:31 -05:00
|
|
|
|
|
|
|
delay(2000);
|
2021-12-30 23:16:03 -05:00
|
|
|
esp_restart();
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
|
|
|
if (currentStatus == 4) {
|
2021-12-12 17:12:31 -05:00
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("BLE Disconnected!");
|
|
|
|
display.println(" ");
|
|
|
|
display.println("exiting...");
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(false); // full refresh
|
2021-12-12 17:12:31 -05:00
|
|
|
delay(1000);
|
|
|
|
break;
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
|
|
|
prevStatus = currentStatus;
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
delay(100);
|
2022-04-28 21:17:00 -04:00
|
|
|
}
|
2021-12-12 17:12:31 -05:00
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
// turn off radios
|
|
|
|
WiFi.mode(WIFI_OFF);
|
|
|
|
btStop();
|
|
|
|
showMenu(menuIndex, false);
|
2021-12-12 17:12:31 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
void Watchy::showSyncNTP() {
|
|
|
|
display.setFullWindow();
|
|
|
|
display.fillScreen(GxEPD_BLACK);
|
|
|
|
display.setFont(&FreeMonoBold9pt7b);
|
|
|
|
display.setTextColor(GxEPD_WHITE);
|
|
|
|
display.setCursor(0, 30);
|
|
|
|
display.println("Syncing NTP... ");
|
2022-10-15 08:09:06 -04:00
|
|
|
display.print("GMT offset: ");
|
|
|
|
display.println(gmtOffset);
|
2022-04-28 21:17:00 -04:00
|
|
|
display.display(false); // full refresh
|
|
|
|
if (connectWiFi()) {
|
|
|
|
if (syncNTP()) {
|
|
|
|
display.println("NTP Sync Success\n");
|
|
|
|
display.println("Current Time Is:");
|
|
|
|
|
|
|
|
RTC.read(currentTime);
|
|
|
|
|
|
|
|
display.print(tmYearToCalendar(currentTime.Year));
|
|
|
|
display.print("/");
|
|
|
|
display.print(currentTime.Month);
|
|
|
|
display.print("/");
|
|
|
|
display.print(currentTime.Day);
|
|
|
|
display.print(" - ");
|
|
|
|
|
|
|
|
if (currentTime.Hour < 10) {
|
|
|
|
display.print("0");
|
|
|
|
}
|
|
|
|
display.print(currentTime.Hour);
|
|
|
|
display.print(":");
|
|
|
|
if (currentTime.Minute < 10) {
|
|
|
|
display.print("0");
|
|
|
|
}
|
|
|
|
display.println(currentTime.Minute);
|
|
|
|
} else {
|
|
|
|
display.println("NTP Sync Failed");
|
2021-12-30 01:50:13 -05:00
|
|
|
}
|
2022-04-28 21:17:00 -04:00
|
|
|
} else {
|
|
|
|
display.println("WiFi Not Configured");
|
|
|
|
}
|
|
|
|
display.display(true); // full refresh
|
|
|
|
delay(3000);
|
|
|
|
showMenu(menuIndex, false);
|
2021-12-30 13:02:40 -05:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:17:00 -04:00
|
|
|
bool Watchy::syncNTP() { // NTP sync - call after connecting to WiFi and
|
|
|
|
// remember to turn it back off
|
2022-10-15 08:09:06 -04:00
|
|
|
return syncNTP(gmtOffset,
|
2022-04-28 21:17:00 -04:00
|
|
|
settings.ntpServer.c_str());
|
2021-12-30 22:47:38 -05:00
|
|
|
}
|
|
|
|
|
2022-10-14 22:23:06 -04:00
|
|
|
bool Watchy::syncNTP(long gmt) {
|
|
|
|
return syncNTP(gmt, settings.ntpServer.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Watchy::syncNTP(long gmt, String ntpServer) {
|
|
|
|
// NTP sync - call after connecting to
|
|
|
|
// WiFi and remember to turn it back off
|
2022-04-28 21:17:00 -04:00
|
|
|
WiFiUDP ntpUDP;
|
|
|
|
NTPClient timeClient(ntpUDP, ntpServer.c_str(), gmt);
|
|
|
|
timeClient.begin();
|
|
|
|
if (!timeClient.forceUpdate()) {
|
|
|
|
return false; // NTP sync failed
|
|
|
|
}
|
|
|
|
tmElements_t tm;
|
|
|
|
breakTime((time_t)timeClient.getEpochTime(), tm);
|
|
|
|
RTC.set(tm);
|
|
|
|
return true;
|
2021-12-30 01:50:13 -05:00
|
|
|
}
|