An example program to test data transfer throughput. Exhibits long latency (2 sec) between hardware callbacks on write event.
Dependencies: BLE_API mbed nRF51822
Diff: TransferService.cpp
- Revision:
- 0:ab775bf55fe4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TransferService.cpp Thu Aug 14 14:13:53 2014 +0000 @@ -0,0 +1,218 @@ +#include "TransferService.h" +#include "Logger.h" +#include "Configuration.h" + +namespace Transfer +{ + +// Transfer base UUID: ADC710C2-xxxx-4BF5-8244-3CEAAA0F87F5 +#define transfer_UUID(x) { 0xAD, 0xC7, 0x10, 0xC2, (((x) & 0xFF00) >> 8), ((x) & 0xFF), 0x4B, 0xF5, 0x82, 0x44, 0x3C, 0xEA, 0xAA, 0x0F, 0x87, 0xF5 } + +// UUID byte arrays +static const uint8_t transferServiceUUID[] = transfer_UUID(0xACDC); +static const uint8_t transferFileInfoUUID[] = transfer_UUID(0xACDF); +static const uint8_t transferFileBlocksUUID[6][16] = +{ + transfer_UUID(0xACE0), + transfer_UUID(0xACE1), + transfer_UUID(0xACE2), + transfer_UUID(0xACE3), + transfer_UUID(0xACE4), + transfer_UUID(0xACE5), +}; + +// UUID objects used to initialise Bluetooth API +static UUID transferServiceUUID_ = UUID(transferServiceUUID); +static UUID transferFileInfoUUID_ = UUID(transferFileInfoUUID); +static UUID transferFileBlocksUUID_[6] = +{ + UUID(transferFileBlocksUUID[0]), + UUID(transferFileBlocksUUID[1]), + UUID(transferFileBlocksUUID[2]), + UUID(transferFileBlocksUUID[3]), + UUID(transferFileBlocksUUID[4]), + UUID(transferFileBlocksUUID[5]), +}; + +// Storage for the value of the characteristics +struct fileInfo_t { + uint16_t length; + uint16_t crc16; +}; +static struct fileInfo_t fileInfo; +struct fileBlock_t { + uint16_t blockNumber; + uint8_t data[16]; +}; +static struct fileBlock_t fileBlocks[6]; // 6 blocks + +// Other things needed for operation +static BLEDevice* ble; +static Timer downloadTimer; + +static uint16_t expectingBlock = 0; + +static bool acceptFile = true; // additional condition whether to accept a file upload or not, currently always accept +static bool downloadInProgress = false; // indicates if we are downloading a file from the phone + +static GattCharacteristic transferFileInfo(transferFileInfoUUID_, + (uint8_t*) &fileInfo, + sizeof(fileInfo), + sizeof(fileInfo), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + +static uint8_t fileBlockProperties = + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE; + +static GattCharacteristic transferFileBlocks[6] = { + GattCharacteristic(transferFileBlocksUUID_[0], (uint8_t*) &fileBlocks[0], sizeof(fileBlock_t), sizeof(fileBlock_t), fileBlockProperties), + GattCharacteristic(transferFileBlocksUUID_[1], (uint8_t*) &fileBlocks[1], sizeof(fileBlock_t), sizeof(fileBlock_t), fileBlockProperties), + GattCharacteristic(transferFileBlocksUUID_[2], (uint8_t*) &fileBlocks[2], sizeof(fileBlock_t), sizeof(fileBlock_t), fileBlockProperties), + GattCharacteristic(transferFileBlocksUUID_[3], (uint8_t*) &fileBlocks[3], sizeof(fileBlock_t), sizeof(fileBlock_t), fileBlockProperties), + GattCharacteristic(transferFileBlocksUUID_[4], (uint8_t*) &fileBlocks[4], sizeof(fileBlock_t), sizeof(fileBlock_t), fileBlockProperties), + GattCharacteristic(transferFileBlocksUUID_[5], (uint8_t*) &fileBlocks[5], sizeof(fileBlock_t), sizeof(fileBlock_t), fileBlockProperties), + }; + +static GattCharacteristic* allChars[] = +{ + &transferFileInfo, + &transferFileBlocks[0], + &transferFileBlocks[1], + &transferFileBlocks[2], + &transferFileBlocks[3], + &transferFileBlocks[4], + &transferFileBlocks[5], +}; + +static GattService transferService(transferServiceUUID_, allChars, sizeof(allChars) / sizeof(GattCharacteristic*)); + +void init(BLEDevice &bleDevice) +{ + downloadInProgress = false; + ble = &bleDevice; + ble->addService(transferService); + DEBUG("Added transfer service\r\n"); +} + +void reset() +{ + // reset internal state on new connection + downloadInProgress = false; +} + +const uint8_t* getServiceUUIDp() +{ + return transferServiceUUID; +} + +void requestBlock(uint16_t); // prototype declaration +void sendFileInfo(); +void refuseFile(); +void sendFileDownloadedSuccessfully(); + +void handleDataWritten(uint16_t handle) +{ + if (!ble) + return; + + int channel = expectingBlock % 6; + + if (handle == transferFileInfo.getHandle()) { + + uint16_t len = sizeof(fileInfo); + ble->readCharacteristicValue(handle, (uint8_t*) &fileInfo, &len); + + if (fileInfo.length == 0 && fileInfo.crc16 == 0) { + // signal to cancel pending upload + downloadInProgress = false; + downloadTimer.reset(); + expectingBlock = 0; + DEBUG("Download RESET\r\n"); + return; + } + + DEBUG("Offered file len=%d, crc=0x%04x, acceptFile=%d, downloadInProgress=%d\r\n", fileInfo.length, fileInfo.crc16, acceptFile, downloadInProgress); + + // Now we must decide whether to accept it or not + if (acceptFile && !downloadInProgress) { + downloadTimer.reset(); + downloadTimer.start(); + downloadInProgress = true; + requestBlock(0); + } else + refuseFile(); + + } else if (handle == transferFileBlocks[channel].getHandle()) { + + uint16_t len = sizeof(fileBlocks[channel]); + ble->readCharacteristicValue(handle, (uint8_t*) &fileBlocks[channel], &len); + + //DEBUG("blk %d on ch %d (total %d)", fileBlocks[channel].blockNumber, channel, ++blk) + //DEBUG("."); + /* + uint8_t byte; + for (int i = 2; i < len; i++) { + byte = *(((uint8_t*) &fileBlock) + i); + DEBUG("%c", byte, byte); + } + */ + + if (fileBlocks[channel].blockNumber != expectingBlock) { + DEBUG("Channel %d ok but expected blk %d, not %d!\r\n", channel, expectingBlock, fileBlocks[channel].blockNumber); + requestBlock(expectingBlock); + return; + } else { + // DEBUG("."); // one dot = one successfully received packet + } + + if (fileBlocks[channel].blockNumber > (fileInfo.length / Config::blockSize)) { + DEBUG("Error: block %d is out of range\r\n", fileBlocks[channel].blockNumber); + return; + } + + // "processing" step disabled + //uint16_t offset = fileBlock.blockNumber * Config::blockSize; + //memcpy(downloadLocation, fileBlock[0].data, Config::blockSize); + + // request next block if needed + uint16_t nextBlock = fileBlocks[channel].blockNumber + 1; + if (nextBlock <= fileInfo.length / Config::blockSize) + requestBlock(nextBlock); + else { + sendFileDownloadedSuccessfully(); + } + } else { + DEBUG("Got data on ch %d, but expected on ch %d!\r\n", handle - 1, channel); + } +} + +void requestBlock(uint16_t blockNumber) +{ + // Requesting a block by sending notification is disabled for speed + //ble->updateCharacteristicValue(transferFileBlocks[0].getHandle(), (uint8_t*) &blockNumber, sizeof(blockNumber), false); + //DEBUG("BlockReq %d --> PHONE\r\n", blockNumber); + expectingBlock = blockNumber; +} + +void sendFileInfo(uint32_t value) +{ + // refusal is indicated by sending a fileInfo with all zeros + ble->updateCharacteristicValue(transferFileInfo.getHandle(), (uint8_t*) &value, sizeof(value), false); +} + +void refuseFile() +{ + sendFileInfo(0); +} + +void sendFileDownloadedSuccessfully() +{ + sendFileInfo(1); + downloadInProgress = false; + downloadTimer.stop(); + DEBUG("File transfer took %0.1f sec\r\n", downloadTimer.read()); +} + +} // namespace transfer \ No newline at end of file