{"action":"created","comment":{"url":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/comments/44437966","id":44437966,"diff_hunk":"@@ -0,0 +1,1029 @@\n+/* mbed Microcontroller Library\n+ * Copyright (c) 2006-2015 ARM Limited\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#ifndef SERVICES_EDDYSTONEBEACON_H_\n+#define SERVICES_EDDYSTONEBEACON_H_\n+\n+#include \"ble/BLE.h\"\n+#include \"mbed-drivers/mbed.h\"\n+\n+#define UUID_URL_BEACON(FIRST, SECOND) {                         \\\n+        0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba,       \\\n+        0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8,          \\\n+}\n+\n+static const uint8_t EDDYSTONE_UUID[] = {0xAA, 0xFE};\n+\n+static const uint8_t UUID_URL_BEACON_SERVICE[]    = UUID_URL_BEACON(0x20, 0x80);\n+static const uint8_t UUID_LOCK_STATE_CHAR[]       = UUID_URL_BEACON(0x20, 0x81);\n+static const uint8_t UUID_LOCK_CHAR[]             = UUID_URL_BEACON(0x20, 0x82);\n+static const uint8_t UUID_UNLOCK_CHAR[]           = UUID_URL_BEACON(0x20, 0x83);\n+static const uint8_t UUID_URL_DATA_CHAR[]         = UUID_URL_BEACON(0x20, 0x84);\n+static const uint8_t UUID_FLAGS_CHAR[]            = UUID_URL_BEACON(0x20, 0x85);\n+static const uint8_t UUID_ADV_POWER_LEVELS_CHAR[] = UUID_URL_BEACON(0x20, 0x86);\n+static const uint8_t UUID_TX_POWER_MODE_CHAR[]    = UUID_URL_BEACON(0x20, 0x87);\n+static const uint8_t UUID_BEACON_PERIOD_CHAR[]    = UUID_URL_BEACON(0x20, 0x88);\n+static const uint8_t UUID_RESET_CHAR[]            = UUID_URL_BEACON(0x20, 0x89);\n+\n+const char DEVICE_NAME[] = \"EDDYSTONE CONFIG\";\n+\n+const char DEFAULT_URL[] = \"http://www.mbed.com/\";\n+\n+class EddystoneService\n+{\n+public:\n+    enum {\n+        TX_POWER_MODE_LOWEST,\n+        TX_POWER_MODE_LOW,\n+        TX_POWER_MODE_MEDIUM,\n+        TX_POWER_MODE_HIGH,\n+        NUM_POWER_MODES\n+    };\n+\n+    /* Operation modes of the EddystoneService:\n+     *      NONE: EddystoneService has been initialised but no memory has been\n+     *            dynamically allocated. Additionally, no services are running\n+     *            nothing is being advertised.\n+     *      CONFIG: EddystoneService has been initialised, the configuration\n+     *              service started and memory has been allocated for BLE\n+     *              characteristics. Memory consumption peaks during CONFIG\n+     *              mode.\n+     *      BEACON: Eddystone service is running as a beacon advertising URL,\n+     *              UID and/or TLM frames depending on how it is configured.\n+     * Note: The main app can change the mode of EddystoneService at any point\n+     * of time by calling startConfigService() or startBeaconService().\n+     * Resources from the previous mode will be freed. It is currently NOT\n+     * possible to force EddystoneService back into MODE_NONE.\n+     */\n+    enum OperationModes {\n+        EDDYSTONE_MODE_NONE,\n+        EDDYSTONE_MODE_CONFIG,\n+        EDDYSTONE_MODE_BEACON\n+    };\n+\n+    enum FrameType {\n+        EDDYSTONE_FRAME_URL,\n+        EDDYSTONE_FRAME_UID,\n+        EDDYSTONE_FRAME_TLM,\n+        NUM_EDDYSTONE_FRAMES\n+    };\n+\n+    enum EddystoneError_t {\n+        EDDYSTONE_ERROR_NONE,\n+        EDDYSTONE_ERROR_INVALID_BEACON_PERIOD,\n+        EDDYSTONE_ERROR_INVALID_CONSEC_FRAMES,\n+        EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL\n+    };\n+\n+    static const uint32_t DEFAULT_CONFIG_PERIOD_MSEC = 1000;\n+\n+    static const uint16_t DEFAULT_BEACON_PERIOD_MSEC = 1000;\n+\n+    // Maximum size of service data in ADV packets\n+    static const uint16_t MAX_ADV_PACKET_SIZE       = 31;\n+\n+    typedef uint8_t Lock_t[16];                             /* 128 bits */\n+    typedef int8_t PowerLevels_t[NUM_POWER_MODES];\n+\n+    static const uint16_t URL_DATA_MAX = 18;\n+    typedef uint8_t UrlData_t[URL_DATA_MAX];\n+\n+    // UID Frame Type subfields\n+    static const size_t UID_NAMESPACEID_SIZE = 10;\n+    typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];\n+    static const size_t UID_INSTANCEID_SIZE = 6;\n+    typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];\n+\n+    // Callbacks for updating BateryVoltage and Temperature\n+    typedef uint16_t (*TlmUpdateCallback_t) (uint16_t);\n+\n+    // Size of Eddystone UUID needed for all frames\n+    static const uint16_t EDDYSTONE_UUID_SIZE = sizeof(EDDYSTONE_UUID);\n+\n+    static const uint16_t TOTAL_CHARACTERISTICS = 9;\n+\n+    struct EddystoneParams_t {\n+        bool             lockState;\n+        Lock_t           lock;\n+        Lock_t           unlock;\n+        uint8_t          flags;\n+        PowerLevels_t    advPowerLevels;\n+        uint8_t          txPowerMode;\n+        uint16_t         beaconPeriod;\n+        uint8_t          tlmVersion;\n+        uint8_t          urlDataLength;\n+        UrlData_t        urlData;\n+        UIDNamespaceID_t uidNamespaceID;\n+        UIDInstanceID_t  uidInstanceID;\n+    };\n+\n+    /* Initialise the EddystoneService using parameters from persistent storage */\n+    EddystoneService(BLE               &bleIn,\n+                     EddystoneParams_t &paramsIn,\n+                     PowerLevels_t     &advPowerLevelsIn,\n+                     PowerLevels_t     &radioPowerLevelsIn,\n+                     uint32_t          advConfigIntervalIn = DEFAULT_CONFIG_PERIOD_MSEC) :\n+        ble(bleIn),\n+        operationMode(EDDYSTONE_MODE_NONE),\n+        urlFrame(paramsIn.urlData, paramsIn.urlDataLength),\n+        uidFrame(paramsIn.uidNamespaceID, paramsIn.uidInstanceID),\n+        tlmFrame(paramsIn.tlmVersion),\n+        resetFlag(false),\n+        tlmBatteryVoltageCallback(NULL),\n+        tlmBeaconTemperatureCallback(NULL)\n+    {\n+        lockState         = paramsIn.lockState;\n+        flags             = paramsIn.flags;\n+        txPowerMode       = paramsIn.txPowerMode;\n+        beaconPeriod      = checkBeaconPeriodBounds(paramsIn.beaconPeriod);\n+\n+        memcpy(lock,             paramsIn.lock,      sizeof(Lock_t));\n+        memcpy(unlock,           paramsIn.unlock,    sizeof(Lock_t));\n+\n+        eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn);\n+    }\n+\n+    /* When using this constructor we need to call setURLData,\n+     * setTMLData and setUIDData to initialise values manually\n+     */\n+    EddystoneService(BLE               &bleIn,\n+                     PowerLevels_t     &advPowerLevelsIn,\n+                     PowerLevels_t     &radioPowerLevelsIn,\n+                     uint32_t          advConfigIntervalIn = DEFAULT_CONFIG_PERIOD_MSEC) :\n+        ble(bleIn),\n+        operationMode(EDDYSTONE_MODE_NONE),\n+        urlFrame(),\n+        uidFrame(),\n+        tlmFrame(),\n+        lockState(false),\n+        resetFlag(false),\n+        lock(),\n+        unlock(),\n+        flags(0),\n+        txPowerMode(0),\n+        beaconPeriod(DEFAULT_BEACON_PERIOD_MSEC),\n+        tlmBatteryVoltageCallback(NULL),\n+        tlmBeaconTemperatureCallback(NULL)\n+    {\n+        eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn);\n+    }\n+\n+    /* Setup callback to update BatteryVoltage in TLM frame */\n+    void onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn)\n+    {\n+        tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn;\n+    }\n+\n+    /* Setup callback to update BeaconTemperature in TLM frame */\n+    void onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn)\n+    {\n+        tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn;\n+    }\n+\n+    void setTLMData(uint8_t tlmVersionIn = 0)\n+    {\n+       tlmFrame.setTLMData(tlmVersionIn);\n+    }\n+\n+    void setURLData(const char *urlDataIn)\n+    {\n+        urlFrame.setURLData(urlDataIn);\n+    }\n+\n+    void setUIDData(UIDNamespaceID_t *uidNamespaceIDIn, UIDInstanceID_t *uidInstanceIDIn)\n+    {\n+        uidFrame.setUIDData(uidNamespaceIDIn, uidInstanceIDIn);\n+    }\n+\n+    EddystoneError_t startConfigService(void)\n+    {\n+        if (operationMode == EDDYSTONE_MODE_CONFIG) {\n+            /* Nothing to do, we are already in config mode */\n+            return EDDYSTONE_ERROR_NONE;\n+        } else if (advConfigInterval == 0) {\n+            /* Nothing to do, the advertisement interval is 0 */\n+            return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;\n+        }\n+\n+        if (operationMode == EDDYSTONE_MODE_BEACON) {\n+            ble.shutdown();\n+            /* Free unused memory */\n+            freeBeaconFrames();\n+            operationMode = EDDYSTONE_MODE_CONFIG;\n+            ble.init(this, &EddystoneService::bleInitComplete);\n+            return EDDYSTONE_ERROR_NONE;\n+        }\n+\n+        operationMode = EDDYSTONE_MODE_CONFIG;\n+        setupConfigService();\n+        return EDDYSTONE_ERROR_NONE;\n+    }\n+\n+    EddystoneError_t startBeaconService(uint16_t consecUrlFramesIn = 2, uint16_t consecUidFramesIn = 2, uint16_t consecTlmFramesIn = 2)\n+    {\n+        if (operationMode == EDDYSTONE_MODE_BEACON) {\n+            /* Nothing to do, we are already in beacon mode */\n+            return EDDYSTONE_ERROR_NONE;\n+        } else if (!consecUrlFramesIn && !consecUidFramesIn && !consecTlmFramesIn) {\n+            /* Nothing to do, the user wants 0 consecutive frames of everything */\n+            return EDDYSTONE_ERROR_INVALID_CONSEC_FRAMES;\n+        } else if (!beaconPeriod) {\n+            /* Nothing to do, the period is 0 for all frames */\n+            return EDDYSTONE_ERROR_INVALID_BEACON_PERIOD;\n+        }\n+\n+        /* Setup tracking of the current advertised frame. Note that this will\n+         * cause URL or UID frames to be advertised first!\n+         */\n+        currentAdvertisedFrame            = EDDYSTONE_FRAME_TLM;\n+        consecFrames[EDDYSTONE_FRAME_URL] = consecUrlFramesIn;\n+        consecFrames[EDDYSTONE_FRAME_UID] = consecUidFramesIn;\n+        consecFrames[EDDYSTONE_FRAME_TLM] = consecTlmFramesIn;\n+\n+        memset(currentConsecFrames, 0, sizeof(uint16_t) * NUM_EDDYSTONE_FRAMES);\n+\n+        if (operationMode == EDDYSTONE_MODE_CONFIG) {\n+            ble.shutdown();\n+            /* Free unused memory */\n+            freeConfigCharacteristics();\n+            operationMode = EDDYSTONE_MODE_BEACON;\n+            ble.init(this, &EddystoneService::bleInitComplete);\n+            return EDDYSTONE_ERROR_NONE;\n+        }\n+\n+        operationMode = EDDYSTONE_MODE_BEACON;\n+        setupBeaconService();\n+        return EDDYSTONE_ERROR_NONE;\n+    }\n+\n+    /* It is not the responsibility of the Eddystone implementation to store\n+     * the configured parameters in persistent storage since this is\n+     * platform-specific. So we provide this function that returns the\n+     * configured values that need to be stored and the main application\n+     * takes care of storing them.\n+     */\n+    void getEddystoneParams(EddystoneParams_t *params)\n+    {\n+        params->lockState     = lockState;\n+        params->flags         = flags;\n+        params->txPowerMode   = txPowerMode;\n+        params->beaconPeriod  = beaconPeriod;\n+        params->tlmVersion    = tlmFrame.getTLMVersion();\n+        params->urlDataLength = urlFrame.getEncodedURLDataLength();\n+\n+        memcpy(params->advPowerLevels, advPowerLevels,               sizeof(PowerLevels_t));\n+        memcpy(params->lock,           lock,                         sizeof(Lock_t));\n+        memcpy(params->unlock,         unlock,                       sizeof(Lock_t));\n+        memcpy(params->urlData,        urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());\n+        memcpy(params->uidNamespaceID, uidFrame.getUIDNamespaceID(), sizeof(UIDNamespaceID_t));\n+        memcpy(params->uidInstanceID,  uidFrame.getUIDInstanceID(),  sizeof(UIDInstanceID_t));\n+    }\n+\n+private:\n+\n+    /* Helper function used only once during constructing the object to avoid\n+     * duplicated code.\n+     */\n+    void eddystoneConstructorHelper(PowerLevels_t &advPowerLevelsIn,\n+                                    PowerLevels_t &radioPowerLevelsIn,\n+                                    uint32_t      advConfigIntervalIn)\n+    {\n+        advConfigInterval = (advConfigIntervalIn > 0) ? checkBeaconPeriodBounds(advConfigIntervalIn) : 0;\n+\n+        memcpy(radioPowerLevels, radioPowerLevelsIn, sizeof(PowerLevels_t));\n+        memcpy(advPowerLevels,   advPowerLevelsIn,   sizeof(PowerLevels_t));\n+\n+        /* TODO: Note that this timer is started from the time EddystoneService\n+         * is initialised and NOT from when the device is booted. So app needs\n+         * to take care that EddystoneService is one of the first things to be\n+         * started!\n+         */\n+        timeSinceBootTimer.start();\n+    }\n+\n+    /* When changing modes, we shutdown and init the BLE instance, so\n+     * this is needed to complete the initialisation task.\n+     */\n+    void bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext)\n+    {\n+        if (initContext->error != BLE_ERROR_NONE) {\n+            /* Initialisation failed */\n+            return;\n+        }\n+\n+        switch (operationMode) {\n+        case EDDYSTONE_MODE_CONFIG:\n+            setupConfigService();\n+            break;\n+        case EDDYSTONE_MODE_BEACON:\n+            setupBeaconService();\n+            break;\n+        default:\n+            /* Some error occurred */\n+            break;\n+        }\n+    }\n+\n+    void swapAdvertisedFrame(void)\n+    {\n+        /* This essentially works out which is the next frame to be swapped in\n+         * and updated the advertised packets. It will eventually terminate\n+         * and in the worst case the frame swapped in is the current advertised\n+         * frame.\n+         */\n+        while (true) {\n+            currentAdvertisedFrame = (currentAdvertisedFrame + 1) % NUM_EDDYSTONE_FRAMES;\n+\n+            if (currentAdvertisedFrame == EDDYSTONE_FRAME_URL && consecFrames[EDDYSTONE_FRAME_URL] > 0) {\n+                updateAdvertisementPacket(rawUrlFrame, urlFrame.getRawFrameSize());\n+                return;\n+            } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_UID && consecFrames[EDDYSTONE_FRAME_UID] > 0) {\n+                updateAdvertisementPacket(rawUidFrame, uidFrame.getRawFrameSize());\n+                return;\n+            } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM && consecFrames[EDDYSTONE_FRAME_UID] > 0) {\n+                /* TODO!!!\n+                 * This might not be the desired behaviour because essentially the TLM frame will NOT be\n+                 * updated while it is advertised! Therefore, if the TLM frame is the ONLY frame being\n+                 * advertised it will never be updated.\n+                 */\n+                if (tlmBeaconTemperatureCallback != NULL) {\n+                    tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature()));\n+                }\n+                if (tlmBatteryVoltageCallback != NULL) {\n+                    tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage()));\n+                }\n+                tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms());\n+                tlmFrame.constructTLMFrame(rawTlmFrame, *this);\n+                updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize());\n+                return;\n+            }\n+        }\n+    }\n+\n+    void updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength) {\n+        ble.gap().clearAdvertisingPayload();\n+        ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);\n+        ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));\n+        ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength);\n+        ble.gap().startAdvertising();\n+    }\n+\n+    void setupBeaconService(void)\n+    {\n+        /* Initialise arrays to hold constructed raw frames */\n+        if (consecFrames[EDDYSTONE_FRAME_URL] > 0) {\n+            rawUrlFrame = new uint8_t[urlFrame.getRawFrameSize()];\n+            urlFrame.constructURLFrame(rawUrlFrame, *this);\n+        }\n+\n+        if (consecFrames[EDDYSTONE_FRAME_UID] > 0) {\n+            rawUidFrame = new uint8_t[uidFrame.getRawFrameSize()];\n+            uidFrame.constructUIDFrame(rawUidFrame, *this);\n+        }\n+\n+        if (consecFrames[EDDYSTONE_FRAME_TLM] > 0) {\n+            rawTlmFrame = new uint8_t[tlmFrame.getRawFrameSize()];\n+            /* Do not initialise because we have to reconstruct every 0.1 secs */\n+        }\n+\n+        /* Configure advertisements */\n+        ble.gap().setTxPower(radioPowerLevels[txPowerMode]);\n+        ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);\n+        ble.gap().setAdvertisingInterval(beaconPeriod);\n+        ble.gap().onRadioNotification(this, &EddystoneService::radioNotificationCallback);\n+\n+        /* Set advertisement packet payload */\n+        swapAdvertisedFrame();\n+    }\n+\n+    void setupConfigService(void)\n+    {\n+        lockStateChar      = new ReadOnlyGattCharacteristic<bool>(UUID_LOCK_STATE_CHAR, &lockState);\n+        lockChar           = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_LOCK_CHAR, lock);\n+        unlockChar         = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlock);\n+        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);\n+        flagsChar          = new ReadWriteGattCharacteristic<uint8_t>(UUID_FLAGS_CHAR, &flags);\n+        advPowerLevelsChar = new ReadWriteArrayGattCharacteristic<int8_t, sizeof(PowerLevels_t)>(UUID_ADV_POWER_LEVELS_CHAR, advPowerLevels);\n+        txPowerModeChar    = new ReadWriteGattCharacteristic<uint8_t>(UUID_TX_POWER_MODE_CHAR, &txPowerMode);\n+        beaconPeriodChar   = new ReadWriteGattCharacteristic<uint16_t>(UUID_BEACON_PERIOD_CHAR, &beaconPeriod);\n+        resetChar          = new WriteOnlyGattCharacteristic<bool>(UUID_RESET_CHAR, &resetFlag);\n+\n+        lockChar->setWriteAuthorizationCallback(this, &EddystoneService::lockAuthorizationCallback);\n+        unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::unlockAuthorizationCallback);\n+        urlDataChar->setWriteAuthorizationCallback(this, &EddystoneService::urlDataWriteAuthorizationCallback);\n+        flagsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint8_t>);\n+        advPowerLevelsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<PowerLevels_t>);\n+        txPowerModeChar->setWriteAuthorizationCallback(this, &EddystoneService::powerModeAuthorizationCallback);\n+        beaconPeriodChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint16_t>);\n+        resetChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<bool>);\n+\n+        charTable[0] = lockStateChar;\n+        charTable[1] = lockChar;\n+        charTable[2] = unlockChar;\n+        charTable[3] = urlDataChar;\n+        charTable[4] = flagsChar;\n+        charTable[5] = advPowerLevelsChar;\n+        charTable[6] = txPowerModeChar;\n+        charTable[7] = beaconPeriodChar;\n+        charTable[8] = resetChar;\n+\n+        GattService configService(UUID_URL_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));\n+\n+        ble.gattServer().addService(configService);\n+        ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback);\n+        updateCharacteristicValues();\n+        setupEddystoneConfigAdvertisements();\n+    }\n+\n+    void freeConfigCharacteristics(void)\n+    {\n+        delete lockStateChar;\n+        delete lockChar;\n+        delete unlockChar;\n+        delete urlDataChar;\n+        delete flagsChar;\n+        delete advPowerLevelsChar;\n+        delete txPowerModeChar;\n+        delete beaconPeriodChar;\n+        delete resetChar;\n+    }\n+\n+    void freeBeaconFrames(void)\n+    {\n+        delete[] rawUrlFrame;\n+        delete[] rawUidFrame;\n+        delete[] rawTlmFrame;\n+    }\n+\n+    void stopAdvertisingCallback(void)\n+    {\n+        /* Stop advertisements and defer the frame swap so that it looks like\n+         * the advertisement period is consistent\n+         */\n+        ble.gap().stopAdvertising();\n+        swapAdvertisedFrame();\n+        stopAdv.attach_us(this, &EddystoneService::swapAdvertisedFrame, beaconPeriod * 1000);\n+    }\n+\n+    void radioNotificationCallback(bool radioActive)\n+    {\n+        if (radioActive) {\n+            /* Do nothing */\n+            return;\n+        }\n+\n+        tlmFrame.updatePduCount();\n+        currentConsecFrames[currentAdvertisedFrame]++;\n+\n+        if (consecFrames[currentAdvertisedFrame] > currentConsecFrames[currentAdvertisedFrame]) {\n+            return;\n+        }\n+\n+        currentConsecFrames[currentAdvertisedFrame] = 0;\n+\n+        stopAdv.attach_us(this, &EddystoneService::stopAdvertisingCallback, 1);\n+    }\n+\n+    /*\n+     * Internal helper function used to update the GATT database following any\n+     * change to the internal state of the service object.\n+     */\n+    void updateCharacteristicValues(void)\n+    {\n+        ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));\n+        ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());\n+        ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));\n+        ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));\n+        ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));\n+        ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));\n+        ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));\n+        ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));\n+    }\n+\n+    void setupEddystoneConfigAdvertisements(void)\n+    {\n+        ble.gap().clearAdvertisingPayload();\n+        ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);\n+\n+        /* UUID is in different order in the ADV frame (!) */\n+        uint8_t reversedServiceUUID[sizeof(UUID_URL_BEACON_SERVICE)];\n+        for (size_t i = 0; i < sizeof(UUID_URL_BEACON_SERVICE); i++) {\n+            reversedServiceUUID[i] = UUID_URL_BEACON_SERVICE[sizeof(UUID_URL_BEACON_SERVICE) - i - 1];\n+        }\n+        ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));\n+        ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);\n+        ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));\n+        ble.gap().accumulateScanResponse(\n+            GapAdvertisingData::TX_POWER_LEVEL,\n+            reinterpret_cast<uint8_t *>(&advPowerLevels[EddystoneService::TX_POWER_MODE_LOW]),\n+            sizeof(uint8_t));\n+\n+        ble.gap().setTxPower(radioPowerLevels[txPowerMode]);\n+        ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));\n+        ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);\n+        ble.gap().setAdvertisingInterval(advConfigInterval);\n+        ble.gap().startAdvertising();\n+    }\n+\n+    void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)\n+    {\n+        if (lockState) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;\n+        } else if (authParams->len != sizeof(Lock_t)) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;\n+        } else if (authParams->offset != 0) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;\n+        } else {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;\n+        }\n+    }\n+\n+    void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)\n+    {\n+        if (!lockState && (authParams->len == sizeof(Lock_t))) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;\n+        } else if (authParams->len != sizeof(Lock_t)) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;\n+        } else if (authParams->offset != 0) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;\n+        } else if (memcmp(authParams->data, lock, sizeof(Lock_t)) != 0) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;\n+        } else {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;\n+        }\n+    }\n+\n+    void urlDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams)\n+    {\n+        if (lockState) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;\n+        } else if (authParams->offset != 0) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;\n+        } else {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;\n+        }\n+    }\n+\n+    void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams)\n+    {\n+        if (lockState) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;\n+        } else if (authParams->len != sizeof(uint8_t)) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;\n+        } else if (authParams->offset != 0) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;\n+        } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;\n+        } else {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;\n+        }\n+    }\n+\n+    template <typename T>\n+    void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams)\n+    {\n+        if (lockState) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;\n+        } else if (authParams->len != sizeof(T)) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;\n+        } else if (authParams->offset != 0) {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;\n+        } else {\n+            authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;\n+        }\n+    }\n+\n+    /*\n+     * This callback is invoked when a GATT client attempts to modify any of the\n+     * characteristics of this service. Attempts to do so are also applied to\n+     * the internal state of this service object.\n+     */\n+    void onDataWrittenCallback(const GattWriteCallbackParams *writeParams)\n+    {\n+        uint16_t handle = writeParams->handle;\n+\n+        if (handle == lockChar->getValueHandle()) {\n+            memcpy(lock, writeParams->data, sizeof(Lock_t));\n+            /* Set the state to be locked by the lock code (note: zeros are a valid lock) */\n+            lockState = true;\n+            ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));\n+            ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));\n+        } else if (handle == unlockChar->getValueHandle()) {\n+            /* Validated earlier */\n+            lockState = false;\n+            ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));\n+            ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));\n+        } else if (handle == urlDataChar->getValueHandle()) {\n+            urlFrame.setEncodedURLData(writeParams->data, writeParams->len);\n+            ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());\n+        } else if (handle == flagsChar->getValueHandle()) {\n+            flags = *(writeParams->data);\n+            ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));\n+        } else if (handle == advPowerLevelsChar->getValueHandle()) {\n+            memcpy(advPowerLevels, writeParams->data, sizeof(PowerLevels_t));\n+            ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));\n+        } else if (handle == txPowerModeChar->getValueHandle()) {\n+            txPowerMode = *(writeParams->data);\n+            ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));\n+        } else if (handle == beaconPeriodChar->getValueHandle()) {\n+            uint16_t tmpBeaconPeriod = checkBeaconPeriodBounds(*((uint16_t *)(writeParams->data)));\n+            if (tmpBeaconPeriod != beaconPeriod) {\n+                beaconPeriod = tmpBeaconPeriod;\n+                ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));\n+            }\n+        } else if (handle == resetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) {\n+            /* Reset characteristics to default values */\n+            flags        = 0;\n+            txPowerMode  = TX_POWER_MODE_LOW;\n+            beaconPeriod = DEFAULT_BEACON_PERIOD_MSEC;\n+\n+            urlFrame.setURLData(DEFAULT_URL);\n+            memset(lock, 0, sizeof(Lock_t));\n+\n+            ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());\n+            ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));\n+            ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));\n+            ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));\n+            ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));\n+        }\n+    }\n+\n+    uint16_t checkBeaconPeriodBounds(uint16_t beaconPeriodIn) const\n+    {\n+        /* Re-map beaconPeriod to within permissible bounds if necessary. */\n+        if (beaconPeriodIn != 0) {\n+            if (beaconPeriodIn < ble.gap().getMinAdvertisingInterval()) {\n+                return ble.gap().getMinAdvertisingInterval();\n+            } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) {\n+                return ble.gap().getMaxAdvertisingInterval();\n+            }\n+        }\n+        return beaconPeriodIn;\n+    }\n+\n+    struct TLMFrameFactory","path":"BLE_EddystoneServiceExperimental/source/EddystoneService.h","position":677,"original_position":677,"commit_id":"7a5ec4f938f28956015eda75d12138423e7d104b","original_commit_id":"7a5ec4f938f28956015eda75d12138423e7d104b","user":{"login":"pan-","id":409941,"avatar_url":"https://avatars.githubusercontent.com/u/409941?v=3","gravatar_id":"","url":"https://api.github.com/users/pan-","html_url":"https://github.com/pan-","followers_url":"https://api.github.com/users/pan-/followers","following_url":"https://api.github.com/users/pan-/following{/other_user}","gists_url":"https://api.github.com/users/pan-/gists{/gist_id}","starred_url":"https://api.github.com/users/pan-/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pan-/subscriptions","organizations_url":"https://api.github.com/users/pan-/orgs","repos_url":"https://api.github.com/users/pan-/repos","events_url":"https://api.github.com/users/pan-/events{/privacy}","received_events_url":"https://api.github.com/users/pan-/received_events","type":"User","site_admin":false},"body":"It seems that this object **is** a TLM factory and not a factory. The instance name reflect that fact: \r\n```c++\r\nTLMFrameFactory                                                 tlmFrame;\r\n```\r\nIs it possible to rename this class ? I also have the feeling that it can go into its own header file.","created_at":"2015-11-10T17:39:23Z","updated_at":"2015-11-10T17:39:23Z","html_url":"https://github.com/ARMmbed/ble-examples/pull/14#discussion_r44437966","pull_request_url":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14","_links":{"self":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/comments/44437966"},"html":{"href":"https://github.com/ARMmbed/ble-examples/pull/14#discussion_r44437966"},"pull_request":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14"}}},"pull_request":{"url":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14","id":49955246,"html_url":"https://github.com/ARMmbed/ble-examples/pull/14","diff_url":"https://github.com/ARMmbed/ble-examples/pull/14.diff","patch_url":"https://github.com/ARMmbed/ble-examples/pull/14.patch","issue_url":"https://api.github.com/repos/ARMmbed/ble-examples/issues/14","number":14,"state":"open","locked":false,"title":"Add experimental EddystoneService","user":{"login":"andresag01","id":3746901,"avatar_url":"https://avatars.githubusercontent.com/u/3746901?v=3","gravatar_id":"","url":"https://api.github.com/users/andresag01","html_url":"https://github.com/andresag01","followers_url":"https://api.github.com/users/andresag01/followers","following_url":"https://api.github.com/users/andresag01/following{/other_user}","gists_url":"https://api.github.com/users/andresag01/gists{/gist_id}","starred_url":"https://api.github.com/users/andresag01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andresag01/subscriptions","organizations_url":"https://api.github.com/users/andresag01/orgs","repos_url":"https://api.github.com/users/andresag01/repos","events_url":"https://api.github.com/users/andresag01/events{/privacy}","received_events_url":"https://api.github.com/users/andresag01/received_events","type":"User","site_admin":false},"body":"Develop experimental example of a new EddystoneService implementation that will eventually replace both EddystoneConfigService and EddystoneService from BLE API in the future. The new implementation is expected to more efficient in terms of memory and have better structured code.","created_at":"2015-11-06T13:48:53Z","updated_at":"2015-11-10T17:39:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"40be466b2d7a6f0db0f0edbef3bdb87987b41c40","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14/commits","review_comments_url":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14/comments","review_comment_url":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ARMmbed/ble-examples/issues/14/comments","statuses_url":"https://api.github.com/repos/ARMmbed/ble-examples/statuses/7a5ec4f938f28956015eda75d12138423e7d104b","head":{"label":"andresag01:oob-oct15","ref":"oob-oct15","sha":"7a5ec4f938f28956015eda75d12138423e7d104b","user":{"login":"andresag01","id":3746901,"avatar_url":"https://avatars.githubusercontent.com/u/3746901?v=3","gravatar_id":"","url":"https://api.github.com/users/andresag01","html_url":"https://github.com/andresag01","followers_url":"https://api.github.com/users/andresag01/followers","following_url":"https://api.github.com/users/andresag01/following{/other_user}","gists_url":"https://api.github.com/users/andresag01/gists{/gist_id}","starred_url":"https://api.github.com/users/andresag01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andresag01/subscriptions","organizations_url":"https://api.github.com/users/andresag01/orgs","repos_url":"https://api.github.com/users/andresag01/repos","events_url":"https://api.github.com/users/andresag01/events{/privacy}","received_events_url":"https://api.github.com/users/andresag01/received_events","type":"User","site_admin":false},"repo":{"id":45401425,"name":"ble-examples-1","full_name":"andresag01/ble-examples-1","owner":{"login":"andresag01","id":3746901,"avatar_url":"https://avatars.githubusercontent.com/u/3746901?v=3","gravatar_id":"","url":"https://api.github.com/users/andresag01","html_url":"https://github.com/andresag01","followers_url":"https://api.github.com/users/andresag01/followers","following_url":"https://api.github.com/users/andresag01/following{/other_user}","gists_url":"https://api.github.com/users/andresag01/gists{/gist_id}","starred_url":"https://api.github.com/users/andresag01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andresag01/subscriptions","organizations_url":"https://api.github.com/users/andresag01/orgs","repos_url":"https://api.github.com/users/andresag01/repos","events_url":"https://api.github.com/users/andresag01/events{/privacy}","received_events_url":"https://api.github.com/users/andresag01/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/andresag01/ble-examples-1","description":"BLE demos using mbed OS and yotta.","fork":true,"url":"https://api.github.com/repos/andresag01/ble-examples-1","forks_url":"https://api.github.com/repos/andresag01/ble-examples-1/forks","keys_url":"https://api.github.com/repos/andresag01/ble-examples-1/keys{/key_id}","collaborators_url":"https://api.github.com/repos/andresag01/ble-examples-1/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/andresag01/ble-examples-1/teams","hooks_url":"https://api.github.com/repos/andresag01/ble-examples-1/hooks","issue_events_url":"https://api.github.com/repos/andresag01/ble-examples-1/issues/events{/number}","events_url":"https://api.github.com/repos/andresag01/ble-examples-1/events","assignees_url":"https://api.github.com/repos/andresag01/ble-examples-1/assignees{/user}","branches_url":"https://api.github.com/repos/andresag01/ble-examples-1/branches{/branch}","tags_url":"https://api.github.com/repos/andresag01/ble-examples-1/tags","blobs_url":"https://api.github.com/repos/andresag01/ble-examples-1/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/andresag01/ble-examples-1/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/andresag01/ble-examples-1/git/refs{/sha}","trees_url":"https://api.github.com/repos/andresag01/ble-examples-1/git/trees{/sha}","statuses_url":"https://api.github.com/repos/andresag01/ble-examples-1/statuses/{sha}","languages_url":"https://api.github.com/repos/andresag01/ble-examples-1/languages","stargazers_url":"https://api.github.com/repos/andresag01/ble-examples-1/stargazers","contributors_url":"https://api.github.com/repos/andresag01/ble-examples-1/contributors","subscribers_url":"https://api.github.com/repos/andresag01/ble-examples-1/subscribers","subscription_url":"https://api.github.com/repos/andresag01/ble-examples-1/subscription","commits_url":"https://api.github.com/repos/andresag01/ble-examples-1/commits{/sha}","git_commits_url":"https://api.github.com/repos/andresag01/ble-examples-1/git/commits{/sha}","comments_url":"https://api.github.com/repos/andresag01/ble-examples-1/comments{/number}","issue_comment_url":"https://api.github.com/repos/andresag01/ble-examples-1/issues/comments{/number}","contents_url":"https://api.github.com/repos/andresag01/ble-examples-1/contents/{+path}","compare_url":"https://api.github.com/repos/andresag01/ble-examples-1/compare/{base}...{head}","merges_url":"https://api.github.com/repos/andresag01/ble-examples-1/merges","archive_url":"https://api.github.com/repos/andresag01/ble-examples-1/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/andresag01/ble-examples-1/downloads","issues_url":"https://api.github.com/repos/andresag01/ble-examples-1/issues{/number}","pulls_url":"https://api.github.com/repos/andresag01/ble-examples-1/pulls{/number}","milestones_url":"https://api.github.com/repos/andresag01/ble-examples-1/milestones{/number}","notifications_url":"https://api.github.com/repos/andresag01/ble-examples-1/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/andresag01/ble-examples-1/labels{/name}","releases_url":"https://api.github.com/repos/andresag01/ble-examples-1/releases{/id}","created_at":"2015-11-02T15:01:48Z","updated_at":"2015-11-02T15:01:48Z","pushed_at":"2015-11-10T15:53:50Z","git_url":"git://github.com/andresag01/ble-examples-1.git","ssh_url":"git@github.com:andresag01/ble-examples-1.git","clone_url":"https://github.com/andresag01/ble-examples-1.git","svn_url":"https://github.com/andresag01/ble-examples-1","homepage":"","size":169,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ARMmbed:oob-oct15","ref":"oob-oct15","sha":"ce3e709fcf8c900114cbcff5c76b78e877ac55f4","user":{"login":"ARMmbed","id":8916057,"avatar_url":"https://avatars.githubusercontent.com/u/8916057?v=3","gravatar_id":"","url":"https://api.github.com/users/ARMmbed","html_url":"https://github.com/ARMmbed","followers_url":"https://api.github.com/users/ARMmbed/followers","following_url":"https://api.github.com/users/ARMmbed/following{/other_user}","gists_url":"https://api.github.com/users/ARMmbed/gists{/gist_id}","starred_url":"https://api.github.com/users/ARMmbed/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ARMmbed/subscriptions","organizations_url":"https://api.github.com/users/ARMmbed/orgs","repos_url":"https://api.github.com/users/ARMmbed/repos","events_url":"https://api.github.com/users/ARMmbed/events{/privacy}","received_events_url":"https://api.github.com/users/ARMmbed/received_events","type":"Organization","site_admin":false},"repo":{"id":40475741,"name":"ble-examples","full_name":"ARMmbed/ble-examples","owner":{"login":"ARMmbed","id":8916057,"avatar_url":"https://avatars.githubusercontent.com/u/8916057?v=3","gravatar_id":"","url":"https://api.github.com/users/ARMmbed","html_url":"https://github.com/ARMmbed","followers_url":"https://api.github.com/users/ARMmbed/followers","following_url":"https://api.github.com/users/ARMmbed/following{/other_user}","gists_url":"https://api.github.com/users/ARMmbed/gists{/gist_id}","starred_url":"https://api.github.com/users/ARMmbed/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ARMmbed/subscriptions","organizations_url":"https://api.github.com/users/ARMmbed/orgs","repos_url":"https://api.github.com/users/ARMmbed/repos","events_url":"https://api.github.com/users/ARMmbed/events{/privacy}","received_events_url":"https://api.github.com/users/ARMmbed/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ARMmbed/ble-examples","description":"BLE demos using mbed OS and yotta.","fork":false,"url":"https://api.github.com/repos/ARMmbed/ble-examples","forks_url":"https://api.github.com/repos/ARMmbed/ble-examples/forks","keys_url":"https://api.github.com/repos/ARMmbed/ble-examples/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ARMmbed/ble-examples/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ARMmbed/ble-examples/teams","hooks_url":"https://api.github.com/repos/ARMmbed/ble-examples/hooks","issue_events_url":"https://api.github.com/repos/ARMmbed/ble-examples/issues/events{/number}","events_url":"https://api.github.com/repos/ARMmbed/ble-examples/events","assignees_url":"https://api.github.com/repos/ARMmbed/ble-examples/assignees{/user}","branches_url":"https://api.github.com/repos/ARMmbed/ble-examples/branches{/branch}","tags_url":"https://api.github.com/repos/ARMmbed/ble-examples/tags","blobs_url":"https://api.github.com/repos/ARMmbed/ble-examples/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ARMmbed/ble-examples/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ARMmbed/ble-examples/git/refs{/sha}","trees_url":"https://api.github.com/repos/ARMmbed/ble-examples/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ARMmbed/ble-examples/statuses/{sha}","languages_url":"https://api.github.com/repos/ARMmbed/ble-examples/languages","stargazers_url":"https://api.github.com/repos/ARMmbed/ble-examples/stargazers","contributors_url":"https://api.github.com/repos/ARMmbed/ble-examples/contributors","subscribers_url":"https://api.github.com/repos/ARMmbed/ble-examples/subscribers","subscription_url":"https://api.github.com/repos/ARMmbed/ble-examples/subscription","commits_url":"https://api.github.com/repos/ARMmbed/ble-examples/commits{/sha}","git_commits_url":"https://api.github.com/repos/ARMmbed/ble-examples/git/commits{/sha}","comments_url":"https://api.github.com/repos/ARMmbed/ble-examples/comments{/number}","issue_comment_url":"https://api.github.com/repos/ARMmbed/ble-examples/issues/comments{/number}","contents_url":"https://api.github.com/repos/ARMmbed/ble-examples/contents/{+path}","compare_url":"https://api.github.com/repos/ARMmbed/ble-examples/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ARMmbed/ble-examples/merges","archive_url":"https://api.github.com/repos/ARMmbed/ble-examples/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ARMmbed/ble-examples/downloads","issues_url":"https://api.github.com/repos/ARMmbed/ble-examples/issues{/number}","pulls_url":"https://api.github.com/repos/ARMmbed/ble-examples/pulls{/number}","milestones_url":"https://api.github.com/repos/ARMmbed/ble-examples/milestones{/number}","notifications_url":"https://api.github.com/repos/ARMmbed/ble-examples/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ARMmbed/ble-examples/labels{/name}","releases_url":"https://api.github.com/repos/ARMmbed/ble-examples/releases{/id}","created_at":"2015-08-10T09:58:49Z","updated_at":"2015-10-26T08:10:36Z","pushed_at":"2015-11-10T15:53:51Z","git_url":"git://github.com/ARMmbed/ble-examples.git","ssh_url":"git@github.com:ARMmbed/ble-examples.git","clone_url":"https://github.com/ARMmbed/ble-examples.git","svn_url":"https://github.com/ARMmbed/ble-examples","homepage":"","size":394,"stargazers_count":1,"watchers_count":1,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":6,"mirror_url":null,"open_issues_count":4,"forks":6,"open_issues":4,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14"},"html":{"href":"https://github.com/ARMmbed/ble-examples/pull/14"},"issue":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/issues/14"},"comments":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/issues/14/comments"},"review_comments":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14/comments"},"review_comment":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/pulls/14/commits"},"statuses":{"href":"https://api.github.com/repos/ARMmbed/ble-examples/statuses/7a5ec4f938f28956015eda75d12138423e7d104b"}}}}