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

Committer:
Bobty
Date:
Tue Mar 01 13:03:32 2016 +0000
Revision:
6:4a12e0f03381
Parent:
5:2682353a8c32
Completed work on display and alarm

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Bobty 0:0d5ac2fd4620 1 // BLE Alarm Watch
Bobty 2:9090120e2656 2 // Based on BLE examples on MBED
Bobty 5:2682353a8c32 3 // Rob Dobson, (c) 2015
Bobty 0:0d5ac2fd4620 4
Bobty 0:0d5ac2fd4620 5 #include "mbed.h"
Bobty 2:9090120e2656 6 #include "BLE.h"
Bobty 6:4a12e0f03381 7 #include "LedDisplay.h"
Bobty 3:a4b8d67de1b1 8 #include "WatchTimeService.h"
Bobty 4:f0b030a3223f 9 #include "nrf51_rtc.h"
Bobty 6:4a12e0f03381 10 #include "DFUService.h"
Bobty 2:9090120e2656 11
Bobty 2:9090120e2656 12 // BLE platform
Bobty 2:9090120e2656 13 BLE ble;
Bobty 2:9090120e2656 14
Bobty 2:9090120e2656 15 // Button press to show time
Bobty 5:2682353a8c32 16 InterruptIn button(p5);
Bobty 2:9090120e2656 17
Bobty 6:4a12e0f03381 18 // Vibration motor
Bobty 6:4a12e0f03381 19 DigitalOut vibrationMotor(p9);
Bobty 6:4a12e0f03381 20
Bobty 2:9090120e2656 21 // Device name - this is the visible name of device on BLE
Bobty 5:2682353a8c32 22 const static char DEVICE_NAME[] = "JoesAlarm";
Bobty 3:a4b8d67de1b1 23
Bobty 3:a4b8d67de1b1 24 // UUIDs of services offered
Bobty 6:4a12e0f03381 25 static const uint16_t uuid16_list[] = {WatchTimeService::WATCHTIME_SERVICE_UUID};
Bobty 3:a4b8d67de1b1 26
Bobty 3:a4b8d67de1b1 27 // Service offering to read and set the time on the watch
Bobty 3:a4b8d67de1b1 28 WatchTimeService *pWatchTimeService;
Bobty 2:9090120e2656 29
Bobty 6:4a12e0f03381 30 // Time watch button last pressed
Bobty 6:4a12e0f03381 31 time_t watchButtonLastPressed = 0;
Bobty 5:2682353a8c32 32
Bobty 6:4a12e0f03381 33 // Time between periodic callbacks used to keep RTC functioning
Bobty 6:4a12e0f03381 34 const int PERIODIC_CALLBACK_SECS = 2;
Bobty 6:4a12e0f03381 35
Bobty 6:4a12e0f03381 36 // Time for double press detection (2 presses within this number of secs == double press)
Bobty 6:4a12e0f03381 37 const int DOUBLE_PRESS_SECS = 2;
Bobty 5:2682353a8c32 38
Bobty 6:4a12e0f03381 39 // Time to display LEDs
Bobty 6:4a12e0f03381 40 const int DISPLAY_ON_TIME = 5;
Bobty 6:4a12e0f03381 41
Bobty 6:4a12e0f03381 42 // Alarm on time
Bobty 6:4a12e0f03381 43 const int ALARM_ON_TIME = 120;
Bobty 6:4a12e0f03381 44
Bobty 6:4a12e0f03381 45 // Control whether time characteristic is updated when BLE is connected
Bobty 6:4a12e0f03381 46 // This is currently set false because it is probably not needed - really just a debug function
Bobty 6:4a12e0f03381 47 // and, since the code runs in an interrupt, it may be brittle although it seems to work ok
Bobty 6:4a12e0f03381 48 int UPDATE_BLE_TIME_WHEN_CONNECTED = false;
Bobty 6:4a12e0f03381 49
Bobty 6:4a12e0f03381 50 // Time alarm set to
Bobty 6:4a12e0f03381 51 time_t alarmSetTime = 0;
Bobty 2:9090120e2656 52
Bobty 6:4a12e0f03381 53 // Ticker needed to keep the RTC from losing time on counter rollover
Bobty 6:4a12e0f03381 54 Ticker periodicCallbackToKeepTimerGoing;
Bobty 6:4a12e0f03381 55
Bobty 6:4a12e0f03381 56 // Timeout to avoid bounce on button
Bobty 6:4a12e0f03381 57 Timeout buttonDebounceTimeout;
Bobty 6:4a12e0f03381 58 bool buttonAlreadyPressed = false;
Bobty 6:4a12e0f03381 59
Bobty 6:4a12e0f03381 60 // Alarm state
Bobty 6:4a12e0f03381 61 enum AlarmState
Bobty 3:a4b8d67de1b1 62 {
Bobty 6:4a12e0f03381 63 AlarmState_off,
Bobty 6:4a12e0f03381 64 AlarmState_on_sounding,
Bobty 6:4a12e0f03381 65 AlarmState_on_resting
Bobty 6:4a12e0f03381 66 };
Bobty 6:4a12e0f03381 67 AlarmState alarmState = AlarmState_off;
Bobty 3:a4b8d67de1b1 68
Bobty 6:4a12e0f03381 69 // Time alarm was activated
Bobty 6:4a12e0f03381 70 time_t alarmInitiatedTime = 0;
Bobty 6:4a12e0f03381 71
Bobty 6:4a12e0f03381 72 // Set alarm on
Bobty 6:4a12e0f03381 73 void AlarmOn()
Bobty 5:2682353a8c32 74 {
Bobty 6:4a12e0f03381 75 alarmState = AlarmState_on_sounding;
Bobty 6:4a12e0f03381 76 vibrationMotor = 1;
Bobty 6:4a12e0f03381 77 alarmInitiatedTime = rtc.time();
Bobty 5:2682353a8c32 78 }
Bobty 5:2682353a8c32 79
Bobty 6:4a12e0f03381 80 // Set alarm on
Bobty 6:4a12e0f03381 81 void AlarmOff()
Bobty 5:2682353a8c32 82 {
Bobty 6:4a12e0f03381 83 alarmState = AlarmState_off;
Bobty 6:4a12e0f03381 84 vibrationMotor = 0;
Bobty 5:2682353a8c32 85 }
Bobty 5:2682353a8c32 86
Bobty 6:4a12e0f03381 87 // Button debounce callback
Bobty 6:4a12e0f03381 88 void buttonDebounceCallback()
Bobty 5:2682353a8c32 89 {
Bobty 6:4a12e0f03381 90 buttonAlreadyPressed = false;
Bobty 5:2682353a8c32 91 }
Bobty 5:2682353a8c32 92
Bobty 6:4a12e0f03381 93 bool datesAreTheSame(time_t dateTime1, time_t dateTime2)
Bobty 5:2682353a8c32 94 {
Bobty 6:4a12e0f03381 95 struct tm * timeinfo;
Bobty 6:4a12e0f03381 96 timeinfo = localtime (&dateTime1);
Bobty 6:4a12e0f03381 97 int alarmMDay = timeinfo->tm_mday;
Bobty 6:4a12e0f03381 98 int alarmMonth = timeinfo->tm_mon;
Bobty 6:4a12e0f03381 99 int alarmYear = timeinfo->tm_year;
Bobty 6:4a12e0f03381 100 timeinfo = localtime (&dateTime2);
Bobty 6:4a12e0f03381 101 return (alarmMDay == timeinfo->tm_mday) && (alarmMonth == timeinfo->tm_mon) && (alarmYear == timeinfo->tm_year);
Bobty 5:2682353a8c32 102 }
Bobty 5:2682353a8c32 103
Bobty 5:2682353a8c32 104 // Handle button press to read watch
Bobty 5:2682353a8c32 105 void buttonPressedCallback(void)
Bobty 5:2682353a8c32 106 {
Bobty 6:4a12e0f03381 107 // Handle debounce
Bobty 6:4a12e0f03381 108 if (buttonAlreadyPressed)
Bobty 6:4a12e0f03381 109 return;
Bobty 6:4a12e0f03381 110 buttonAlreadyPressed = true;
Bobty 6:4a12e0f03381 111 buttonDebounceTimeout.attach(buttonDebounceCallback, 0.2);
Bobty 6:4a12e0f03381 112
Bobty 6:4a12e0f03381 113 // Stop alarm
Bobty 6:4a12e0f03381 114 AlarmOff();
Bobty 6:4a12e0f03381 115
Bobty 5:2682353a8c32 116 // Get the time to display
Bobty 6:4a12e0f03381 117 time_t curTime = rtc.time();
Bobty 5:2682353a8c32 118
Bobty 5:2682353a8c32 119 // Check if we should display current time or alarm time
Bobty 6:4a12e0f03381 120 if (curTime - watchButtonLastPressed > DOUBLE_PRESS_SECS)
Bobty 5:2682353a8c32 121 {
Bobty 6:4a12e0f03381 122 ledDisplay.start(rtc.time(), LedDisplay::DispType_CurTime, DISPLAY_ON_TIME, false);
Bobty 5:2682353a8c32 123 }
Bobty 5:2682353a8c32 124 else
Bobty 5:2682353a8c32 125 {
Bobty 6:4a12e0f03381 126 bool alarmIsLaterToday = (alarmSetTime > curTime) && datesAreTheSame(alarmSetTime, curTime);
Bobty 6:4a12e0f03381 127 ledDisplay.start(alarmSetTime, LedDisplay::DispType_AlarmTime, DISPLAY_ON_TIME, alarmIsLaterToday);
Bobty 5:2682353a8c32 128 }
Bobty 5:2682353a8c32 129
Bobty 5:2682353a8c32 130 // Remember when button last pressed
Bobty 6:4a12e0f03381 131 watchButtonLastPressed = curTime;
Bobty 5:2682353a8c32 132 }
Bobty 5:2682353a8c32 133
Bobty 6:4a12e0f03381 134 // Button released
Bobty 5:2682353a8c32 135 void buttonReleasedCallback(void)
Bobty 5:2682353a8c32 136 {
Bobty 5:2682353a8c32 137 }
Bobty 5:2682353a8c32 138
Bobty 5:2682353a8c32 139 // Handle BLE disconnection - restart advertising
Bobty 5:2682353a8c32 140 void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
Bobty 5:2682353a8c32 141 {
Bobty 5:2682353a8c32 142 ble.gap().startAdvertising();
Bobty 5:2682353a8c32 143 }
Bobty 5:2682353a8c32 144
Bobty 6:4a12e0f03381 145 // Callback required for RTC operation
Bobty 5:2682353a8c32 146 void periodicCallback(void)
Bobty 5:2682353a8c32 147 {
Bobty 5:2682353a8c32 148 // Update the rtc library time (it says in the notes on the rtc lib that this needs to happen
Bobty 5:2682353a8c32 149 // more than once every few hundred seconds to avoid a rollover
Bobty 6:4a12e0f03381 150 time_t curTime = rtc.time();
Bobty 6:4a12e0f03381 151
Bobty 6:4a12e0f03381 152 // Update the time characteristic if we are connected
Bobty 6:4a12e0f03381 153 if (UPDATE_BLE_TIME_WHEN_CONNECTED)
Bobty 6:4a12e0f03381 154 {
Bobty 6:4a12e0f03381 155 Gap::GapState_t gapState = ble.gap().getState();
Bobty 6:4a12e0f03381 156 if (gapState.connected)
Bobty 6:4a12e0f03381 157 {
Bobty 6:4a12e0f03381 158 time_t curTime = rtc.time();
Bobty 6:4a12e0f03381 159 pWatchTimeService->writeWatchTime(curTime);
Bobty 6:4a12e0f03381 160 }
Bobty 6:4a12e0f03381 161 }
Bobty 6:4a12e0f03381 162
Bobty 6:4a12e0f03381 163 // Check if alarm went off recently
Bobty 6:4a12e0f03381 164 if (alarmInitiatedTime + PERIODIC_CALLBACK_SECS*2 + ALARM_ON_TIME > curTime)
Bobty 6:4a12e0f03381 165 {
Bobty 6:4a12e0f03381 166 // Stop the alarm after it has been on for the allotted time
Bobty 6:4a12e0f03381 167 if (alarmInitiatedTime + ALARM_ON_TIME < curTime)
Bobty 6:4a12e0f03381 168 AlarmOff();
Bobty 6:4a12e0f03381 169 }
Bobty 6:4a12e0f03381 170 else
Bobty 6:4a12e0f03381 171 {
Bobty 6:4a12e0f03381 172 // Check if time for alarm
Bobty 6:4a12e0f03381 173 if (alarmSetTime > 0)
Bobty 6:4a12e0f03381 174 {
Bobty 6:4a12e0f03381 175 if ((curTime >= alarmSetTime) && (curTime < alarmSetTime + PERIODIC_CALLBACK_SECS*2))
Bobty 6:4a12e0f03381 176 {
Bobty 6:4a12e0f03381 177 AlarmOn();
Bobty 6:4a12e0f03381 178 }
Bobty 6:4a12e0f03381 179 }
Bobty 6:4a12e0f03381 180 }
Bobty 6:4a12e0f03381 181
Bobty 6:4a12e0f03381 182 // Pulse the alarm
Bobty 6:4a12e0f03381 183 if (alarmState == AlarmState_on_sounding)
Bobty 6:4a12e0f03381 184 {
Bobty 6:4a12e0f03381 185 alarmState = AlarmState_on_resting;
Bobty 6:4a12e0f03381 186 vibrationMotor = 0;
Bobty 6:4a12e0f03381 187 }
Bobty 6:4a12e0f03381 188 else if (alarmState == AlarmState_on_resting)
Bobty 6:4a12e0f03381 189 {
Bobty 6:4a12e0f03381 190 alarmState = AlarmState_on_sounding;
Bobty 6:4a12e0f03381 191 vibrationMotor = 1;
Bobty 6:4a12e0f03381 192 }
Bobty 6:4a12e0f03381 193
Bobty 6:4a12e0f03381 194 // // TEST TEST TEST
Bobty 6:4a12e0f03381 195 // int testMin = 0;
Bobty 6:4a12e0f03381 196 // int testHr = 0;
Bobty 6:4a12e0f03381 197 // if (testNum < 6)
Bobty 6:4a12e0f03381 198 // testMin = 1 << testNum;
Bobty 6:4a12e0f03381 199 // else
Bobty 6:4a12e0f03381 200 // testHr = 1 << (testNum - 6);
Bobty 6:4a12e0f03381 201 // testNum++;
Bobty 6:4a12e0f03381 202 // if (testNum >= 11)
Bobty 6:4a12e0f03381 203 // testNum = 0;
Bobty 6:4a12e0f03381 204 //
Bobty 6:4a12e0f03381 205 // uint8_t testTime[] = { uint8_t(2015/256), uint8_t(2015%256), 12, 18, testHr, testMin, 0 };
Bobty 6:4a12e0f03381 206 // time_t testTimeT = WatchTimeService::watchTimeToUnixTime(testTime);
Bobty 6:4a12e0f03381 207 //
Bobty 6:4a12e0f03381 208 // ledDisplay.start(testTimeT, LedDisplay::DispType_CurTime, DISPLAY_ON_TIME);
Bobty 6:4a12e0f03381 209
Bobty 5:2682353a8c32 210 }
Bobty 5:2682353a8c32 211
Bobty 6:4a12e0f03381 212 // Helper
Bobty 5:2682353a8c32 213 void setRTCfromWatchTime(const uint8_t* pWatchTime)
Bobty 5:2682353a8c32 214 {
Bobty 6:4a12e0f03381 215 time_t timest = WatchTimeService::watchTimeToUnixTime(pWatchTime);
Bobty 5:2682353a8c32 216 if ((int)timest != -1)
Bobty 5:2682353a8c32 217 {
Bobty 5:2682353a8c32 218 rtc.set_time(timest);
Bobty 5:2682353a8c32 219 }
Bobty 5:2682353a8c32 220 }
Bobty 5:2682353a8c32 221
Bobty 3:a4b8d67de1b1 222 void onDataWrittenCallback(const GattWriteCallbackParams *params)
Bobty 3:a4b8d67de1b1 223 {
Bobty 3:a4b8d67de1b1 224 // Check if this is time setting
Bobty 6:4a12e0f03381 225 if (pWatchTimeService->getWatchTimeValueHandle() == params->handle)
Bobty 3:a4b8d67de1b1 226 {
Bobty 3:a4b8d67de1b1 227 if (params->len == WatchTimeService::WatchTime_BlockSize)
Bobty 3:a4b8d67de1b1 228 {
Bobty 5:2682353a8c32 229 setRTCfromWatchTime(params->data);
Bobty 3:a4b8d67de1b1 230 }
Bobty 3:a4b8d67de1b1 231 }
Bobty 6:4a12e0f03381 232 else if (pWatchTimeService->getAlarmTimeValueHandle() == params->handle)
Bobty 6:4a12e0f03381 233 {
Bobty 6:4a12e0f03381 234 if (params->len == WatchTimeService::WatchTime_BlockSize)
Bobty 6:4a12e0f03381 235 {
Bobty 6:4a12e0f03381 236 time_t timest = WatchTimeService::watchTimeToUnixTime(params->data);
Bobty 6:4a12e0f03381 237 if (((int)timest != -1) && ((int)timest != 0))
Bobty 6:4a12e0f03381 238 alarmSetTime = timest;
Bobty 6:4a12e0f03381 239 else
Bobty 6:4a12e0f03381 240 alarmSetTime = 0;
Bobty 6:4a12e0f03381 241 }
Bobty 6:4a12e0f03381 242 }
Bobty 3:a4b8d67de1b1 243 }
Bobty 3:a4b8d67de1b1 244
Bobty 6:4a12e0f03381 245 // DEBUG CODE
Bobty 4:f0b030a3223f 246 void print_time()
Bobty 4:f0b030a3223f 247 {
Bobty 4:f0b030a3223f 248 time_t rawtime=rtc.time();
Bobty 4:f0b030a3223f 249
Bobty 4:f0b030a3223f 250 // massage the time into a human-friendly format for printing
Bobty 4:f0b030a3223f 251 struct tm * timeinfo;
Bobty 4:f0b030a3223f 252 timeinfo = localtime(&rawtime);
Bobty 4:f0b030a3223f 253 char date[24];
Bobty 4:f0b030a3223f 254 strftime(date,sizeof(date),"%H:%M:%S on %m/%d/%G",timeinfo);
Bobty 4:f0b030a3223f 255 printf("The current time is %s.\r\n",date);
Bobty 4:f0b030a3223f 256 }
Bobty 4:f0b030a3223f 257
Bobty 2:9090120e2656 258 int main(void)
Bobty 2:9090120e2656 259 {
Bobty 3:a4b8d67de1b1 260 printf("AlarmWatch\r\n");
Bobty 3:a4b8d67de1b1 261
Bobty 6:4a12e0f03381 262 // This is necessary to keep the clock ticking - doesn't have to be so frequent
Bobty 6:4a12e0f03381 263 // According to this https://developer.mbed.org/users/fxschumacher/code/nRF51_rtc_example/file/c1f06d0a5e11/main.cpp
Bobty 6:4a12e0f03381 264 // any time less than 512 seconds is ok
Bobty 6:4a12e0f03381 265 periodicCallbackToKeepTimerGoing.attach(periodicCallback, PERIODIC_CALLBACK_SECS);
Bobty 6:4a12e0f03381 266
Bobty 6:4a12e0f03381 267 // Handlers for the button press
Bobty 2:9090120e2656 268 button.fall(buttonPressedCallback);
Bobty 2:9090120e2656 269 button.rise(buttonReleasedCallback);
Bobty 2:9090120e2656 270
Bobty 5:2682353a8c32 271 // Clear display
Bobty 6:4a12e0f03381 272 ledDisplay.clear();
Bobty 5:2682353a8c32 273
Bobty 2:9090120e2656 274 // BLE init
Bobty 2:9090120e2656 275 ble.init();
Bobty 2:9090120e2656 276 ble.gap().onDisconnection(disconnectionCallback);
Bobty 3:a4b8d67de1b1 277 ble.onDataWritten(onDataWrittenCallback);
Bobty 3:a4b8d67de1b1 278
Bobty 3:a4b8d67de1b1 279 // Watch Time Service
Bobty 6:4a12e0f03381 280 uint8_t initialTime[] = { uint8_t(2015/256), uint8_t(2015%256), 12, 18, 10, 10, 0 };
Bobty 6:4a12e0f03381 281 uint8_t alarmTime[] = { 0, 0, 0, 0, 0, 0, 0 };
Bobty 6:4a12e0f03381 282 WatchTimeService watchTimeService(ble, initialTime, alarmTime);
Bobty 5:2682353a8c32 283 setRTCfromWatchTime(initialTime);
Bobty 3:a4b8d67de1b1 284 pWatchTimeService = &watchTimeService;
Bobty 5:2682353a8c32 285
Bobty 6:4a12e0f03381 286 /* Enable over-the-air firmware updates. Instantiating DFUSservice introduces a
Bobty 6:4a12e0f03381 287 * control characteristic which can be used to trigger the application to
Bobty 6:4a12e0f03381 288 * handover control to a resident bootloader. */
Bobty 6:4a12e0f03381 289 DFUService dfu(ble);
Bobty 6:4a12e0f03381 290
Bobty 2:9090120e2656 291 // Setup advertising
Bobty 2:9090120e2656 292 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
Bobty 2:9090120e2656 293 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
Bobty 2:9090120e2656 294 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
Bobty 2:9090120e2656 295 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
Bobty 2:9090120e2656 296 ble.gap().setAdvertisingInterval(1000); /* 1000ms. */
Bobty 2:9090120e2656 297 ble.gap().startAdvertising();
Bobty 2:9090120e2656 298
Bobty 6:4a12e0f03381 299 // Turn off any alarm
Bobty 6:4a12e0f03381 300 AlarmOff();
Bobty 6:4a12e0f03381 301
Bobty 5:2682353a8c32 302 while (true)
Bobty 5:2682353a8c32 303 {
Bobty 2:9090120e2656 304 ble.waitForEvent();
Bobty 2:9090120e2656 305 }
Bobty 2:9090120e2656 306 }