Спасибо за подсказку. Сейчас делаю клиента, а потом буду делать сервер (по-новой, старый был в SSv4). Оказалось, что для characteristic discovery необходимо знать хэндл сервиса. Правда, в программе CySmart его подсмотреть не удалось. Не нашел, где это можно увидеть. Да и донгл, что-то не хотел ни с чем соединяться. Программу CySmart переинсталлировал - не помогло. Помогла только перепрошивка фирмвари донгла. Была 1.0.0.53, стала 1.0.0.54 - тогда только удалось соединиться. Пока разбирался с этим CySmart, в своей клиентской программе сделал просмотр сервисов сервера и получение хэндла. В качестве сервера выступал модуль HM-11. Выяснил, что требуемый UUID сервиса 0xFFE0 и получил хэндл 0x0010FFFF. Вот отладочный вывод:
Код: Выделить всё
f4:84:4c:4d:d5:13 (4) NonamePRK▒
Scanning stopped
Service handler = 0010ffff
Discovered service 16bit: ffe0
Characteristic handler = 0012
Discovered characteristic 16bit: ffe1
Правда, когда оживил CySmart там нашел что хэндл сервиса всего 0x0010. Но в хидерах SDK это поле имеет тип uint32_t:
Код: Выделить всё
PACKSTRUCT( struct sl_bt_evt_gatt_service_s
{
uint8_t connection; /**< Connection handle */
uint32_t service; /**< GATT service handle */
uint8array uuid; /**< Service UUID in little endian format */
})
Так что может и хорошо, что сам получал хэндл, а не подсматривал в другой программе.
Сам поиск делал аналогично поиску устройства из статьи: посылаю запрос и сравниваю полученные массивы с заданными UUID.
вот как это выглядит - это модуль сериального порта для связи с HM-11 (и совместимыми модулями):
Спойлер
Код: Выделить всё
/*
* serial.c
*
* Created on: 18 февр. 2021 г.
* Author: wl
*/
#include "sl_bluetooth.h"
#include "gatt_db.h"
#include "app.h"
#include "sl_app_assert.h"
#include "sl_iostream_uart.h"
#include "sl_iostream_init_usart_instances.h"
#include "printf.h"
static uint8_t connection_handler, respLen, *respData;
static uint16_t char_handle = 0xFFFF, handle, gatt_result;
static uint32_t service_handle;
unsigned int ii, char_handle_defined=0, service_discovered=0;
static uint8_t service_UUID[] = {0xe0, 0xff}, characteristic_UUID[] = {0xe1, 0xff};
unsigned int gatt_procedure_in_progress = 0;
void serial_on_event(sl_bt_msg_t *evt) {
sl_status_t sc;
switch (SL_BT_MSG_ID(evt->header)) {
case sl_bt_evt_connection_opened_id:
connection_handler = evt->data.evt_connection_opened.connection;
sc = sl_bt_gatt_discover_primary_services_by_uuid(connection_handler, sizeof(service_UUID), service_UUID);
gatt_procedure_in_progress = 1;
// sc = sl_bt_gatt_discover_primary_services(connection_handler);
sl_app_assert(sc == SL_STATUS_OK, "[E: 0x%04x] gatt_discover_primary_sevice_by_uuid\r\n", (int)sc);
break;
case sl_bt_evt_gatt_service_id:
if (0 == memcmp(evt->data.evt_gatt_service.uuid.data,service_UUID, evt->data.evt_gatt_service.uuid.len)) {
service_handle = evt->data.evt_gatt_service.service;
service_discovered = 1;
printf("Service handler = %08x\r\n", service_handle);
sc = sl_bt_gatt_discover_characteristics_by_uuid(connection_handler,
service_handle, sizeof(characteristic_UUID),
characteristic_UUID);
sl_app_assert(sc == SL_STATUS_OK, "[E: 0x%04x] gatt_discover_characteristics_by_uuid\r\n", (int)sc);
gatt_procedure_in_progress = 1;
}
if (evt->data.evt_gatt_service.uuid.len == 2) {
printf("Discovered service 16bit: %04x\r\n",
evt->data.evt_gatt_service.uuid.data[0] + (evt->data.evt_gatt_service.uuid.data[1] << 8));
} else {
printf("Discovered service %08x%08x%08x%08x (%02x)\r\n",
evt->data.evt_gatt_service.uuid.data[12] + (evt->data.evt_gatt_service.uuid.data[13] << 8) +....
evt->data.evt_gatt_service.uuid.len);
}
break;
case sl_bt_evt_gatt_characteristic_id:
if (0 == memcmp(evt->data.evt_gatt_characteristic.uuid.data, characteristic_UUID, evt->data.evt_gatt_characteristic.uuid.len)) {
char_handle = evt->data.evt_gatt_characteristic.characteristic;
char_handle_defined = 1;
printf("Characteristic handler = %04x\r\n", char_handle);
sl_bt_gatt_set_characteristic_notification(connection_handler, char_handle, sl_bt_gatt_notification);
gatt_procedure_in_progress = 1;
}
if (evt->data.evt_gatt_characteristic.uuid.len == 2) {
printf("Discovered characteristic 16bit: %04x\r\n",
evt->data.evt_gatt_characteristic.uuid.data[0] + (evt->data.evt_gatt_characteristic.uuid.data[1] << 8));
} else {
printf("Discovered characteristic %08x%08x%08x%08x (%02x)\r\n",
evt->data.evt_gatt_characteristic.uuid.data[12] + (evt->data.evt_gatt_characteristic.uuid.data[13] << 8)+....
evt->data.evt_gatt_characteristic.uuid.len);
}
break;
case sl_bt_evt_gatt_procedure_completed_id:
gatt_result = evt->data.evt_gatt_procedure_completed.result;
gatt_procedure_in_progress = 0;
sl_app_assert(gatt_result == 0, "[E: 0x%04x] gatt_procedure_completed_id\r\n", (int)gatt_result);
break;
// -------------------------------
// This event indicates that a connection was closed.
case sl_bt_evt_connection_closed_id:
char_handle_defined=0;
service_discovered=0;
char_handle = 0xFFFF;
break;
case sl_bt_evt_gatt_characteristic_value_id:
handle = evt->data.evt_gatt_characteristic_value.characteristic;
if (handle == char_handle) {
respData = evt->data.evt_gatt_characteristic_value.value.data;
respLen = evt->data.evt_gatt_characteristic_value.value.len;
sc = sl_iostream_write(sl_iostream_vcom_handle, respData, respLen);
sl_app_assert(sc == SL_STATUS_OK, "[E: 0x04x] iostream_write\r\n", (int)sc);
}
break;
}
return;
}
void app_process_action(void) {
static size_t len = 0;
static uint16_t sent_len = 0;
uint8_t data[20];
sl_status_t sc;
char c;
sl_app_assert( len == sent_len, "not_sent %04x bytes, len - sent_len");
sent_len = len = 0;
// read up to 20 characters from local buffer
while(len < 20) {
if (SL_STATUS_OK == sl_iostream_getchar(sl_iostream_vcom_handle, &c)) {
data[len++] = c;
} else {
break;
}
}
if (service_discovered) {
if(len > 0) {
sc = sl_bt_gatt_write_characteristic_value_without_response(connection_handler, char_handle, len, data, &sent_len);
}
} else {
sent_len = len;
sl_iostream_write(sl_iostream_vcom_handle, data, len);
}
}
Тут я сделал "разделение" вызова sl_bt_on_event() из app.c . Т.е. этот вызов проходится и через app.c , и через serial.c. В app.c только процедуры соединения устройства (пароль, бондинг итп), а в serial - только обслуживание сервиса передачи данных. И, подумал, что можно было бы сделать еще файлы для других сервисов. Но....
Сейчас "график" работы выглядит так:
Произошло соединение-
sl_bt_evt_connection_opened_id: делаем запрос на поиск сервиса по UUID .
нашли сервис
sl_bt_evt_gatt_service_id: узнали хэндл, делаем запрос на поиск характеристики по UUID
нашли характеристику
sl_bt_evt_gatt_characteristic_id: всё, включаем нотификацию и работаем пока не разъединимся.
Да вот проблема в том, что за раз можно делать только один gatt-запрос. Т.е. пока я по факту соединения запрашиваю один UUID сервис - всё хорошо, но второй-то запрос обломится. И вот как принято эти запросы разделять/сериализировать? Флагами? Я тут уже попытался ввести флаг gatt_procedure_in_progress, который пока нигде не принимаю во внимание. Но ведь проблема в том, что событие соединения с сервером sl_bt_evt_connection_opened_id больше в этой жизни не повторится. Делать ивенты? Но их всего 32 - маловато будет.