Auto updating alarm watch - accepts alarm settings from a BLE device like a Raspberry Pi and buzzes at the appropriate time - also displays binary time
Dependencies: BLE_API mbed-src nRF51822 nrf51_rtc
main.cpp
00001 // BLE Alarm Watch 00002 // Based on BLE examples on MBED 00003 // Rob Dobson, (c) 2015 00004 00005 #include "mbed.h" 00006 #include "BLE.h" 00007 #include "LedDisplay.h" 00008 #include "WatchTimeService.h" 00009 #include "nrf51_rtc.h" 00010 #include "DFUService.h" 00011 00012 // BLE platform 00013 BLE ble; 00014 00015 // Button press to show time 00016 InterruptIn button(p5); 00017 00018 // Vibration motor 00019 DigitalOut vibrationMotor(p9); 00020 00021 // Device name - this is the visible name of device on BLE 00022 const static char DEVICE_NAME[] = "JoesAlarm"; 00023 00024 // UUIDs of services offered 00025 static const uint16_t uuid16_list[] = {WatchTimeService::WATCHTIME_SERVICE_UUID}; 00026 00027 // Service offering to read and set the time on the watch 00028 WatchTimeService *pWatchTimeService; 00029 00030 // Time watch button last pressed 00031 time_t watchButtonLastPressed = 0; 00032 00033 // Time between periodic callbacks used to keep RTC functioning 00034 const int PERIODIC_CALLBACK_SECS = 2; 00035 00036 // Time for double press detection (2 presses within this number of secs == double press) 00037 const int DOUBLE_PRESS_SECS = 2; 00038 00039 // Time to display LEDs 00040 const int DISPLAY_ON_TIME = 5; 00041 00042 // Alarm on time 00043 const int ALARM_ON_TIME = 120; 00044 00045 // Control whether time characteristic is updated when BLE is connected 00046 // This is currently set false because it is probably not needed - really just a debug function 00047 // and, since the code runs in an interrupt, it may be brittle although it seems to work ok 00048 int UPDATE_BLE_TIME_WHEN_CONNECTED = false; 00049 00050 // Time alarm set to 00051 time_t alarmSetTime = 0; 00052 00053 // Ticker needed to keep the RTC from losing time on counter rollover 00054 Ticker periodicCallbackToKeepTimerGoing; 00055 00056 // Timeout to avoid bounce on button 00057 Timeout buttonDebounceTimeout; 00058 bool buttonAlreadyPressed = false; 00059 00060 // Alarm state 00061 enum AlarmState 00062 { 00063 AlarmState_off, 00064 AlarmState_on_sounding, 00065 AlarmState_on_resting 00066 }; 00067 AlarmState alarmState = AlarmState_off; 00068 00069 // Time alarm was activated 00070 time_t alarmInitiatedTime = 0; 00071 00072 // Set alarm on 00073 void AlarmOn() 00074 { 00075 alarmState = AlarmState_on_sounding; 00076 vibrationMotor = 1; 00077 alarmInitiatedTime = rtc.time(); 00078 } 00079 00080 // Set alarm on 00081 void AlarmOff() 00082 { 00083 alarmState = AlarmState_off; 00084 vibrationMotor = 0; 00085 } 00086 00087 // Button debounce callback 00088 void buttonDebounceCallback() 00089 { 00090 buttonAlreadyPressed = false; 00091 } 00092 00093 bool datesAreTheSame(time_t dateTime1, time_t dateTime2) 00094 { 00095 struct tm * timeinfo; 00096 timeinfo = localtime (&dateTime1); 00097 int alarmMDay = timeinfo->tm_mday; 00098 int alarmMonth = timeinfo->tm_mon; 00099 int alarmYear = timeinfo->tm_year; 00100 timeinfo = localtime (&dateTime2); 00101 return (alarmMDay == timeinfo->tm_mday) && (alarmMonth == timeinfo->tm_mon) && (alarmYear == timeinfo->tm_year); 00102 } 00103 00104 // Handle button press to read watch 00105 void buttonPressedCallback(void) 00106 { 00107 // Handle debounce 00108 if (buttonAlreadyPressed) 00109 return; 00110 buttonAlreadyPressed = true; 00111 buttonDebounceTimeout.attach(buttonDebounceCallback, 0.2); 00112 00113 // Stop alarm 00114 AlarmOff(); 00115 00116 // Get the time to display 00117 time_t curTime = rtc.time(); 00118 00119 // Check if we should display current time or alarm time 00120 if (curTime - watchButtonLastPressed > DOUBLE_PRESS_SECS) 00121 { 00122 ledDisplay.start(rtc.time(), LedDisplay::DispType_CurTime, DISPLAY_ON_TIME, false); 00123 } 00124 else 00125 { 00126 bool alarmIsLaterToday = (alarmSetTime > curTime) && datesAreTheSame(alarmSetTime, curTime); 00127 ledDisplay.start(alarmSetTime, LedDisplay::DispType_AlarmTime, DISPLAY_ON_TIME, alarmIsLaterToday); 00128 } 00129 00130 // Remember when button last pressed 00131 watchButtonLastPressed = curTime; 00132 } 00133 00134 // Button released 00135 void buttonReleasedCallback(void) 00136 { 00137 } 00138 00139 // Handle BLE disconnection - restart advertising 00140 void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) 00141 { 00142 ble.gap().startAdvertising(); 00143 } 00144 00145 // Callback required for RTC operation 00146 void periodicCallback(void) 00147 { 00148 // Update the rtc library time (it says in the notes on the rtc lib that this needs to happen 00149 // more than once every few hundred seconds to avoid a rollover 00150 time_t curTime = rtc.time(); 00151 00152 // Update the time characteristic if we are connected 00153 if (UPDATE_BLE_TIME_WHEN_CONNECTED) 00154 { 00155 Gap::GapState_t gapState = ble.gap().getState(); 00156 if (gapState.connected) 00157 { 00158 time_t curTime = rtc.time(); 00159 pWatchTimeService->writeWatchTime(curTime); 00160 } 00161 } 00162 00163 // Check if alarm went off recently 00164 if (alarmInitiatedTime + PERIODIC_CALLBACK_SECS*2 + ALARM_ON_TIME > curTime) 00165 { 00166 // Stop the alarm after it has been on for the allotted time 00167 if (alarmInitiatedTime + ALARM_ON_TIME < curTime) 00168 AlarmOff(); 00169 } 00170 else 00171 { 00172 // Check if time for alarm 00173 if (alarmSetTime > 0) 00174 { 00175 if ((curTime >= alarmSetTime) && (curTime < alarmSetTime + PERIODIC_CALLBACK_SECS*2)) 00176 { 00177 AlarmOn(); 00178 } 00179 } 00180 } 00181 00182 // Pulse the alarm 00183 if (alarmState == AlarmState_on_sounding) 00184 { 00185 alarmState = AlarmState_on_resting; 00186 vibrationMotor = 0; 00187 } 00188 else if (alarmState == AlarmState_on_resting) 00189 { 00190 alarmState = AlarmState_on_sounding; 00191 vibrationMotor = 1; 00192 } 00193 00194 // // TEST TEST TEST 00195 // int testMin = 0; 00196 // int testHr = 0; 00197 // if (testNum < 6) 00198 // testMin = 1 << testNum; 00199 // else 00200 // testHr = 1 << (testNum - 6); 00201 // testNum++; 00202 // if (testNum >= 11) 00203 // testNum = 0; 00204 // 00205 // uint8_t testTime[] = { uint8_t(2015/256), uint8_t(2015%256), 12, 18, testHr, testMin, 0 }; 00206 // time_t testTimeT = WatchTimeService::watchTimeToUnixTime(testTime); 00207 // 00208 // ledDisplay.start(testTimeT, LedDisplay::DispType_CurTime, DISPLAY_ON_TIME); 00209 00210 } 00211 00212 // Helper 00213 void setRTCfromWatchTime(const uint8_t* pWatchTime) 00214 { 00215 time_t timest = WatchTimeService::watchTimeToUnixTime(pWatchTime); 00216 if ((int)timest != -1) 00217 { 00218 rtc.set_time(timest); 00219 } 00220 } 00221 00222 void onDataWrittenCallback(const GattWriteCallbackParams *params) 00223 { 00224 // Check if this is time setting 00225 if (pWatchTimeService->getWatchTimeValueHandle() == params->handle) 00226 { 00227 if (params->len == WatchTimeService::WatchTime_BlockSize) 00228 { 00229 setRTCfromWatchTime(params->data); 00230 } 00231 } 00232 else if (pWatchTimeService->getAlarmTimeValueHandle() == params->handle) 00233 { 00234 if (params->len == WatchTimeService::WatchTime_BlockSize) 00235 { 00236 time_t timest = WatchTimeService::watchTimeToUnixTime(params->data); 00237 if (((int)timest != -1) && ((int)timest != 0)) 00238 alarmSetTime = timest; 00239 else 00240 alarmSetTime = 0; 00241 } 00242 } 00243 } 00244 00245 // DEBUG CODE 00246 void print_time() 00247 { 00248 time_t rawtime=rtc.time(); 00249 00250 // massage the time into a human-friendly format for printing 00251 struct tm * timeinfo; 00252 timeinfo = localtime(&rawtime); 00253 char date[24]; 00254 strftime(date,sizeof(date),"%H:%M:%S on %m/%d/%G",timeinfo); 00255 printf("The current time is %s.\r\n",date); 00256 } 00257 00258 int main(void) 00259 { 00260 printf("AlarmWatch\r\n"); 00261 00262 // This is necessary to keep the clock ticking - doesn't have to be so frequent 00263 // According to this https://developer.mbed.org/users/fxschumacher/code/nRF51_rtc_example/file/c1f06d0a5e11/main.cpp 00264 // any time less than 512 seconds is ok 00265 periodicCallbackToKeepTimerGoing.attach(periodicCallback, PERIODIC_CALLBACK_SECS); 00266 00267 // Handlers for the button press 00268 button.fall(buttonPressedCallback); 00269 button.rise(buttonReleasedCallback); 00270 00271 // Clear display 00272 ledDisplay.clear(); 00273 00274 // BLE init 00275 ble.init(); 00276 ble.gap().onDisconnection(disconnectionCallback); 00277 ble.onDataWritten(onDataWrittenCallback); 00278 00279 // Watch Time Service 00280 uint8_t initialTime[] = { uint8_t(2015/256), uint8_t(2015%256), 12, 18, 10, 10, 0 }; 00281 uint8_t alarmTime[] = { 0, 0, 0, 0, 0, 0, 0 }; 00282 WatchTimeService watchTimeService(ble, initialTime, alarmTime); 00283 setRTCfromWatchTime(initialTime); 00284 pWatchTimeService = &watchTimeService; 00285 00286 /* Enable over-the-air firmware updates. Instantiating DFUSservice introduces a 00287 * control characteristic which can be used to trigger the application to 00288 * handover control to a resident bootloader. */ 00289 DFUService dfu(ble); 00290 00291 // Setup advertising 00292 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00293 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); 00294 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); 00295 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); 00296 ble.gap().setAdvertisingInterval(1000); /* 1000ms. */ 00297 ble.gap().startAdvertising(); 00298 00299 // Turn off any alarm 00300 AlarmOff(); 00301 00302 while (true) 00303 { 00304 ble.waitForEvent(); 00305 } 00306 }
Generated on Tue Jul 12 2022 19:13:45 by 1.7.2