adafruit / Adafruit_CircuitPython_BLE_File_Transfer

Simple (not fast) File Transfer service for BLE
MIT License
28 stars 5 forks source link

Introduction

.. image:: https://readthedocs.org/projects/adafruit-circuitpython-ble_file_transfer/badge/?version=latest :target: https://docs.circuitpython.org/projects/ble_file_transfer/en/latest/ :alt: Documentation Status

.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/blob/main/badges/adafruit_discord.svg :target: https://adafru.it/discord :alt: Discord

.. image:: https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer/workflows/Build%20CI/badge.svg :target: https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer/actions :alt: Build Status

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black :alt: Code Style: Black

Simple BLE Service for reading and writing files over BLE. This BLE service is geared towards file transfer to and from a device running the service. A core part of the protocol is free space responses so that the server can be a memory limited device. The free space responses allow for small buffer sizes that won't be overwhelmed by the client.

Dependencies

This driver depends on:

Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading the Adafruit library and driver bundle <https://circuitpython.org/libraries> or individual libraries can be installed using circup <https://github.com/adafruit/circup>.

Installing from PyPI

On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally from PyPI <https://pypi.org/project/adafruit-circuitpython-ble_file_transfer/>_. To install for current user:

.. code-block:: shell

pip3 install adafruit-circuitpython-ble-file-transfer

To install system-wide (this may be required in some cases):

.. code-block:: shell

sudo pip3 install adafruit-circuitpython-ble-file-transfer

To install in a virtual environment in your current project:

.. code-block:: shell

mkdir project-name && cd project-name
python3 -m venv .venv
source .venv/bin/activate
pip3 install adafruit-circuitpython-ble-file-transfer

Usage Examples

See examples/ble_file_transfer_simpletest.py <examples/ble_file_transfer_simpletest.py>_ for a client example. A stub server implementation is in examples/ble_file_transfer_stub_server.py <examples/ble_file_transfer_stub_server.py>_.

Protocol

The file transfer protocol is meant to be simple and easy to implement. It uses free space counts as a way to rate limit file content data transfer. All multi-byte numbers are encoded with the least significant byte first ("<" in CPython's struct module).

GATT Service

The UUID of the service is 0xfebb, Adafruit's 16-bit service UUID.

The base UUID used in characteristics is ADAFxxxx-4669-6C65-5472-616E73666572. The 16-bit numbers below are substituted into the xxxx portion.

The service has two characteristics:

Time resolution

Time resolution varies based filesystem type. FATFS can only get down to the 2 second bound after 1980. Littlefs can do 64-bit nanoseconds after January 1st, 1970.

To account for this, the protocol has time in 64-bit nanoseconds after January 1st, 1970. However, the server will respond with a potentially truncated version that is the value stored.

Also note that devices serving the file transfer protocol may not have it's own clock so do not rely on time ordering. Any internal writes may set the time incorrectly. So, we only recommend using the value as a cache key.

Commands

Commands always start with a fixed header. The first entry is always the command number itself encoded in a single byte. The number of subsequent entries in the header will vary by command. The entire header must be sent as a unit so set the characteristic with the full header packet. You can combine multiple commands into a single write as long as the complete header is in the packet.

Paths use / as a separator and full paths must start with /.

All numbers are unsigned.

All values are aligned with respect to the start of the packet.

Status bytes are 0x01 for OK and 0x02 for error. Values other than 0x01 are errors. 0x00 should not be used for a specific error but still considered an error. 0x05 is an error for trying to modify a read-only filesystem.

0x10 - Read a file ++++++++++++++++++++++

Given a full path, returns the full contents of the file.

The header is four fixed entries and a variable length path:

The server will respond with:

If the chunk length is smaller than the total length, then the client will request more data by sending:

The transaction is complete after the server has replied with all data. (No acknowledgement needed from the client.)

0x20 - Write a file +++++++++++++++++++++++

Writes the content to the given full path. If the file exists, it will be overwritten. Content may be written as received so an interrupted transfer may lead to a truncated file.

Offset larger than the existing file size will introduce zeros into the gap.

The header is four fixed entries and a variable length path:

The server will repeatedly respond until the total length has been transferred with:

The client will repeatedly respond until the total length has been transferred with:

The transaction is complete after the server has received all data and replied with a status with 0 free space and offset set to the content length.

NOTE: Current time was added in version 3. The rest of the packets remained the same.

0x30 - Delete a file or directory +++++++++++++++++++++++++++++++++++++

Deletes the file or directory at the given full path. Non-empty directories will have their contents deleted as well.

The header is two fixed entries and a variable length path:

The server will reply with:

NOTE: In version 2, this command now deletes contents of a directory as well. It won't error.

0x40 - Make a directory +++++++++++++++++++++++++++

Creates a new directory at the given full path. If a parent directory does not exist, then it will also be created. If any name conflicts with an existing file, an error will be returned.

The header is two fixed entries and a variable length path:

The server will reply with:

0x50 - List a directory +++++++++++++++++++++++++++

Lists all of the contents in a directory given a full path. Returned paths are relative to the given path to reduce duplication.

The header is two fixed entries and a variable length path:

The server will reply with n+1 entries for a directory with n files:

The transaction is complete when the final entry is sent from the server. It will have entry number == total entries and zeros for flags, file size and path length.

0x60 - Move a file or directory +++++++++++++++++++++++++++++++++++

Moves a file or directory at a given path to a different path. Can be used to rename as well. The two paths are sent separated by a byte so that the server may null-terminate the string itself. The client may send anything there.

The header is two fixed entries and a variable length path:

The server will reply with:

NOTE: This is added in version 4.

Versions

Version 2

Version 3

Version 4

Contributing

Contributions are welcome! Please read our Code of Conduct <https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer/blob/main/CODE_OF_CONDUCT.md>_ before contributing to help this project stay welcoming.

Documentation

For information on building library documentation, please check out this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>_.