ESP SPP API Печать
Добавил(а) microsin   

SPP расшифровывается как Serial Port Protocol, в данном контексте это библиотека Espressif для ESP32, которая позволяет организовать поддержку последовательного порта по протоколу Classic Bluetooth. С помощью этой библиотеки можно создать устройство Bluetooth, которое после успешного соединения (pairing) на компьютере будет представляться как COM-порт.

Примечание: в этой статье приведен перевод главы, посвященной SPP из документации [1]. Незнакомые термины и сокращения см. в Словарике, в конце статьи.

Сначала немного разберемся с терминологией. Классическое устройство Bluetooth может работать либо как инициирующее соединение устройство (обычно это смартфон или компьютер), тогда оно обозначается как INITIATOR, и является клиентом для сервера. Либо оно работает как принимающее соединение устройство (клавиатура, последовательный порт, аудиоустройство и т. д.), тогда оно обозначается как ACCEPTOR, прослушивает входящие соединения и является сервером. Модуль SPP может работать либо в режиме передачи данных ESP_SPP_MODE_CB, либо в режиме ESP_SPP_MODE_VFS, виртуальной файловой системы [3].

Пример см. в папке bluetooth/bluedroid/classic_bt среди примеров ESP-IDF [4], где содержится пример приложения SPP demo. Этот пример позволяет обнаружить службу (service) подключиться, отправить и принять данные SPP. Поставляется 2 основных примера:

bluetooth/bluedroid/classic_bt/bt_spp_acceptor
bluetooth/bluedroid/classic_bt/bt_spp_initiator.

Прием данных осуществляется в обработке события ESP_SPP_DATA_IND_EVT. Передача данных не демонстрируется, но её можно легко осуществить с помощью функции esp_spp_write и обработки событий ESP_SPP_WRITE_EVT и ESP_SPP_CONG_EVT.

Файл заголовка для API-функций:

components/bt/host/bluedroid/api/include/api/esp_spp_api.h

Функции, которые возвращают значения типа esp_err_t, в случае успеха возвратят ESP_OK, иначе будет возвращен код ошибки. Базовые коды ошибок определены в заголовке esp_err.h, другие коды ошибок определяются в заголовочных файлах отдельных модулей библиотеки.

esp_err_t esp_spp_register_callback (esp_spp_cb_t callback);

Эта функция вызывается для инициализации callback-функции для модуля SPP.

Параметры

[in] callback: указатель на инициализируемую callback-функцию.

esp_err_t esp_spp_init (esp_spp_mode_t mode);

Эта функция вызывается для инициализации модуля SPP. Когда операция завершена, будет вызвана callback-функция с событием ESP_SPP_INIT_EVT. Эта функция должна быть вызвана после успешного завершения esp_bluedroid_enable().

Параметры

[in] mode: выбор режима SPP, ESP_SPP_MODE_CB или ESP_SPP_MODE_VFS.

esp_err_t esp_spp_deinit (void);

Эта функция вызывается для отмены инициализации модуля SPP. Операция сначала закроет активное соединение SPP, затем будет вызвана callback-функция с событием ESP_SPP_CLOSE_EVT, и значение handle в параметре события будет равно дескриптору соединения. Когда операция завершена, будет вызвана callback-функция с событием ESP_SPP_UNINIT_EVT. Эта функция должна быть вызвана после успешного завершения esp_spp_init().

esp_err_t esp_spp_start_discovery (esp_bd_addr_t bd_addr);

Эта функция вызывается для распознавания службы (service discovery) для служб, которые предоставлены имеющимся устройством пира. Когда операция завершена, будет вызвана callback-функция с событием ESP_SPP_DISCOVERY_COMP_EVT. Эта функция должна быть вызвана после успешного вызова esp_spp_init() и перед esp_spp_deinit().

Параметры

[in] bd_addr: адрес внешнего удаленного устройства bluetooth.

esp_err_t esp_spp_connect (esp_spp_sec_t sec_mask,
                           esp_spp_role_t role,
                           uint8_t remote_scn,
                           esp_bd_addr_t peer_bd_addr);

Эта функция выполняет соединение SPP с внешним удаленным устройством (пиром) по указанному адресу. Когда соединение инициировано или не получилось инициировать соединение, будет вызвана callback-функция с событием ESP_SPP_CL_INIT_EVT. Когда соединение было установлено или потерпело неудачу, будет вызвана callback-функция с событием ESP_SPP_OPEN_EVT. Эта функция должна быть вызвана после успешного esp_spp_init() successful и перед esp_spp_deinit().

