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

Committer:
rgrover1
Date:
2014-12-09
Revision:
5:90b73268e270
Parent:
4:29ae814ca55e

File content as of revision 5:90b73268e270:

#include "mbed.h"

#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 transferFileBlockUUID[16] = transfer_UUID(0xACE0);

// Storage for the value of the characteristics
typedef struct {
    uint16_t length;
    uint16_t crc16;
} FileInfo_t;
static FileInfo_t fileInfo;
typedef struct {
    uint16_t blockNumber;
    uint8_t data[16];
} FileBlock_t;
static FileBlock_t fileBlock;

// 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 GattCharacteristic transferFileBlock(transferFileBlockUUID,
                                            (uint8_t *)&fileBlock,
                                            sizeof(FileBlock_t),
                                            sizeof(FileBlock_t),
                                            GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE |
                                            GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);

static GattCharacteristic *allChars[] = {&transferFileInfo, &transferFileBlock};
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 sendFileInfo();
void refuseFile();
void sendFileDownloadedSuccessfully();

void handleDataWritten(const GattCharacteristicWriteCBParams *params)
{
    if (!ble) {
        return;
    }

    if (params->charHandle == transferFileInfo.getValueAttribute().getHandle()) {
        if (params->len != sizeof(FileInfo_t)) {
            DEBUG("invalid write into fileInfo characteristic\r\n");
            return;
        }
        memcpy(&fileInfo, params->data, params->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=%u, crc=0x%04x, acceptFile=%u, downloadInProgress=%u\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;
            expectingBlock     = 0;
        } else {
            refuseFile();
        }
    } else if (params->charHandle == transferFileBlock.getValueAttribute().getHandle()) {
        if (params->len != sizeof(FileBlock_t)) {
            DEBUG("invalid write into fileInfo characteristic\r\n");
            return;
        }
        memcpy(&fileBlock, params->data, params->len);

        if (fileBlock.blockNumber != expectingBlock) {
            DEBUG("Expected blk %u, not %u!\r\n", expectingBlock, fileBlock.blockNumber);
        } else if (fileBlock.blockNumber <= (fileInfo.length / Config::blockSize)) {
            expectingBlock = fileBlock.blockNumber + 1;
            if (fileBlock.blockNumber == ((fileInfo.length / Config::blockSize) - 1)) {
                sendFileDownloadedSuccessfully();
            }
        } else {
            DEBUG("Error: block %u is out of range\r\n", fileBlock.blockNumber);
        }
    } else {
        DEBUG("Got data on unexpected characteristic handle %u!\r\n", params->charHandle);
    }
}

void sendFileInfo(uint32_t value)
{
    // refusal is indicated by sending a fileInfo with all zeros
    ble->updateCharacteristicValue(transferFileInfo.getValueAttribute().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