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