Параметры

[in] sec_mask: Security Setting Mask. Рекомендуется использовать только ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHORIZE или ESP_SPP_SEC_AUTHENTICATE.
[in] role: master или slave.
[in] remote_scn: SCN для удаленного внешнего устройства bluetooth.
[in] peer_bd_addr: адрес для удаленного внешнего устройства bluetooth.

esp_err_t esp_spp_disconnect (uint32_t handle);

Эта функция закроет соединение SPP. Когда операция завершена, будет вызвана callback-функция с событием ESP_SPP_CLOSE_EVT. Эта функция должна быть вызвана после успеха esp_spp_init() и перед esp_spp_deinit().

Параметры

[in] handle: дескриптор соединения.

esp_err_t esp_spp_start_srv (esp_spp_sec_t sec_mask,
                             esp_spp_role_t role,
                             uint8_t local_scn,
                             const char *name);

Эта функция создает сервер SPP, и начинает прослушивание радиоканала на предмет поступления запроса соединения (SPP connection request) от удаленного устройства Bluetooth. Когда сервер успешно запустится, будет вызвана callback-функция с событием ESP_SPP_START_EVT. Когда установится соединение, будет вызвана callback-функция с событием ESP_SPP_SRV_OPEN_EVT. Эта функция должна быть вызвана после успеха esp_spp_init() и перед esp_spp_deinit().

Параметры

[in] sec_mask: Security Setting Mask. Рекомендуется использовать только ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHORIZE или ESP_SPP_SEC_AUTHENTICATE.
[in] role: master или slave.
[in] local_scn: определенный канал, который ходите получить. Если указан канал 0, то это значит любой канал.
[in] name: имя сервера.

esp_err_t esp_spp_stop_srv (void);

Останавливает все серверы SPP. Операция закроет сначала все активные соединения SPP, затем будет вызвана callback-функция с событием ESP_SPP_CLOSE_EVT, параметр handle будет соответствовать дескриптору соединения (если соединений было несколько, то callback-функция будет вызвана соответствующее количество раз). Когда операция завершится, будет вызвана callback-функция с событием ESP_SPP_SRV_STOP_EVT. Эта функция должна быть вызвана после успеха esp_spp_init() и перед esp_spp_deinit().

esp_err_t esp_spp_stop_srv_scn (uint8_t scn);

Остановит определенный сервер SPP. Операция закроет сначала все активные соединения SPP, затем будет вызвана callback-функция с событием ESP_SPP_CLOSE_EVT, параметр handle будет соответствовать дескриптору соединений. Когда операция завершится, будет вызвана callback-функция с событием ESP_SPP_SRV_STOP_EVT. Эта функция должна быть вызвана после успеха esp_spp_init() и перед esp_spp_deinit().

Параметры

[in] scn: номер канала сервера (server channel number).

esp_err_t esp_spp_write (uint32_t handle, int len, uint8_t *p_data);

Записывает данные, только для режима ESP_SPP_MODE_CB. Когда эту функцию нужно вызывать несколько раз, строго рекомендуется делать повторные вызовы после получения предыдущего события ESP_SPP_WRITE_EVT с параметром cong равным false (что означает отсутствие перегрузки канала данными). Если предыдущее событие ESP_SPP_WRITE_EVT было с параметром cong равным true (означает перегрузку канала данными), то функция может быть вызвана повторно, когда принято событие ESP_SPP_CONG_EVT с параметром cong равным false. Эта функция должна вызываться после установки соединения между initiator и acceptor.

Параметры

[in] handle: дескриптор соединения.
[in] len: длина записываемых данных.
[in] p_data: записываемые данные.

esp_err_t esp_spp_vfs_register (void);

Эта функция используется для регистрации VFS. Сейчас SPP поддерживает только операции write, read и close.

[Объединения]

Объединение для параметра callback-функции SPP.

