Download a stream of data to a peripheral over BLE.
Dependencies: BLE_API mbed nRF51822
A simple demonstration of downloading a stream onto a peripheral over BLE. There's a corresponding Python script to driver the client.
TransferService.cpp@1:d623a5792ce5, 2014-08-21 (annotated)
- Committer:
- rgrover1
- Date:
- Thu Aug 21 14:09:18 2014 +0000
- Revision:
- 1:d623a5792ce5
- Parent:
- 0:4eaf82806f06
- Child:
- 2:4ca946e0ebdc
minor cleanup.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rgrover1 | 0:4eaf82806f06 | 1 | #include "TransferService.h" |
rgrover1 | 0:4eaf82806f06 | 2 | #include "Logger.h" |
rgrover1 | 0:4eaf82806f06 | 3 | #include "Configuration.h" |
rgrover1 | 0:4eaf82806f06 | 4 | |
rgrover1 | 0:4eaf82806f06 | 5 | namespace Transfer { |
rgrover1 | 0:4eaf82806f06 | 6 | // Transfer base UUID: ADC710C2-xxxx-4BF5-8244-3CEAAA0F87F5 |
rgrover1 | 0:4eaf82806f06 | 7 | #define transfer_UUID(x) {0xAD, 0xC7, 0x10, 0xC2, (((x) & 0xFF00) >> 8), ((x) & 0xFF), 0x4B, 0xF5, 0x82, 0x44, 0x3C, 0xEA, 0xAA, 0x0F, 0x87, 0xF5} |
rgrover1 | 0:4eaf82806f06 | 8 | |
rgrover1 | 0:4eaf82806f06 | 9 | // UUID byte arrays |
rgrover1 | 0:4eaf82806f06 | 10 | static const uint8_t transferServiceUUID[] = transfer_UUID(0xACDC); |
rgrover1 | 0:4eaf82806f06 | 11 | static const uint8_t transferFileInfoUUID[] = transfer_UUID(0xACDF); |
rgrover1 | 0:4eaf82806f06 | 12 | static const uint8_t transferFileBlockUUID[16] = transfer_UUID(0xACE0); |
rgrover1 | 0:4eaf82806f06 | 13 | |
rgrover1 | 0:4eaf82806f06 | 14 | // Storage for the value of the characteristics |
rgrover1 | 0:4eaf82806f06 | 15 | struct fileInfo_t { |
rgrover1 | 0:4eaf82806f06 | 16 | uint16_t length; |
rgrover1 | 0:4eaf82806f06 | 17 | uint16_t crc16; |
rgrover1 | 0:4eaf82806f06 | 18 | }; |
rgrover1 | 0:4eaf82806f06 | 19 | static struct fileInfo_t fileInfo; |
rgrover1 | 0:4eaf82806f06 | 20 | struct fileBlock_t { |
rgrover1 | 0:4eaf82806f06 | 21 | uint16_t blockNumber; |
rgrover1 | 0:4eaf82806f06 | 22 | uint8_t data[16]; |
rgrover1 | 0:4eaf82806f06 | 23 | }; |
rgrover1 | 0:4eaf82806f06 | 24 | static struct fileBlock_t fileBlock; |
rgrover1 | 0:4eaf82806f06 | 25 | |
rgrover1 | 0:4eaf82806f06 | 26 | // Other things needed for operation |
rgrover1 | 0:4eaf82806f06 | 27 | static BLEDevice* ble; |
rgrover1 | 0:4eaf82806f06 | 28 | static Timer downloadTimer; |
rgrover1 | 0:4eaf82806f06 | 29 | |
rgrover1 | 0:4eaf82806f06 | 30 | static uint16_t expectingBlock = 0; |
rgrover1 | 0:4eaf82806f06 | 31 | |
rgrover1 | 0:4eaf82806f06 | 32 | static bool acceptFile = true; // additional condition whether to accept a file upload or not, currently always accept |
rgrover1 | 0:4eaf82806f06 | 33 | static bool downloadInProgress = false; // indicates if we are downloading a file from the phone |
rgrover1 | 0:4eaf82806f06 | 34 | |
rgrover1 | 0:4eaf82806f06 | 35 | static GattCharacteristic transferFileInfo(transferFileInfoUUID, |
rgrover1 | 0:4eaf82806f06 | 36 | (uint8_t *)&fileInfo, |
rgrover1 | 0:4eaf82806f06 | 37 | sizeof(fileInfo), |
rgrover1 | 0:4eaf82806f06 | 38 | sizeof(fileInfo), |
rgrover1 | 0:4eaf82806f06 | 39 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); |
rgrover1 | 0:4eaf82806f06 | 40 | |
rgrover1 | 0:4eaf82806f06 | 41 | static GattCharacteristic transferFileBlock(transferFileBlockUUID, |
rgrover1 | 0:4eaf82806f06 | 42 | (uint8_t *)&fileBlock, |
rgrover1 | 0:4eaf82806f06 | 43 | sizeof(fileBlock_t), |
rgrover1 | 0:4eaf82806f06 | 44 | sizeof(fileBlock_t), |
rgrover1 | 0:4eaf82806f06 | 45 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | |
rgrover1 | 0:4eaf82806f06 | 46 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); |
rgrover1 | 0:4eaf82806f06 | 47 | |
rgrover1 | 0:4eaf82806f06 | 48 | static GattCharacteristic *allChars[] = {&transferFileInfo, &transferFileBlock}; |
rgrover1 | 0:4eaf82806f06 | 49 | static GattService transferService(transferServiceUUID, allChars, sizeof(allChars) / sizeof(GattCharacteristic *)); |
rgrover1 | 0:4eaf82806f06 | 50 | |
rgrover1 | 0:4eaf82806f06 | 51 | void init(BLEDevice &bleDevice) |
rgrover1 | 0:4eaf82806f06 | 52 | { |
rgrover1 | 0:4eaf82806f06 | 53 | downloadInProgress = false; |
rgrover1 | 0:4eaf82806f06 | 54 | ble = &bleDevice; |
rgrover1 | 0:4eaf82806f06 | 55 | ble->addService(transferService); |
rgrover1 | 0:4eaf82806f06 | 56 | DEBUG("Added transfer service\r\n"); |
rgrover1 | 0:4eaf82806f06 | 57 | } |
rgrover1 | 0:4eaf82806f06 | 58 | |
rgrover1 | 0:4eaf82806f06 | 59 | void reset() |
rgrover1 | 0:4eaf82806f06 | 60 | { |
rgrover1 | 0:4eaf82806f06 | 61 | // reset internal state on new connection |
rgrover1 | 0:4eaf82806f06 | 62 | downloadInProgress = false; |
rgrover1 | 0:4eaf82806f06 | 63 | } |
rgrover1 | 0:4eaf82806f06 | 64 | |
rgrover1 | 0:4eaf82806f06 | 65 | const uint8_t *getServiceUUIDp() |
rgrover1 | 0:4eaf82806f06 | 66 | { |
rgrover1 | 0:4eaf82806f06 | 67 | return transferServiceUUID; |
rgrover1 | 0:4eaf82806f06 | 68 | } |
rgrover1 | 0:4eaf82806f06 | 69 | |
rgrover1 | 0:4eaf82806f06 | 70 | void sendFileInfo(); |
rgrover1 | 0:4eaf82806f06 | 71 | void refuseFile(); |
rgrover1 | 0:4eaf82806f06 | 72 | void sendFileDownloadedSuccessfully(); |
rgrover1 | 0:4eaf82806f06 | 73 | |
rgrover1 | 0:4eaf82806f06 | 74 | void handleDataWritten(uint16_t handle) |
rgrover1 | 0:4eaf82806f06 | 75 | { |
rgrover1 | 0:4eaf82806f06 | 76 | if (!ble) { |
rgrover1 | 0:4eaf82806f06 | 77 | return; |
rgrover1 | 0:4eaf82806f06 | 78 | } |
rgrover1 | 0:4eaf82806f06 | 79 | |
rgrover1 | 0:4eaf82806f06 | 80 | if (handle == transferFileInfo.getHandle()) { |
rgrover1 | 0:4eaf82806f06 | 81 | uint16_t len = sizeof(fileInfo); |
rgrover1 | 0:4eaf82806f06 | 82 | ble->readCharacteristicValue(handle, (uint8_t *) &fileInfo, &len); |
rgrover1 | 0:4eaf82806f06 | 83 | |
rgrover1 | 0:4eaf82806f06 | 84 | if ((fileInfo.length == 0) && (fileInfo.crc16 == 0)) { |
rgrover1 | 0:4eaf82806f06 | 85 | // signal to cancel pending upload |
rgrover1 | 0:4eaf82806f06 | 86 | downloadInProgress = false; |
rgrover1 | 0:4eaf82806f06 | 87 | downloadTimer.reset(); |
rgrover1 | 0:4eaf82806f06 | 88 | expectingBlock = 0; |
rgrover1 | 0:4eaf82806f06 | 89 | DEBUG("Download RESET\r\n"); |
rgrover1 | 0:4eaf82806f06 | 90 | return; |
rgrover1 | 0:4eaf82806f06 | 91 | } |
rgrover1 | 0:4eaf82806f06 | 92 | |
rgrover1 | 1:d623a5792ce5 | 93 | DEBUG("Offered file len=%u, crc=0x%04x, acceptFile=%u, downloadInProgress=%u\r\n", |
rgrover1 | 1:d623a5792ce5 | 94 | fileInfo.length, fileInfo.crc16, acceptFile, downloadInProgress); |
rgrover1 | 0:4eaf82806f06 | 95 | |
rgrover1 | 0:4eaf82806f06 | 96 | // Now we must decide whether to accept it or not |
rgrover1 | 0:4eaf82806f06 | 97 | if (acceptFile && !downloadInProgress) { |
rgrover1 | 0:4eaf82806f06 | 98 | downloadTimer.reset(); |
rgrover1 | 0:4eaf82806f06 | 99 | downloadTimer.start(); |
rgrover1 | 0:4eaf82806f06 | 100 | downloadInProgress = true; |
rgrover1 | 1:d623a5792ce5 | 101 | expectingBlock = 0; |
rgrover1 | 0:4eaf82806f06 | 102 | } else { |
rgrover1 | 0:4eaf82806f06 | 103 | refuseFile(); |
rgrover1 | 0:4eaf82806f06 | 104 | } |
rgrover1 | 0:4eaf82806f06 | 105 | } else if (handle == transferFileBlock.getHandle()) { |
rgrover1 | 0:4eaf82806f06 | 106 | uint16_t len = sizeof(fileBlock); |
rgrover1 | 0:4eaf82806f06 | 107 | ble->readCharacteristicValue(handle, (uint8_t *) &fileBlock, &len); |
rgrover1 | 0:4eaf82806f06 | 108 | |
rgrover1 | 0:4eaf82806f06 | 109 | if (fileBlock.blockNumber != expectingBlock) { |
rgrover1 | 1:d623a5792ce5 | 110 | DEBUG("Expected blk %u, not %u!\r\n", expectingBlock, fileBlock.blockNumber); |
rgrover1 | 1:d623a5792ce5 | 111 | } else if (fileBlock.blockNumber <= (fileInfo.length / Config::blockSize)) { |
rgrover1 | 1:d623a5792ce5 | 112 | expectingBlock = fileBlock.blockNumber + 1; |
rgrover1 | 1:d623a5792ce5 | 113 | if (fileBlock.blockNumber == ((fileInfo.length / Config::blockSize) - 1)) { |
rgrover1 | 1:d623a5792ce5 | 114 | sendFileDownloadedSuccessfully(); |
rgrover1 | 1:d623a5792ce5 | 115 | } |
rgrover1 | 0:4eaf82806f06 | 116 | } else { |
rgrover1 | 1:d623a5792ce5 | 117 | DEBUG("Error: block %u is out of range\r\n", fileBlock.blockNumber); |
rgrover1 | 0:4eaf82806f06 | 118 | } |
rgrover1 | 0:4eaf82806f06 | 119 | } else { |
rgrover1 | 0:4eaf82806f06 | 120 | DEBUG("Got data on unexpected characteristic handle %u!\r\n", handle); |
rgrover1 | 0:4eaf82806f06 | 121 | } |
rgrover1 | 0:4eaf82806f06 | 122 | } |
rgrover1 | 0:4eaf82806f06 | 123 | |
rgrover1 | 0:4eaf82806f06 | 124 | void sendFileInfo(uint32_t value) |
rgrover1 | 0:4eaf82806f06 | 125 | { |
rgrover1 | 0:4eaf82806f06 | 126 | // refusal is indicated by sending a fileInfo with all zeros |
rgrover1 | 0:4eaf82806f06 | 127 | ble->updateCharacteristicValue(transferFileInfo.getHandle(), (uint8_t *) &value, sizeof(value), false); |
rgrover1 | 0:4eaf82806f06 | 128 | } |
rgrover1 | 0:4eaf82806f06 | 129 | |
rgrover1 | 0:4eaf82806f06 | 130 | void refuseFile() |
rgrover1 | 0:4eaf82806f06 | 131 | { |
rgrover1 | 0:4eaf82806f06 | 132 | sendFileInfo(0); |
rgrover1 | 0:4eaf82806f06 | 133 | } |
rgrover1 | 0:4eaf82806f06 | 134 | |
rgrover1 | 0:4eaf82806f06 | 135 | void sendFileDownloadedSuccessfully() |
rgrover1 | 0:4eaf82806f06 | 136 | { |
rgrover1 | 0:4eaf82806f06 | 137 | sendFileInfo(1); |
rgrover1 | 0:4eaf82806f06 | 138 | downloadInProgress = false; |
rgrover1 | 0:4eaf82806f06 | 139 | downloadTimer.stop(); |
rgrover1 | 0:4eaf82806f06 | 140 | DEBUG("File transfer took %0.1f sec\r\n", downloadTimer.read()); |
rgrover1 | 0:4eaf82806f06 | 141 | } |
rgrover1 | 0:4eaf82806f06 | 142 | } // namespace transfer |