Kenji Arai / Mbed 2 deprecated BLE_EddystoneBeacon_w_ACC_TY51822

Dependencies:   BLE_API LIS3DH mbed nRF51822 BMC050 nRF51_LowPwr nRF51_Vdd

Fork of BLE_EddystoneBeacon_Service by Bluetooth Low Energy

Files at this revision

API Documentation at this revision

Comitter:
andresag
Date:
Thu Nov 26 16:36:04 2015 +0000
Parent:
33:862e6e0831ea
Child:
35:c64495aac4d1
Commit message:
Update example to use latest version of BLE_API and nRF51822 library. Furthermore, the example now uses a new Eddystone implementation rather than the deprecated implementation under ble/services.

Changed in this revision

BLE_API.lib Show annotated file Show diff for this revision Revisions of this file
EddystoneService.cpp Show annotated file Show diff for this revision Revisions of this file
EddystoneService.h Show annotated file Show diff for this revision Revisions of this file
EddystoneTypes.h Show annotated file Show diff for this revision Revisions of this file
TLMFrame.cpp Show annotated file Show diff for this revision Revisions of this file
TLMFrame.h Show annotated file Show diff for this revision Revisions of this file
UIDFrame.cpp Show annotated file Show diff for this revision Revisions of this file
UIDFrame.h Show annotated file Show diff for this revision Revisions of this file
URLFrame.cpp Show annotated file Show diff for this revision Revisions of this file
URLFrame.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
nRF51822.lib Show annotated file Show diff for this revision Revisions of this file
--- a/BLE_API.lib	Thu Oct 01 18:30:37 2015 +0000
+++ b/BLE_API.lib	Thu Nov 26 16:36:04 2015 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#d494ad3e87bd
+http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#c6c50b28ccd2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EddystoneService.cpp	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,570 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EddystoneService.h"
+
+/* Initialise the EddystoneService using parameters from persistent storage */
+EddystoneService::EddystoneService(BLE                 &bleIn,
+                                   EddystoneParams_t   &paramsIn,
+                                   const PowerLevels_t &advPowerLevelsIn,
+                                   const PowerLevels_t &radioPowerLevelsIn,
+                                   uint32_t            advConfigIntervalIn) :
+    ble(bleIn),
+    operationMode(EDDYSTONE_MODE_NONE),
+    urlFrame(paramsIn.urlData, paramsIn.urlDataLength),
+    uidFrame(paramsIn.uidNamespaceID, paramsIn.uidInstanceID),
+    tlmFrame(paramsIn.tlmVersion),
+    resetFlag(false),
+    tlmBatteryVoltageCallback(NULL),
+    tlmBeaconTemperatureCallback(NULL)
+{
+    lockState         = paramsIn.lockState;
+    flags             = paramsIn.flags;
+    txPowerMode       = paramsIn.txPowerMode;
+    beaconPeriod      = correctAdvertisementPeriod(paramsIn.beaconPeriod);
+
+    memcpy(lock,             paramsIn.lock,      sizeof(Lock_t));
+    memcpy(unlock,           paramsIn.unlock,    sizeof(Lock_t));
+
+    eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn);
+}
+
+/* When using this constructor we need to call setURLData,
+ * setTMLData and setUIDData to initialise values manually
+ */
+EddystoneService::EddystoneService(BLE                 &bleIn,
+                                   const PowerLevels_t &advPowerLevelsIn,
+                                   const PowerLevels_t &radioPowerLevelsIn,
+                                   uint32_t            advConfigIntervalIn) :
+    ble(bleIn),
+    operationMode(EDDYSTONE_MODE_NONE),
+    urlFrame(),
+    uidFrame(),
+    tlmFrame(),
+    lockState(false),
+    resetFlag(false),
+    lock(),
+    unlock(),
+    flags(0),
+    txPowerMode(0),
+    beaconPeriod(DEFAULT_BEACON_PERIOD_MSEC),
+    tlmBatteryVoltageCallback(NULL),
+    tlmBeaconTemperatureCallback(NULL)
+{
+    eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn);
+}
+
+/* Setup callback to update BatteryVoltage in TLM frame */
+void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn)
+{
+    tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn;
+}
+
+/* Setup callback to update BeaconTemperature in TLM frame */
+void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn)
+{
+    tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn;
+}
+
+void EddystoneService::setTLMData(uint8_t tlmVersionIn)
+{
+   tlmFrame.setTLMData(tlmVersionIn);
+}
+
+void EddystoneService::setURLData(const char *urlDataIn)
+{
+    urlFrame.setURLData(urlDataIn);
+}
+
+void EddystoneService::setUIDData(const UIDNamespaceID_t *uidNamespaceIDIn, const UIDInstanceID_t *uidInstanceIDIn)
+{
+    uidFrame.setUIDData(uidNamespaceIDIn, uidInstanceIDIn);
+}
+
+EddystoneService::EddystoneError_t EddystoneService::startConfigService(void)
+{
+    if (operationMode == EDDYSTONE_MODE_CONFIG) {
+        /* Nothing to do, we are already in config mode */
+        return EDDYSTONE_ERROR_NONE;
+    } else if (advConfigInterval == 0) {
+        /* Nothing to do, the advertisement interval is 0 */
+        return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
+    }
+
+    if (operationMode == EDDYSTONE_MODE_BEACON) {
+        ble.shutdown();
+        /* Free unused memory */
+        freeBeaconFrames();
+        operationMode = EDDYSTONE_MODE_CONFIG;
+        ble.init(this, &EddystoneService::bleInitComplete);
+        return EDDYSTONE_ERROR_NONE;
+    }
+
+    operationMode = EDDYSTONE_MODE_CONFIG;
+    setupConfigService();
+    return EDDYSTONE_ERROR_NONE;
+}
+
+EddystoneService::EddystoneError_t EddystoneService::startBeaconService(uint16_t consecUrlFramesIn, uint16_t consecUidFramesIn, uint16_t consecTlmFramesIn)
+{
+    if (operationMode == EDDYSTONE_MODE_BEACON) {
+        /* Nothing to do, we are already in beacon mode */
+        return EDDYSTONE_ERROR_NONE;
+    } else if (!consecUrlFramesIn && !consecUidFramesIn && !consecTlmFramesIn) {
+        /* Nothing to do, the user wants 0 consecutive frames of everything */
+        return EDDYSTONE_ERROR_INVALID_CONSEC_FRAMES;
+    } else if (!beaconPeriod) {
+        /* Nothing to do, the period is 0 for all frames */
+        return EDDYSTONE_ERROR_INVALID_BEACON_PERIOD;
+    }
+
+    /* Setup tracking of the current advertised frame. Note that this will
+     * cause URL or UID frames to be advertised first!
+     */
+    currentAdvertisedFrame            = EDDYSTONE_FRAME_TLM;
+    consecFrames[EDDYSTONE_FRAME_URL] = consecUrlFramesIn;
+    consecFrames[EDDYSTONE_FRAME_UID] = consecUidFramesIn;
+    consecFrames[EDDYSTONE_FRAME_TLM] = consecTlmFramesIn;
+
+    memset(currentConsecFrames, 0, sizeof(uint16_t) * NUM_EDDYSTONE_FRAMES);
+
+    if (operationMode == EDDYSTONE_MODE_CONFIG) {
+        ble.shutdown();
+        /* Free unused memory */
+        freeConfigCharacteristics();
+        operationMode = EDDYSTONE_MODE_BEACON;
+        ble.init(this, &EddystoneService::bleInitComplete);
+        return EDDYSTONE_ERROR_NONE;
+    }
+
+    operationMode = EDDYSTONE_MODE_BEACON;
+    setupBeaconService();
+    return EDDYSTONE_ERROR_NONE;
+}
+
+/* It is not the responsibility of the Eddystone implementation to store
+ * the configured parameters in persistent storage since this is
+ * platform-specific. So we provide this function that returns the
+ * configured values that need to be stored and the main application
+ * takes care of storing them.
+ */
+void EddystoneService::getEddystoneParams(EddystoneParams_t *params)
+{
+    params->lockState     = lockState;
+    params->flags         = flags;
+    params->txPowerMode   = txPowerMode;
+    params->beaconPeriod  = beaconPeriod;
+    params->tlmVersion    = tlmFrame.getTLMVersion();
+    params->urlDataLength = urlFrame.getEncodedURLDataLength();
+
+    memcpy(params->advPowerLevels, advPowerLevels,               sizeof(PowerLevels_t));
+    memcpy(params->lock,           lock,                         sizeof(Lock_t));
+    memcpy(params->unlock,         unlock,                       sizeof(Lock_t));
+    memcpy(params->urlData,        urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
+    memcpy(params->uidNamespaceID, uidFrame.getUIDNamespaceID(), sizeof(UIDNamespaceID_t));
+    memcpy(params->uidInstanceID,  uidFrame.getUIDInstanceID(),  sizeof(UIDInstanceID_t));
+}
+
+/* Helper function used only once during constructing the object to avoid
+ * duplicated code.
+ */
+void EddystoneService::eddystoneConstructorHelper(const PowerLevels_t &advPowerLevelsIn,
+                                                  const PowerLevels_t &radioPowerLevelsIn,
+                                                  uint32_t            advConfigIntervalIn)
+{
+    advConfigInterval = (advConfigIntervalIn > 0) ? correctAdvertisementPeriod(advConfigIntervalIn) : 0;
+
+    memcpy(radioPowerLevels, radioPowerLevelsIn, sizeof(PowerLevels_t));
+    memcpy(advPowerLevels,   advPowerLevelsIn,   sizeof(PowerLevels_t));
+
+    /* TODO: Note that this timer is started from the time EddystoneService
+     * is initialised and NOT from when the device is booted. So app needs
+     * to take care that EddystoneService is one of the first things to be
+     * started!
+     */
+    timeSinceBootTimer.start();
+}
+
+/* When changing modes, we shutdown and init the BLE instance, so
+ * this is needed to complete the initialisation task.
+ */
+void EddystoneService::bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext)
+{
+    if (initContext->error != BLE_ERROR_NONE) {
+        /* Initialisation failed */
+        return;
+    }
+
+    switch (operationMode) {
+    case EDDYSTONE_MODE_CONFIG:
+        setupConfigService();
+        break;
+    case EDDYSTONE_MODE_BEACON:
+        setupBeaconService();
+        break;
+    default:
+        /* Some error occurred */
+        break;
+    }
+}
+
+void EddystoneService::swapAdvertisedFrame(void)
+{
+    /* This essentially works out which is the next frame to be swapped in
+     * and updated the advertised packets. It will eventually terminate
+     * and in the worst case the frame swapped in is the current advertised
+     * frame.
+     */
+    while (true) {
+        currentAdvertisedFrame = (currentAdvertisedFrame + 1) % NUM_EDDYSTONE_FRAMES;
+
+        if (currentAdvertisedFrame == EDDYSTONE_FRAME_URL && consecFrames[EDDYSTONE_FRAME_URL] > 0) {
+            updateAdvertisementPacket(rawUrlFrame, urlFrame.getRawFrameSize());
+            return;
+        } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_UID && consecFrames[EDDYSTONE_FRAME_UID] > 0) {
+            updateAdvertisementPacket(rawUidFrame, uidFrame.getRawFrameSize());
+            return;
+        } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM && consecFrames[EDDYSTONE_FRAME_UID] > 0) {
+            updateRawTLMFrame();
+            updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize());
+            return;
+        }
+    }
+}
+
+/* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available),
+ * then updates the raw frame data and finally updates the actual advertised packet. This operation must be
+ * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the
+ * Eddystone specification.
+ */
+void EddystoneService::updateRawTLMFrame(void)
+{
+    if (tlmBeaconTemperatureCallback != NULL) {
+        tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature()));
+    }
+    if (tlmBatteryVoltageCallback != NULL) {
+        tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage()));
+    }
+    tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms());
+    tlmFrame.constructTLMFrame(rawTlmFrame);
+}
+
+void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength)
+{
+    ble.gap().clearAdvertisingPayload();
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength);
+}
+
+void EddystoneService::setupBeaconService(void)
+{
+    /* Initialise arrays to hold constructed raw frames */
+    if (consecFrames[EDDYSTONE_FRAME_URL] > 0) {
+        rawUrlFrame = new uint8_t[urlFrame.getRawFrameSize()];
+        urlFrame.constructURLFrame(rawUrlFrame, advPowerLevels[txPowerMode]);
+    }
+
+    if (consecFrames[EDDYSTONE_FRAME_UID] > 0) {
+        rawUidFrame = new uint8_t[uidFrame.getRawFrameSize()];
+        uidFrame.constructUIDFrame(rawUidFrame, advPowerLevels[txPowerMode]);
+    }
+
+    if (consecFrames[EDDYSTONE_FRAME_TLM] > 0) {
+        rawTlmFrame = new uint8_t[tlmFrame.getRawFrameSize()];
+        /* Do not initialise because we have to reconstruct every 0.1 secs */
+    }
+
+    /* Configure advertisements */
+    ble.gap().setTxPower(radioPowerLevels[txPowerMode]);
+    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
+    ble.gap().setAdvertisingInterval(beaconPeriod);
+    ble.gap().onRadioNotification(this, &EddystoneService::radioNotificationCallback);
+    ble.gap().initRadioNotification();
+
+    /* Set advertisement packet payload */
+    swapAdvertisedFrame();
+
+    /* Start advertising */
+    ble.gap().startAdvertising();
+}
+
+void EddystoneService::setupConfigService(void)
+{
+    lockStateChar      = new ReadOnlyGattCharacteristic<bool>(UUID_LOCK_STATE_CHAR, &lockState);
+    lockChar           = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_LOCK_CHAR, lock);
+    unlockChar         = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlock);
+    urlDataChar        = new GattCharacteristic(UUID_URL_DATA_CHAR, urlFrame.getEncodedURLData(), 0, URL_DATA_MAX, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
+    flagsChar          = new ReadWriteGattCharacteristic<uint8_t>(UUID_FLAGS_CHAR, &flags);
+    advPowerLevelsChar = new ReadWriteArrayGattCharacteristic<int8_t, sizeof(PowerLevels_t)>(UUID_ADV_POWER_LEVELS_CHAR, advPowerLevels);
+    txPowerModeChar    = new ReadWriteGattCharacteristic<uint8_t>(UUID_TX_POWER_MODE_CHAR, &txPowerMode);
+    beaconPeriodChar   = new ReadWriteGattCharacteristic<uint16_t>(UUID_BEACON_PERIOD_CHAR, &beaconPeriod);
+    resetChar          = new WriteOnlyGattCharacteristic<bool>(UUID_RESET_CHAR, &resetFlag);
+
+    lockChar->setWriteAuthorizationCallback(this, &EddystoneService::lockAuthorizationCallback);
+    unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::unlockAuthorizationCallback);
+    urlDataChar->setWriteAuthorizationCallback(this, &EddystoneService::urlDataWriteAuthorizationCallback);
+    flagsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint8_t>);
+    advPowerLevelsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<PowerLevels_t>);
+    txPowerModeChar->setWriteAuthorizationCallback(this, &EddystoneService::powerModeAuthorizationCallback);
+    beaconPeriodChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint16_t>);
+    resetChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<bool>);
+
+    charTable[0] = lockStateChar;
+    charTable[1] = lockChar;
+    charTable[2] = unlockChar;
+    charTable[3] = urlDataChar;
+    charTable[4] = flagsChar;
+    charTable[5] = advPowerLevelsChar;
+    charTable[6] = txPowerModeChar;
+    charTable[7] = beaconPeriodChar;
+    charTable[8] = resetChar;
+
+    GattService configService(UUID_URL_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
+
+    ble.gattServer().addService(configService);
+    ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback);
+    updateCharacteristicValues();
+    setupEddystoneConfigAdvertisements();
+}
+
+void EddystoneService::freeConfigCharacteristics(void)
+{
+    delete lockStateChar;
+    delete lockChar;
+    delete unlockChar;
+    delete urlDataChar;
+    delete flagsChar;
+    delete advPowerLevelsChar;
+    delete txPowerModeChar;
+    delete beaconPeriodChar;
+    delete resetChar;
+}
+
+void EddystoneService::freeBeaconFrames(void)
+{
+    delete[] rawUrlFrame;
+    delete[] rawUidFrame;
+    delete[] rawTlmFrame;
+}
+
+void EddystoneService::radioNotificationCallback(bool radioActive)
+{
+    if (radioActive) {
+        /* Do nothing */
+        return;
+    }
+
+    tlmFrame.updatePduCount();
+    currentConsecFrames[currentAdvertisedFrame]++;
+
+    if (consecFrames[currentAdvertisedFrame] > currentConsecFrames[currentAdvertisedFrame]) {
+        if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM) {
+            /* Update the TLM frame otherwise we will not meet the 0.1 secs resolution of
+             * the Eddystone specification.
+             */
+            updateRawTLMFrame();
+            updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize());
+        }
+        /* Keep advertising the same frame */
+        return;
+    }
+
+    currentConsecFrames[currentAdvertisedFrame] = 0;
+
+#ifdef YOTTA_CFG_MBED_OS
+    minar::Scheduler::postCallback(this, &EddystoneService::swapAdvertisedFrame);
+#else
+    swapAdvertisedFrameTimeout.attach_us(this, &EddystoneService::swapAdvertisedFrame, 1);
+#endif
+}
+
+/*
+ * Internal helper function used to update the GATT database following any
+ * change to the internal state of the service object.
+ */
+void EddystoneService::updateCharacteristicValues(void)
+{
+    ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
+    ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
+    ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
+    ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));
+    ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
+    ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));
+    ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
+    ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));
+}
+
+void EddystoneService::setupEddystoneConfigAdvertisements(void)
+{
+    ble.gap().clearAdvertisingPayload();
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+
+    /* UUID is in different order in the ADV frame (!) */
+    uint8_t reversedServiceUUID[sizeof(UUID_URL_BEACON_SERVICE)];
+    for (size_t i = 0; i < sizeof(UUID_URL_BEACON_SERVICE); i++) {
+        reversedServiceUUID[i] = UUID_URL_BEACON_SERVICE[sizeof(UUID_URL_BEACON_SERVICE) - i - 1];
+    }
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
+    ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
+    ble.gap().accumulateScanResponse(
+        GapAdvertisingData::TX_POWER_LEVEL,
+        reinterpret_cast<uint8_t *>(&advPowerLevels[TX_POWER_MODE_LOW]),
+        sizeof(uint8_t));
+
+    ble.gap().setTxPower(radioPowerLevels[txPowerMode]);
+    ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
+    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.gap().setAdvertisingInterval(advConfigInterval);
+    ble.gap().startAdvertising();
+}
+
+void EddystoneService::lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+    if (lockState) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
+    } else if (authParams->len != sizeof(Lock_t)) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+    } else if (authParams->offset != 0) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+    } else {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+    }
+}
+
+void EddystoneService::unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+    if (!lockState && (authParams->len == sizeof(Lock_t))) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+    } else if (authParams->len != sizeof(Lock_t)) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+    } else if (authParams->offset != 0) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+    } else if (memcmp(authParams->data, lock, sizeof(Lock_t)) != 0) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
+    } else {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+    }
+}
+
+void EddystoneService::urlDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+    if (lockState) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
+    } else if (authParams->offset != 0) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+    } else {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+    }
+}
+
+void EddystoneService::powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+    if (lockState) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
+    } else if (authParams->len != sizeof(uint8_t)) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+    } else if (authParams->offset != 0) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+    } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
+    } else {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+    }
+}
+
+template <typename T>
+void EddystoneService::basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+    if (lockState) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
+    } else if (authParams->len != sizeof(T)) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+    } else if (authParams->offset != 0) {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+    } else {
+        authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+    }
+}
+
+/*
+ * This callback is invoked when a GATT client attempts to modify any of the
+ * characteristics of this service. Attempts to do so are also applied to
+ * the internal state of this service object.
+ */
+void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams)
+{
+    uint16_t handle = writeParams->handle;
+
+    if (handle == lockChar->getValueHandle()) {
+        memcpy(lock, writeParams->data, sizeof(Lock_t));
+        /* Set the state to be locked by the lock code (note: zeros are a valid lock) */
+        lockState = true;
+        ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
+        ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
+    } else if (handle == unlockChar->getValueHandle()) {
+        /* Validated earlier */
+        lockState = false;
+        ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));
+        ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
+    } else if (handle == urlDataChar->getValueHandle()) {
+        urlFrame.setEncodedURLData(writeParams->data, writeParams->len);
+        ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
+    } else if (handle == flagsChar->getValueHandle()) {
+        flags = *(writeParams->data);
+        ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
+    } else if (handle == advPowerLevelsChar->getValueHandle()) {
+        memcpy(advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
+        ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));
+    } else if (handle == txPowerModeChar->getValueHandle()) {
+        txPowerMode = *(writeParams->data);
+        ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
+    } else if (handle == beaconPeriodChar->getValueHandle()) {
+        uint16_t tmpBeaconPeriod = correctAdvertisementPeriod(*((uint16_t *)(writeParams->data)));
+        if (tmpBeaconPeriod != beaconPeriod) {
+            beaconPeriod = tmpBeaconPeriod;
+            ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));
+        }
+    } else if (handle == resetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) {
+        /* Reset characteristics to default values */
+        flags        = 0;
+        txPowerMode  = TX_POWER_MODE_LOW;
+        beaconPeriod = DEFAULT_BEACON_PERIOD_MSEC;
+
+        urlFrame.setURLData(DEFAULT_URL);
+        memset(lock, 0, sizeof(Lock_t));
+
+        ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
+        ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
+        ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
+        ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));
+        ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
+    }
+}
+
+uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const
+{
+    /* Re-map beaconPeriod to within permissible bounds if necessary. */
+    if (beaconPeriodIn != 0) {
+        if (beaconPeriodIn < ble.gap().getMinAdvertisingInterval()) {
+            return ble.gap().getMinAdvertisingInterval();
+        } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) {
+            return ble.gap().getMaxAdvertisingInterval();
+        }
+    }
+    return beaconPeriodIn;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EddystoneService.h	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,238 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EDDYSTONESERVICE_H__
+#define __EDDYSTONESERVICE_H__
+
+#include "ble/BLE.h"
+#include "EddystoneTypes.h"
+#include "URLFrame.h"
+#include "UIDFrame.h"
+#include "TLMFrame.h"
+#include <string.h>
+#ifdef YOTTA_CFG_MBED_OS
+    #include "mbed-drivers/mbed.h"
+#else
+    #include "mbed.h"
+#endif
+
+class EddystoneService
+{
+public:
+    static const uint16_t TOTAL_CHARACTERISTICS = 9;
+
+    static const uint32_t DEFAULT_CONFIG_PERIOD_MSEC = 1000;
+    static const uint16_t DEFAULT_BEACON_PERIOD_MSEC = 1000;
+
+    /* Operation modes of the EddystoneService:
+     *      NONE: EddystoneService has been initialised but no memory has been
+     *            dynamically allocated. Additionally, no services are running
+     *            nothing is being advertised.
+     *      CONFIG: EddystoneService has been initialised, the configuration
+     *              service started and memory has been allocated for BLE
+     *              characteristics. Memory consumption peaks during CONFIG
+     *              mode.
+     *      BEACON: Eddystone service is running as a beacon advertising URL,
+     *              UID and/or TLM frames depending on how it is configured.
+     * Note: The main app can change the mode of EddystoneService at any point
+     * of time by calling startConfigService() or startBeaconService().
+     * Resources from the previous mode will be freed. It is currently NOT
+     * possible to force EddystoneService back into MODE_NONE.
+     */
+    enum OperationModes {
+        EDDYSTONE_MODE_NONE,
+        EDDYSTONE_MODE_CONFIG,
+        EDDYSTONE_MODE_BEACON
+    };
+
+    struct EddystoneParams_t {
+        bool             lockState;
+        Lock_t           lock;
+        Lock_t           unlock;
+        uint8_t          flags;
+        PowerLevels_t    advPowerLevels;
+        uint8_t          txPowerMode;
+        uint16_t         beaconPeriod;
+        uint8_t          tlmVersion;
+        uint8_t          urlDataLength;
+        UrlData_t        urlData;
+        UIDNamespaceID_t uidNamespaceID;
+        UIDInstanceID_t  uidInstanceID;
+    };
+
+    enum EddystoneError_t {
+        EDDYSTONE_ERROR_NONE,
+        EDDYSTONE_ERROR_INVALID_BEACON_PERIOD,
+        EDDYSTONE_ERROR_INVALID_CONSEC_FRAMES,
+        EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL
+    };
+
+    enum FrameType {
+        EDDYSTONE_FRAME_URL,
+        EDDYSTONE_FRAME_UID,
+        EDDYSTONE_FRAME_TLM,
+        NUM_EDDYSTONE_FRAMES
+    };
+
+    /* Initialise the EddystoneService using parameters from persistent storage */
+    EddystoneService(BLE                 &bleIn,
+                     EddystoneParams_t   &paramsIn,
+                     const PowerLevels_t &advPowerLevelsIn,
+                     const PowerLevels_t &radioPowerLevelsIn,
+                     uint32_t            advConfigIntervalIn = DEFAULT_CONFIG_PERIOD_MSEC);
+
+    /* When using this constructor we need to call setURLData,
+     * setTMLData and setUIDData to initialise values manually
+     */
+    EddystoneService(BLE                 &bleIn,
+                     const PowerLevels_t &advPowerLevelsIn,
+                     const PowerLevels_t &radioPowerLevelsIn,
+                     uint32_t            advConfigIntervalIn = DEFAULT_CONFIG_PERIOD_MSEC);
+
+    /* Setup callback to update BatteryVoltage in TLM frame */
+    void onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn);
+
+    /* Setup callback to update BeaconTemperature in TLM frame */
+    void onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn);
+
+    void setTLMData(uint8_t tlmVersionIn = 0);
+
+    void setURLData(const char *urlDataIn);
+
+    void setUIDData(const UIDNamespaceID_t *uidNamespaceIDIn, const UIDInstanceID_t *uidInstanceIDIn);
+
+    EddystoneError_t startConfigService(void);
+
+    EddystoneError_t startBeaconService(uint16_t consecUrlFramesIn = 2, uint16_t consecUidFramesIn = 2, uint16_t consecTlmFramesIn = 2);
+
+    /* It is not the responsibility of the Eddystone implementation to store
+     * the configured parameters in persistent storage since this is
+     * platform-specific. So we provide this function that returns the
+     * configured values that need to be stored and the main application
+     * takes care of storing them.
+     */
+    void getEddystoneParams(EddystoneParams_t *params);
+
+private:
+    /* Helper function used only once during constructing the object to avoid
+     * duplicated code.
+     */
+    void eddystoneConstructorHelper(const PowerLevels_t &advPowerLevelsIn,
+                                    const PowerLevels_t &radioPowerLevelsIn,
+                                    uint32_t            advConfigIntervalIn);
+
+    /* When changing modes, we shutdown and init the BLE instance, so
+     * this is needed to complete the initialisation task.
+     */
+    void bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext);
+
+    void swapAdvertisedFrame(void);
+
+    void updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength);
+
+    /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available),
+     * then updates the raw frame data and finally updates the actual advertised packet. This operation must be
+     * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the
+     * Eddystone specification.
+     */
+    void updateRawTLMFrame(void);
+
+    void setupBeaconService(void);
+
+    void setupConfigService(void);
+
+    void freeConfigCharacteristics(void);
+
+    void freeBeaconFrames(void);
+
+    void radioNotificationCallback(bool radioActive);
+
+    /*
+     * Internal helper function used to update the GATT database following any
+     * change to the internal state of the service object.
+     */
+    void updateCharacteristicValues(void);
+
+    void setupEddystoneConfigAdvertisements(void);
+
+    void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+    void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+    void urlDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+    void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+    template <typename T>
+    void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+    /*
+     * This callback is invoked when a GATT client attempts to modify any of the
+     * characteristics of this service. Attempts to do so are also applied to
+     * the internal state of this service object.
+     */
+    void onDataWrittenCallback(const GattWriteCallbackParams *writeParams);
+
+    uint16_t correctAdvertisementPeriod(uint16_t beaconPeriodIn) const;
+
+    BLE                                                             &ble;
+    uint32_t                                                        advConfigInterval;
+    uint8_t                                                         operationMode;
+
+    URLFrame                                                        urlFrame;
+    UIDFrame                                                        uidFrame;
+    TLMFrame                                                        tlmFrame;
+
+    PowerLevels_t                                                   radioPowerLevels;
+    PowerLevels_t                                                   advPowerLevels;
+    bool                                                            lockState;
+    bool                                                            resetFlag;
+    Lock_t                                                          lock;
+    Lock_t                                                          unlock;
+    uint8_t                                                         flags;
+    uint8_t                                                         txPowerMode;
+    uint16_t                                                        beaconPeriod;
+
+    ReadOnlyGattCharacteristic<bool>                                *lockStateChar;
+    WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>       *lockChar;
+    WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>       *unlockChar;
+    GattCharacteristic                                              *urlDataChar;
+    ReadWriteGattCharacteristic<uint8_t>                            *flagsChar;
+    ReadWriteArrayGattCharacteristic<int8_t, sizeof(PowerLevels_t)> *advPowerLevelsChar;
+    ReadWriteGattCharacteristic<uint8_t>                            *txPowerModeChar;
+    ReadWriteGattCharacteristic<uint16_t>                           *beaconPeriodChar;
+    WriteOnlyGattCharacteristic<bool>                               *resetChar;
+
+    uint8_t                                                         *rawUrlFrame;
+    uint8_t                                                         *rawUidFrame;
+    uint8_t                                                         *rawTlmFrame;
+
+    uint16_t                                                        consecFrames[NUM_EDDYSTONE_FRAMES];
+    uint16_t                                                        currentConsecFrames[NUM_EDDYSTONE_FRAMES];
+    uint8_t                                                         currentAdvertisedFrame;
+
+    TlmUpdateCallback_t                                             tlmBatteryVoltageCallback;
+    TlmUpdateCallback_t                                             tlmBeaconTemperatureCallback;
+
+    Timer                                                           timeSinceBootTimer;
+#ifndef YOTTA_CFG_MBED_OS
+    Timeout                                                         swapAdvertisedFrameTimeout;
+#endif
+
+    GattCharacteristic                                              *charTable[TOTAL_CHARACTERISTICS];
+};
+
+#endif  /* __EDDYSTONESERVICE_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EddystoneTypes.h	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,72 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EDDYSTONETYPES_H__
+#define __EDDYSTONETYPES_H__
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define UUID_URL_BEACON(FIRST, SECOND) {                         \
+        0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba,       \
+        0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8,          \
+}
+
+const uint8_t EDDYSTONE_UUID[] = {0xAA, 0xFE};
+
+const uint8_t UUID_URL_BEACON_SERVICE[]    = UUID_URL_BEACON(0x20, 0x80);
+const uint8_t UUID_LOCK_STATE_CHAR[]       = UUID_URL_BEACON(0x20, 0x81);
+const uint8_t UUID_LOCK_CHAR[]             = UUID_URL_BEACON(0x20, 0x82);
+const uint8_t UUID_UNLOCK_CHAR[]           = UUID_URL_BEACON(0x20, 0x83);
+const uint8_t UUID_URL_DATA_CHAR[]         = UUID_URL_BEACON(0x20, 0x84);
+const uint8_t UUID_FLAGS_CHAR[]            = UUID_URL_BEACON(0x20, 0x85);
+const uint8_t UUID_ADV_POWER_LEVELS_CHAR[] = UUID_URL_BEACON(0x20, 0x86);
+const uint8_t UUID_TX_POWER_MODE_CHAR[]    = UUID_URL_BEACON(0x20, 0x87);
+const uint8_t UUID_BEACON_PERIOD_CHAR[]    = UUID_URL_BEACON(0x20, 0x88);
+const uint8_t UUID_RESET_CHAR[]            = UUID_URL_BEACON(0x20, 0x89);
+
+const char DEVICE_NAME[] = "EDDYSTONE CONFIG";
+
+const char DEFAULT_URL[] = "http://www.mbed.com/";
+
+enum PowerModes {
+    TX_POWER_MODE_LOWEST,
+    TX_POWER_MODE_LOW,
+    TX_POWER_MODE_MEDIUM,
+    TX_POWER_MODE_HIGH,
+    NUM_POWER_MODES
+};
+
+/* 128 bits of lock */
+typedef uint8_t Lock_t[16];
+typedef int8_t PowerLevels_t[NUM_POWER_MODES];
+
+const uint16_t URL_DATA_MAX = 18;
+typedef uint8_t UrlData_t[URL_DATA_MAX];
+
+/* UID Frame Type subfields */
+const size_t UID_NAMESPACEID_SIZE = 10;
+typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
+const size_t UID_INSTANCEID_SIZE = 6;
+typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];
+
+/* Callbacks for updating BateryVoltage and Temperature */
+typedef uint16_t (*TlmUpdateCallback_t) (uint16_t);
+
+/* Size of Eddystone UUID needed for all frames */
+const uint16_t EDDYSTONE_UUID_SIZE = sizeof(EDDYSTONE_UUID);
+
+#endif /* __EDDYSTONETYPES_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TLMFrame.cpp	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,105 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TLMFrame.h"
+
+TLMFrame::TLMFrame(uint8_t  tlmVersionIn,
+                   uint16_t tlmBatteryVoltageIn,
+                   uint16_t tlmBeaconTemperatureIn,
+                   uint32_t tlmPduCountIn,
+                   uint32_t tlmTimeSinceBootIn) :
+    tlmVersion(tlmVersionIn),
+    lastTimeSinceBootRead(0),
+    tlmBatteryVoltage(tlmBatteryVoltageIn),
+    tlmBeaconTemperature(tlmBeaconTemperatureIn),
+    tlmPduCount(tlmPduCountIn),
+    tlmTimeSinceBoot(tlmTimeSinceBootIn)
+{
+}
+
+void TLMFrame::setTLMData(uint8_t tlmVersionIn)
+{
+    /* According to the Eddystone spec BatteryVoltage is 0 and
+     * BeaconTemperature is 0x8000 if not supported
+     */
+    tlmVersion           = tlmVersionIn;
+    tlmBatteryVoltage    = 0;
+    tlmBeaconTemperature = 0x8000;
+    tlmPduCount          = 0;
+    tlmTimeSinceBoot     = 0;
+}
+
+void TLMFrame::constructTLMFrame(uint8_t *rawFrame)
+{
+    size_t index = 0;
+    rawFrame[index++] = EDDYSTONE_UUID[0];                    // 16-bit Eddystone UUID
+    rawFrame[index++] = EDDYSTONE_UUID[1];
+    rawFrame[index++] = FRAME_TYPE_TLM;                       // Eddystone frame type = Telemetry
+    rawFrame[index++] = tlmVersion;                           // TLM Version Number
+    rawFrame[index++] = (uint8_t)(tlmBatteryVoltage >> 8);    // Battery Voltage[0]
+    rawFrame[index++] = (uint8_t)(tlmBatteryVoltage >> 0);    // Battery Voltage[1]
+    rawFrame[index++] = (uint8_t)(tlmBeaconTemperature >> 8); // Beacon Temp[0]
+    rawFrame[index++] = (uint8_t)(tlmBeaconTemperature >> 0); // Beacon Temp[1]
+    rawFrame[index++] = (uint8_t)(tlmPduCount >> 24);         // PDU Count [0]
+    rawFrame[index++] = (uint8_t)(tlmPduCount >> 16);         // PDU Count [1]
+    rawFrame[index++] = (uint8_t)(tlmPduCount >> 8);          // PDU Count [2]
+    rawFrame[index++] = (uint8_t)(tlmPduCount >> 0);          // PDU Count [3]
+    rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 24);    // Time Since Boot [0]
+    rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 16);    // Time Since Boot [1]
+    rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 8);     // Time Since Boot [2]
+    rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 0);     // Time Since Boot [3]
+}
+
+size_t TLMFrame::getRawFrameSize(void) const
+{
+    return FRAME_SIZE_TLM + EDDYSTONE_UUID_SIZE;
+}
+
+void TLMFrame::updateTimeSinceBoot(uint32_t nowInMillis)
+{
+    tlmTimeSinceBoot      += (nowInMillis - lastTimeSinceBootRead) / 100;
+    lastTimeSinceBootRead  = nowInMillis;
+}
+
+void TLMFrame::updateBatteryVoltage(uint16_t tlmBatteryVoltageIn)
+{
+    tlmBatteryVoltage = tlmBatteryVoltageIn;
+}
+
+void TLMFrame::updateBeaconTemperature(uint16_t tlmBeaconTemperatureIn)
+{
+    tlmBeaconTemperature = tlmBeaconTemperatureIn;
+}
+
+void TLMFrame::updatePduCount(void)
+{
+    tlmPduCount++;
+}
+
+uint16_t TLMFrame::getBatteryVoltage(void) const
+{
+    return tlmBatteryVoltage;
+}
+
+uint16_t TLMFrame::getBeaconTemperature(void) const
+{
+    return tlmBeaconTemperature;
+}
+
+uint8_t TLMFrame::getTLMVersion(void) const
+{
+    return tlmVersion;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TLMFrame.h	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,63 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TLMFRAME_H__
+#define __TLMFRAME_H__
+
+#include "EddystoneTypes.h"
+
+class TLMFrame
+{
+public:
+    TLMFrame(uint8_t  tlmVersionIn           = 0,
+             uint16_t tlmBatteryVoltageIn    = 0,
+             uint16_t tlmBeaconTemperatureIn = 0x8000,
+             uint32_t tlmPduCountIn          = 0,
+             uint32_t tlmTimeSinceBootIn     = 0);
+
+    void setTLMData(uint8_t tlmVersionIn = 0);
+
+    void constructTLMFrame(uint8_t *rawFrame);
+
+    size_t getRawFrameSize(void) const;
+
+    void updateTimeSinceBoot(uint32_t nowInMillis);
+
+    void updateBatteryVoltage(uint16_t tlmBatteryVoltageIn);
+
+    void updateBeaconTemperature(uint16_t tlmBeaconTemperatureIn);
+
+    void updatePduCount(void);
+
+    uint16_t getBatteryVoltage(void) const;
+
+    uint16_t getBeaconTemperature(void) const;
+
+    uint8_t getTLMVersion(void) const;
+
+private:
+    static const uint8_t FRAME_TYPE_TLM = 0x20;
+    static const uint8_t FRAME_SIZE_TLM = 14;
+
+    uint8_t              tlmVersion;
+    uint32_t             lastTimeSinceBootRead;
+    uint16_t             tlmBatteryVoltage;
+    uint16_t             tlmBeaconTemperature;
+    uint32_t             tlmPduCount;
+    uint32_t             tlmTimeSinceBoot;
+};
+
+#endif  /* __TLMFRAME_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UIDFrame.cpp	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,67 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UIDFrame.h"
+
+UIDFrame::UIDFrame(void)
+{
+    memset(uidNamespaceID, 0, sizeof(UIDNamespaceID_t));
+    memset(uidInstanceID,  0,  sizeof(UIDInstanceID_t));
+}
+
+UIDFrame::UIDFrame(const UIDNamespaceID_t uidNamespaceIDIn, const UIDInstanceID_t  uidInstanceIDIn)
+{
+    memcpy(uidNamespaceID, uidNamespaceIDIn, sizeof(UIDNamespaceID_t));
+    memcpy(uidInstanceID,  uidInstanceIDIn,  sizeof(UIDInstanceID_t));
+}
+
+void UIDFrame::setUIDData(const UIDNamespaceID_t *uidNamespaceIDIn, const UIDInstanceID_t *uidInstanceIDIn)
+{
+    memcpy(uidNamespaceID, uidNamespaceIDIn, sizeof(UIDNamespaceID_t));
+    memcpy(uidInstanceID,  uidInstanceIDIn,  sizeof(UIDInstanceID_t));
+}
+
+void UIDFrame::constructUIDFrame(uint8_t *rawFrame, int8_t advPowerLevel)
+{
+    size_t index = 0;
+
+    rawFrame[index++] = EDDYSTONE_UUID[0];                                   // 16-bit Eddystone UUID
+    rawFrame[index++] = EDDYSTONE_UUID[1];
+    rawFrame[index++] = FRAME_TYPE_UID;                                      // 1B  Type
+    rawFrame[index++] = advPowerLevel;                                       // 1B  Power @ 0meter
+
+    memcpy(rawFrame + index, uidNamespaceID, sizeof(UIDNamespaceID_t));      // 10B Namespace ID
+    index += sizeof(UIDNamespaceID_t);
+    memcpy(rawFrame + index, uidInstanceID, sizeof(UIDInstanceID_t));        // 6B Instance ID
+    index += sizeof(UIDInstanceID_t);
+
+    memset(rawFrame + index, 0, 2 * sizeof(uint8_t));                        // 2B RFU, which are unused
+}
+
+size_t UIDFrame::getRawFrameSize(void) const
+{
+    return FRAME_SIZE_UID + EDDYSTONE_UUID_SIZE;
+}
+
+uint8_t* UIDFrame::getUIDNamespaceID(void)
+{
+    return uidNamespaceID;
+}
+
+uint8_t* UIDFrame::getUIDInstanceID(void)
+{
+    return uidInstanceID;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UIDFrame.h	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,48 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __UIDFRAME_H__
+#define __UIDFRAME_H__
+
+#include <string.h>
+#include "EddystoneTypes.h"
+
+class UIDFrame
+{
+public:
+    UIDFrame(void);
+
+    UIDFrame(const UIDNamespaceID_t uidNamespaceIDIn, const UIDInstanceID_t  uidInstanceIDIn);
+
+    void setUIDData(const UIDNamespaceID_t *uidNamespaceIDIn, const UIDInstanceID_t *uidInstanceIDIn);
+
+    void constructUIDFrame(uint8_t *rawFrame, int8_t advPowerLevel);
+
+    size_t getRawFrameSize(void) const;
+
+    uint8_t* getUIDNamespaceID(void);
+
+    uint8_t* getUIDInstanceID(void);
+
+private:
+    static const uint8_t FRAME_TYPE_UID = 0x00;
+    static const uint8_t FRAME_SIZE_UID = 20;
+
+    UIDNamespaceID_t     uidNamespaceID;
+    UIDInstanceID_t      uidInstanceID;
+};
+
+#endif  /* __UIDFRAME_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/URLFrame.cpp	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,141 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "URLFrame.h"
+
+URLFrame::URLFrame(void)
+{
+    urlDataLength = 0;
+    memset(urlData, 0, sizeof(UrlData_t));
+}
+
+URLFrame::URLFrame(const char *urlDataIn)
+{
+    encodeURL(urlDataIn);
+}
+
+URLFrame::URLFrame(UrlData_t urlDataIn, uint8_t urlDataLength)
+{
+    if (urlDataLength > URL_DATA_MAX) {
+        memcpy(urlData, urlDataIn, URL_DATA_MAX);
+    } else {
+        memcpy(urlData, urlDataIn, urlDataLength);
+    }
+}
+
+void URLFrame::constructURLFrame(uint8_t* rawFrame, int8_t advPowerLevel)
+{
+    size_t index = 0;
+    rawFrame[index++] = EDDYSTONE_UUID[0];            // 16-bit Eddystone UUID
+    rawFrame[index++] = EDDYSTONE_UUID[1];
+    rawFrame[index++] = FRAME_TYPE_URL;               // 1B  Type
+    rawFrame[index++] = advPowerLevel;                // 1B  Power @ 0meter
+    memcpy(rawFrame + index, urlData, urlDataLength); // Encoded URL
+}
+
+size_t URLFrame::getRawFrameSize(void) const
+{
+    return urlDataLength + FRAME_MIN_SIZE_URL + EDDYSTONE_UUID_SIZE;
+}
+
+uint8_t* URLFrame::getEncodedURLData(void)
+{
+    return urlData;
+}
+
+uint8_t URLFrame::getEncodedURLDataLength(void) const
+{
+    return urlDataLength;
+}
+
+void URLFrame::setURLData(const char *urlDataIn)
+{
+    encodeURL(urlDataIn);
+}
+
+void URLFrame::setEncodedURLData(const uint8_t* urlEncodedDataIn, const uint8_t urlEncodedDataLengthIn)
+{
+    urlDataLength = urlEncodedDataLengthIn;
+    memcpy(urlData, urlEncodedDataIn, urlEncodedDataLengthIn);
+}
+
+void URLFrame::encodeURL(const char *urlDataIn)
+{
+    const char  *prefixes[] = {
+        "http://www.",
+        "https://www.",
+        "http://",
+        "https://",
+    };
+    const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
+    const char  *suffixes[]   = {
+        ".com/",
+        ".org/",
+        ".edu/",
+        ".net/",
+        ".info/",
+        ".biz/",
+        ".gov/",
+        ".com",
+        ".org",
+        ".edu",
+        ".net",
+        ".info",
+        ".biz",
+        ".gov"
+    };
+    const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
+
+    urlDataLength = 0;
+    memset(urlData, 0, sizeof(UrlData_t));
+
+    if ((urlDataIn == NULL) || (strlen(urlDataIn) == 0)) {
+        return;
+    }
+
+    /*
+     * handle prefix
+     */
+    for (size_t i = 0; i < NUM_PREFIXES; i++) {
+        size_t prefixLen = strlen(prefixes[i]);
+        if (strncmp(urlDataIn, prefixes[i], prefixLen) == 0) {
+            urlData[urlDataLength++]  = i;
+            urlDataIn                      += prefixLen;
+            break;
+        }
+    }
+
+    /*
+     * handle suffixes
+     */
+    while (*urlDataIn && (urlDataLength < URL_DATA_MAX)) {
+        /* check for suffix match */
+        size_t i;
+        for (i = 0; i < NUM_SUFFIXES; i++) {
+            size_t suffixLen = strlen(suffixes[i]);
+            if (strncmp(urlDataIn, suffixes[i], suffixLen) == 0) {
+                urlData[urlDataLength++]  = i;
+                urlDataIn                      += suffixLen;
+                break; /* from the for loop for checking against suffixes */
+            }
+        }
+        /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
+        if (i == NUM_SUFFIXES) {
+            urlData[urlDataLength++] = *urlDataIn;
+            ++urlDataIn;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/URLFrame.h	Thu Nov 26 16:36:04 2015 +0000
@@ -0,0 +1,56 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __URLFRAME_H__
+#define __URLFRAME_H__
+
+#include "EddystoneTypes.h"
+#include <string.h>
+
+class URLFrame
+{
+public:
+    URLFrame(void);
+
+    URLFrame(const char *urlDataIn);
+
+    URLFrame(UrlData_t urlDataIn, uint8_t urlDataLength);
+
+    void constructURLFrame(uint8_t* rawFrame, int8_t advPowerLevel);
+
+    size_t getRawFrameSize(void) const;
+
+    uint8_t* getEncodedURLData(void);
+
+    uint8_t getEncodedURLDataLength(void) const;
+
+    void setURLData(const char *urlDataIn);
+
+    void setEncodedURLData(const uint8_t* urlEncodedDataIn, const uint8_t urlEncodedDataLengthIn);
+
+private:
+    void encodeURL(const char *urlDataIn);
+
+    static const uint8_t FRAME_TYPE_URL     = 0x10;
+    /* Even if the URL is 0 bytes we still need to include the type and txPower i.e. 2 bytes */
+    static const uint8_t FRAME_MIN_SIZE_URL = 2;
+
+    uint8_t              urlDataLength;
+    UrlData_t            urlData;
+
+};
+
+#endif /* __URLFRAME_H__ */
--- a/main.cpp	Thu Oct 01 18:30:37 2015 +0000
+++ b/main.cpp	Thu Nov 26 16:36:04 2015 +0000
@@ -15,59 +15,80 @@
  */
 
 #include "mbed.h"
-#include "BLE.h"
-#include "ble/services/EddystoneService.h"
+#include "ble/BLE.h"
+#include "EddystoneService.h"
+
+EddystoneService *eddyServicePtr;
 
-BLE ble;
-uint8_t UIDnamespace[] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA}; // 10Bytes for Namespace UUID
-uint8_t UIDinstance[]  = {0xbb,0xcc,0xdd,0xee,0xff,0x00}; // 6Bytes for Instance UUID
-char Url[] = "http://www.mbed.org";
-int8_t radioTxPower = 20;
-int8_t advTxPower = -20;
-uint16_t beaconPeriodus = 1000;
-uint8_t tlmVersion = 0x00;
-
+void onBleInitError(BLE::InitializationCompleteCallbackContext* initContext)
+{
+    /* Initialization error handling goes here... */
+    (void) initContext;
+}
 
-//Callbacks for temperature / battery updates
-Ticker tlmBattery;
-Ticker tlmTemperature;
-int battery = 0;
-int temp = 0;
+/*
+ * Update TLM frame battery voltage value.
+ */
+uint16_t tlmBatteryVoltageCallback(uint16_t prevBatteryVoltage)
+{
+    prevBatteryVoltage++;
+    return prevBatteryVoltage;
+}
 
-// Setup Eddystone Service 
-EddystoneService eddyBeacon(ble,beaconPeriodus,radioTxPower);
-
-
-// Function to update beacon battery voltage
-void tlmBatteryCallback(void){
-    eddyBeacon.updateTlmBatteryVoltage(battery++);
+/*
+ * Update TLM frame beacon temperature value.
+ */
+uint16_t tlmBeaconTemperatureCallback(uint16_t prevBeaconTemperature)
+{
+    prevBeaconTemperature++;
+    return prevBeaconTemperature;
 }
 
-// Function to update Beacon Temperature
-void tlmTemperatureCallback(void){
-    eddyBeacon.updateTlmBeaconTemp(temp++);
+void bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext)
+{
+    BLE         &ble  = initContext->ble;
+    ble_error_t error = initContext->error;
+
+    if (error != BLE_ERROR_NONE) {
+        onBleInitError(initContext);
+        return;
+    }
+
+    /* Set UID and TLM frame data */
+    const UIDNamespaceID_t uidNamespaceID = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
+    const UIDInstanceID_t  uidInstanceID  = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
+    uint8_t tlmVersion = 0x00;
+
+    /* Initialize a EddystoneBeaconConfig service providing config params, default URI, and power levels. */
+    static const PowerLevels_t defaultAdvPowerLevels = {-47, -33, -21, -13}; // Values for ADV packets related to firmware levels, calibrated based on measured values at 1m
+    static const PowerLevels_t radioPowerLevels      = {-30, -16, -4, 4};    // Values for radio power levels, provided by manufacturer.
+
+    /* Set everything to defaults */
+    eddyServicePtr = new EddystoneService(ble, defaultAdvPowerLevels, radioPowerLevels);
+
+    /* Set default URL, UID and TLM frame data if not initialized through the config service */
+    eddyServicePtr->setURLData("http://mbed.org");
+    eddyServicePtr->setUIDData(&uidNamespaceID, &uidInstanceID);
+    eddyServicePtr->setTLMData(tlmVersion);
+
+    /* Set battery voltage and temperature callbacks */
+    eddyServicePtr->onTLMBatteryVoltageUpdate(tlmBatteryVoltageCallback);
+    eddyServicePtr->onTLMBeaconTemperatureUpdate(tlmBeaconTemperatureCallback);
+
+    /* Start Eddystone in config mode */
+    eddyServicePtr->startBeaconService(5, 5, 5);
 }
 
 int main(void)
 {
-    printf("Starting Example\r\n"); // To enable low power mode disable all printf's
-    ble.init();
-    
-    // Set Eddystone Frame Data (TLM,UID,URI...etc)
-    eddyBeacon.setTLMFrameData(tlmVersion,5.0);
-    eddyBeacon.setURLFrameData(advTxPower, Url, 2.0);
-    eddyBeacon.setUIDFrameData(advTxPower, UIDnamespace, UIDinstance, 3.0);
+    BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
+    ble.init(bleInitComplete);
+
+    /* SpinWait for initialization to complete. This is necessary because the
+     * BLE object is used in the main loop below. */
+    while (ble.hasInitialized()  == false) { /* spin loop */ }
     
-    // Attach functions to modify TLM data
-    tlmTemperature.attach(tlmTemperatureCallback,2.0f);
-    tlmBattery.attach(tlmBatteryCallback, 1.0f);
-
-    // Start Advertising the eddystone service. 
-    eddyBeacon.start(); 
-    ble.gap().startAdvertising();
-    
-    printf("Running...\r\n");
     while (true) {
-        ble.waitForEvent();
+        ble.waitForEvent(); /* this will return upon any system event (such as an interrupt or a ticker wakeup) */
     }
 }
--- a/mbed.bld	Thu Oct 01 18:30:37 2015 +0000
+++ b/mbed.bld	Thu Nov 26 16:36:04 2015 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/mbed_official/code/mbed/builds/4f6c30876dfa
\ No newline at end of file
+http://mbed.org/users/mbed_official/code/mbed/builds/9296ab0bfc11
\ No newline at end of file
--- a/nRF51822.lib	Thu Oct 01 18:30:37 2015 +0000
+++ b/nRF51822.lib	Thu Nov 26 16:36:04 2015 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#088f5738bf18
+http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#1762975cb3cb