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.
sendStream.py@5:90b73268e270, 2014-12-09 (annotated)
- Committer:
- rgrover1
- Date:
- Tue Dec 09 09:05:43 2014 +0000
- Revision:
- 5:90b73268e270
- Parent:
- 0:4eaf82806f06
fix build issues; and update underlying libraries.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rgrover1 | 0:4eaf82806f06 | 1 | #!/usr/bin/python |
rgrover1 | 0:4eaf82806f06 | 2 | # |
rgrover1 | 0:4eaf82806f06 | 3 | # This is a very simple test driver for demonstrating the StreamDownloader. |
rgrover1 | 0:4eaf82806f06 | 4 | |
rgrover1 | 0:4eaf82806f06 | 5 | import pexpect |
rgrover1 | 0:4eaf82806f06 | 6 | import sys |
rgrover1 | 0:4eaf82806f06 | 7 | import time |
rgrover1 | 0:4eaf82806f06 | 8 | import select |
rgrover1 | 0:4eaf82806f06 | 9 | import re |
rgrover1 | 0:4eaf82806f06 | 10 | import struct |
rgrover1 | 0:4eaf82806f06 | 11 | |
rgrover1 | 0:4eaf82806f06 | 12 | class StreamDownloaderClient(object): |
rgrover1 | 0:4eaf82806f06 | 13 | """docstring for StreamDownloaderClient""" |
rgrover1 | 0:4eaf82806f06 | 14 | def __init__(self, bluetoothAddr, length): |
rgrover1 | 0:4eaf82806f06 | 15 | super(StreamDownloaderClient, self).__init__() |
rgrover1 | 0:4eaf82806f06 | 16 | self.bluetoothAddr = bluetoothAddr |
rgrover1 | 0:4eaf82806f06 | 17 | |
rgrover1 | 0:4eaf82806f06 | 18 | self.con = pexpect.spawn('gatttool -b ' + bluetoothAddr + ' --interactive -t random') |
rgrover1 | 0:4eaf82806f06 | 19 | self.con.expect('\[LE\]>', timeout=600) |
rgrover1 | 0:4eaf82806f06 | 20 | |
rgrover1 | 0:4eaf82806f06 | 21 | self.con.sendline('connect') |
rgrover1 | 0:4eaf82806f06 | 22 | self.con.expect('\[CON\]\[.+\]\[LE\]>') |
rgrover1 | 0:4eaf82806f06 | 23 | print('connected') |
rgrover1 | 0:4eaf82806f06 | 24 | |
rgrover1 | 0:4eaf82806f06 | 25 | # ensure that we've got the correct primary service available |
rgrover1 | 0:4eaf82806f06 | 26 | self.con.sendline('primary') |
rgrover1 | 0:4eaf82806f06 | 27 | self.con.expect('\[CON\]\[.+\]\[LE\]>') |
rgrover1 | 0:4eaf82806f06 | 28 | self.con.expect('\[CON\]\[.+\]\[LE\]>') |
rgrover1 | 0:4eaf82806f06 | 29 | |
rgrover1 | 0:4eaf82806f06 | 30 | emptyString = re.compile('^\s*$') |
rgrover1 | 0:4eaf82806f06 | 31 | for line in self.con.before.decode('utf-8').split('\r\n'): |
rgrover1 | 0:4eaf82806f06 | 32 | m = re.match('^attr handle: (0x[\dabcdef]{4}).*adc710c2-acdc-4bf5-8244-3ceaaa0f87f5.*', line) |
rgrover1 | 0:4eaf82806f06 | 33 | if m: |
rgrover1 | 0:4eaf82806f06 | 34 | break |
rgrover1 | 0:4eaf82806f06 | 35 | if not m: |
rgrover1 | 0:4eaf82806f06 | 36 | self.disconnect() |
rgrover1 | 0:4eaf82806f06 | 37 | |
rgrover1 | 0:4eaf82806f06 | 38 | self.serviceHandle = m.group(1) |
rgrover1 | 0:4eaf82806f06 | 39 | print('discovered service handle as ' + self.serviceHandle) |
rgrover1 | 0:4eaf82806f06 | 40 | |
rgrover1 | 0:4eaf82806f06 | 41 | self.discoverCharHandles() |
rgrover1 | 0:4eaf82806f06 | 42 | self.sendFileInfoBlock(length) |
rgrover1 | 0:4eaf82806f06 | 43 | for blocknum in range(64): |
rgrover1 | 0:4eaf82806f06 | 44 | self.sendFileDataBlock(blocknum) |
rgrover1 | 0:4eaf82806f06 | 45 | |
rgrover1 | 0:4eaf82806f06 | 46 | self.disconnect() |
rgrover1 | 0:4eaf82806f06 | 47 | |
rgrover1 | 0:4eaf82806f06 | 48 | def discoverCharHandles(self): |
rgrover1 | 0:4eaf82806f06 | 49 | self.con.sendline('characteristics ' + self.serviceHandle) |
rgrover1 | 0:4eaf82806f06 | 50 | self.con.expect('\[CON\]\[.+\]\[LE\]>') |
rgrover1 | 0:4eaf82806f06 | 51 | self.con.expect('\[CON\]\[.+\]\[LE\]>') |
rgrover1 | 0:4eaf82806f06 | 52 | for line in self.con.before.decode('utf-8').split('\r\n'): |
rgrover1 | 0:4eaf82806f06 | 53 | uuidRe = re.compile('.* char value handle: (0x[\dabcdef]{4}).*uuid: adc710c2\-(\w{4}).*') |
rgrover1 | 0:4eaf82806f06 | 54 | m = uuidRe.match(line) |
rgrover1 | 0:4eaf82806f06 | 55 | if m: |
rgrover1 | 0:4eaf82806f06 | 56 | if m.group(2) == 'acdf': |
rgrover1 | 0:4eaf82806f06 | 57 | self.transferFileInfoHandle = m.group(1) |
rgrover1 | 0:4eaf82806f06 | 58 | print("transferFileInfoHandle: " + self.transferFileInfoHandle) |
rgrover1 | 0:4eaf82806f06 | 59 | elif m.group(2) == 'ace0': |
rgrover1 | 0:4eaf82806f06 | 60 | self.transferFileBlockHandle = m.group(1) |
rgrover1 | 0:4eaf82806f06 | 61 | print("transferFileBlockHandle: " + self.transferFileBlockHandle) |
rgrover1 | 0:4eaf82806f06 | 62 | |
rgrover1 | 0:4eaf82806f06 | 63 | def sendFileInfoBlock(self, length): |
rgrover1 | 0:4eaf82806f06 | 64 | print("setup fileInfoBlock for transferring {} bytes".format(length)) |
rgrover1 | 0:4eaf82806f06 | 65 | |
rgrover1 | 0:4eaf82806f06 | 66 | writeCommand = 'char-write-req ' + self.transferFileInfoHandle + ' ' |
rgrover1 | 0:4eaf82806f06 | 67 | for c in struct.pack('<HH', length, 0): |
rgrover1 | 0:4eaf82806f06 | 68 | writeCommand = writeCommand + '{:02x}'.format(c) |
rgrover1 | 0:4eaf82806f06 | 69 | self.con.sendline(writeCommand) |
rgrover1 | 0:4eaf82806f06 | 70 | self.con.expect('\[CON\]\[.+\]\[LE\]> Characteristic value was written successfully', timeout=200) |
rgrover1 | 0:4eaf82806f06 | 71 | |
rgrover1 | 0:4eaf82806f06 | 72 | def sendFileDataBlock(self, blocknum): |
rgrover1 | 0:4eaf82806f06 | 73 | print("will send command for block {}".format(blocknum)) |
rgrover1 | 0:4eaf82806f06 | 74 | |
rgrover1 | 0:4eaf82806f06 | 75 | writeCommand = 'char-write-req ' + self.transferFileBlockHandle + ' ' |
rgrover1 | 0:4eaf82806f06 | 76 | for c in struct.pack('<HBBBBBBBBBBBBBBBB', blocknum, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15): |
rgrover1 | 0:4eaf82806f06 | 77 | writeCommand = writeCommand + '{:02x}'.format(c) |
rgrover1 | 0:4eaf82806f06 | 78 | self.con.sendline(writeCommand) |
rgrover1 | 0:4eaf82806f06 | 79 | self.con.expect('\[CON\]\[.+\]\[LE\]> Characteristic value was written successfully', timeout=200) |
rgrover1 | 0:4eaf82806f06 | 80 | |
rgrover1 | 0:4eaf82806f06 | 81 | def disconnect(self): |
rgrover1 | 0:4eaf82806f06 | 82 | self.con.sendline('disconnect') |
rgrover1 | 0:4eaf82806f06 | 83 | self.con.expect('\[\s+\]\[.+\]\[LE\]>') |
rgrover1 | 0:4eaf82806f06 | 84 | |
rgrover1 | 0:4eaf82806f06 | 85 | def main(): |
rgrover1 | 0:4eaf82806f06 | 86 | bluetoothAddr = "CC:59:FD:D8:3B:A9" # update as necessary |
rgrover1 | 0:4eaf82806f06 | 87 | |
rgrover1 | 0:4eaf82806f06 | 88 | target = StreamDownloaderClient(bluetoothAddr, 1024) |
rgrover1 | 0:4eaf82806f06 | 89 | |
rgrover1 | 0:4eaf82806f06 | 90 | if __name__ == "__main__": |
rgrover1 | 0:4eaf82806f06 | 91 | main() |