From 4e8f5fc1fb4fb40d70b456b823374c2f396aeb6f Mon Sep 17 00:00:00 2001 From: Michael-Paul Moore Date: Mon, 11 Apr 2022 13:31:21 -0700 Subject: [PATCH] Finaly can build. --- library.json | 2 + src/BLE.cpp | 3 + src/Watchy.cpp | 1012 ----------------------------- src/Watchy.h | 90 --- src/WatchyExpanded.cpp | 21 +- src/WatchyExpanded.h | 6 +- src/{config.h => watchy_config.h} | 38 +- 7 files changed, 51 insertions(+), 1121 deletions(-) delete mode 100644 src/Watchy.cpp delete mode 100644 src/Watchy.h rename src/{config.h => watchy_config.h} (50%) diff --git a/library.json b/library.json index 32a1d37..c0235c7 100644 --- a/library.json +++ b/library.json @@ -20,6 +20,8 @@ { "name": "Arduino_JSON" }, { "name": "DS3232RTC" }, { "name": "NTPClient" }, + { "name": "ESP32 BLE Arduino" }, + { "name": "Adafruit BusIO" }, { "name": "Rtc_Pcf8563", "version": "https://github.com/orbitalair/Rtc_Pcf8563.git#master" diff --git a/src/BLE.cpp b/src/BLE.cpp index 17f2433..70dd92e 100644 --- a/src/BLE.cpp +++ b/src/BLE.cpp @@ -1,5 +1,8 @@ #include "BLE.h" +// Expanded +#include "watchy_config.h" + #define SERVICE_UUID_ESPOTA "cd77498e-1ac8-48b6-aba8-4161c7342fce" #define CHARACTERISTIC_UUID_ID "cd77498f-1ac8-48b6-aba8-4161c7342fce" diff --git a/src/Watchy.cpp b/src/Watchy.cpp deleted file mode 100644 index 4477ad4..0000000 --- a/src/Watchy.cpp +++ /dev/null @@ -1,1012 +0,0 @@ -#include "Watchy.h" - -#include "DSEG7_Classic_Bold_53.h" - -WatchyRTC Watchy::RTC; -GxEPD2_BW Watchy::display(GxEPD2_154_D67(DISPLAY_CS, DISPLAY_DC, DISPLAY_RES, DISPLAY_BUSY)); - -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; -RTC_DATA_ATTR int weatherIntervalCounter = -1; -RTC_DATA_ATTR bool displayFullInit = true; - -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 - 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 - if(guiState == kWatchFace_State){ - RTC.read(currentTime); - showWatchFace(true); //partial updates on tick - } - break; - case ESP_SLEEP_WAKEUP_EXT1: //button Press - handleButtonPress(); - break; - default: //reset - RTC.config(datetime); - _bmaConfig(); - RTC.read(currentTime); - showWatchFace(false); //full update on reset - break; - } - deepSleep(); -} - -void Watchy::displayBusyCallback(const void*) -{ - gpio_wakeup_enable((gpio_num_t)BUSY, GPIO_INTR_LOW_LEVEL); - esp_sleep_enable_gpio_wakeup(); - esp_light_sleep_start(); -} - -void Watchy::deepSleep() -{ - display.hibernate(); - displayFullInit = false; // Notify not to init it again - RTC.clearAlarm(); //resets the alarm flag in the RTC - // Set pins 0-39 to input to avoid power leaking out - for(int i=0; i<40; i++) { - 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(); -} - -void Watchy::handleButtonPress(){ - uint64_t wakeupBit = esp_sleep_get_ext1_wakeup_status(); - //Menu Button - if (wakeupBit & MENU_BTN_MASK){ - if(guiState == kWatchFace_State){//enter menu state if coming from watch face - showMenu(menuIndex, false); - }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; - } - }else if(guiState == FW_UPDATE_STATE){ - updateFWBegin(); - } - } - //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 == kWatchFace_State){ - return; - } - } - //Up Button - else if (wakeupBit & UP_BTN_MASK){ - if(guiState == MAIN_MENU_STATE){//increment menu index - menuIndex--; - if(menuIndex < 0){ - menuIndex = MENU_LENGTH - 1; - } - showMenu(menuIndex, true); - }else if(guiState == kWatchFace_State){ - return; - } - } - //Down Button - else if (wakeupBit & DOWN_BTN_MASK){ - if(guiState == MAIN_MENU_STATE){//decrement menu index - menuIndex++; - if(menuIndex > MENU_LENGTH - 1){ - menuIndex = 0; - } - showMenu(menuIndex, true); - }else if(guiState == kWatchFace_State){ - return; - } - } - - /***************** fast menu *****************/ - bool timeout = false; - long lastTimeout = millis(); - pinMode(MENU_BTN_PIN, INPUT); - pinMode(BACK_BTN_PIN, INPUT); - pinMode(UP_BTN_PIN, INPUT); - pinMode(DOWN_BTN_PIN, INPUT); - 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; - } - showFastMenu(menuIndex); - } - } - } - } -} - -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", "DS3231", "PCF8563" }; - display.print("RTC: "); - display.println(RTC_HW[RTC.rtcType]); //0 = UNKNOWN, 1 = DS3231, 2 = PCF8563 - - display.print("Batt: "); - float voltage = getBatteryVoltage(); - display.print(voltage); - display.println("V"); - - display.display(false); //full refresh - - guiState = APP_STATE; -} - -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); -} - -void Watchy::vibMotor(uint8_t intervalMs, uint8_t length){ - pinMode(VIB_MOTOR_PIN, OUTPUT); - bool motorOn = false; - for(int i=0; i SET_DAY){ - break; - } - } - if(digitalRead(BACK_BTN_PIN) == 1){ - if(setIndex != SET_HOUR){ - setIndex--; - } - } - - blink = 1 - blink; - - 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; - } - } - - 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; - } - } - - display.fillScreen(GxEPD_BLACK); - display.setTextColor(GxEPD_WHITE); - display.setFont(&DSEG7_Classic_Bold_53); - - display.setCursor(5, 80); - if(setIndex == SET_HOUR){//blink hour digits - display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK); - } - if(hour < 10){ - display.print("0"); - } - display.print(hour); - - display.setTextColor(GxEPD_WHITE); - display.print(":"); - - display.setCursor(108, 80); - if(setIndex == SET_MINUTE){//blink minute digits - display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK); - } - if(minute < 10){ - display.print("0"); - } - display.print(minute); - - display.setTextColor(GxEPD_WHITE); - - display.setFont(&FreeMonoBold9pt7b); - display.setCursor(45, 150); - if(setIndex == SET_YEAR){//blink minute digits - display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK); - } - display.print(2000+year); - - display.setTextColor(GxEPD_WHITE); - display.print("/"); - - if(setIndex == SET_MONTH){//blink minute digits - display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK); - } - if(month < 10){ - display.print("0"); - } - display.print(month); - - display.setTextColor(GxEPD_WHITE); - display.print("/"); - - if(setIndex == SET_DAY){//blink minute digits - display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK); - } - if(day < 10){ - display.print("0"); - } - display.print(day); - display.display(true); //partial refresh - } - - tmElements_t tm; - tm.Month = month; - tm.Day = day; - tm.Year = y2kYearToTm(year); - tm.Hour = hour; - tm.Minute = minute; - tm.Second = 0; - - RTC.set(tm); - - showMenu(menuIndex, false); - -} - -void Watchy::showAccelerometer(){ - display.setFullWindow(); - display.fillScreen(GxEPD_BLACK); - display.setFont(&FreeMonoBold9pt7b); - display.setTextColor(GxEPD_WHITE); - - Accel acc; - - long previousMillis = 0; - long interval = 200; - - guiState = APP_STATE; - - pinMode(BACK_BTN_PIN, INPUT); - - while(1){ - - unsigned long currentMillis = millis(); - - 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); - - display.setCursor(30, 130); - 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; - } - - } - display.display(true); //full refresh - } - } - - showMenu(menuIndex, false); -} - -void Watchy::showWatchFace(bool partialRefresh){ - display.setFullWindow(); - drawWatchFace(); - display.display(partialRefresh); //partial refresh - guiState = kWatchFace_State; -} - -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); -} - -weatherData Watchy::getWeatherData(){ - return getWeatherData(settings.cityID, settings.weatherUnit, settings.weatherLang, settings.weatherURL, settings.weatherAPIKey, settings.weatherUpdateInterval); -} - -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 = responseObject["weather"][0]["main"]; - }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; - } - weatherIntervalCounter = 0; - }else{ - weatherIntervalCounter++; - } - return currentWeather; -} - -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; - } -} - -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; -} - -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()); -} - -void Watchy::_bmaConfig(){ - - if (sensor.begin(_readRegister, _writeRegister, delay) == false) { - //fail to init BMA - return; - } - - // 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(); -} - -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()); - } - display.display(false); //full refresh - //turn off radios - WiFi.mode(WIFI_OFF); - btStop(); - display.epd2.setBusyCallback(displayBusyCallback); //enable lightsleep on busy - guiState = APP_STATE; -} - -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()); - display.display(false); //full refresh -} - -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(); - } - } - return WIFI_CONFIGURED; -} - -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; -} - -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){ - currentStatus = BT.updateStatus(); - if(prevStatus != currentStatus || prevStatus == 1){ - if(currentStatus == 0){ - 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..."); - display.display(false); //full refresh - } - if(currentStatus == 1){ - 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"); - display.display(true); //partial refresh - } - if(currentStatus == 2){ - 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..."); - display.display(false); //full refresh - - delay(2000); - esp_restart(); - } - if(currentStatus == 4){ - 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..."); - display.display(false); //full refresh - delay(1000); - break; - } - prevStatus = currentStatus; - } - delay(100); - } - - //turn off radios - WiFi.mode(WIFI_OFF); - btStop(); - showMenu(menuIndex, false); -} - -void Watchy::showSyncNTP(){ - display.setFullWindow(); - display.fillScreen(GxEPD_BLACK); - display.setFont(&FreeMonoBold9pt7b); - display.setTextColor(GxEPD_WHITE); - display.setCursor(0, 30); - display.println("Syncing NTP... "); - 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"); - } - }else{ - display.println("WiFi Not Configured"); - } - display.display(true); //full refresh - delay(3000); - showMenu(menuIndex, false); -} - -bool Watchy::syncNTP(){ //NTP sync - call after connecting to WiFi and remember to turn it back off - return syncNTP(settings.gmtOffset, settings.dstOffset, settings.ntpServer.c_str()); -} - -bool Watchy::syncNTP(long gmt, int dst, String ntpServer){ //NTP sync - call after connecting to WiFi and remember to turn it back off - 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; -} - - -JSONVar& Watchy::getWeatherJSON() -{ - getWeatherJSON(settings.cityID, settings.weatherUnit, settings.weatherLang, settings.weatherURL, - settings.weatherAPIKey, settings.weatherUpdateInterval); - return m_responseObject; -} - -JSONVar& Watchy::getWeatherJSON(String cityID, String units, String lang, String url, String apiKey, uint8_t updateInterval) -{ - 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 - const 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(); - const JSONVar& jsonNew = JSON.parse(payload); - const JSONVar jsonOld = m_responseObject; - m_responseObject = jsonNew; - if (!json.hasOwnProperty("weather")) - m_responseObject["weather"] = jsonOld["weather"]; - } - else - { - //http error - } - - http.end(); - //turn off radios - WiFi.mode(WIFI_OFF); - btStop(); - } - weatherIntervalCounter = 0; - } - else - { - ++weatherIntervalCounter; - } - - return m_responseObject; -} diff --git a/src/Watchy.h b/src/Watchy.h deleted file mode 100644 index 97e5047..0000000 --- a/src/Watchy.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "DSEG7_Classic_Bold_53.h" -#include "WatchyRTC.h" -#include "BLE.h" -#include "bma.h" -#include "config.h" - -typedef struct weatherData{ - int8_t temperature; - int16_t weatherConditionCode; - bool isMetric; - String weatherDescription; -}weatherData; - -typedef struct watchySettings{ - //Weather Settings - String cityID; - String weatherAPIKey; - String weatherURL; - String weatherUnit; - String weatherLang; - int8_t weatherUpdateInterval; - //NTP Settings - String ntpServer; - int gmtOffset; - int dstOffset; -} watchySettings; - -class Watchy -{ - public: - static WatchyRTC RTC; - static GxEPD2_BW display; - tmElements_t currentTime; - watchySettings settings; - public: - explicit Watchy(const watchySettings& s) : settings(s){} //constructor - void init(String datetime = ""); - void deepSleep(); - static void displayBusyCallback(const void*); - float getBatteryVoltage(); - void vibMotor(uint8_t intervalMs = 100, uint8_t length = 20); - - void handleButtonPress(); - void showMenu(byte menuIndex, bool partialRefresh); - void showFastMenu(byte menuIndex); - void showAbout(); - void showBuzz(); - void showAccelerometer(); - void showUpdateFW(); - void showSyncNTP(); - bool syncNTP(); - bool syncNTP(long gmt, int dst, String ntpServer); - void setTime(); - void setupWifi(); - bool connectWiFi(); - weatherData getWeatherData(); - weatherData getWeatherData(String cityID, String units, String lang, String url, String apiKey, uint8_t updateInterval); - void updateFWBegin(); - - void showWatchFace(bool partialRefresh); - virtual void drawWatchFace(); //override this method for different watch faces - - // Expanded - JSONVar& getWeatherJSON(); - JSONVar& getWeatherJSON(String cityID, String units, String lang, String url, String apiKey, - uint8_t updateInterval); - - private: - void _bmaConfig(); - static void _configModeCallback(WiFiManager *myWiFiManager); - static uint16_t _readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len); - static uint16_t _writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len); -}; - -extern RTC_DATA_ATTR int guiState; -extern RTC_DATA_ATTR int menuIndex; -extern RTC_DATA_ATTR BMA423 sensor; -extern RTC_DATA_ATTR bool WIFI_CONFIGURED; -extern RTC_DATA_ATTR bool BLE_CONFIGURED; diff --git a/src/WatchyExpanded.cpp b/src/WatchyExpanded.cpp index 73b55f3..9302d12 100644 --- a/src/WatchyExpanded.cpp +++ b/src/WatchyExpanded.cpp @@ -9,10 +9,7 @@ // Fonts #include -// Watchy -#include "WatchyRTC.h" - -CWatchyExpanded::CWatchyExpanded() : m_display(GxEPD2_154_D67(DISPLAY_CS, DISPLAY_DC, DISPLAY_RES, DISPLAY_BUSY)) +CWatchyExpanded::CWatchyExpanded() : m_display(GxEPD2_154_D67(wcd::cs, wcd::dc, wcd::reset, wcd::busy)) { } @@ -25,7 +22,7 @@ void CWatchyExpanded::Init() { const esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); //get wake up reason Wire.begin(SDA, SCL); //init i2c - RTC.init(); + m_rtc.init(); // Init the display here for all cases, if unused, it will do nothing m_display.init(0, true, 10, true); // 10ms by spec, and fast pulldown reset @@ -34,9 +31,9 @@ void CWatchyExpanded::Init() switch (wakeup_reason) { case ESP_SLEEP_WAKEUP_EXT0: //RTC Alarm - if(m_guiState == kWatchFace_State) + if(m_guiState == wc::kWatchFace_State) { - RTC.read(m_currentTime); + m_rtc.read(m_currentTime); UpdateScreen(); //partial updates on tick } break; @@ -55,7 +52,7 @@ void CWatchyExpanded::Init() void CWatchyExpanded::DisplayBusyCallback(const void*) { - gpio_wakeup_enable((gpio_num_t)BUSY, GPIO_INTR_LOW_LEVEL); + gpio_wakeup_enable(static_cast(wcd::busy), GPIO_INTR_LOW_LEVEL); esp_sleep_enable_gpio_wakeup(); esp_light_sleep_start(); } @@ -67,7 +64,7 @@ void CWatchyExpanded::UpdateScreen() DrawBasicClock(); // Temp m_display.display(true); //partial refresh - m_guiState = kWatchFace_State; + m_guiState = wc::kWatchFace_State; } void CWatchyExpanded::DrawBasicClock() @@ -98,12 +95,12 @@ void CWatchyExpanded::DrawBasicClock() void CWatchyExpanded::DeepSleep() { m_display.hibernate(); - RTC.clearAlarm(); //resets the alarm flag in the RTC + m_rtc.clearAlarm(); //resets the alarm flag in the RTC for(int i=0; i<40; i++) // Set pins 0-39 to input to avoid power leaking out 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_sleep_enable_ext0_wakeup(wc::rtc_pin, 0); //enable deep sleep wake on RTC interrupt + esp_sleep_enable_ext1_wakeup(wc::btn_pin_mask, ESP_EXT1_WAKEUP_ANY_HIGH); //enable deep sleep wake on button press esp_deep_sleep_start(); } diff --git a/src/WatchyExpanded.h b/src/WatchyExpanded.h index a263b73..639fe18 100644 --- a/src/WatchyExpanded.h +++ b/src/WatchyExpanded.h @@ -10,10 +10,11 @@ #include // Watchy -#include "config.h" +#include "WatchyRTC.h" // Expanded #include "WatchFace.h" +#include "watchy_config.h" // Defs class CWatchFace; @@ -40,5 +41,6 @@ class CWatchyExpanded GxEPD2_BW m_display; tmElements_t m_currentTime; - std::int8_t m_guiState = kWatchFace_State; + std::int8_t m_guiState = wc::kWatchFace_State; + WatchyRTC m_rtc; }; diff --git a/src/config.h b/src/watchy_config.h similarity index 50% rename from src/config.h rename to src/watchy_config.h index 6cb9a78..7ee851f 100644 --- a/src/config.h +++ b/src/watchy_config.h @@ -1,14 +1,9 @@ #pragma once -//display -#define DISPLAY_WIDTH 200 -#define DISPLAY_HEIGHT 200 -#define BTN_PIN_MASK MENU_BTN_MASK|BACK_BTN_MASK|UP_BTN_MASK|DOWN_BTN_MASK //wifi #define WIFI_AP_TIMEOUT 60 #define WIFI_AP_SSID "Watchy AP" //menu -constexpr std::int8_t kWatchFace_State{-1}; #define MAIN_MENU_STATE 0 #define APP_STATE 1 #define FW_UPDATE_STATE 2 @@ -31,3 +26,36 @@ constexpr std::int8_t kWatchFace_State{-1}; #define HARDWARE_VERSION_MINOR 0 //Versioning #define WATCHY_LIB_VER "1.4.0" + +namespace watchy_config +{ + +// Menu +constexpr std::int8_t kWatchFace_State{-1}; + +namespace display +{ +// Display +#define DISPLAY_WIDTH 200 +#define DISPLAY_HEIGHT 200 + +// display pins +constexpr std::uint8_t cs{5}; // Unsure what this means. +constexpr std::uint8_t dc{10}; // Unsure what this means. +constexpr std::uint8_t reset{9}; +constexpr std::uint8_t busy{19}; +}; + +// RTC pins +constexpr gpio_num_t rtc_pin{GPIO_NUM_27}; + +// btn pins & masks +constexpr std::uint64_t menu_btn_mask{GPIO_SEL_26}; +constexpr std::uint64_t back_btn_mask{GPIO_SEL_25}; +constexpr std::uint64_t up_btn_mask{GPIO_SEL_32}; +constexpr std::uint64_t down_btn_mask{GPIO_SEL_4}; +constexpr std::uint64_t btn_pin_mask{menu_btn_mask|back_btn_mask|up_btn_mask|down_btn_mask}; +}; + +namespace wc = watchy_config; +namespace wcd = watchy_config::display;