# MIT license; Copyright (c) 2021 Jim Mussared # This is a BLE file server, based very loosely on the Object Transfer Service # specification. It demonstrated transfering data over an L2CAP channel, as # well as using notifications and GATT writes on a characteristic. # The server supports downloading and uploading files, as well as querying # directory listings and file sizes. # In order to access the file server, a client must connect, then establish an # L2CAP channel. To being an operation, a command is written to the control # characteristic, including a command number, sequence number, and filesystem # path. The response will be either via a notification on the control # characteristic (e.g. file size), or via the L2CAP channel (file contents or # directory listing). import sys sys.path.append("") from micropython import const import uasyncio as asyncio import aioble import bluetooth import struct import os # Randomly generated UUIDs. _FILE_SERVICE_UUID = bluetooth.UUID("0492fcec-7194-11eb-9439-0242ac130002") _CONTROL_CHARACTERISTIC_UUID = bluetooth.UUID("0492fcec-7194-11eb-9439-0242ac130003") # How frequently to send advertising beacons. _ADV_INTERVAL_MS = 250_000 _COMMAND_SEND = const(0) _COMMAND_RECV = const(1) # Not yet implemented. _COMMAND_LIST = const(2) _COMMAND_SIZE = const(3) _COMMAND_DONE = const(4) _STATUS_OK = const(0) _STATUS_NOT_IMPLEMENTED = const(1) _STATUS_NOT_FOUND = const(2) _L2CAP_PSN = const(22) _L2CAP_MTU = const(128) # Register GATT server. file_service = aioble.Service(_FILE_SERVICE_UUID) control_characteristic = aioble.Characteristic( file_service, _CONTROL_CHARACTERISTIC_UUID, write=True, notify=True ) aioble.register_services(file_service) send_file = None recv_file = None list_path = None op_seq = None l2cap_event = asyncio.Event() def send_done_notification(connection, status=_STATUS_OK): global op_seq control_characteristic.notify(connection, struct.pack(". command = msg[0] seq = msg[1] file = msg[2:].decode() if command == _COMMAND_SEND: op_seq = seq send_file = file l2cap_event.set() elif command == _COMMAND_RECV: op_seq = seq recv_file = file l2cap_event.set() elif command == _COMMAND_LIST: op_seq = seq list_path = file l2cap_event.set() elif command == _COMMAND_SIZE: try: stat = os.stat(file) size = stat[6] status = 0 except OSError as e: size = 0 status = _STATUS_NOT_FOUND control_characteristic.notify( connection, struct.pack("