An example program to test data transfer throughput. Exhibits long latency (2 sec) between hardware callbacks on write event.

Dependencies:   BLE_API mbed nRF51822

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