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
- Committer:
- Bobty
- Date:
- 2016-03-01
- Revision:
- 6:4a12e0f03381
- Parent:
- 5:2682353a8c32
File content as of revision 6:4a12e0f03381:
// BLE Alarm Watch // Based on BLE examples on MBED // Rob Dobson, (c) 2015 #include "mbed.h" #include "BLE.h" #include "LedDisplay.h" #include "WatchTimeService.h" #include "nrf51_rtc.h" #include "DFUService.h" // BLE platform BLE ble; // Button press to show time InterruptIn button(p5); // Vibration motor DigitalOut vibrationMotor(p9); // Device name - this is the visible name of device on BLE const static char DEVICE_NAME[] = "JoesAlarm"; // UUIDs of services offered static const uint16_t uuid16_list[] = {WatchTimeService::WATCHTIME_SERVICE_UUID}; // Service offering to read and set the time on the watch WatchTimeService *pWatchTimeService; // Time watch button last pressed time_t watchButtonLastPressed = 0; // Time between periodic callbacks used to keep RTC functioning const int PERIODIC_CALLBACK_SECS = 2; // Time for double press detection (2 presses within this number of secs == double press) const int DOUBLE_PRESS_SECS = 2; // Time to display LEDs const int DISPLAY_ON_TIME = 5; // Alarm on time const int ALARM_ON_TIME = 120; // Control whether time characteristic is updated when BLE is connected // This is currently set false because it is probably not needed - really just a debug function // and, since the code runs in an interrupt, it may be brittle although it seems to work ok int UPDATE_BLE_TIME_WHEN_CONNECTED = false; // Time alarm set to time_t alarmSetTime = 0; // Ticker needed to keep the RTC from losing time on counter rollover Ticker periodicCallbackToKeepTimerGoing; // Timeout to avoid bounce on button Timeout buttonDebounceTimeout; bool buttonAlreadyPressed = false; // Alarm state enum AlarmState { AlarmState_off, AlarmState_on_sounding, AlarmState_on_resting }; AlarmState alarmState = AlarmState_off; // Time alarm was activated time_t alarmInitiatedTime = 0; // Set alarm on void AlarmOn() { alarmState = AlarmState_on_sounding; vibrationMotor = 1; alarmInitiatedTime = rtc.time(); } // Set alarm on void AlarmOff() { alarmState = AlarmState_off; vibrationMotor = 0; } // Button debounce callback void buttonDebounceCallback() { buttonAlreadyPressed = false; } bool datesAreTheSame(time_t dateTime1, time_t dateTime2) { struct tm * timeinfo; timeinfo = localtime (&dateTime1); int alarmMDay = timeinfo->tm_mday; int alarmMonth = timeinfo->tm_mon; int alarmYear = timeinfo->tm_year; timeinfo = localtime (&dateTime2); return (alarmMDay == timeinfo->tm_mday) && (alarmMonth == timeinfo->tm_mon) && (alarmYear == timeinfo->tm_year); } // Handle button press to read watch void buttonPressedCallback(void) { // Handle debounce if (buttonAlreadyPressed) return; buttonAlreadyPressed = true; buttonDebounceTimeout.attach(buttonDebounceCallback, 0.2); // Stop alarm AlarmOff(); // Get the time to display time_t curTime = rtc.time(); // Check if we should display current time or alarm time if (curTime - watchButtonLastPressed > DOUBLE_PRESS_SECS) { ledDisplay.start(rtc.time(), LedDisplay::DispType_CurTime, DISPLAY_ON_TIME, false); } else { bool alarmIsLaterToday = (alarmSetTime > curTime) && datesAreTheSame(alarmSetTime, curTime); ledDisplay.start(alarmSetTime, LedDisplay::DispType_AlarmTime, DISPLAY_ON_TIME, alarmIsLaterToday); } // Remember when button last pressed watchButtonLastPressed = curTime; } // Button released void buttonReleasedCallback(void) { } // Handle BLE disconnection - restart advertising void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) { ble.gap().startAdvertising(); } // Callback required for RTC operation void periodicCallback(void) { // Update the rtc library time (it says in the notes on the rtc lib that this needs to happen // more than once every few hundred seconds to avoid a rollover time_t curTime = rtc.time(); // Update the time characteristic if we are connected if (UPDATE_BLE_TIME_WHEN_CONNECTED) { Gap::GapState_t gapState = ble.gap().getState(); if (gapState.connected) { time_t curTime = rtc.time(); pWatchTimeService->writeWatchTime(curTime); } } // Check if alarm went off recently if (alarmInitiatedTime + PERIODIC_CALLBACK_SECS*2 + ALARM_ON_TIME > curTime) { // Stop the alarm after it has been on for the allotted time if (alarmInitiatedTime + ALARM_ON_TIME < curTime) AlarmOff(); } else { // Check if time for alarm if (alarmSetTime > 0) { if ((curTime >= alarmSetTime) && (curTime < alarmSetTime + PERIODIC_CALLBACK_SECS*2)) { AlarmOn(); } } } // Pulse the alarm if (alarmState == AlarmState_on_sounding) { alarmState = AlarmState_on_resting; vibrationMotor = 0; } else if (alarmState == AlarmState_on_resting) { alarmState = AlarmState_on_sounding; vibrationMotor = 1; } // // TEST TEST TEST // int testMin = 0; // int testHr = 0; // if (testNum < 6) // testMin = 1 << testNum; // else // testHr = 1 << (testNum - 6); // testNum++; // if (testNum >= 11) // testNum = 0; // // uint8_t testTime[] = { uint8_t(2015/256), uint8_t(2015%256), 12, 18, testHr, testMin, 0 }; // time_t testTimeT = WatchTimeService::watchTimeToUnixTime(testTime); // // ledDisplay.start(testTimeT, LedDisplay::DispType_CurTime, DISPLAY_ON_TIME); } // Helper void setRTCfromWatchTime(const uint8_t* pWatchTime) { time_t timest = WatchTimeService::watchTimeToUnixTime(pWatchTime); if ((int)timest != -1) { rtc.set_time(timest); } } void onDataWrittenCallback(const GattWriteCallbackParams *params) { // Check if this is time setting if (pWatchTimeService->getWatchTimeValueHandle() == params->handle) { if (params->len == WatchTimeService::WatchTime_BlockSize) { setRTCfromWatchTime(params->data); } } else if (pWatchTimeService->getAlarmTimeValueHandle() == params->handle) { if (params->len == WatchTimeService::WatchTime_BlockSize) { time_t timest = WatchTimeService::watchTimeToUnixTime(params->data); if (((int)timest != -1) && ((int)timest != 0)) alarmSetTime = timest; else alarmSetTime = 0; } } } // DEBUG CODE void print_time() { time_t rawtime=rtc.time(); // massage the time into a human-friendly format for printing struct tm * timeinfo; timeinfo = localtime(&rawtime); char date[24]; strftime(date,sizeof(date),"%H:%M:%S on %m/%d/%G",timeinfo); printf("The current time is %s.\r\n",date); } int main(void) { printf("AlarmWatch\r\n"); // This is necessary to keep the clock ticking - doesn't have to be so frequent // According to this https://developer.mbed.org/users/fxschumacher/code/nRF51_rtc_example/file/c1f06d0a5e11/main.cpp // any time less than 512 seconds is ok periodicCallbackToKeepTimerGoing.attach(periodicCallback, PERIODIC_CALLBACK_SECS); // Handlers for the button press button.fall(buttonPressedCallback); button.rise(buttonReleasedCallback); // Clear display ledDisplay.clear(); // BLE init ble.init(); ble.gap().onDisconnection(disconnectionCallback); ble.onDataWritten(onDataWrittenCallback); // Watch Time Service uint8_t initialTime[] = { uint8_t(2015/256), uint8_t(2015%256), 12, 18, 10, 10, 0 }; uint8_t alarmTime[] = { 0, 0, 0, 0, 0, 0, 0 }; WatchTimeService watchTimeService(ble, initialTime, alarmTime); setRTCfromWatchTime(initialTime); pWatchTimeService = &watchTimeService; /* Enable over-the-air firmware updates. Instantiating DFUSservice introduces a * control characteristic which can be used to trigger the application to * handover control to a resident bootloader. */ DFUService dfu(ble); // Setup advertising ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); ble.gap().setAdvertisingInterval(1000); /* 1000ms. */ ble.gap().startAdvertising(); // Turn off any alarm AlarmOff(); while (true) { ble.waitForEvent(); } }