typedef union {
   // Событие SPP_INIT_EVT
   struct spp_init_evt_param {
      esp_spp_status_t status;         /*!< статус */
   } init;
 
   // Событие SPP_UNINIT_EVT
   struct spp_uninit_evt_param {
      esp_spp_status_t status;         /*!< статус */
   } uninit;
 
   // Событие SPP_DISCOVERY_COMP_EVT
   struct spp_discovery_comp_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint8_t scn_num;                 /*!< номер канала сервера */
      uint8_t scn[ESP_SPP_MAX_SCN];    /*!< channel # */
      const char *service_name[ESP_SPP_MAX_SCN]; /*!< имя сервиса */
   } disc_comp;
 
   // Событие ESP_SPP_OPEN_EVT
   struct spp_open_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         handle;         /*!< дескриптор соединения */
      int              fd;             /*!< дескриптор файла, только для ESP_SPP_MODE_VFS */
      esp_bd_addr_t    rem_bda;        /*!< адрес пира */
   } open;
 
   // Событие ESP_SPP_SRV_OPEN_EVT
   struct spp_srv_open_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         handle;         /*!< дескриптор соединения */
      uint32_t         new_listen_handle; /*!< новый дескриптор прослушивания */
      int              fd;             /*!< дескриптор файла, только для ESP_SPP_MODE_VFS */
      esp_bd_addr_t    rem_bda;        /*!< адрес пира */
   } srv_open;
 
   // Событие ESP_SPP_CLOSE_EVT
   struct spp_close_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         port_status;    /*!< состояние порта PORT */
      uint32_t         handle;         /*!< дескриптор соединения */
      bool             async;          /*!< FALSE, если отключение инициировано локально */
   } close;
 
   // Событие ESP_SPP_START_EVT
   struct spp_start_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         handle;         /*!< дескриптор соединения */
      uint8_t          sec_id;         /*!< security ID, используемый этим сервером */
      uint8_t          scn;            /*!< номер канала сервера */
      bool             use_co;         /*!< TRUE для использования co_rfc_data */
   } start;
 
   // Событие ESP_SPP_SRV_STOP_EVT
   struct spp_srv_stop_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint8_t          scn;            /*!< номер канала сервера */
   } srv_stop;
 
   // Событие ESP_SPP_CL_INIT_EVT
   struct spp_cl_init_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         handle;         /*!< дескриптор соединения */
      uint8_t          sec_id;         /*!< security ID, используемый этим сервером */
      bool             use_co;         /*!< TRUE для использования co_rfc_data */
   } cl_init;
 
   // Событие ESP_SPP_WRITE_EVT
   struct spp_write_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         handle;         /*!< дескриптор соединения */
      int              len;            /*!< длина записанных данных */
      bool             cong;           /*!< статус перегруженности */
   } write;
 
   // Событие ESP_SPP_DATA_IND_EVT
   struct spp_data_ind_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         handle;         /*!< дескриптор соединения */
      uint16_t         len;            /*!< длина данных */
      uint8_t          *data;          /*!< принятые данные */
   } data_ind;
 
   // Событие ESP_SPP_CONG_EVT
   struct spp_cong_evt_param {
      esp_spp_status_t status;         /*!< статус */
      uint32_t         handle;         /*!< дескриптор соединения */
      bool             cong;           /*!< TRUE была перегрузка данными,
                                            FALSE перегрузки не было */
   } cong;
} esp_spp_cb_param_t;

[Макросы]

ESP_SPP_SEC_NONE
Безопасность не используется. Соответствует BTA_SEC_NONE в bta/bta_api.h.

ESP_SPP_SEC_AUTHORIZE
Требуется авторизация (только в процессе установки соединения). Соответствует BTA_SEC_AUTHORIZE в bta/bta_api.h

ESP_SPP_SEC_AUTHENTICATE
Требуется аутентификация. Соответствует BTA_SEC_AUTHENTICATE в bta/bta_api.h.

ESP_SPP_SEC_ENCRYPT
Требуется шифрование. Соответствует BTA_SEC_ENCRYPT в bta/bta_api.h.

ESP_SPP_SEC_MODE4_LEVEL4
Mode 4 level 4 service, например поддержка защиты incoming/outgoing MITM и шифрования P-256. Соответствует BTA_SEC_MODE4_LEVEL4 в bta/bta_api.h.

ESP_SPP_SEC_MITM
Защита MITM, соответствует BTA_SEC_MITM в bta/bta_api.h.

ESP_SPP_SEC_IN_16_DIGITS
Минимум 16 цифр для pin-кода. Соответствует BTA_SEC_IN_16_DIGITS в bta/bta_api.h.

ESP_SPP_MAX_MTU
Максимальный MTU для SPP.

ESP_SPP_MAX_SCN
Максимальный SCN для SPP.

[Определения типов]

typedef uint16_t esp_spp_sec_t;

Тип, используемый для маски безопасности.

typedef void() esp_spp_cb_t (esp_spp_cb_event_t event, esp_spp_cb_param_t *param);

