Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Puck/Puck.h@0:b8221deeaa87, 2015-04-30 (annotated)
- Committer:
- Grimmkey
- Date:
- Thu Apr 30 20:46:27 2015 +0000
- Revision:
- 0:b8221deeaa87
Georgia Institute of Technology ECE 4180 Spring 2015 Jazz Hands project, Nordic nRF51822 half
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Grimmkey | 0:b8221deeaa87 | 1 | /** |
Grimmkey | 0:b8221deeaa87 | 2 | * Copyright 2014 Nordic Semiconductor |
Grimmkey | 0:b8221deeaa87 | 3 | * |
Grimmkey | 0:b8221deeaa87 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
Grimmkey | 0:b8221deeaa87 | 5 | * you may not use this file except in compliance with the License. |
Grimmkey | 0:b8221deeaa87 | 6 | * You may obtain a copy of the License at |
Grimmkey | 0:b8221deeaa87 | 7 | * |
Grimmkey | 0:b8221deeaa87 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
Grimmkey | 0:b8221deeaa87 | 9 | * |
Grimmkey | 0:b8221deeaa87 | 10 | * Unless required by applicable law or agreed to in writing, software |
Grimmkey | 0:b8221deeaa87 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
Grimmkey | 0:b8221deeaa87 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
Grimmkey | 0:b8221deeaa87 | 13 | * See the License for the specific language governing permissions and |
Grimmkey | 0:b8221deeaa87 | 14 | * limitations under the License |
Grimmkey | 0:b8221deeaa87 | 15 | */ |
Grimmkey | 0:b8221deeaa87 | 16 | |
Grimmkey | 0:b8221deeaa87 | 17 | |
Grimmkey | 0:b8221deeaa87 | 18 | #ifndef __PUCK_HPP__ |
Grimmkey | 0:b8221deeaa87 | 19 | #define __PUCK_HPP__ |
Grimmkey | 0:b8221deeaa87 | 20 | |
Grimmkey | 0:b8221deeaa87 | 21 | #include "BLEDevice.h" |
Grimmkey | 0:b8221deeaa87 | 22 | #include "mbed.h" |
Grimmkey | 0:b8221deeaa87 | 23 | #include "Log.h" |
Grimmkey | 0:b8221deeaa87 | 24 | #include <vector> |
Grimmkey | 0:b8221deeaa87 | 25 | |
Grimmkey | 0:b8221deeaa87 | 26 | enum PuckState { |
Grimmkey | 0:b8221deeaa87 | 27 | CONNECTING, |
Grimmkey | 0:b8221deeaa87 | 28 | CONNECTED, |
Grimmkey | 0:b8221deeaa87 | 29 | ADVERTISING, |
Grimmkey | 0:b8221deeaa87 | 30 | DISCONNECTED |
Grimmkey | 0:b8221deeaa87 | 31 | }; |
Grimmkey | 0:b8221deeaa87 | 32 | |
Grimmkey | 0:b8221deeaa87 | 33 | const UUID stringToUUID(const char* str); |
Grimmkey | 0:b8221deeaa87 | 34 | |
Grimmkey | 0:b8221deeaa87 | 35 | typedef void (*CharacteristicWriteCallback)(const uint8_t* value, uint8_t length); |
Grimmkey | 0:b8221deeaa87 | 36 | |
Grimmkey | 0:b8221deeaa87 | 37 | typedef struct { |
Grimmkey | 0:b8221deeaa87 | 38 | const UUID* uuid; |
Grimmkey | 0:b8221deeaa87 | 39 | std::vector<CharacteristicWriteCallback>* callbacks; |
Grimmkey | 0:b8221deeaa87 | 40 | } CharacteristicWriteCallbacks; |
Grimmkey | 0:b8221deeaa87 | 41 | |
Grimmkey | 0:b8221deeaa87 | 42 | /** |
Grimmkey | 0:b8221deeaa87 | 43 | * @brief A library for easier setup and prototyping of IoT devices (pucks), by collecting everything that is common for all pucks in one place. |
Grimmkey | 0:b8221deeaa87 | 44 | * |
Grimmkey | 0:b8221deeaa87 | 45 | */ |
Grimmkey | 0:b8221deeaa87 | 46 | class Puck { |
Grimmkey | 0:b8221deeaa87 | 47 | private: |
Grimmkey | 0:b8221deeaa87 | 48 | Puck() {} |
Grimmkey | 0:b8221deeaa87 | 49 | Puck(const Puck&); |
Grimmkey | 0:b8221deeaa87 | 50 | Puck& operator=(const Puck&); |
Grimmkey | 0:b8221deeaa87 | 51 | |
Grimmkey | 0:b8221deeaa87 | 52 | BLEDevice ble; |
Grimmkey | 0:b8221deeaa87 | 53 | uint8_t beaconPayload[25]; |
Grimmkey | 0:b8221deeaa87 | 54 | PuckState state; |
Grimmkey | 0:b8221deeaa87 | 55 | std::vector<GattService*> services; |
Grimmkey | 0:b8221deeaa87 | 56 | std::vector<GattCharacteristic*> characteristics; |
Grimmkey | 0:b8221deeaa87 | 57 | std::vector<CharacteristicWriteCallbacks*> writeCallbacks; |
Grimmkey | 0:b8221deeaa87 | 58 | std::vector<CharacteristicWriteCallback> pendingCallbackStack; |
Grimmkey | 0:b8221deeaa87 | 59 | std::vector<const uint8_t*> pendingCallbackParameterDataStack; |
Grimmkey | 0:b8221deeaa87 | 60 | std::vector<uint8_t> pendingCallbackParameterLengthStack; |
Grimmkey | 0:b8221deeaa87 | 61 | |
Grimmkey | 0:b8221deeaa87 | 62 | GattCharacteristic **previousCharacteristics; |
Grimmkey | 0:b8221deeaa87 | 63 | |
Grimmkey | 0:b8221deeaa87 | 64 | public: |
Grimmkey | 0:b8221deeaa87 | 65 | static Puck &getPuck(); |
Grimmkey | 0:b8221deeaa87 | 66 | |
Grimmkey | 0:b8221deeaa87 | 67 | BLEDevice &getBle() { return ble; } |
Grimmkey | 0:b8221deeaa87 | 68 | PuckState getState() { return state; } |
Grimmkey | 0:b8221deeaa87 | 69 | void setState(PuckState state); |
Grimmkey | 0:b8221deeaa87 | 70 | void init(uint16_t minor); |
Grimmkey | 0:b8221deeaa87 | 71 | void startAdvertising(); |
Grimmkey | 0:b8221deeaa87 | 72 | void stopAdvertising(); |
Grimmkey | 0:b8221deeaa87 | 73 | void disconnect(); |
Grimmkey | 0:b8221deeaa87 | 74 | bool drive(); |
Grimmkey | 0:b8221deeaa87 | 75 | int countFreeMemory(); |
Grimmkey | 0:b8221deeaa87 | 76 | void onDataWritten(GattAttribute::Handle_t handle, const uint8_t* data, const uint8_t length); |
Grimmkey | 0:b8221deeaa87 | 77 | void addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties = 0xA); |
Grimmkey | 0:b8221deeaa87 | 78 | |
Grimmkey | 0:b8221deeaa87 | 79 | void onCharacteristicWrite(const UUID* uuid, CharacteristicWriteCallback callback); |
Grimmkey | 0:b8221deeaa87 | 80 | void updateCharacteristicValue(const UUID uuid, uint8_t* value, int length); |
Grimmkey | 0:b8221deeaa87 | 81 | |
Grimmkey | 0:b8221deeaa87 | 82 | uint8_t* getCharacteristicValue(const UUID uuid); |
Grimmkey | 0:b8221deeaa87 | 83 | }; |
Grimmkey | 0:b8221deeaa87 | 84 | |
Grimmkey | 0:b8221deeaa87 | 85 | /** |
Grimmkey | 0:b8221deeaa87 | 86 | * @brief Returns singleton instance of puck object. |
Grimmkey | 0:b8221deeaa87 | 87 | * |
Grimmkey | 0:b8221deeaa87 | 88 | * @return singleton instance of puck object. |
Grimmkey | 0:b8221deeaa87 | 89 | */ |
Grimmkey | 0:b8221deeaa87 | 90 | Puck &Puck::getPuck() { |
Grimmkey | 0:b8221deeaa87 | 91 | static Puck _puckSingletonInstance; |
Grimmkey | 0:b8221deeaa87 | 92 | return _puckSingletonInstance; |
Grimmkey | 0:b8221deeaa87 | 93 | } |
Grimmkey | 0:b8221deeaa87 | 94 | |
Grimmkey | 0:b8221deeaa87 | 95 | void onDisconnection(Gap::Handle_t handle, Gap::DisconnectionReason_t disconnectReason) { |
Grimmkey | 0:b8221deeaa87 | 96 | LOG_INFO("Disconnected.\n"); |
Grimmkey | 0:b8221deeaa87 | 97 | Puck::getPuck().setState(DISCONNECTED); |
Grimmkey | 0:b8221deeaa87 | 98 | } |
Grimmkey | 0:b8221deeaa87 | 99 | |
Grimmkey | 0:b8221deeaa87 | 100 | void onConnection(Gap::Handle_t handle, |
Grimmkey | 0:b8221deeaa87 | 101 | Gap::addr_type_t peerAddrType, |
Grimmkey | 0:b8221deeaa87 | 102 | const Gap::address_t peerAddr, |
Grimmkey | 0:b8221deeaa87 | 103 | const Gap::ConnectionParams_t * connectionParams) { |
Grimmkey | 0:b8221deeaa87 | 104 | LOG_INFO("Connected.\n"); |
Grimmkey | 0:b8221deeaa87 | 105 | Puck::getPuck().setState(CONNECTED); |
Grimmkey | 0:b8221deeaa87 | 106 | } |
Grimmkey | 0:b8221deeaa87 | 107 | |
Grimmkey | 0:b8221deeaa87 | 108 | void onDataWrittenCallback(const GattCharacteristicWriteCBParams *context) { |
Grimmkey | 0:b8221deeaa87 | 109 | Puck::getPuck().onDataWritten(context->charHandle, context->data, context->len); |
Grimmkey | 0:b8221deeaa87 | 110 | } |
Grimmkey | 0:b8221deeaa87 | 111 | |
Grimmkey | 0:b8221deeaa87 | 112 | bool isEqualUUID(const UUID* uuidA, const UUID uuidB) { |
Grimmkey | 0:b8221deeaa87 | 113 | const uint8_t* uuidABase = uuidA->getBaseUUID(); |
Grimmkey | 0:b8221deeaa87 | 114 | const uint8_t* uuidBBase = uuidB.getBaseUUID(); |
Grimmkey | 0:b8221deeaa87 | 115 | |
Grimmkey | 0:b8221deeaa87 | 116 | for(int i = 0; i < 16; i++) { |
Grimmkey | 0:b8221deeaa87 | 117 | if(uuidABase[i] != uuidBBase[i]) { |
Grimmkey | 0:b8221deeaa87 | 118 | return false; |
Grimmkey | 0:b8221deeaa87 | 119 | } |
Grimmkey | 0:b8221deeaa87 | 120 | } |
Grimmkey | 0:b8221deeaa87 | 121 | if(uuidA->getShortUUID() != uuidB.getShortUUID()) { |
Grimmkey | 0:b8221deeaa87 | 122 | return false; |
Grimmkey | 0:b8221deeaa87 | 123 | } |
Grimmkey | 0:b8221deeaa87 | 124 | return true; |
Grimmkey | 0:b8221deeaa87 | 125 | } |
Grimmkey | 0:b8221deeaa87 | 126 | |
Grimmkey | 0:b8221deeaa87 | 127 | /** |
Grimmkey | 0:b8221deeaa87 | 128 | * @brief Returns UUID representation of a 16-character string. |
Grimmkey | 0:b8221deeaa87 | 129 | * |
Grimmkey | 0:b8221deeaa87 | 130 | */ |
Grimmkey | 0:b8221deeaa87 | 131 | const UUID stringToUUID(const char* str) { |
Grimmkey | 0:b8221deeaa87 | 132 | uint8_t array[16]; |
Grimmkey | 0:b8221deeaa87 | 133 | for(int i = 0; i < 16; i++) { |
Grimmkey | 0:b8221deeaa87 | 134 | array[i] = str[i]; |
Grimmkey | 0:b8221deeaa87 | 135 | } |
Grimmkey | 0:b8221deeaa87 | 136 | return UUID(array); |
Grimmkey | 0:b8221deeaa87 | 137 | } |
Grimmkey | 0:b8221deeaa87 | 138 | |
Grimmkey | 0:b8221deeaa87 | 139 | void Puck::disconnect() { |
Grimmkey | 0:b8221deeaa87 | 140 | ble.disconnect(Gap::LOCAL_HOST_TERMINATED_CONNECTION); |
Grimmkey | 0:b8221deeaa87 | 141 | } |
Grimmkey | 0:b8221deeaa87 | 142 | |
Grimmkey | 0:b8221deeaa87 | 143 | /** |
Grimmkey | 0:b8221deeaa87 | 144 | * @brief Approximates malloc-able heap space. Do not use in production code, as it may crash. |
Grimmkey | 0:b8221deeaa87 | 145 | * |
Grimmkey | 0:b8221deeaa87 | 146 | */ |
Grimmkey | 0:b8221deeaa87 | 147 | int Puck::countFreeMemory() { |
Grimmkey | 0:b8221deeaa87 | 148 | int blocksize = 256; |
Grimmkey | 0:b8221deeaa87 | 149 | int amount = 0; |
Grimmkey | 0:b8221deeaa87 | 150 | while (blocksize > 0) { |
Grimmkey | 0:b8221deeaa87 | 151 | amount += blocksize; |
Grimmkey | 0:b8221deeaa87 | 152 | LOG_VERBOSE("Trying to malloc %i bytes... ", amount); |
Grimmkey | 0:b8221deeaa87 | 153 | char *p = (char *) malloc(amount); |
Grimmkey | 0:b8221deeaa87 | 154 | if (p == NULL) { |
Grimmkey | 0:b8221deeaa87 | 155 | LOG_VERBOSE("FAIL!\n", amount); |
Grimmkey | 0:b8221deeaa87 | 156 | amount -= blocksize; |
Grimmkey | 0:b8221deeaa87 | 157 | blocksize /= 2; |
Grimmkey | 0:b8221deeaa87 | 158 | } else { |
Grimmkey | 0:b8221deeaa87 | 159 | free(p); |
Grimmkey | 0:b8221deeaa87 | 160 | LOG_VERBOSE("OK!\n", amount); |
Grimmkey | 0:b8221deeaa87 | 161 | } |
Grimmkey | 0:b8221deeaa87 | 162 | } |
Grimmkey | 0:b8221deeaa87 | 163 | LOG_DEBUG("Free memory: %i bytes.\n", amount); |
Grimmkey | 0:b8221deeaa87 | 164 | return amount; |
Grimmkey | 0:b8221deeaa87 | 165 | } |
Grimmkey | 0:b8221deeaa87 | 166 | |
Grimmkey | 0:b8221deeaa87 | 167 | void Puck::setState(PuckState state) { |
Grimmkey | 0:b8221deeaa87 | 168 | LOG_DEBUG("Changed state to %i\n", state); |
Grimmkey | 0:b8221deeaa87 | 169 | this->state = state; |
Grimmkey | 0:b8221deeaa87 | 170 | } |
Grimmkey | 0:b8221deeaa87 | 171 | |
Grimmkey | 0:b8221deeaa87 | 172 | /** |
Grimmkey | 0:b8221deeaa87 | 173 | * @brief Call after finishing configuring puck (adding services, characteristics, callbacks). |
Grimmkey | 0:b8221deeaa87 | 174 | Starts advertising over bluetooth le. |
Grimmkey | 0:b8221deeaa87 | 175 | * |
Grimmkey | 0:b8221deeaa87 | 176 | * @parameter minor |
Grimmkey | 0:b8221deeaa87 | 177 | * Minor number to use for iBeacon identifier. |
Grimmkey | 0:b8221deeaa87 | 178 | * |
Grimmkey | 0:b8221deeaa87 | 179 | */ |
Grimmkey | 0:b8221deeaa87 | 180 | void Puck::init(uint16_t minor) { |
Grimmkey | 0:b8221deeaa87 | 181 | /* |
Grimmkey | 0:b8221deeaa87 | 182 | * The Beacon payload (encapsulated within the MSD advertising data structure) |
Grimmkey | 0:b8221deeaa87 | 183 | * has the following composition: |
Grimmkey | 0:b8221deeaa87 | 184 | * 128-Bit UUID = E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61 |
Grimmkey | 0:b8221deeaa87 | 185 | * Major/Minor = 1337 / XXXX |
Grimmkey | 0:b8221deeaa87 | 186 | * Tx Power = C8 |
Grimmkey | 0:b8221deeaa87 | 187 | */ |
Grimmkey | 0:b8221deeaa87 | 188 | uint8_t beaconPayloadTemplate[] = { |
Grimmkey | 0:b8221deeaa87 | 189 | 0x00, 0x00, // Company identifier code (0x004C == Apple) |
Grimmkey | 0:b8221deeaa87 | 190 | 0x02, // ID |
Grimmkey | 0:b8221deeaa87 | 191 | 0x15, // length of the remaining payload |
Grimmkey | 0:b8221deeaa87 | 192 | 0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, // UUID |
Grimmkey | 0:b8221deeaa87 | 193 | 0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61, |
Grimmkey | 0:b8221deeaa87 | 194 | 0x13, 0x37, // the major value to differenciate a location (Our app requires 1337 as major number) |
Grimmkey | 0:b8221deeaa87 | 195 | 0x00, 0x00, // the minor value to differenciate a location (Change this to differentiate location pucks) |
Grimmkey | 0:b8221deeaa87 | 196 | 0xC8 // 2's complement of the Tx power (-56dB) |
Grimmkey | 0:b8221deeaa87 | 197 | }; |
Grimmkey | 0:b8221deeaa87 | 198 | beaconPayloadTemplate[22] = minor >> 8; |
Grimmkey | 0:b8221deeaa87 | 199 | beaconPayloadTemplate[23] = minor & 255; |
Grimmkey | 0:b8221deeaa87 | 200 | |
Grimmkey | 0:b8221deeaa87 | 201 | for (int i=0; i < 25; i++) { |
Grimmkey | 0:b8221deeaa87 | 202 | beaconPayload[i] = beaconPayloadTemplate[i]; |
Grimmkey | 0:b8221deeaa87 | 203 | } |
Grimmkey | 0:b8221deeaa87 | 204 | |
Grimmkey | 0:b8221deeaa87 | 205 | ble.init(); |
Grimmkey | 0:b8221deeaa87 | 206 | LOG_DEBUG("Inited BLEDevice.\n"); |
Grimmkey | 0:b8221deeaa87 | 207 | setState(DISCONNECTED); |
Grimmkey | 0:b8221deeaa87 | 208 | |
Grimmkey | 0:b8221deeaa87 | 209 | char deviceName[10]; |
Grimmkey | 0:b8221deeaa87 | 210 | sprintf(&deviceName[0], "Puck %04X", minor); |
Grimmkey | 0:b8221deeaa87 | 211 | deviceName[9] = '\0'; |
Grimmkey | 0:b8221deeaa87 | 212 | ble.setDeviceName((const uint8_t*) deviceName); |
Grimmkey | 0:b8221deeaa87 | 213 | |
Grimmkey | 0:b8221deeaa87 | 214 | ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
Grimmkey | 0:b8221deeaa87 | 215 | LOG_DEBUG("Accumulate advertising payload: BREDR_NOT_SUPPORTED | LE_GENERAL_DISCOVERABLE.\n"); |
Grimmkey | 0:b8221deeaa87 | 216 | |
Grimmkey | 0:b8221deeaa87 | 217 | ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, beaconPayload, sizeof(beaconPayload)); |
Grimmkey | 0:b8221deeaa87 | 218 | LOG_DEBUG("Accumulate advertising payload: beacon data.\n"); |
Grimmkey | 0:b8221deeaa87 | 219 | |
Grimmkey | 0:b8221deeaa87 | 220 | ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
Grimmkey | 0:b8221deeaa87 | 221 | LOG_DEBUG("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n"); |
Grimmkey | 0:b8221deeaa87 | 222 | |
Grimmkey | 0:b8221deeaa87 | 223 | int hundredMillisecondsInAdvertisingIntervalFormat = 160; |
Grimmkey | 0:b8221deeaa87 | 224 | ble.setAdvertisingInterval(hundredMillisecondsInAdvertisingIntervalFormat); |
Grimmkey | 0:b8221deeaa87 | 225 | LOG_DEBUG("Set advertising interval: 160 (100 ms).\n"); |
Grimmkey | 0:b8221deeaa87 | 226 | |
Grimmkey | 0:b8221deeaa87 | 227 | ble.onDisconnection(onDisconnection); |
Grimmkey | 0:b8221deeaa87 | 228 | ble.onConnection(onConnection); |
Grimmkey | 0:b8221deeaa87 | 229 | ble.onDataWritten(onDataWrittenCallback); |
Grimmkey | 0:b8221deeaa87 | 230 | LOG_DEBUG("Hooked up internal event handlers.\n"); |
Grimmkey | 0:b8221deeaa87 | 231 | |
Grimmkey | 0:b8221deeaa87 | 232 | for(int i = 0; i < services.size(); i++) { |
Grimmkey | 0:b8221deeaa87 | 233 | ble.addService(*services[i]); |
Grimmkey | 0:b8221deeaa87 | 234 | LOG_DEBUG("Added service %x to BLEDevice\n", services[i]); |
Grimmkey | 0:b8221deeaa87 | 235 | } |
Grimmkey | 0:b8221deeaa87 | 236 | |
Grimmkey | 0:b8221deeaa87 | 237 | LOG_INFO("Inited puck as 0x%X.\n", minor); |
Grimmkey | 0:b8221deeaa87 | 238 | } |
Grimmkey | 0:b8221deeaa87 | 239 | |
Grimmkey | 0:b8221deeaa87 | 240 | void Puck::startAdvertising() { |
Grimmkey | 0:b8221deeaa87 | 241 | ble.startAdvertising(); |
Grimmkey | 0:b8221deeaa87 | 242 | LOG_INFO("Starting to advertise.\n"); |
Grimmkey | 0:b8221deeaa87 | 243 | setState(ADVERTISING); |
Grimmkey | 0:b8221deeaa87 | 244 | } |
Grimmkey | 0:b8221deeaa87 | 245 | |
Grimmkey | 0:b8221deeaa87 | 246 | void Puck::stopAdvertising() { |
Grimmkey | 0:b8221deeaa87 | 247 | if(state == ADVERTISING) { |
Grimmkey | 0:b8221deeaa87 | 248 | ble.stopAdvertising(); |
Grimmkey | 0:b8221deeaa87 | 249 | LOG_INFO("Stopped advertising.\n"); |
Grimmkey | 0:b8221deeaa87 | 250 | setState(DISCONNECTED); |
Grimmkey | 0:b8221deeaa87 | 251 | } else { |
Grimmkey | 0:b8221deeaa87 | 252 | LOG_WARN("Tried to stop advertising, but advertising is already stopped!\n"); |
Grimmkey | 0:b8221deeaa87 | 253 | } |
Grimmkey | 0:b8221deeaa87 | 254 | } |
Grimmkey | 0:b8221deeaa87 | 255 | |
Grimmkey | 0:b8221deeaa87 | 256 | /** |
Grimmkey | 0:b8221deeaa87 | 257 | * @brief Extends the given gatt service with the given gatt characteristic. |
Grimmkey | 0:b8221deeaa87 | 258 | * If the service doesn't exist, it is created. |
Grimmkey | 0:b8221deeaa87 | 259 | * |
Grimmkey | 0:b8221deeaa87 | 260 | * @param serviceUuid |
Grimmkey | 0:b8221deeaa87 | 261 | UUID of the gatt service to be extended. |
Grimmkey | 0:b8221deeaa87 | 262 | * |
Grimmkey | 0:b8221deeaa87 | 263 | * @param characteristicUuid |
Grimmkey | 0:b8221deeaa87 | 264 | * UUID to use for this characteristic. |
Grimmkey | 0:b8221deeaa87 | 265 | * |
Grimmkey | 0:b8221deeaa87 | 266 | * @param bytes |
Grimmkey | 0:b8221deeaa87 | 267 | * Length in bytes of this characteristic's value. |
Grimmkey | 0:b8221deeaa87 | 268 | * |
Grimmkey | 0:b8221deeaa87 | 269 | * @param properties |
Grimmkey | 0:b8221deeaa87 | 270 | * 8-bit bit field containing the characteristic's properties. See @ref ble_gatt_char_properties_t. |
Grimmkey | 0:b8221deeaa87 | 271 | * |
Grimmkey | 0:b8221deeaa87 | 272 | * @return Void. |
Grimmkey | 0:b8221deeaa87 | 273 | */ |
Grimmkey | 0:b8221deeaa87 | 274 | void Puck::addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties) { |
Grimmkey | 0:b8221deeaa87 | 275 | MBED_ASSERT(bytes <= 20); |
Grimmkey | 0:b8221deeaa87 | 276 | uint16_t size = sizeof(uint8_t) * bytes; |
Grimmkey | 0:b8221deeaa87 | 277 | uint8_t* value = (uint8_t*) malloc(size); |
Grimmkey | 0:b8221deeaa87 | 278 | if(value == NULL) { |
Grimmkey | 0:b8221deeaa87 | 279 | LOG_ERROR("Unable to malloc value for characteristic. Possibly out of memory!\n"); |
Grimmkey | 0:b8221deeaa87 | 280 | } |
Grimmkey | 0:b8221deeaa87 | 281 | |
Grimmkey | 0:b8221deeaa87 | 282 | GattCharacteristic* characteristic = new GattCharacteristic(characteristicUuid, value, size, size, properties); |
Grimmkey | 0:b8221deeaa87 | 283 | characteristics.push_back(characteristic); |
Grimmkey | 0:b8221deeaa87 | 284 | |
Grimmkey | 0:b8221deeaa87 | 285 | |
Grimmkey | 0:b8221deeaa87 | 286 | GattService* service = NULL; |
Grimmkey | 0:b8221deeaa87 | 287 | |
Grimmkey | 0:b8221deeaa87 | 288 | int removeIndex = -1; |
Grimmkey | 0:b8221deeaa87 | 289 | for(int i = 0; i < services.size(); i++) { |
Grimmkey | 0:b8221deeaa87 | 290 | if(isEqualUUID(&services[i]->getUUID(), serviceUuid)) { |
Grimmkey | 0:b8221deeaa87 | 291 | service = services[i]; |
Grimmkey | 0:b8221deeaa87 | 292 | removeIndex = i; |
Grimmkey | 0:b8221deeaa87 | 293 | break; |
Grimmkey | 0:b8221deeaa87 | 294 | } |
Grimmkey | 0:b8221deeaa87 | 295 | } |
Grimmkey | 0:b8221deeaa87 | 296 | GattCharacteristic** characteristics = NULL; |
Grimmkey | 0:b8221deeaa87 | 297 | int characteristicsLength = 0; |
Grimmkey | 0:b8221deeaa87 | 298 | if(service != NULL) { |
Grimmkey | 0:b8221deeaa87 | 299 | characteristicsLength = service->getCharacteristicCount() + 1; |
Grimmkey | 0:b8221deeaa87 | 300 | characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength); |
Grimmkey | 0:b8221deeaa87 | 301 | if(characteristics == NULL) { |
Grimmkey | 0:b8221deeaa87 | 302 | LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n"); |
Grimmkey | 0:b8221deeaa87 | 303 | } |
Grimmkey | 0:b8221deeaa87 | 304 | for(int i = 0; i < characteristicsLength; i++) { |
Grimmkey | 0:b8221deeaa87 | 305 | characteristics[i] = service->getCharacteristic(i); |
Grimmkey | 0:b8221deeaa87 | 306 | } |
Grimmkey | 0:b8221deeaa87 | 307 | services.erase(services.begin() + removeIndex); |
Grimmkey | 0:b8221deeaa87 | 308 | delete service; |
Grimmkey | 0:b8221deeaa87 | 309 | free(previousCharacteristics); |
Grimmkey | 0:b8221deeaa87 | 310 | } else { |
Grimmkey | 0:b8221deeaa87 | 311 | characteristicsLength = 1; |
Grimmkey | 0:b8221deeaa87 | 312 | characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength); |
Grimmkey | 0:b8221deeaa87 | 313 | if(characteristics == NULL) { |
Grimmkey | 0:b8221deeaa87 | 314 | LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n"); |
Grimmkey | 0:b8221deeaa87 | 315 | } |
Grimmkey | 0:b8221deeaa87 | 316 | } |
Grimmkey | 0:b8221deeaa87 | 317 | |
Grimmkey | 0:b8221deeaa87 | 318 | characteristics[characteristicsLength - 1] = characteristic; |
Grimmkey | 0:b8221deeaa87 | 319 | previousCharacteristics = characteristics; |
Grimmkey | 0:b8221deeaa87 | 320 | service = new GattService(serviceUuid, characteristics, characteristicsLength); |
Grimmkey | 0:b8221deeaa87 | 321 | services.push_back(service); |
Grimmkey | 0:b8221deeaa87 | 322 | LOG_DEBUG("Added characteristic.\n"); |
Grimmkey | 0:b8221deeaa87 | 323 | } |
Grimmkey | 0:b8221deeaa87 | 324 | |
Grimmkey | 0:b8221deeaa87 | 325 | |
Grimmkey | 0:b8221deeaa87 | 326 | /** |
Grimmkey | 0:b8221deeaa87 | 327 | * @brief Update the value of the given gatt characteristic. |
Grimmkey | 0:b8221deeaa87 | 328 | * |
Grimmkey | 0:b8221deeaa87 | 329 | * @param uuid |
Grimmkey | 0:b8221deeaa87 | 330 | UUID of the gatt characteristic to be updated. |
Grimmkey | 0:b8221deeaa87 | 331 | * |
Grimmkey | 0:b8221deeaa87 | 332 | * @param value |
Grimmkey | 0:b8221deeaa87 | 333 | * New value of the characteristic. |
Grimmkey | 0:b8221deeaa87 | 334 | * |
Grimmkey | 0:b8221deeaa87 | 335 | * @param length |
Grimmkey | 0:b8221deeaa87 | 336 | * Length in bytes of the characteristic's value. |
Grimmkey | 0:b8221deeaa87 | 337 | * |
Grimmkey | 0:b8221deeaa87 | 338 | * @return Void. |
Grimmkey | 0:b8221deeaa87 | 339 | */ |
Grimmkey | 0:b8221deeaa87 | 340 | void Puck::updateCharacteristicValue(const UUID uuid, uint8_t* value, int length) { |
Grimmkey | 0:b8221deeaa87 | 341 | GattCharacteristic* characteristic = NULL; |
Grimmkey | 0:b8221deeaa87 | 342 | for(int i = 0; i < characteristics.size(); i++) { |
Grimmkey | 0:b8221deeaa87 | 343 | GattAttribute &gattAttribute = characteristics[i]->getValueAttribute(); |
Grimmkey | 0:b8221deeaa87 | 344 | if(isEqualUUID(&gattAttribute.getUUID(), uuid)) { |
Grimmkey | 0:b8221deeaa87 | 345 | characteristic = characteristics[i]; |
Grimmkey | 0:b8221deeaa87 | 346 | break; |
Grimmkey | 0:b8221deeaa87 | 347 | } |
Grimmkey | 0:b8221deeaa87 | 348 | } |
Grimmkey | 0:b8221deeaa87 | 349 | if(characteristic != NULL) { |
Grimmkey | 0:b8221deeaa87 | 350 | ble.updateCharacteristicValue(characteristic->getValueHandle(), value, length); |
Grimmkey | 0:b8221deeaa87 | 351 | LOG_VERBOSE("Updated characteristic value.\n"); |
Grimmkey | 0:b8221deeaa87 | 352 | } else { |
Grimmkey | 0:b8221deeaa87 | 353 | LOG_WARN("Tried to update an unkown characteristic!\n"); |
Grimmkey | 0:b8221deeaa87 | 354 | } |
Grimmkey | 0:b8221deeaa87 | 355 | } |
Grimmkey | 0:b8221deeaa87 | 356 | |
Grimmkey | 0:b8221deeaa87 | 357 | /** |
Grimmkey | 0:b8221deeaa87 | 358 | * @brief Pass control to the bluetooth stack, executing pending callbacks afterwards. Should be used inside a while condition loop. |
Grimmkey | 0:b8221deeaa87 | 359 | * |
Grimmkey | 0:b8221deeaa87 | 360 | * Example: |
Grimmkey | 0:b8221deeaa87 | 361 | * @code |
Grimmkey | 0:b8221deeaa87 | 362 | * while (puck->drive()) { |
Grimmkey | 0:b8221deeaa87 | 363 | * // Do stuff |
Grimmkey | 0:b8221deeaa87 | 364 | * } |
Grimmkey | 0:b8221deeaa87 | 365 | * @endcode |
Grimmkey | 0:b8221deeaa87 | 366 | * |
Grimmkey | 0:b8221deeaa87 | 367 | * @return true. |
Grimmkey | 0:b8221deeaa87 | 368 | * |
Grimmkey | 0:b8221deeaa87 | 369 | */ |
Grimmkey | 0:b8221deeaa87 | 370 | bool Puck::drive() { |
Grimmkey | 0:b8221deeaa87 | 371 | if(state == DISCONNECTED) { |
Grimmkey | 0:b8221deeaa87 | 372 | startAdvertising(); |
Grimmkey | 0:b8221deeaa87 | 373 | } |
Grimmkey | 0:b8221deeaa87 | 374 | |
Grimmkey | 0:b8221deeaa87 | 375 | ble.waitForEvent(); |
Grimmkey | 0:b8221deeaa87 | 376 | |
Grimmkey | 0:b8221deeaa87 | 377 | while(pendingCallbackStack.size() > 0) { |
Grimmkey | 0:b8221deeaa87 | 378 | pendingCallbackStack.back()(pendingCallbackParameterDataStack.back(), pendingCallbackParameterLengthStack.back()); |
Grimmkey | 0:b8221deeaa87 | 379 | pendingCallbackStack.pop_back(); |
Grimmkey | 0:b8221deeaa87 | 380 | pendingCallbackParameterDataStack.pop_back(); |
Grimmkey | 0:b8221deeaa87 | 381 | pendingCallbackParameterLengthStack.pop_back(); |
Grimmkey | 0:b8221deeaa87 | 382 | } |
Grimmkey | 0:b8221deeaa87 | 383 | return true; |
Grimmkey | 0:b8221deeaa87 | 384 | } |
Grimmkey | 0:b8221deeaa87 | 385 | |
Grimmkey | 0:b8221deeaa87 | 386 | /** |
Grimmkey | 0:b8221deeaa87 | 387 | * @brief Register callback to be triggered on characteristic write. |
Grimmkey | 0:b8221deeaa87 | 388 | * |
Grimmkey | 0:b8221deeaa87 | 389 | * @parameter uuid |
Grimmkey | 0:b8221deeaa87 | 390 | * UUID of the gatt characteristic to bind callback to. |
Grimmkey | 0:b8221deeaa87 | 391 | * |
Grimmkey | 0:b8221deeaa87 | 392 | * @parameter callback |
Grimmkey | 0:b8221deeaa87 | 393 | * CharacteristicWriteCallback to be executed on characteristic write.It's signature should be void (*CharacteristicWriteCallback)(const uint8_t* value, uint8_t length); "value" is the value that was written, and "length" is the length of the value that was written. |
Grimmkey | 0:b8221deeaa87 | 394 | * |
Grimmkey | 0:b8221deeaa87 | 395 | * @return Void. |
Grimmkey | 0:b8221deeaa87 | 396 | * |
Grimmkey | 0:b8221deeaa87 | 397 | */ |
Grimmkey | 0:b8221deeaa87 | 398 | void Puck::onCharacteristicWrite(const UUID* uuid, CharacteristicWriteCallback callback) { |
Grimmkey | 0:b8221deeaa87 | 399 | CharacteristicWriteCallbacks* cb = NULL; |
Grimmkey | 0:b8221deeaa87 | 400 | for(int i = 0; i< writeCallbacks.size(); i++) { |
Grimmkey | 0:b8221deeaa87 | 401 | if(isEqualUUID(writeCallbacks[i]->uuid, *uuid)) { |
Grimmkey | 0:b8221deeaa87 | 402 | cb = writeCallbacks[i]; |
Grimmkey | 0:b8221deeaa87 | 403 | break; |
Grimmkey | 0:b8221deeaa87 | 404 | } |
Grimmkey | 0:b8221deeaa87 | 405 | } |
Grimmkey | 0:b8221deeaa87 | 406 | if(cb == NULL) { |
Grimmkey | 0:b8221deeaa87 | 407 | cb = (CharacteristicWriteCallbacks*) malloc(sizeof(CharacteristicWriteCallbacks)); |
Grimmkey | 0:b8221deeaa87 | 408 | if(cb == NULL) { |
Grimmkey | 0:b8221deeaa87 | 409 | LOG_ERROR("Could not malloc CharacteristicWriteCallbacks container. Possibly out of memory!\n"); |
Grimmkey | 0:b8221deeaa87 | 410 | } |
Grimmkey | 0:b8221deeaa87 | 411 | cb->uuid = uuid; |
Grimmkey | 0:b8221deeaa87 | 412 | cb->callbacks = new std::vector<CharacteristicWriteCallback>(); |
Grimmkey | 0:b8221deeaa87 | 413 | writeCallbacks.push_back(cb); |
Grimmkey | 0:b8221deeaa87 | 414 | } |
Grimmkey | 0:b8221deeaa87 | 415 | cb->callbacks->push_back(callback); |
Grimmkey | 0:b8221deeaa87 | 416 | LOG_VERBOSE("Bound characteristic write callback (uuid: %x, callback: %x)\n", uuid, callback); |
Grimmkey | 0:b8221deeaa87 | 417 | } |
Grimmkey | 0:b8221deeaa87 | 418 | |
Grimmkey | 0:b8221deeaa87 | 419 | /** |
Grimmkey | 0:b8221deeaa87 | 420 | * @brief Returns current value of provided gatt characteristic. |
Grimmkey | 0:b8221deeaa87 | 421 | * |
Grimmkey | 0:b8221deeaa87 | 422 | */ |
Grimmkey | 0:b8221deeaa87 | 423 | uint8_t* Puck::getCharacteristicValue(const UUID uuid) { |
Grimmkey | 0:b8221deeaa87 | 424 | LOG_VERBOSE("Reading characteristic value for UUID %x\n", uuid); |
Grimmkey | 0:b8221deeaa87 | 425 | for(int i = 0; i < characteristics.size(); i++) { |
Grimmkey | 0:b8221deeaa87 | 426 | GattAttribute &gattAttribute = characteristics[i]->getValueAttribute(); |
Grimmkey | 0:b8221deeaa87 | 427 | if(isEqualUUID(&gattAttribute.getUUID(), uuid)) { |
Grimmkey | 0:b8221deeaa87 | 428 | return gattAttribute.getValuePtr(); |
Grimmkey | 0:b8221deeaa87 | 429 | } |
Grimmkey | 0:b8221deeaa87 | 430 | } |
Grimmkey | 0:b8221deeaa87 | 431 | LOG_WARN("Tried to read an unknown characteristic!"); |
Grimmkey | 0:b8221deeaa87 | 432 | return NULL; |
Grimmkey | 0:b8221deeaa87 | 433 | } |
Grimmkey | 0:b8221deeaa87 | 434 | |
Grimmkey | 0:b8221deeaa87 | 435 | /** |
Grimmkey | 0:b8221deeaa87 | 436 | * @brief For internal use only. Exposed to hack around mbed framework limitation. |
Grimmkey | 0:b8221deeaa87 | 437 | * |
Grimmkey | 0:b8221deeaa87 | 438 | */ |
Grimmkey | 0:b8221deeaa87 | 439 | void Puck::onDataWritten(GattAttribute::Handle_t handle, const uint8_t* data, uint8_t length) { |
Grimmkey | 0:b8221deeaa87 | 440 | for (int i = 0; i < characteristics.size(); i++) { |
Grimmkey | 0:b8221deeaa87 | 441 | GattCharacteristic* characteristic = characteristics[i]; |
Grimmkey | 0:b8221deeaa87 | 442 | |
Grimmkey | 0:b8221deeaa87 | 443 | if (characteristic->getValueHandle() == handle) { |
Grimmkey | 0:b8221deeaa87 | 444 | GattAttribute &gattAttribute = characteristic->getValueAttribute(); |
Grimmkey | 0:b8221deeaa87 | 445 | |
Grimmkey | 0:b8221deeaa87 | 446 | for(int j = 0; j < writeCallbacks.size(); j++) { |
Grimmkey | 0:b8221deeaa87 | 447 | CharacteristicWriteCallbacks* characteristicWriteCallbacks = writeCallbacks[j]; |
Grimmkey | 0:b8221deeaa87 | 448 | |
Grimmkey | 0:b8221deeaa87 | 449 | if(isEqualUUID(characteristicWriteCallbacks->uuid, gattAttribute.getUUID())) { |
Grimmkey | 0:b8221deeaa87 | 450 | for(int k = 0; k < characteristicWriteCallbacks->callbacks->size(); k++) { |
Grimmkey | 0:b8221deeaa87 | 451 | pendingCallbackStack.push_back(characteristicWriteCallbacks->callbacks->at(k)); |
Grimmkey | 0:b8221deeaa87 | 452 | |
Grimmkey | 0:b8221deeaa87 | 453 | pendingCallbackParameterDataStack.push_back(data); |
Grimmkey | 0:b8221deeaa87 | 454 | pendingCallbackParameterLengthStack.push_back(length); |
Grimmkey | 0:b8221deeaa87 | 455 | } |
Grimmkey | 0:b8221deeaa87 | 456 | return; |
Grimmkey | 0:b8221deeaa87 | 457 | } |
Grimmkey | 0:b8221deeaa87 | 458 | } |
Grimmkey | 0:b8221deeaa87 | 459 | } |
Grimmkey | 0:b8221deeaa87 | 460 | } |
Grimmkey | 0:b8221deeaa87 | 461 | } |
Grimmkey | 0:b8221deeaa87 | 462 | |
Grimmkey | 0:b8221deeaa87 | 463 | |
Grimmkey | 0:b8221deeaa87 | 464 | #endif // __PUCK_HPP__ |