Pull request
Dependencies: BLE_API mbed nRF51822
Fork of BLE_URIBeacon by
Diff: main.cpp
- Revision:
- 13:b82d8db73633
- Parent:
- 11:c77cc2b74101
- Child:
- 14:868a1207022d
diff -r a60959597fb4 -r b82d8db73633 main.cpp --- a/main.cpp Thu Jan 22 16:15:44 2015 +0000 +++ b/main.cpp Fri Feb 13 13:05:49 2015 +0000 @@ -1,11 +1,11 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2013 ARM Limited +/* + * Copyright 2014-2015 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,38 +14,244 @@ * limitations under the License. */ +#include <stdint.h> +#include <stddef.h> +#include <nrf_error.h> #include "mbed.h" #include "BLEDevice.h" -#include "nrfURIBeaconConfigService.h" +#include "URIBeaconConfigService.h" #include "DFUService.h" +#include "pstorage.h" #include "DeviceInformationService.h" +// Struct to hold persistent data across power cycles +struct PersistentData_t { + uint32_t magic; + URIBeaconConfigService::Params_t params; + uint8_t pad[4]; +} __attribute__ ((aligned (4))); + +static const int PERSISTENT_DATA_ALIGNED_SIZE = sizeof(PersistentData_t); +// Seconds after power-on that config service is available. +static const int ADVERTISING_TIMEOUT_SECONDS = 60; +// Advertising interval for config service. +static const int ADVERTISING_INTERVAL_MSEC = 1000; +// Maximum size of service data in ADV packets +static const int SERVICE_DATA_MAX = 31; +// Magic that identifies persistent storage +static const uint32_t MAGIC = 0x1BEAC000; +// Values for ADV packets related to firmware levels +static URIBeaconConfigService::PowerLevels_t defaultAdvPowerLevels = {-20, -4, 0, 10}; +// Values for setTxPower() indexed by power mode. +static const int8_t firmwarePowerLevels[] = {-20, -4, 0, 10}; + BLEDevice ble; +URIBeaconConfigService *uriBeaconConfig; +pstorage_handle_t pstorageHandle; +PersistentData_t persistentData; + +/* LEDs for indication */ +DigitalOut connectionStateLed(LED1); +DigitalOut advertisingStateLed(LED2); + +/* Dummy callback handler needed by Nordic's pstorage module. */ +void pstorageNotificationCallback(pstorage_handle_t *p_handle, + uint8_t op_code, + uint32_t result, + uint8_t * p_data, + uint32_t data_len) { + /* APP_ERROR_CHECK(result); */ +} + +void pstorageLoad() { + pstorage_init(); + pstorage_module_param_t pstorageParams = { + .cb = pstorageNotificationCallback, + .block_size = PERSISTENT_DATA_ALIGNED_SIZE, + .block_count = 1 + }; + pstorage_register(&pstorageParams, &pstorageHandle); + if (pstorage_load(reinterpret_cast<uint8_t *>(&persistentData), + &pstorageHandle, PERSISTENT_DATA_ALIGNED_SIZE, 0) != NRF_SUCCESS) { + // On failure zero out and let the service reset to defaults + memset(&persistentData, 0, sizeof(PersistentData_t)); + } +} + + +void pstorageSave() { + if (persistentData.magic != MAGIC) { + persistentData.magic = MAGIC; + pstorage_store(&pstorageHandle, + reinterpret_cast<uint8_t *>(&persistentData), + sizeof(PersistentData_t), + 0 /* offset */); + } else { + pstorage_update(&pstorageHandle, + reinterpret_cast<uint8_t *>(&persistentData), + sizeof(PersistentData_t), + 0 /* offset */); + } +} + +void startAdvertisingUriBeaconConfig() { + char DEVICE_NAME[] = "mUriBeacon Config"; + + ble.clearAdvertisingPayload(); + + // Stops advertising the UriBeacon Config Service after a delay + ble.setAdvertisingTimeout(ADVERTISING_TIMEOUT_SECONDS); + + ble.accumulateAdvertisingPayload( + GapAdvertisingData::BREDR_NOT_SUPPORTED | + GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + + // UUID is in different order in the ADV frame (!) + uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)]; + for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) { + reversedServiceUUID[i] = + UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1]; + } + ble.accumulateAdvertisingPayload( + GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, + reversedServiceUUID, + sizeof(reversedServiceUUID)); -void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) -{ + ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG); + ble.accumulateScanResponse( + GapAdvertisingData::COMPLETE_LOCAL_NAME, + reinterpret_cast<uint8_t *>(&DEVICE_NAME), + sizeof(DEVICE_NAME)); + ble.accumulateScanResponse( + GapAdvertisingData::TX_POWER_LEVEL, + reinterpret_cast<uint8_t *>( + &defaultAdvPowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]), + sizeof(uint8_t)); + + ble.setTxPower( + firmwarePowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]); + + ble.setDeviceName(reinterpret_cast<uint8_t *>(&DEVICE_NAME)); + ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + ble.setAdvertisingInterval( + Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC)); ble.startAdvertising(); } +void startAdvertisingUriBeacon() { + uint8_t serviceData[SERVICE_DATA_MAX]; + int serviceDataLen = 0; + + advertisingStateLed = 1; + connectionStateLed = 1; + + ble.shutdown(); + ble.init(); + + // Fields from the Service + int beaconPeriod = persistentData.params.beaconPeriod; + int txPowerMode = persistentData.params.txPowerMode; + int uriDataLength = persistentData.params.uriDataLength; + URIBeaconConfigService::UriData_t &uriData = persistentData.params.uriData; + URIBeaconConfigService::PowerLevels_t &advPowerLevels = persistentData.params.advPowerLevels; + uint8_t flags = persistentData.params.flags; + + pstorageSave(); + + delete uriBeaconConfig; + uriBeaconConfig = NULL; + + ble.clearAdvertisingPayload(); + ble.setTxPower(firmwarePowerLevels[txPowerMode]); + + ble.setAdvertisingType( + GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); + + ble.setAdvertisingInterval( + Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(beaconPeriod)); + + ble.accumulateAdvertisingPayload( + GapAdvertisingData::BREDR_NOT_SUPPORTED | + GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + + ble.accumulateAdvertisingPayload( + GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, + sizeof(BEACON_UUID)); + + serviceData[serviceDataLen++] = BEACON_UUID[0]; + serviceData[serviceDataLen++] = BEACON_UUID[1]; + serviceData[serviceDataLen++] = flags; + serviceData[serviceDataLen++] = advPowerLevels[txPowerMode]; + for (int j=0; j < uriDataLength; j++) { + serviceData[serviceDataLen++] = uriData[j]; + } + + ble.accumulateAdvertisingPayload( + GapAdvertisingData::SERVICE_DATA, + serviceData, serviceDataLen); + + ble.startAdvertising(); +} + +// After advertising timeout, stop config and switch to UriBeacon +void timeout(void) { + Gap::GapState_t state; + state = ble.getGapState(); + if (!state.connected) { + startAdvertisingUriBeacon(); + } +} + +// When connected to config service, change the LEDs +void connectionCallback(Gap::Handle_t handle, + Gap::addr_type_t peerAddrType, + const Gap::address_t peerAddr, + const Gap::ConnectionParams_t *params) { + advertisingStateLed = 1; + connectionStateLed = 0; +} + +// When disconnected from config service, start advertising UriBeacon +void disconnectionCallback(Gap::Handle_t handle, + Gap::DisconnectionReason_t reason) { + advertisingStateLed = 0; // on + connectionStateLed = 1; // off + startAdvertisingUriBeacon(); +} + int main(void) { + advertisingStateLed = 0; // on + connectionStateLed = 1; // off + ble.init(); ble.onDisconnection(disconnectionCallback); + ble.onConnection(connectionCallback); + // Advertising timeout + ble.onTimeout(timeout); - nrfURIBeaconConfigService uriBeaconConfig(ble, "http://www.mbed.org"); - if (!uriBeaconConfig.configuredSuccessfully()) { + pstorageLoad(); + bool resetToDefaults = persistentData.magic != MAGIC; + + URIBeaconConfigService::UriData_t defaultUriData; + size_t defaultUriDataLength; + URIBeaconConfigService::encodeURI("http://uribeacon.org", defaultUriData, defaultUriDataLength); + + uriBeaconConfig = new URIBeaconConfigService(ble, persistentData.params, resetToDefaults, + defaultUriData, defaultUriDataLength, defaultAdvPowerLevels); + if (!uriBeaconConfig->configuredSuccessfully()) { error("failed to accommodate URI"); } - /* optional use of the API offered by URIBeaconConfigService */ - const int8_t powerLevels[] = {-20, -4, 0, 10}; - uriBeaconConfig.setTxPowerLevels(powerLevels); - uriBeaconConfig.setTxPowerMode(URIBeaconConfigService::TX_POWER_MODE_LOW); - /* Setup auxiliary services. */ - DFUService dfu(ble); /* To allow over-the-air firmware udpates. optional. */ - DeviceInformationService deviceInfo(ble, "ARM", "URIBeacon2", "SN1", "hw-rev1", "fw-rev1", "soft-rev1"); /* optional */ + // Setup auxiliary services to allow over-the-air firmware updates, etc + DFUService dfu(ble); + DeviceInformationService deviceInfo( + ble, "ARM", "UriBeacon", "SN1", "hw-rev1", "fw-rev1", "soft-rev1"); - ble.startAdvertising(); + /* Start out by advertising the configService for a limited time after + * startup; and switch to the normal non-connectible beacon functionality + * afterwards. */ + startAdvertisingUriBeaconConfig(); while (true) { ble.waitForEvent();