Тип для callback-функции SPP. Когда обрабатывается событие ESP_SPP_DATA_IND_EVT, настоятельно рекомендуется кешировать приходящие данные, и обрабатывать их в задаче приложения с более низким приоритетом вместо того, чтобы обрабатывать их непосредственно в callback-функции.

Параметры

event: тип события.
param: указывает на параметр для callback-функции. В настоящее время для этого используется объединение.

[Перечисления]

typedef enum {
   ESP_SPP_SUCCESS = 0,    /*!< Успешная операция. */
   ESP_SPP_FAILURE,        /*!< Какая-то ошибка (generic failure). */
   ESP_SPP_BUSY,           /*!< Временная невозможность обработки этого запроса. */
   ESP_SPP_NO_DATA,        /*!< Нет данных. */
   ESP_SPP_NO_RESOURCE,    /*!< Недостаточно ресурсов. */
   ESP_SPP_NEED_INIT,      /*!< Сначала надо инициализировать модуль SPP. */
   ESP_SPP_NEED_DEINIT,    /*!< Сначала надо отменить инициализацию модуля SPP. */
   ESP_SPP_NO_CONNECTION,  /*!< Вероятно соединение закрыто. */
   ESP_SPP_NO_SERVER,      /*!< Нет сервера SPP. */
} esp_spp_status_t;

typedef enum {
   ESP_SPP_ROLE_MASTER = 0,   /*!< Роль мастера */
   ESP_SPP_ROLE_SLAVE  = 1,   /*!< Роль подчиненного устройства */
} esp_spp_role_t;

typedef enum {
   ESP_SPP_MODE_CB  = 0,   /*!< При поступлении данных будет вызвана
                                callback-функция, где в её параметре будут
                                переданы эти данные. */
   ESP_SPP_MODE_VFS = 1,   /*!< Для чтения/записи данных используется VFS. */
} esp_spp_mode_t;

Это перечисление используется для обозначения событий callback-функции.

typedef enum {
   ESP_SPP_INIT_EVT           = 0,  /*!< При инициализации SPP. */
   ESP_SPP_UNINIT_EVT         = 1,  /*!< При отмене инициализации SPP. */
   ESP_SPP_DISCOVERY_COMP_EVT = 8,  /*!< При завершении SDP. */
   ESP_SPP_OPEN_EVT           = 26, /*!< Когда открыто соединение с клиентом SPP. */
   ESP_SPP_CLOSE_EVT          = 27, /*!< Когда соединение SPP закрыто. */
   ESP_SPP_START_EVT          = 28, /*!< Когда запустился сервер SPP. */
   ESP_SPP_CL_INIT_EVT        = 29, /*!< Когда клиент SPP инициировал соединение. */
   ESP_SPP_DATA_IND_EVT       = 30, /*!< Когда на соединение SPP поступили данные.
                                         Только для режима ESP_SPP_MODE_CB. */
   ESP_SPP_CONG_EVT           = 31, /*!< Когда поменялся статус перегруженности
                                         данными соединения SPP.
                                         Только для режима ESP_SPP_MODE_CB. */
   ESP_SPP_WRITE_EVT          = 33, /*!< Когда завершилась операция записи SPP.
                                         Только для режима ESP_SPP_MODE_CB. */
   ESP_SPP_SRV_OPEN_EVT       = 34, /*!< Когда открыто соединение сервера SPP. */
   ESP_SPP_SRV_STOP_EVT       = 35, /*!< Когда остановлен сервер SPP. */
} esp_spp_cb_event_t;

[Словарик]

acceptor устройство Bluietooth, ожидающее поступление запроса на соединение от устройства initiator.

BD Address BD_ADDR, Bluetooth Device Address.

BTA Bluetooth Application Layer.

BTE Bluetooth Embedded System.

initiator устройство Bluetooth, которое инициирует соединение с устройством acceptor.

LMP Link Manager Protocol, протокол управления соединением.

MITM Man-In-The_Middle, защита от атаки "человек посередине".

MTU Maximum Transfer Unit, максимально допустимый размер для передаваемого/принимаемого блока данных.

PDU Protocol Data Unit.

peer пир, противоположный участник соединения Bluetooth.

SCN Service Channel Number.

SDP Service Discovery Protocol, протокол распознавания поддерживаемой службы.

VFS Virtual FileSystem [3].

[Ссылки]

1. ESP SPP API site:docs.espressif.com.
2. Android Bluetooth Architecture site:medium.com.
3. ESP VFS: виртуальная файловая система.
4. Установка среды разработки ESP-IDF для ESP32.