A library for easier setup and prototyping of IoT devices (pucks), by collecting everything that is common for all pucks in one place.

Dependencies:   BLE_API nRF51822

Dependents:   ir-puck display-puck ir-puck2 BLE_ScoringDevice ... more

/media/uploads/stiaje/header.jpg

Introduction

Raspberry Pi took the maker community by storm when it launched in 2012. With its internet access it allowed small projects to be internet-of-things enabled. We have created a platform to take this one step further.

Our platform, called the Puck platform, is an internet of things platform for mbed. mbed makes it easy to program embedded hardware for people new to embedded systems. Our platform is built upon the first mbed chip with Bluetooth, the nRF51822 created by Nordic Semiconductor. We hope to create a community around these BLE devices where people contribute to the project, and share their designs with each other. Everything is open-source, of course, with lots of supporting materials.

We make it easy to rapidly prototype and develop Bluetooth LE enabled devices - get up and running in under 10 lines of code.

Tutorials and in-depth documentation is available at the project's GitHub page

Pucks

We've developed a handful of awesome examples to demonstrate the platform. These examples are named 'Pucks'. By talking to the internet through your smartphone, the barrier to creating your own Internet of Things device is lower than ever.

Committer:
sigveseb
Date:
Thu Jul 24 14:27:55 2014 +0000
Revision:
8:49ffd38fb401
Parent:
7:c07c01c2a741
Child:
10:67e4694f2d74
Add support for characteristic value updating from the mbed

Who changed what in which revision?

