diff --git a/src/AbstractRTC.cpp b/src/AbstractRTC.cpp new file mode 100644 index 0000000..0fb9ed5 --- /dev/null +++ b/src/AbstractRTC.cpp @@ -0,0 +1,46 @@ +#include +#include "AbstractRTC.h" +#include "DS3232.h" +#include "PCF8563.h" + +#define RTC_DS_ADDR 0x68 +#define RTC_PCF_ADDR 0x51 + +static bool _canConnectTo(int addr) { + byte error; + Wire.beginTransmission(addr); + error = Wire.endTransmission(addr); + return error == 0; +} + +AbstractRTC* AbstractRTC::init() { + if (_canConnectTo(RTC_DS_ADDR)) { + return new DS3232(); + } + + if (_canConnectTo(RTC_PCF_ADDR)) { + return new PCF8563(); + } + + return new AbstractRTC(); +} + +String AbstractRTC::getValue(String data, char separator, int index) { + int found = 0; + int strIndex[] = {0, -1}; + int maxIndex = data.length()-1; + + for (int i=0; i <= maxIndex && found <= index; i++) { + if (data.charAt(i)==separator || i==maxIndex) { + found++; + strIndex[0] = strIndex[1] + 1; + strIndex[1] = (i == maxIndex) ? i + 1 : i; + } + } + + if (found > index) { + return data.substring(strIndex[0], strIndex[1]); + } + + return ""; +} \ No newline at end of file diff --git a/src/AbstractRTC.h b/src/AbstractRTC.h new file mode 100644 index 0000000..65a6276 --- /dev/null +++ b/src/AbstractRTC.h @@ -0,0 +1,29 @@ +#ifndef ABSTRACT_RTC_H +#define ABSTRACT_RTC_H +#define YEAR_OFFSET_DS3232 1970 +#define YEAR_OFFSET_PCF 2000 + +#define NO_TEMPERATURE_ERR 255 + +#define RTC_TYPE_DS3232 0 +#define RTC_TYPE_PCF8563 1 +#define RTC_TYPE_NONE -1 + +#include +#include + +class AbstractRTC { +public: + virtual ~AbstractRTC() {} + virtual void config(String datetime) {} + virtual void clearAlarm() {} + virtual void read(tmElements_t &tm) {} + virtual void set(tmElements_t tm) {} + virtual uint8_t temperature() { return NO_TEMPERATURE_ERR; } + virtual int rtcType(){ return RTC_TYPE_NONE; } + static AbstractRTC* init(); +protected: + String getValue(String data, char separator, int index); +}; + +#endif \ No newline at end of file diff --git a/src/DS3232.cpp b/src/DS3232.cpp new file mode 100644 index 0000000..506caee --- /dev/null +++ b/src/DS3232.cpp @@ -0,0 +1,44 @@ +#include "DS3232.h" + +void DS3232::config(String datetime) { + if (datetime != "") { + tmElements_t tm; + // offset from 1970, since year is stored in uint8_t + tm.Year = getValue(datetime, ':', 0).toInt() - YEAR_OFFSET_DS3232; + tm.Month = getValue(datetime, ':', 1).toInt(); + tm.Day = getValue(datetime, ':', 2).toInt(); + tm.Hour = getValue(datetime, ':', 3).toInt(); + tm.Minute = getValue(datetime, ':', 4).toInt(); + tm.Second = getValue(datetime, ':', 5).toInt(); + time_t t = makeTime(tm); + rtc_ds.set(t); + } + + // https://github.com/JChristensen/DS3232RTC + rtc_ds.squareWave(SQWAVE_NONE); //disable square wave output + rtc_ds.setAlarm(ALM2_EVERY_MINUTE, 0, 0, 0, 0); //alarm wakes up Watchy every minute + rtc_ds.alarmInterrupt(ALARM_2, true); //enable alarm interrupt +} + +void DS3232::clearAlarm() { + rtc_ds.alarm(ALARM_2); +} + +void DS3232::read(tmElements_t &tm) { + rtc_ds.read(tm); + tm.Year = tm.Year - 30; //reset to offset from 2000 +} + +void DS3232::set(tmElements_t tm) { + tm.Year = tm.Year + 2000 - YEAR_OFFSET_DS3232; + time_t t = makeTime(tm); + rtc_ds.set(t); +} + +uint8_t DS3232::temperature() { + return rtc_ds.temperature(); +} + +int DS3232::rtcType() { + return RTC_TYPE_DS3232; +} \ No newline at end of file diff --git a/src/DS3232.h b/src/DS3232.h new file mode 100644 index 0000000..631b37d --- /dev/null +++ b/src/DS3232.h @@ -0,0 +1,14 @@ +#include +#include "AbstractRTC.h" + +class DS3232 : public AbstractRTC { +public: + DS3232RTC rtc_ds; // TODO: We should not have public member variables + ~DS3232() {} + void config(String datetime); + void clearAlarm(); + void read(tmElements_t &tm); + void set(tmElements_t tm); + uint8_t temperature(); + int rtcType(); +}; \ No newline at end of file diff --git a/src/PCF8563.cpp b/src/PCF8563.cpp new file mode 100644 index 0000000..2027d4a --- /dev/null +++ b/src/PCF8563.cpp @@ -0,0 +1,63 @@ +#include "PCF8563.h" + +void PCF8563::config(String datetime) { + if (datetime != "") { + int Year = getValue(datetime, ':', 0).toInt(); + int Month = getValue(datetime, ':', 1).toInt(); + int Day = getValue(datetime, ':', 2).toInt(); + int Hour = getValue(datetime, ':', 3).toInt(); + int Minute = getValue(datetime, ':', 4).toInt(); + int Second = getValue(datetime, ':', 5).toInt(); + //day, weekday, month, century(1=1900, 0=2000), year(0-99) + int dayOfWeek = getDayOfWeek(Day, Month, Year); + + // offset from 2000 + rtc_pcf.setDate(Day, dayOfWeek, Month, 0, Year - YEAR_OFFSET_PCF); + //hr, min, sec + rtc_pcf.setTime(Hour, Minute, Second); + } + + clearAlarm(); +} + +void PCF8563::clearAlarm() { + int nextAlarmMinute = 0; + rtc_pcf.clearAlarm(); // resets the alarm flag in the RTC + nextAlarmMinute = rtc_pcf.getMinute(); + nextAlarmMinute = (nextAlarmMinute == 59) ? 0 : (nextAlarmMinute + 1); //set alarm to trigger 1 minute from now + rtc_pcf.setAlarm(nextAlarmMinute, 99, 99, 99); +} + +void PCF8563::read(tmElements_t &tm) { + tm.Month = rtc_pcf.getMonth(); + if (tm.Month == 0){ //PCF8563 POR sets month = 0 for some reason + tm.Month = 1; + tm.Year = 21; // TODO: I feel nervous about this--it's only 21 for a year, right? + } else { + tm.Year = rtc_pcf.getYear(); + } + tm.Day = rtc_pcf.getDay(); + tm.Wday = rtc_pcf.getWeekday() + 1; + tm.Hour = rtc_pcf.getHour(); + tm.Minute = rtc_pcf.getMinute(); + tm.Second = rtc_pcf.getSecond(); +} + +void PCF8563::set(tmElements_t tm) { + int dayOfWeek = getDayOfWeek(tm.Day, tm.Month, tm.Year + YEAR_OFFSET_PCF); + rtc_pcf.setDate(tm.Day, dayOfWeek, tm.Month, 0, tm.Year); + rtc_pcf.setTime(tm.Hour, tm.Minute, tm.Second); + clearAlarm(); +} + +int PCF8563::getDayOfWeek(int d, int m, int y) { + static int t[] = { 0, 3, 2, 5, 0, 3, + 5, 1, 4, 6, 2, 4 }; + y -= m < 3; + return ( y + y / 4 - y / 100 + + y / 400 + t[m - 1] + d) % 7; +} + +int PCF8563::rtcType() { + return RTC_TYPE_PCF8563; +} \ No newline at end of file diff --git a/src/PCF8563.h b/src/PCF8563.h new file mode 100644 index 0000000..d5439f8 --- /dev/null +++ b/src/PCF8563.h @@ -0,0 +1,15 @@ +#include +#include "AbstractRTC.h" + +class PCF8563 : public AbstractRTC { +public: + Rtc_Pcf8563 rtc_pcf; // TODO: We should not have public member variables + ~PCF8563() {} + void config(String datetime); + void clearAlarm(); + void read(tmElements_t &tm); + void set(tmElements_t tm); + int rtcType(); +private: + int getDayOfWeek(int d, int m, int y); +}; diff --git a/src/Watchy.cpp b/src/Watchy.cpp index 668deac..54b6e60 100644 --- a/src/Watchy.cpp +++ b/src/Watchy.cpp @@ -590,11 +590,13 @@ weatherData Watchy::getWeatherData(){ } float Watchy::getBatteryVoltage(){ - if(RTC.rtcType == DS3232_RTC_TYPE){ + if (RTC.rtcType() == RTC_TYPE_DS3232){ return analogReadMilliVolts(V10_ADC_PIN) / 1000.0f * 2.0f; // Battery voltage goes through a 1/2 divider. - }else{ + } else if (RTC.rtcType() == RTC_TYPE_PCF8563) { return analogReadMilliVolts(V15_ADC_PIN) / 1000.0f * 2.0f; } + + return 0.0f; } uint16_t Watchy::_readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len) @@ -619,7 +621,6 @@ uint16_t Watchy::_writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uin } void Watchy::_bmaConfig(){ - if (sensor.begin(_readRegister, _writeRegister, delay) == false) { //fail to init BMA return; @@ -723,11 +724,11 @@ void Watchy::setupWifi(){ display.fillScreen(GxEPD_BLACK); display.setFont(&FreeMonoBold9pt7b); display.setTextColor(GxEPD_WHITE); - if(!wifiManager.autoConnect(WIFI_AP_SSID)) {//WiFi setup failed + if (!wifiManager.autoConnect(WIFI_AP_SSID)) {//WiFi setup failed display.setCursor(0, 30); display.println("Setup failed &"); display.println("timed out!"); - }else{ + } else { display.println("Connected to"); display.println(WiFi.SSID()); } @@ -754,12 +755,12 @@ void Watchy::_configModeCallback (WiFiManager *myWiFiManager) { } bool Watchy::connectWiFi(){ - if(WL_CONNECT_FAILED == WiFi.begin()){//WiFi not setup, you can also use hard coded credentials with WiFi.begin(SSID,PASS); + 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 + } else { + if (WL_CONNECTED == WiFi.waitForConnectResult()){//attempt to connect for 10s WIFI_CONFIGURED = true; - }else{//connection failed, time out + } else {//connection failed, time out WIFI_CONFIGURED = false; //turn off radios WiFi.mode(WIFI_OFF); @@ -801,72 +802,72 @@ void Watchy::updateFWBegin(){ display.println(" "); display.println("Waiting for"); display.println("connection..."); - display.display(false); //full refresh + 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 + 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(); + 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; } - 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); + delay(100); } //turn off radios @@ -874,25 +875,3 @@ void Watchy::updateFWBegin(){ btStop(); showMenu(menuIndex, false); } - -// time_t compileTime() -// { -// const time_t FUDGE(10); //fudge factor to allow for upload time, etc. (seconds, YMMV) -// const char *compDate = __DATE__, *compTime = __TIME__, *months = "JanFebMarAprMayJunJulAugSepOctNovDec"; -// char compMon[3], *m; - -// strncpy(compMon, compDate, 3); -// compMon[3] = '\0'; -// m = strstr(months, compMon); - -// tmElements_t tm; -// tm.Month = ((m - months) / 3 + 1); -// tm.Day = atoi(compDate + 4); -// tm.Year = atoi(compDate + 7) - YEAR_OFFSET; // offset from 1970, since year is stored in uint8_t -// tm.Hour = atoi(compTime); -// tm.Minute = atoi(compTime + 3); -// tm.Second = atoi(compTime + 6); - -// time_t t = makeTime(tm); -// return t + FUDGE; //add fudge factor to allow for compile time -// } diff --git a/src/WatchyRTC.cpp b/src/WatchyRTC.cpp index 2c7ef9c..64bac43 100644 --- a/src/WatchyRTC.cpp +++ b/src/WatchyRTC.cpp @@ -2,30 +2,8 @@ WatchyRTC::WatchyRTC() {} -// TODO: We can probably put all of this logic into AbstractRTC as a class -// function. It would simplify this class even more, which would be nice -bool WatchyRTC::_canConnectTo(int addr) { - byte error; - Wire.beginTransmission(addr); - error = Wire.endTransmission(addr); - return error == 0; -} - void WatchyRTC::init(){ - if (_canConnectTo(RTC_DS_ADDR)) { - rtcType = DS3232_RTC_TYPE; - _rtc = new DS3232(); - return; - } - - if (_canConnectTo(RTC_PCF_ADDR)) { - rtcType = PCF8563_RTC_TYPE; - _rtc = new PCF8563(); - return; - } - - rtcType = NO_RTC_TYPE; - _rtc = new AbstractRTC(); + _rtc = AbstractRTC::init(); } void WatchyRTC::config(String datetime){ @@ -46,4 +24,8 @@ void WatchyRTC::set(tmElements_t tm){ uint8_t WatchyRTC::temperature(){ return _rtc->temperature(); +} + +int WatchyRTC::rtcType() { + return _rtc->rtcType(); } \ No newline at end of file diff --git a/src/WatchyRTC.h b/src/WatchyRTC.h index 7107b5e..34ea880 100644 --- a/src/WatchyRTC.h +++ b/src/WatchyRTC.h @@ -1,169 +1,11 @@ #ifndef WATCHY_RTC_H #define WATCHY_RTC_H -#include -#include - -#define DS3232_RTC_TYPE 0 -#define PCF8563_RTC_TYPE 1 -#define NO_RTC_TYPE 255 - -#define RTC_DS_ADDR 0x68 -#define RTC_PCF_ADDR 0x51 -#define YEAR_OFFSET_DS3232 1970 -#define YEAR_OFFSET_PCF 2000 - -#define NO_TEMPERATURE_ERR 255 - -// TODO: So we're relying on an rtcType as a multiplexer, making our WatchyRTC code a bit -// more complex. A way around this is to use a command pattern instead: -// https://sourcemaking.com/design_patterns/command -// -// That way we don't have to have a bunch of conditionals floating around in this class. -// It will simplify the process of adding a new RTC chip later as well. - -class AbstractRTC { -public: - virtual ~AbstractRTC() {} - virtual void config(String datetime) {} - virtual void clearAlarm() {} - virtual void read(tmElements_t &tm) {} - virtual void set(tmElements_t tm) {} - virtual uint8_t temperature() { return NO_TEMPERATURE_ERR; } -protected: - String getValue(String data, char separator, int index) { - int found = 0; - int strIndex[] = {0, -1}; - int maxIndex = data.length()-1; - - for (int i=0; i <= maxIndex && found <= index; i++) { - if (data.charAt(i)==separator || i==maxIndex) { - found++; - strIndex[0] = strIndex[1] + 1; - strIndex[1] = (i == maxIndex) ? i + 1 : i; - } - } - - if (found > index) { - return data.substring(strIndex[0], strIndex[1]); - } - - return ""; - } -}; - -class DS3232 : public AbstractRTC { -public: - DS3232RTC rtc_ds; // TODO: We should not have public member variables - ~DS3232() {} - - void config(String datetime) { - if (datetime != "") { - tmElements_t tm; - // offset from 1970, since year is stored in uint8_t - tm.Year = getValue(datetime, ':', 0).toInt() - YEAR_OFFSET_DS3232; - tm.Month = getValue(datetime, ':', 1).toInt(); - tm.Day = getValue(datetime, ':', 2).toInt(); - tm.Hour = getValue(datetime, ':', 3).toInt(); - tm.Minute = getValue(datetime, ':', 4).toInt(); - tm.Second = getValue(datetime, ':', 5).toInt(); - time_t t = makeTime(tm); - rtc_ds.set(t); - } - - // https://github.com/JChristensen/DS3232RTC - rtc_ds.squareWave(SQWAVE_NONE); //disable square wave output - rtc_ds.setAlarm(ALM2_EVERY_MINUTE, 0, 0, 0, 0); //alarm wakes up Watchy every minute - rtc_ds.alarmInterrupt(ALARM_2, true); //enable alarm interrupt - } - - void clearAlarm() { - rtc_ds.alarm(ALARM_2); - } - - void read(tmElements_t &tm) { - rtc_ds.read(tm); - tm.Year = tm.Year - 30; //reset to offset from 2000 - } - - void set(tmElements_t tm) { - tm.Year = tm.Year + 2000 - YEAR_OFFSET_DS3232; - time_t t = makeTime(tm); - rtc_ds.set(t); - } - - uint8_t temperature() { - return rtc_ds.temperature(); - } -}; - -class PCF8563 : public AbstractRTC { -public: - Rtc_Pcf8563 rtc_pcf; // TODO: We should not have public member variables - ~PCF8563() {} - - void config(String datetime) { - if (datetime != "") { - int Year = getValue(datetime, ':', 0).toInt(); - int Month = getValue(datetime, ':', 1).toInt(); - int Day = getValue(datetime, ':', 2).toInt(); - int Hour = getValue(datetime, ':', 3).toInt(); - int Minute = getValue(datetime, ':', 4).toInt(); - int Second = getValue(datetime, ':', 5).toInt(); - //day, weekday, month, century(1=1900, 0=2000), year(0-99) - int dayOfWeek = getDayOfWeek(Day, Month, Year); - - // offset from 2000 - rtc_pcf.setDate(Day, dayOfWeek, Month, 0, Year - YEAR_OFFSET_PCF); - //hr, min, sec - rtc_pcf.setTime(Hour, Minute, Second); - } - - clearAlarm(); - } - - void clearAlarm() { - int nextAlarmMinute = 0; - rtc_pcf.clearAlarm(); // resets the alarm flag in the RTC - nextAlarmMinute = rtc_pcf.getMinute(); - nextAlarmMinute = (nextAlarmMinute == 59) ? 0 : (nextAlarmMinute + 1); //set alarm to trigger 1 minute from now - rtc_pcf.setAlarm(nextAlarmMinute, 99, 99, 99); - } - - void read(tmElements_t &tm) { - tm.Month = rtc_pcf.getMonth(); - if (tm.Month == 0){ //PCF8563 POR sets month = 0 for some reason - tm.Month = 1; - tm.Year = 21; // TODO: I feel nervous about this--it's only 21 for a year, right? - } else { - tm.Year = rtc_pcf.getYear(); - } - tm.Day = rtc_pcf.getDay(); - tm.Wday = rtc_pcf.getWeekday() + 1; - tm.Hour = rtc_pcf.getHour(); - tm.Minute = rtc_pcf.getMinute(); - tm.Second = rtc_pcf.getSecond(); - } - - void set(tmElements_t tm) { - int dayOfWeek = getDayOfWeek(tm.Day, tm.Month, tm.Year + YEAR_OFFSET_PCF); - rtc_pcf.setDate(tm.Day, dayOfWeek, tm.Month, 0, tm.Year); - rtc_pcf.setTime(tm.Hour, tm.Minute, tm.Second); - clearAlarm(); - } -private: - int getDayOfWeek(int d, int m, int y) { - static int t[] = { 0, 3, 2, 5, 0, 3, - 5, 1, 4, 6, 2, 4 }; - y -= m < 3; - return ( y + y / 4 - y / 100 + - y / 400 + t[m - 1] + d) % 7; - } -}; +#include +#include +#include "AbstractRTC.h" class WatchyRTC { - public: - uint8_t rtcType; public: WatchyRTC(); void init(); @@ -172,8 +14,8 @@ class WatchyRTC { void read(tmElements_t &tm); void set(tmElements_t tm); uint8_t temperature(); + int rtcType(); private: - bool _canConnectTo(int addr); AbstractRTC* _rtc; };