Eddystone Beacon only for TYBLE16

Dependencies:   TYBLE16_BASE

Please refer flowing link.
/users/kenjiArai/notebook/tyble16-module-will-become-a-mbed-family--mbedliza/

Files at this revision

API Documentation at this revision

Comitter:
kenjiArai
Date:
Sun Feb 25 02:26:02 2018 +0000
Child:
1:95acd5ad69e8
Commit message:
Eddystone Beacon only for TYBLE16

Changed in this revision

Eddystone/EddystoneService.cpp Show annotated file Show diff for this revision Revisions of this file
Eddystone/EddystoneService.h Show annotated file Show diff for this revision Revisions of this file
Eddystone/EddystoneTypes.h Show annotated file Show diff for this revision Revisions of this file
Eddystone/TLMFrame.cpp Show annotated file Show diff for this revision Revisions of this file
Eddystone/TLMFrame.h Show annotated file Show diff for this revision Revisions of this file
Eddystone/UIDFrame.cpp Show annotated file Show diff for this revision Revisions of this file
Eddystone/UIDFrame.h Show annotated file Show diff for this revision Revisions of this file
Eddystone/URLFrame.cpp Show annotated file Show diff for this revision Revisions of this file
Eddystone/URLFrame.h Show annotated file Show diff for this revision Revisions of this file
TYBLE16_BASE.lib 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Eddystone/EddystoneService.cpp	Sun Feb 25 02:26:02 2018 +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/Eddystone/EddystoneService.h	Sun Feb 25 02:26:02 2018 +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/Eddystone/EddystoneTypes.h	Sun Feb 25 02:26:02 2018 +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/Eddystone/TLMFrame.cpp	Sun Feb 25 02:26:02 2018 +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/Eddystone/TLMFrame.h	Sun Feb 25 02:26:02 2018 +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/Eddystone/UIDFrame.cpp	Sun Feb 25 02:26:02 2018 +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/Eddystone/UIDFrame.h	Sun Feb 25 02:26:02 2018 +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/Eddystone/URLFrame.cpp	Sun Feb 25 02:26:02 2018 +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/Eddystone/URLFrame.h	Sun Feb 25 02:26:02 2018 +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__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TYBLE16_BASE.lib	Sun Feb 25 02:26:02 2018 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/kenjiArai/code/TYBLE16_BASE/#63f6490ba2c8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sun Feb 25 02:26:02 2018 +0000
@@ -0,0 +1,155 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2013 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.
+ */
+
+/*
+ *  /////// Works well on TYBLE16 Module ///////
+ *  Modified by Kenji Arai
+ *      http://www.page.sannet.ne.jp/kenjia/index.html
+ *      http://mbed.org/users/kenjiArai/
+ *
+ *      Started:  Feburary   18th, 2018
+ *      Revised:  Feburary   25th, 2018
+ *
+ *  Original:
+ *      nRF51822_SimpleControls
+ *      https://developer.mbed.org/teams
+ *                      /Bluetooth-Low-Energy/code/BLE_EddystoneBeacon_Service/
+ *  Tested Controller Device:
+ *      iPhone6 Physical Web (PhyWeb) By Viet Hoa Dinh
+ *      https://itunes.apple.com/us/app/physical-web/id927653608?mt=8
+ */
+
+//  Include --------------------------------------------------------------------
+#include "mbed.h"
+#include "TYBLE16_BASE.h"
+#include "BLE.h"
+#include "EddystoneService.h"
+
+//  Object ---------------------------------------------------------------------
+DigitalOut  led(P0_5);
+Serial      pc(P0_1, P0_3);
+
+//  Definition -----------------------------------------------------------------
+
+//  RAM ------------------------------------------------------------------------
+EddystoneService    *eddyServicePtr;
+Gap::Address_t      my_mac;
+
+//  ROM / Constant data --------------------------------------------------------
+
+//  Function prototypes --------------------------------------------------------
+int8_t check_dice(void);
+uint16_t update_vdd(uint16_t x);
+uint16_t update_temp(uint16_t x);
+void onBleInitError(BLE::InitializationCompleteCallbackContext* initContext);
+void bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext);
+
+//------------------------------------------------------------------------------
+//  Control Program
+//------------------------------------------------------------------------------
+// !!!!!!!! Please select number of 0 to 6 !!!!!!!!!!!!!!!!!
+int8_t check_dice(void)
+{
+    return 5;
+}
+
+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;
+
+    Gap::AddressType_t my_mac_type;
+    ble.gap().getAddress(&my_mac_type, my_mac);
+    // Initialize a EddystoneBeaconConfig service
+    // Values for ADV packets related to firmware levels,
+    // calibrated based on measured values at 1m
+    static const PowerLevels_t defaultAdvPowerLevels = {-47, -33, -21, -13};
+    // Values for radio power levels, provided by manufacturer.
+    static const PowerLevels_t radioPowerLevels      = {-30, -16, -4, 4};
+    eddyServicePtr = new EddystoneService(ble,
+                                          defaultAdvPowerLevels,
+                                          radioPowerLevels);
+    // created short cut web addres by http://bitly.oshiire.org/
+    switch (check_dice()) {
+        case 1:     // Switch sience(mbed)
+            eddyServicePtr->setURLData("http://bit.ly/1oJh91B");
+            break;
+        case 2:    // switch sience(HP)
+            eddyServicePtr->setURLData("http://bit.ly/1oJhP7g");
+            break;
+        case 3:    // Taiyo Yuden BLE
+            eddyServicePtr->setURLData("http://bit.ly/1VvuCVr");
+            break;
+        case 4:    // Taiyo Yuden
+            eddyServicePtr->setURLData("http://bit.ly/1Vvtp0l");
+            break;
+        case 5:    // JH1PJL(mbed)
+            eddyServicePtr->setURLData("http://bit.ly/1Vvt51J");
+            break;
+        case 6:    // JH1PJL(HP)
+            eddyServicePtr->setURLData("http://bit.ly/1VvteT0");
+            break;
+        case 0:
+        default:   // Mbed
+            eddyServicePtr->setURLData("http://mbed.org");
+            break;
+    }
+    eddyServicePtr->setUIDData(&uidNamespaceID, &uidInstanceID);
+    eddyServicePtr->setTLMData(tlmVersion);
+    // Start Eddystone in config mode
+    eddyServicePtr->startBeaconService(5, 5, 5);
+}
+
+int main(void)
+{
+    led = 1;
+    BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
+    ble.init(bleInitComplete);
+    while (ble.hasInitialized() == false) {
+        ;
+    }
+    // Check TYBLE-16 configuration
+    cpu_sys();
+    if (compile_condition == false) {
+        pc.printf("This is wrong configuration!!\r\n");
+        while(true) {
+            led = !led;
+            wait(0.2);
+        }
+    }
+    //
+    led = 0;
+    while (true) {
+        ble.waitForEvent();
+    }
+}
+
+void onBleInitError(BLE::InitializationCompleteCallbackContext* initContext)
+{
+    // Initialization error handling goes here...
+    (void) initContext;
+}