UserRevisionLine numberNew contents of line
sigveseb 3:5432b38585ea 1 #ifndef __PUCK_HPP__
sigveseb 3:5432b38585ea 2 #define __PUCK_HPP__
sigveseb 3:5432b38585ea 3
sigveseb 3:5432b38585ea 4 #include "BLEDevice.h"
sigveseb 3:5432b38585ea 5 #include <vector>
cristea 7:c07c01c2a741 6 #include "Log.h"
sigveseb 3:5432b38585ea 7
sigveseb 3:5432b38585ea 8 enum PuckState {
sigveseb 3:5432b38585ea 9 CONNECTING,
sigveseb 3:5432b38585ea 10 CONNECTED,
sigveseb 3:5432b38585ea 11 ADVERTISING,
sigveseb 3:5432b38585ea 12 DISCONNECTED
sigveseb 3:5432b38585ea 13 };
sigveseb 3:5432b38585ea 14
sigveseb 3:5432b38585ea 15 const UUID stringToUUID(const char* str);
sigveseb 3:5432b38585ea 16
sigveseb 3:5432b38585ea 17 typedef void (*CharacteristicWriteCallback)(uint8_t* value);
sigveseb 3:5432b38585ea 18
sigveseb 3:5432b38585ea 19 typedef struct {
sigveseb 3:5432b38585ea 20 const UUID* uuid;
sigveseb 3:5432b38585ea 21 std::vector<CharacteristicWriteCallback>* callbacks;
sigveseb 3:5432b38585ea 22 } CharacteristicWriteCallbacks;
sigveseb 3:5432b38585ea 23
sigveseb 3:5432b38585ea 24
sigveseb 3:5432b38585ea 25 class Puck {
sigveseb 3:5432b38585ea 26 private:
sigveseb 3:5432b38585ea 27 Puck() {}
sigveseb 3:5432b38585ea 28 Puck(const Puck&);
sigveseb 3:5432b38585ea 29 Puck& operator=(const Puck&);
sigveseb 3:5432b38585ea 30
sigveseb 3:5432b38585ea 31 BLEDevice ble;
sigveseb 3:5432b38585ea 32 uint8_t beaconPayload[25];
sigveseb 3:5432b38585ea 33 PuckState state;
sigveseb 3:5432b38585ea 34 std::vector<GattService*> services;
sigveseb 3:5432b38585ea 35 std::vector<GattCharacteristic*> characteristics;
sigveseb 3:5432b38585ea 36 std::vector<CharacteristicWriteCallbacks*> writeCallbacks;
sigveseb 3:5432b38585ea 37 std::vector<CharacteristicWriteCallback> pendingCallbackStack;
sigveseb 3:5432b38585ea 38 std::vector<uint8_t*> pendingCallbackParameterStack;
sigveseb 3:5432b38585ea 39
stiaje 4:91506772210d 40 GattCharacteristic **previousCharacteristics;
stiaje 4:91506772210d 41
sigveseb 3:5432b38585ea 42 public:
sigveseb 3:5432b38585ea 43 static Puck &getPuck();
sigveseb 3:5432b38585ea 44
stiaje 5:2f2a2ac6b231 45 BLEDevice &getBle() { return ble; }
sigveseb 3:5432b38585ea 46 PuckState getState() { return state; }
sigveseb 3:5432b38585ea 47 void setState(PuckState state);
sigveseb 3:5432b38585ea 48 void init(uint16_t minor);
sigveseb 3:5432b38585ea 49 void startAdvertising();
sigveseb 3:5432b38585ea 50 void stopAdvertising();
sigveseb 3:5432b38585ea 51 bool drive();
sigveseb 3:5432b38585ea 52 void onDataWritten(uint16_t handle);
sigveseb 3:5432b38585ea 53 void addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties = 0xA);
sigveseb 3:5432b38585ea 54 void onCharacteristicWrite(const UUID uuid, CharacteristicWriteCallback callback);
sigveseb 8:49ffd38fb401 55 void updateCharacteristicValue(const UUID uuid, uint8_t* value, int length);
sigveseb 3:5432b38585ea 56 uint8_t* getCharacteristicValue(const UUID uuid);
sigveseb 3:5432b38585ea 57 };
sigveseb 3:5432b38585ea 58
sigveseb 3:5432b38585ea 59 Puck &Puck::getPuck() {
sigveseb 3:5432b38585ea 60 static Puck _puckSingletonInstance;
sigveseb 3:5432b38585ea 61 return _puckSingletonInstance;
sigveseb 3:5432b38585ea 62 }
sigveseb 3:5432b38585ea 63
sigveseb 3:5432b38585ea 64
sigveseb 3:5432b38585ea 65 void onDisconnection(void) {
sigveseb 3:5432b38585ea 66 LOG_INFO("Disconnected.\n");
sigveseb 3:5432b38585ea 67 Puck::getPuck().setState(DISCONNECTED);
sigveseb 3:5432b38585ea 68 }
sigveseb 3:5432b38585ea 69
sigveseb 3:5432b38585ea 70 void onConnection(void) {
sigveseb 3:5432b38585ea 71 LOG_INFO("Connected.\n");
sigveseb 3:5432b38585ea 72 Puck::getPuck().setState(CONNECTED);
sigveseb 3:5432b38585ea 73 }
sigveseb 3:5432b38585ea 74
sigveseb 3:5432b38585ea 75 void onDataWrittenCallback(uint16_t handle) {
sigveseb 3:5432b38585ea 76 Puck::getPuck().onDataWritten(handle);
sigveseb 3:5432b38585ea 77 }
sigveseb 3:5432b38585ea 78
sigveseb 3:5432b38585ea 79 bool isEqualUUID(const UUID* uuidA, const UUID uuidB) {
sigveseb 3:5432b38585ea 80 const uint8_t* uuidABase = uuidA->getBaseUUID();
sigveseb 3:5432b38585ea 81 const uint8_t* uuidBBase = uuidB.getBaseUUID();
sigveseb 3:5432b38585ea 82 if(uuidA->getShortUUID() != uuidB.getShortUUID()) {
sigveseb 3:5432b38585ea 83 return false;
sigveseb 3:5432b38585ea 84 }
sigveseb 3:5432b38585ea 85 for(int i = 0; i < 16; i++) {
sigveseb 3:5432b38585ea 86 if(uuidABase[i] != uuidBBase[i]) {
sigveseb 3:5432b38585ea 87 return false;
sigveseb 3:5432b38585ea 88 }
sigveseb 3:5432b38585ea 89 }
sigveseb 3:5432b38585ea 90 return true;
sigveseb 3:5432b38585ea 91 }
sigveseb 3:5432b38585ea 92
sigveseb 3:5432b38585ea 93 const UUID stringToUUID(const char* str) {
sigveseb 3:5432b38585ea 94 uint8_t array[16];
sigveseb 3:5432b38585ea 95 for(int i = 0; i < 16; i++) {
sigveseb 3:5432b38585ea 96 array[i] = str[i];
sigveseb 3:5432b38585ea 97 }
sigveseb 3:5432b38585ea 98 return UUID(array);
sigveseb 3:5432b38585ea 99 }
sigveseb 3:5432b38585ea 100
sigveseb 3:5432b38585ea 101 void Puck::setState(PuckState state) {
sigveseb 3:5432b38585ea 102 LOG_DEBUG("Changed state to %i\n", state);
sigveseb 3:5432b38585ea 103 this->state = state;
sigveseb 3:5432b38585ea 104 }
sigveseb 3:5432b38585ea 105
sigveseb 3:5432b38585ea 106 void Puck::init(uint16_t minor) {
sigveseb 3:5432b38585ea 107 /*
sigveseb 3:5432b38585ea 108 * The Beacon payload (encapsulated within the MSD advertising data structure)
sigveseb 3:5432b38585ea 109 * has the following composition:
sigveseb 3:5432b38585ea 110 * 128-Bit UUID = E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61
sigveseb 3:5432b38585ea 111 * Major/Minor = 1337 / XXXX
sigveseb 3:5432b38585ea 112 * Tx Power = C8
sigveseb 3:5432b38585ea 113 */
sigveseb 3:5432b38585ea 114 uint8_t beaconPayloadTemplate[] = {
sigveseb 3:5432b38585ea 115 0x00, 0x00, // Company identifier code (0x004C == Apple)
sigveseb 3:5432b38585ea 116 0x02, // ID
sigveseb 3:5432b38585ea 117 0x15, // length of the remaining payload
sigveseb 3:5432b38585ea 118 0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, // UUID
sigveseb 3:5432b38585ea 119 0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61,
sigveseb 3:5432b38585ea 120 0x13, 0x37, // the major value to differenciate a location (Our app requires 1337 as major number)
sigveseb 3:5432b38585ea 121 0x00, 0x00, // the minor value to differenciate a location (Change this to differentiate location pucks)
sigveseb 3:5432b38585ea 122 0xC8 // 2's complement of the Tx power (-56dB)
sigveseb 3:5432b38585ea 123 };
sigveseb 3:5432b38585ea 124 beaconPayloadTemplate[22] = minor >> 8;
sigveseb 3:5432b38585ea 125 beaconPayloadTemplate[23] = minor & 255;
sigveseb 3:5432b38585ea 126
sigveseb 3:5432b38585ea 127 for (int i=0; i < 25; i++) {
sigveseb 3:5432b38585ea 128 beaconPayload[i] = beaconPayloadTemplate[i];
sigveseb 3:5432b38585ea 129 }
sigveseb 3:5432b38585ea 130
sigveseb 3:5432b38585ea 131 ble.init();
sigveseb 3:5432b38585ea 132 LOG_VERBOSE("Inited BLEDevice.\n");
sigveseb 3:5432b38585ea 133 setState(DISCONNECTED);
sigveseb 3:5432b38585ea 134
sigveseb 3:5432b38585ea 135 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
sigveseb 3:5432b38585ea 136 LOG_VERBOSE("Accumulate advertising payload: BREDR_NOT_SUPPORTED.\n");
sigveseb 3:5432b38585ea 137
sigveseb 3:5432b38585ea 138 ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, beaconPayload, sizeof(beaconPayload));
sigveseb 3:5432b38585ea 139 LOG_VERBOSE("Accumulate advertising payload: beacon data.\n");
sigveseb 3:5432b38585ea 140
sigveseb 3:5432b38585ea 141 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
sigveseb 3:5432b38585ea 142 LOG_VERBOSE("Setting advertising type: ADV_CONNECTABLE_UNDIRECTED.\n");
sigveseb 3:5432b38585ea 143
sigveseb 3:5432b38585ea 144 int hundredMillisecondsInAdvertisingIntervalFormat = 160;
sigveseb 3:5432b38585ea 145 ble.setAdvertisingInterval(hundredMillisecondsInAdvertisingIntervalFormat);
sigveseb 3:5432b38585ea 146 LOG_VERBOSE("Set advertising interval: 160 (100 ms).\n");
sigveseb 3:5432b38585ea 147
sigveseb 3:5432b38585ea 148 ble.onDisconnection(onDisconnection);
sigveseb 3:5432b38585ea 149 ble.onConnection(onConnection);
sigveseb 3:5432b38585ea 150 ble.onDataWritten(onDataWrittenCallback);
sigveseb 3:5432b38585ea 151
sigveseb 3:5432b38585ea 152 for(int i = 0; i < services.size(); i++) {
sigveseb 3:5432b38585ea 153 ble.addService(*services[i]);
sigveseb 3:5432b38585ea 154 LOG_VERBOSE("Added service %x to BLEDevice\n", services[i]);
sigveseb 3:5432b38585ea 155 }
sigveseb 3:5432b38585ea 156
sigveseb 3:5432b38585ea 157 LOG_INFO("Inited puck as 0x%X.\n", minor);
sigveseb 3:5432b38585ea 158 }
sigveseb 3:5432b38585ea 159
sigveseb 3:5432b38585ea 160 void Puck::startAdvertising() {
sigveseb 3:5432b38585ea 161 ble.startAdvertising();
sigveseb 3:5432b38585ea 162 LOG_INFO("Starting to advertise.\n");
sigveseb 3:5432b38585ea 163 setState(ADVERTISING);
sigveseb 3:5432b38585ea 164 }
sigveseb 3:5432b38585ea 165
sigveseb 3:5432b38585ea 166 void Puck::stopAdvertising() {
sigveseb 3:5432b38585ea 167 if(state == ADVERTISING) {
sigveseb 3:5432b38585ea 168 ble.stopAdvertising();
sigveseb 3:5432b38585ea 169 LOG_INFO("Stopped advertising.\n");
sigveseb 3:5432b38585ea 170 setState(DISCONNECTED);
sigveseb 3:5432b38585ea 171 } else {
sigveseb 3:5432b38585ea 172 LOG_WARN("Tried to stop advertising, but advertising is already stopped!\n");
sigveseb 3:5432b38585ea 173 }
sigveseb 3:5432b38585ea 174 }
sigveseb 3:5432b38585ea 175
sigveseb 3:5432b38585ea 176
sigveseb 3:5432b38585ea 177 void Puck::addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties) {
sigveseb 3:5432b38585ea 178 MBED_ASSERT(bytes <= 20);
sigveseb 3:5432b38585ea 179
sigveseb 3:5432b38585ea 180 uint16_t size = sizeof(uint8_t) * bytes;
sigveseb 3:5432b38585ea 181 uint8_t* value = (uint8_t*) malloc(size);
sigveseb 3:5432b38585ea 182 GattCharacteristic* characteristic = new GattCharacteristic(characteristicUuid, value, size, size, properties);
sigveseb 3:5432b38585ea 183 characteristics.push_back(characteristic);
sigveseb 3:5432b38585ea 184 GattService* service = NULL;
sigveseb 3:5432b38585ea 185 int removeIndex = -1;
sigveseb 3:5432b38585ea 186 for(int i = 0; i < services.size(); i++) {
sigveseb 3:5432b38585ea 187 if(isEqualUUID(&services[i]->getUUID(), serviceUuid)) {
sigveseb 3:5432b38585ea 188 service = services[i];
sigveseb 3:5432b38585ea 189 removeIndex = i;
sigveseb 3:5432b38585ea 190 break;
sigveseb 3:5432b38585ea 191 }
sigveseb 3:5432b38585ea 192 }
sigveseb 3:5432b38585ea 193 GattCharacteristic** characteristics = NULL;
sigveseb 3:5432b38585ea 194 int characteristicsLength = 0;
sigveseb 3:5432b38585ea 195 if(service != NULL) {
sigveseb 3:5432b38585ea 196 characteristicsLength = service->getCharacteristicCount() + 1;
sigveseb 3:5432b38585ea 197 characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
sigveseb 3:5432b38585ea 198 for(int i = 0; i < characteristicsLength; i++) {
sigveseb 3:5432b38585ea 199 characteristics[i] = service->getCharacteristic(i);
sigveseb 3:5432b38585ea 200 }
sigveseb 3:5432b38585ea 201 services.erase(services.begin() + removeIndex);
sigveseb 3:5432b38585ea 202 delete service;
stiaje 4:91506772210d 203 free(previousCharacteristics);
sigveseb 3:5432b38585ea 204 } else {
sigveseb 3:5432b38585ea 205 characteristicsLength = 1;
sigveseb 3:5432b38585ea 206 characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength);
sigveseb 3:5432b38585ea 207 }
sigveseb 3:5432b38585ea 208 characteristics[characteristicsLength - 1] = characteristic;
stiaje 4:91506772210d 209 previousCharacteristics = characteristics;
sigveseb 3:5432b38585ea 210 service = new GattService(serviceUuid, characteristics, characteristicsLength);
sigveseb 3:5432b38585ea 211 services.push_back(service);
sigveseb 3:5432b38585ea 212 }
sigveseb 3:5432b38585ea 213
sigveseb 8:49ffd38fb401 214
sigveseb 8:49ffd38fb401 215 void Puck::updateCharacteristicValue(const UUID uuid, uint8_t* value, int length) {
sigveseb 8:49ffd38fb401 216 GattCharacteristic* characteristic = NULL;
sigveseb 8:49ffd38fb401 217 for( int i = 0; i < characteristics.size(); i++) {
sigveseb 8:49ffd38fb401 218 if(isEqualUUID(&characteristics[i]->getUUID(), uuid)) {
sigveseb 8:49ffd38fb401 219 characteristic = characteristics[i];
sigveseb 8:49ffd38fb401 220 break;
sigveseb 8:49ffd38fb401 221 }
sigveseb 8:49ffd38fb401 222 }
sigveseb 8:49ffd38fb401 223 if(characteristic != NULL) {
sigveseb 8:49ffd38fb401 224 ble.updateCharacteristicValue(characteristic->getHandle(), value, length);
sigveseb 8:49ffd38fb401 225 LOG_VERBOSE("Updated characteristic value.\n");
sigveseb 8:49ffd38fb401 226 } else {
sigveseb 8:49ffd38fb401 227 LOG_WARN("Tried to update an unkown characteristic!\n");
sigveseb 8:49ffd38fb401 228 }
sigveseb 8:49ffd38fb401 229 }
sigveseb 8:49ffd38fb401 230
sigveseb 3:5432b38585ea 231 bool Puck::drive() {
sigveseb 3:5432b38585ea 232 ble.waitForEvent();
sigveseb 3:5432b38585ea 233 if(state == DISCONNECTED) {
sigveseb 3:5432b38585ea 234 startAdvertising();
sigveseb 3:5432b38585ea 235 }
sigveseb 3:5432b38585ea 236 while(pendingCallbackStack.size() > 0) {
sigveseb 3:5432b38585ea 237 LOG_VERBOSE("PendingCallbackStack size: %i\n", pendingCallbackStack.size());
sigveseb 3:5432b38585ea 238 pendingCallbackStack.back()(pendingCallbackParameterStack.back());
sigveseb 3:5432b38585ea 239 pendingCallbackStack.pop_back();
sigveseb 3:5432b38585ea 240 pendingCallbackParameterStack.pop_back();
sigveseb 3:5432b38585ea 241 LOG_VERBOSE("Callback fired\n");
sigveseb 3:5432b38585ea 242 }
sigveseb 3:5432b38585ea 243 return true;
sigveseb 3:5432b38585ea 244 }
sigveseb 3:5432b38585ea 245
sigveseb 3:5432b38585ea 246
sigveseb 3:5432b38585ea 247 void Puck::onCharacteristicWrite(const UUID uuid, CharacteristicWriteCallback callback) {
sigveseb 3:5432b38585ea 248 CharacteristicWriteCallbacks* cb = NULL;
sigveseb 3:5432b38585ea 249 for(int i = 0; i< writeCallbacks.size(); i++) {
sigveseb 3:5432b38585ea 250 if(isEqualUUID(writeCallbacks[i]->uuid, uuid)) {
sigveseb 3:5432b38585ea 251 cb = writeCallbacks[i];
sigveseb 3:5432b38585ea 252 break;
sigveseb 3:5432b38585ea 253 }
sigveseb 3:5432b38585ea 254 }
sigveseb 3:5432b38585ea 255 if(cb == NULL) {
sigveseb 3:5432b38585ea 256 cb = (CharacteristicWriteCallbacks*) malloc(sizeof(CharacteristicWriteCallbacks));
sigveseb 3:5432b38585ea 257 cb->uuid = &uuid;
sigveseb 3:5432b38585ea 258 cb->callbacks = new std::vector<CharacteristicWriteCallback>();
sigveseb 3:5432b38585ea 259 writeCallbacks.push_back(cb);
sigveseb 3:5432b38585ea 260 }
sigveseb 3:5432b38585ea 261 cb->callbacks->push_back(callback);
sigveseb 3:5432b38585ea 262 LOG_VERBOSE("Bound characteristic write callback (uuid: %x, callback: %x)\n", uuid, callback);
sigveseb 3:5432b38585ea 263 }
sigveseb 3:5432b38585ea 264
sigveseb 3:5432b38585ea 265
sigveseb 3:5432b38585ea 266 uint8_t* Puck::getCharacteristicValue(const UUID uuid) {
sigveseb 3:5432b38585ea 267 LOG_VERBOSE("Reading characteristic value for UUID %x\n", uuid);
sigveseb 3:5432b38585ea 268 for(int i = 0; i < characteristics.size(); i++) {
sigveseb 3:5432b38585ea 269 GattCharacteristic* characteristic = characteristics[i];
sigveseb 3:5432b38585ea 270 if(isEqualUUID(&characteristic->getUUID(), uuid)) {
sigveseb 3:5432b38585ea 271 return characteristic->getValuePtr();
sigveseb 3:5432b38585ea 272 }
sigveseb 3:5432b38585ea 273 }
sigveseb 3:5432b38585ea 274 LOG_WARN("Tried to read an unknown characteristic!");
sigveseb 3:5432b38585ea 275 return NULL;
sigveseb 3:5432b38585ea 276 }
sigveseb 3:5432b38585ea 277
sigveseb 3:5432b38585ea 278
sigveseb 3:5432b38585ea 279 void Puck::onDataWritten(uint16_t handle) {
sigveseb 3:5432b38585ea 280 for (int i = 0; i < characteristics.size(); i++) {
sigveseb 3:5432b38585ea 281 GattCharacteristic* characteristic = characteristics[i];
sigveseb 3:5432b38585ea 282 if (characteristic->getHandle() == handle) {
sigveseb 3:5432b38585ea 283 uint16_t maxLength = characteristic->getMaxLength();
sigveseb 3:5432b38585ea 284 ble.readCharacteristicValue(handle, characteristic->getValuePtr(), &maxLength);
sigveseb 3:5432b38585ea 285 for(int j = 0; j < writeCallbacks.size(); j++) {
sigveseb 3:5432b38585ea 286 CharacteristicWriteCallbacks* characteristicWriteCallbacks = writeCallbacks[j];
sigveseb 3:5432b38585ea 287 if(isEqualUUID(characteristicWriteCallbacks->uuid, characteristic->getUUID())) {
sigveseb 3:5432b38585ea 288 for(int k = 0; k < characteristicWriteCallbacks->callbacks->size(); k++) {
sigveseb 3:5432b38585ea 289 pendingCallbackStack.push_back(characteristicWriteCallbacks->callbacks->at(k));
sigveseb 3:5432b38585ea 290 pendingCallbackParameterStack.push_back(characteristic->getValuePtr());
sigveseb 3:5432b38585ea 291 }
sigveseb 3:5432b38585ea 292 }
sigveseb 3:5432b38585ea 293 return;
sigveseb 3:5432b38585ea 294 }
sigveseb 3:5432b38585ea 295 }
sigveseb 3:5432b38585ea 296 }
sigveseb 3:5432b38585ea 297 }
sigveseb 3:5432b38585ea 298
sigveseb 3:5432b38585ea 299
sigveseb 3:5432b38585ea 300
sigveseb 3:5432b38585ea 301 #endif // __PUCK_HPP__