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:
- 2015-12-14
- Revision:
- 5:2682353a8c32
- Parent:
- 4:f0b030a3223f
- Child:
- 6:4a12e0f03381
File content as of revision 5:2682353a8c32:
// BLE Alarm Watch // Based on BLE examples on MBED // Rob Dobson, (c) 2015 #include "mbed.h" #include "BLE.h" #include "ButtonService.h" #include "WatchTimeService.h" #include "nrf51_rtc.h" // BLE platform BLE ble; // Hour LEDs DigitalOut hourPins[5] = { p0, p1, p2, p3, p4 }; // Minute LEDs DigitalOut minPins[6] = { p23, p24, p25, p28, p29, p30 }; // Button press to show time InterruptIn button(p5); // 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[] = {ButtonService::BUTTON_SERVICE_UUID, WatchTimeService::WATCHTIME_SERVICE_UUID}; // Service offering to read and set the time on the watch WatchTimeService *pWatchTimeService; // Time display state variables const int timeDisplayState_None = 0; const int timeDisplayState_ShowTime = 1; const int timeDisplayState_ShowAlarm = 2; int timeDisplayState = timeDisplayState_None; uint8_t curBinaryLedDisplayVal[WatchTimeService::WatchTime_BlockSize]; int timeDisplayLastButtonTime = 0; const int timeDisplayShowTimeSecs = 10; const int timeDisplayMoveToShowAlarmSecs = 5; // Cur time disp info int curTimeLEDBitPos = 0; // TEST CODE int callbackCount = 0; uint8_t testbuf[WatchTimeService::WatchTime_BlockSize]; time_t retval = 0; int servcode = 0; int buflen = 0; int mycode = 0; int offs = 0; int butcode = 0; ButtonService *buttonServicePtr; Ticker periodicCallbackToKeepTimerGoing; Timeout timerForLEDMuxing; time_t watchTimeToUnix(const uint8_t* pWatchTime) { struct tm tminfo; tminfo.tm_year = int(pWatchTime[0])*256 + pWatchTime[1] - 1900; tminfo.tm_mon = pWatchTime[2] - 1; tminfo.tm_mday = pWatchTime[3]; tminfo.tm_hour = pWatchTime[4]; tminfo.tm_min = pWatchTime[5]; tminfo.tm_sec = pWatchTime[6]; tminfo.tm_isdst = -1; time_t timest = mktime(&tminfo); return timest; } void unixTimeToWatchTime(time_t unixTime, uint8_t* pWatchTime) { // Convert to localtime struct tm * timeinfo; timeinfo = localtime (&unixTime); pWatchTime[0] = (timeinfo->tm_year + 1900) / 256; pWatchTime[1] = (timeinfo->tm_year + 1900) % 256; pWatchTime[2] = timeinfo->tm_mon + 1; pWatchTime[3] = timeinfo->tm_mday; pWatchTime[4] = timeinfo->tm_hour; pWatchTime[5] = timeinfo->tm_min; pWatchTime[6] = timeinfo->tm_sec; } int watchTimeToBCD(const uint8_t* pWatchTime) { // Simply combine the hour and minute values in a 4 digit BCD number int bcdHourMin = pWatchTime[4] / 10; bcdHourMin = (bcdHourMin << 4) + pWatchTime[4] % 10; bcdHourMin = (bcdHourMin << 4) + pWatchTime[5] / 10; bcdHourMin = (bcdHourMin << 4) + pWatchTime[5] % 10; return bcdHourMin; } void callbackForLEDMuxing(); void clearTimeLEDs() { for (int i = 0; i < 5; i++) hourPins[i] = 0; for (int i = 0; i < 6; i++) minPins[i] = 0; } //uint8_t* GetCurTimeAsWatchTime() //{ // // Get current time and convert to displayable time // time_t rawtime=rtc.time(); // unixTimeToWatchTime(rawtime, curBinaryLedDisplayVal); //} void nextShowingTimeLEDs() { // Get current time and convert to displayable time time_t rawtime=rtc.time(); unixTimeToWatchTime(rawtime, curBinaryLedDisplayVal); // Clear LEDs clearTimeLEDs(); // Stop displaying time after a certain number of seconds if (rawtime - timeDisplayLastButtonTime >= timeDisplayShowTimeSecs) return; // Display binary time int hours = curBinaryLedDisplayVal[4]; int mins = curBinaryLedDisplayVal[5]; int mask = 1 << curTimeLEDBitPos; if ((curTimeLEDBitPos < 5) && ((hours & mask) != 0)) hourPins[curTimeLEDBitPos] = 1; if ((mins & mask) != 0) minPins[curTimeLEDBitPos] = 1; curTimeLEDBitPos++; if (curTimeLEDBitPos > 5) curTimeLEDBitPos = 0; // Set for another callback timerForLEDMuxing.attach(callbackForLEDMuxing, 0.001); } void callbackForLEDMuxing() { nextShowingTimeLEDs(); } void startShowingTimeLEDs() { curTimeLEDBitPos = 0; timerForLEDMuxing.attach(callbackForLEDMuxing, 0.001); } void stopShowingTimeLEDs() { clearTimeLEDs(); } // Handle button press to read watch void buttonPressedCallback(void) { // Get the time to display time_t rawtime=rtc.time(); // Check if we should display current time or alarm time if (rawtime - timeDisplayLastButtonTime > timeDisplayMoveToShowAlarmSecs) { timeDisplayState = timeDisplayState_ShowTime; } else { // TO BE DONE // - move alarm time value to the curBinaryLedDisplayVal timeDisplayState = timeDisplayState_ShowAlarm; } startShowingTimeLEDs(); // Remember when button last pressed timeDisplayLastButtonTime = rawtime; // Update the button-state service buttonServicePtr->updateButtonState(true); } // TEST CODE void buttonReleasedCallback(void) { // Update the button-state service buttonServicePtr->updateButtonState(false); } // Handle BLE disconnection - restart advertising void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) { ble.gap().startAdvertising(); } // TEST CODE 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 rtc.time(); } void setRTCfromWatchTime(const uint8_t* pWatchTime) { time_t timest = watchTimeToUnix(pWatchTime); retval = timest; if ((int)timest != -1) { rtc.set_time(timest); minPins[5] = !minPins[5]; } } void onDataWrittenCallback(const GattWriteCallbackParams *params) { // TEST code callbackCount++; memcpy(testbuf, params->data, WatchTimeService::WatchTime_BlockSize); servcode = params->handle; buflen = params->len; mycode = pWatchTimeService->getValueHandle(); butcode = buttonServicePtr->getValueHandle(); offs = params->offset; // Check if this is time setting if (pWatchTimeService->getValueHandle() == params->handle) { if (params->len == WatchTimeService::WatchTime_BlockSize) { setRTCfromWatchTime(params->data); } } } // TEST 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"); // TEST CODE memset(testbuf, 0, WatchTimeService::WatchTime_BlockSize); int loopCount = 0; periodicCallbackToKeepTimerGoing.attach(periodicCallback, 1); button.fall(buttonPressedCallback); button.rise(buttonReleasedCallback); // Clear display clearTimeLEDs(); // BLE init ble.init(); ble.gap().onDisconnection(disconnectionCallback); ble.onDataWritten(onDataWrittenCallback); // TEST CODE ButtonService buttonService(ble, false /* initial value for button pressed */); buttonServicePtr = &buttonService; // Watch Time Service uint8_t initialTime[] = { uint8_t(2015/256), uint8_t(2015%256), 7, 26, 12, 8, 0 }; WatchTimeService watchTimeService(ble, initialTime); setRTCfromWatchTime(initialTime); pWatchTimeService = &watchTimeService; // 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(); uint8_t curWatchTime[WatchTimeService::WatchTime_BlockSize]; while (true) { ble.waitForEvent(); // TEST CODE loopCount++; if (loopCount < 5) continue; loopCount = 0; print_time(); // Get current time and convert to watch time time_t rawtime=rtc.time(); unixTimeToWatchTime(rawtime, curWatchTime); pWatchTimeService->writeWatchTime(curWatchTime); /* printf ("Timest %02x %02x %02x %02x %02x %02x %02x %ld\r\n", testbuf[0], testbuf[1], testbuf[2], testbuf[3], testbuf[4], testbuf[5], testbuf[6], retval); printf ("serv %d buflen %d mycode %d offs %d butcode %d\r\n", servcode, buflen, mycode, offs, butcode); printf ("val %ld\r\n", watchTimeService.getValueHandle()); print_time(); if (timeDisplayState != timeDisplayState_None) { int watchLedTime = watchTimeToBCD(curBinaryLedDisplayVal); printf("watchTime %04x = ", watchLedTime); for (int i = 15; i >= 0; i--) { printf("%d", (watchLedTime >> i) % 2); } printf("\r\n"); } */ } } /* #include "mbed.h" #include "BLEDevice.h" #include "DeviceInformationService.h" // BLE Device etc BLEDevice ble; DigitalOut ledIndicator(LED1); DigitalOut testOut(p1); // Device name const static char DEVICE_NAME[] = "JOESALARM"; // UUID for CurTimeService & TimeBlockCharacteristic const uint16_t UUID_CUR_TIME_SERVICE = 0xA000; const uint16_t UUID_TIME_BLOCK_CHARACTERISTIC = 0xA001; // List of supported service UUIDs static const uint16_t uuid16_list[] = {UUID_CUR_TIME_SERVICE}; // Time is packed in an array of bytes const int SIZE_OF_TIME_BLOCK = 7; uint8_t timeBlockInitValue[SIZE_OF_TIME_BLOCK]; uint8_t curTimeBlock[SIZE_OF_TIME_BLOCK]; // GATT Characteristic for time block GattCharacteristic timeBlockCharacteristic(UUID_TIME_BLOCK_CHARACTERISTIC, timeBlockInitValue, SIZE_OF_TIME_BLOCK, SIZE_OF_TIME_BLOCK, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); // Callback when connection lost void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) { ble.startAdvertising(); // restart advertising } // Callback for ticker int tickCount = 0; bool showInfo = false; int writeSinceLast = 0; void periodicCallback(void) { ledIndicator = !ledIndicator; testOut = !testOut; tickCount++; if (tickCount > 100) { showInfo = true; tickCount = 0; } } // Data written callback void onDataWrittenCallback(const GattCharacteristicWriteCBParams *params) { if ((params->charHandle == timeBlockCharacteristic.getValueHandle())) { // Validate time int year = params->data[0] * 100 + params->data[1]; int month = params->data[2]; int day = params->data[3]; // && (params->len == SIZE_OF_TIME_BLOCK) writeSinceLast = params->len; memcpy(curTimeBlock, params->data, SIZE_OF_TIME_BLOCK); } writeSinceLast = true; } // Main int main(void) { ledIndicator = 0; testOut = 0; // Ticker is interrupt driven Ticker ticker; ticker.attach_us(periodicCallback, 100000); // Initial value for the time block characteristic memset(timeBlockInitValue, 0, sizeof(timeBlockInitValue)); // Init BLE and register callbacks ble.init(); ble.onDisconnection(disconnectionCallback); ble.onDataWritten(onDataWrittenCallback); // Add the time setting service GattCharacteristic *charTable[] = {&timeBlockCharacteristic}; GattService timeBlockService(UUID_CUR_TIME_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); ble.addService(timeBlockService); // Setup advertising // BREDR_NOT_SUPPORTED means classic bluetooth not supported; // LE_GENERAL_DISCOVERABLE means that this peripheral can be // discovered by any BLE scanner--i.e. any phone. ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); // Add services to payload ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // This is where we're collecting the device name into the advertisement payload. ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); // We'd like for this BLE peripheral to be connectable. ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); // set the interval at which advertisements are sent out; this has // an implication power consumption--radio activity being a // biggest draw on average power. The other software controllable // parameter which influences power is the radio's TX power // level--there's an API to adjust that. ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000)); ble.startAdvertising(); while (true) { ble.waitForEvent(); if (showInfo) { printf("Time info: "); for (int i = 0; i < SIZE_OF_TIME_BLOCK; i++) { printf("%02d", curTimeBlock[i]); } printf(" - writeSinceLast %d\r\n", writeSinceLast); showInfo = false; writeSinceLast = 0; } } } */