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.

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?

UserRevisionLine numberNew 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