В программе клиента формируется событие когда придёт ответ сервера на запрос. Дождитесь его и потом посылайте следующий.
Имеется в виду событие sl_bt_evt_gatt_procedure_completed_id:? Хорошо, допустим, я получил информацию о завершении и послал следующий запрос, потом снова получил и снова послал. Т.е. весь characteristic discovery я могу делать в этом case. Но из какого события посылать первый запрос? Хотелось бы юниформности. Хотя мне еще надо над этим подумать, с учетом моего желания распихать обработчики сервисов по разным файлам, так, чтобы они без нужды не пересекались.
Еще изучал (немного) код из KBA_BT_0903: SPP-over-BLE example. И мне показалось, что там клиент узнаёт о сервисах сервера еще до соединения в событии gecko_evt_le_gap_scan_response_id:. Возможно ли это или я не понимаю? Хотя EFR Connect тоже показывает некоторые сервисы еще до соединения с сервером. Хотя, это позволит мне только отфильтровать устройства с требуемым UUID. Handle я могу получить только после соединения. Пока я фильтрую просто по "таблице моих устройств".
Еще есть непонятность по функции write_without_response:
Не смог понять чем value_len, отличается от sent_len. Я так понимаю, что в sent_len будет записано сколько из заданных value_len отослано. Но вот в какой момент это значение там оказывается? Оно там меняется сразу или там надо указатель на статическую переменную? А то я укажу динамическую, а она испарится, когда программа выйдет из функции, где я делаю этот вызов и потом кто-то что-то запишет не туда.
Прошивку донгла я брал из каталога CySmart v1.3 "C:\Program Files (x86)\Cypress\CySmart\1.3\dongle\CY5672\BLE_HID_CySmart_Dongle.hex" и оно мне зашило это: Спойлер
Сегодня и завтра у меня тяжёлые дни, подумаю над вопросами и напишу позже. Кое-что мне в памяти освежить нужно. Насчёт прошивки донгла, следует использовать PSOC Programmer, a саму прошивку самому скачать с сайта. Естественно, следует выбирать прошивку для Вашей модели донгла (их несколько). Если пытаться загрузить чужую прошивку, Programmer этого не допустит, так что ничего испортить не удастся.
В программе клиента первым делом следует установить соединение с сервером и получить connection handle. При соединении генерируется специальное событие и первый запрос на чтение сервисов/характеристик можно сделать оттуда. Вообще, есть очень полезный документ с примером чтения клиентом сервисов и характеристик сервера. Думаю, он ответит на все Ваши вопросы выше. Он, правда, под версию стека 2.х, но для современной версии стека изменения минимальны.
Цитата:
клиент узнаёт о сервисах сервера еще до соединения в событии gecko_evt_le_gap_scan_response_id:. Возможно ли это или я не понимаю?
Конечно, возможно. Для этого и существуют advertisements и scan response. Вы можете там сконфигурировать передаваемую информацию о поддерживаемых сервером сервисах (или их части). Как это сделать - см. серию статей в категории KBA_BT_020x. Они, правда, тоже под старый стек и частично переписаны под новый в docs.silabs.com, однако не всегда с прежней подробностью. Если хотите, там-же в полях можно передавать какие handles у требуемых характеристик в BT DB для ускорения процедуры их чтения клиентом. Точнее, при этом можно вообще без этой процедуры обойтись.
Цитата:
Пока я фильтрую просто по "таблице моих устройств".
Если делаете это для поиска своего сервера при сканировании, то можно , например, в scan response передавать уникальное поле для вашего сервера и искать устройства, содержащие это поле в scan response.
Цитата:
Не смог понять чем value_len, отличается от sent_len
value_len - это входной параметр API и он должен отражать реальную длину посылаемых данных. sent_len - выходной параметр (pointer на статичекую переменную). Поскольку подавляющее большинство BT API non-blocking, то значение переменной по ссылке будет установлено только по фактическому завершению передачи данных.
Спасибо за подсказки. Вроде, разобрался и даже удалось что-то сделать.
Применил машину состояний, которая работает в каждом модуле.
Код:
typedef enum { idle, discover_service, discover_charactristic, enable_notification, active } bt_state;
По коннекту в app.c посылаю просто поиск всех серсисов, а в "подчинённых" файлах меняю машину состяний на discover_service и по нахождению своих сервисов каждый модуль запоминает его хендл. А когда наступит событие, что операция завершена - каждый модуль попытается искать характеристики по UUID. Конечно, первому передавшему команду это удастся (и он изменит статус на discover_characteristic), а остальные получат ошибку gatt_operation_in_progress (или что-то в этом роде). Короче, им придётся ждать, пока первый модуль не выполнит свои операции и не успокоится. Тогда эти же процедуры сможет выполнить следующий модуль, затем следующий итд.
Вот, если кого интересует, идентификация последовательного порта модуля HM-11 (хотя достаточно поменять строчку uint8_t service_UUID[] = {0xe0, 0xff}, characteristic_UUID[] = {0xe1, 0xff} и искать любые другие характеристики): Спойлер
Код:
/* * 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"
case sl_bt_evt_gatt_procedure_completed_id: gatt_result = evt->data.evt_gatt_procedure_completed.result; sl_app_assert(gatt_result == 0,"[E: 0x%04x] gatt_procedure_completed_id\r\n",(int)gatt_result); switch (serial_state){ case discover_service: if (service_discovered){ sc = sl_bt_gatt_discover_characteristics_by_uuid(connection_handler, service_handle, sizeof(characteristic_UUID), characteristic_UUID); if (sc == SL_STATUS_OK) serial_state = discover_charactristic; // sl_app_assert(, "[E: 0x%04x] gatt_discover_characteristics_by_uuid\r\n", (int)sc); } break;
case discover_charactristic: if (char_handle_defined){ serial_state = enable_notification; sl_bt_gatt_set_characteristic_notification(connection_handler, char_handle, sl_bt_gatt_notification); } break;
case enable_notification: serial_state = active; break;
default: break; } 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; serial_state = idle; break;
Качественное и безопасное устройство, работающее от аккумулятора, должно учитывать его физические и химические свойства, профили заряда и разряда, их изменение во времени и под влиянием различных условий, таких как температура и ток нагрузки. Мы расскажем о литий-ионных аккумуляторных батареях EVE и нескольких решениях от различных китайских компаний, рекомендуемых для разработок приложений с использованием этих АКБ. Представленные в статье китайские аналоги помогут заменить продукцию западных брендов с оптимизацией цены без потери качества.
Похоже, Вы во всём разобральсь. Поздравляю! Кстати, недавно общался со своим контактом с фирмы - они признали и осознали, что при обработке внешних прерываний sleep manager-ом иногда происходит какая-то фигня (как у нас). Контакт оформил ticket в группу развития стека. Будем надеяться, что ситуация с этим улучшится в следующих релизах. Нет книг без опечаток как и программ без багов.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Тогда, хочу вернуться к вопросу о разделении процессов. В AN1255 я что-то не нашел... Вот допустим, я хочу сделать отдельный процесс для кнопок и в нём делаю функцию обрабатывающую события БТ стека: button_on_event(evt). Как сделать, чтобы эта функция вошла в цепочку вызовов?
Вот в проекте iBeacon там такая конструкция в sl_bluetooth.c :
именно этого ответа нет. Но есть обходной вариант. Как я писал, его можно вызывать из "своего" on_event. Я чуть выше приводил свой serial.c, в котором обрабатываются события и там я "выбираю и обрабатываю" только то, что интересно тому модулю. И этот вот serial_on_event тривиально вызываю из sl_bt_on_event, который находится в app.c. Вот фрагмент. Я попытался "перевести" на SDK3 код из статьи. Почти всему нашел эквивалентные функции, кроме одной (она закомментирована). Хотя и без неё нормально работает.
Вопрос. А кто должен вызывать функцию sl_bt_sm_increase_security клиент или сервер?
И еще вопрос... вот всюду сервисы, характеристики. А про дескрипторы что-то ничего не пишут. Конкретно меня интересует сервис Environmental и его характеристики, на вроде температуры. Вот они всегда передаются как 16 бит и с двумя десятичными знаками за запятой или это может зависеть от дескрипторов?
кто должен вызывать функцию sl_bt_sm_increase_security клиент или сервер?
Зависит от того, чего хотите добиться. Вызывать её могут один или оба девайса, но в большинстве случаев вызывать её вообще не требуется.
Цитата:
они всегда передаются как 16 бит и с двумя десятичными знаками за запятой или это может зависеть от дескрипторов?
В каком формате передаётся характеристика от дескрипторов вообще не зависит. Они лишь информируют клиента в каком формате она передаётся сервером, что в свою очередь определяется разработчиком. Однако, если приложениям клиента и сервера этот формат известен, соответствующий дескриптор вообще можно не использовать. Для SIG сервисов и характеристик/дескрипторов описание их имеется на сайте bluetooth.com. В частности, для Environmental Sensing Service, см. здесь.
В каком формате передаётся характеристика от дескрипторов вообще не зависит.
Ммм, умнее не стал... Вроде в дескрипторах ничего такого найти не могу. Только точность, периодичность и диапазон. Но... вот запись чата с предыдущего воркшопа:
Цитата:
19:00:44 From Alvin Schatte to Everyone : If I wanted to format temperature in deg F, I would modify that value where you showed, but would the deg C in the display have to be changed in the EFR Connect app? ... 19:03:39 From Mike Glazebrook to Everyone : @Alvin, the Unit Attribute of the characteristic would need to be modified as well. This will let the phone know that the data is in C or F
просто я делаю очередной термометр (клиент) и хочу заложиться на стандартый сервис. А вдруг, у меня будет стороннее устройсво (сервер) сообщающее температуру и я хочу, чтобы моё его правильно понимало. И вот тут я не понял, где находится этот "Unit Attribute".
Полагаю, там речь шла про EFR Connect. Насколько я помню, она использует проприетарный сервис для данных сенсоров, т.е. нужно знать как устроено приложение, чтобы заставить его через дескриптор показывать температуру по желаемой шкале. Именно, что передать в этом дескрипторе. Стандарта даже в характеристиках с 16-биным UUID нет, если не оговорено в документах на сайте SIG.
Ну вот опять... перенёс свой проект с отладочной платы на свою, компилирую и получаю толпу варнингов: 'sl_bt_system_set_soft_timer' is deprecated. На отладочной плате проект делал еще в SDK 3.1.2, а новый проект уже в 3.2.0... когда пару месяцев назад так же сделали с assert и log - там не страшно: просто поменялось название с sl_ на app_. А теперь предлагают пользоваться каким-то слиптаймером? А события? теперь весь отлаженный проект снова перелопачивать?
Да, это бич всех систем большой сложности - постоянные "усовершенствования" и "улучшения", рано или поздно делающие проекты несовместимыми. Насчёт soft таймера - я согласен, что это дублирование соответствующих API слип-таймера, так что я приветствую его деприкацию. К тому-же у меня с soft таймером были проблемы надёжности, особенно с задержками менее 1 сек, так что я даже отказался от его использования в пользу аппаратного LETIMER-a. Ну а для отлаженного проекта - всегда можно оставить предыдущую версию системы.
Ну я был не рад... так как это поломало мне всё, а первая попытка чтения документации на слип-таймер у меня вызвало недоумение. Читаю описание полей хендла таймера и тихо потухаю от информативности описаний полей: priority - Priority of timer; option_flags - Option flags; next - Pointer to next element in list.. что за список? Совершенно без объяснения. Только позже дошло, что мне эту структуру не нужно заполнять самому. Ну ладно, вроде работает.
А вот есть стратегический вопрос. Как лучше, с точки энергоэффективности сервера, который является датчиком температуры. Чтобы клиент по включении питания соединился нашел хендлы и держал соединение, и, скажем, раз в минуту опрашивал температуру или раз в минуту установить соединение, найти хендлы, считать температуру, разорвать соединение и через минуту всё по-новой?
И вдогонку... сейчас у меня реализован первый вариант, но я наблюдаю постоянную потерю соединения через неопределённый интервал. Может раз в минуту, может и 10 минут продержаться соединение. Просто появляется event sl_bt_evt_connection_closed_id: reason = 0x1008 - SL_STATUS_BT_CTRL_CONNECTION_TIMEOUT. И пока не могу придумать, кто в этом виноват.
Если речь идёт только о передаче температуры, оба решения некудышние. Держать соединение энергозатратно, т.к. максимальный интервал соединения 4 сек и при обращении к серверу с периодом 60 сек будет много пустых пакетов, на передачу и приём которых тратится энергия. Кроме того, велика вероятность разрыва соединения. Второе решение в этом плане лучше, но если база данных сервера (хендлы) не обновляется в процессе работы, лучше считать её один раз и держать на стороне клиента. Потом просто читать интересующую характеристику по хендлу. Если заранее известен UUID характеристики, то проще найти только её хендл один раз и потом использовать. А ещё лучше в плане потребления забить в клиент хендл интересующей характеристики и читать только её.
Я тут рассуждаю если да кабы без информации что делает система. Если назначение её просто читать температуру из сервера, лучший в плане потребления способ организовать сервер как бикон и передавать температуру в поле User Defined Data или прото как часть имени. Вот для примера так организованы данные в моём гаражном биконе, передающим положение двери и температуру в имени и его потребление. Период трансляции 1 сек, период измерений 10 сек. Работает от CR2032. Бикон не включает радио на приём, что также способствует снижению потребления.
Цитата:
я наблюдаю постоянную потерю соединения
Всё правильно и виновников много, в том числе внешних и неконтролируемых. BLE изначально не предназначен для постоянного соединения. Как известно периодически с периодом connection interval производится переход на другую частоту, и если на ней помеха, то создаётся угроза соединению. С этим можно бороться, поиграв с параметрами соединения (latency и timeout), или просто пересоединиться.
Система - просто показометр тмпературы с двумя шкалами (дом/улица). Поэтому хочется подключаться к двум датчикам. И мне пока хотелось избежать изготовления серверной части. Поэтому в данный момент серверами у меня работают платы Thunderboard Sense и TB sense 2 с родной аппликацией, которая вещает сервис Environment sensing с UUID=0x181a и хараектеристикой Temperature. Соединение создаётся по BT адресу, потом выясняются хэндлы (хэндл сервиса всё-равно надо выяснить иначе хэндл характеристи нельзя узнать) и дальше идёт работа. При отвале соединения, программа ждёт адвертисмента (сканирование пассивное) и подключается по-новой.
Вот после последнего воркшопа, когда я посмотрел Network Analyzer-ом на пакеты, я так понял что пакеты всё равно там носятся толпами вне зависимости от передачи данных - поэтому такой вопрос и возник. Еще, для того чтобы к одному сенсору я мог подключиться несколькими устройствами, я возобновляю адвертисмент после установления соединения - но это тоже должно повышать расход энергии?
Beacon - это просто сервер который делает адвертисмент, но который non-connectable? И так понимаю, нет каког-то стандарта, на вещание температуры среды? Собственно, я свою конструкцию решил перелопатить, потому готов изменить всё принципиально.
В теории ДА, вопрос насколько. Тут всё зависит от временных параметров соединения и advertisement, а также от длины advertisement пакета. Типично, период advertisement можно установить 1 сек для уменьшения нагрузки на батарею, в то время как connection interval типично около 100мс. В дефолтном приложении сенсоров, залитых в демо-платы, оба периода раз в 10 меньше, и длина advertisement близка к максимальной. Поэтому потребление больше, чем следовало-бы. Если хотите питать серверы от CR-ки, лучше увеличить оба периода как я сказал (если бикон - то только один период) и сократить длину пакета до минимума.
Цитата:
Beacon - это просто сервер который делает адвертисмент, но который non-connectable?
Да. В Студио есть демо-проект iBeacon, его можно взять за основу. Для примера код app.c моего бикона. Разрабатывал его давно, поэтому там встречаются depricated API. Для дальнейшего улучшения следовало-бы изменять имя только если текущее измерение отличается от прежнего. Спойлер
typedef struct { uint8_t flags_len; // Length of the Flags field. uint8_t flags_type; // Type of the Flags field. uint8_t flags; // Flags field. uint8_t field_len; // Length of device name. uint8_t field_type; // Device name (9). char beacon_data[8]; } beacon_adv_data_t;
static void bcn_setup_adv_beaconing(void);
// The advertising set handle allocated from Bluetooth stack. static uint8_t advertising_set_handle = 0xff; int8_t zOut; static beacon_adv_data_t beacon_adv_data;
// Start advertising in user mode and disable connections. sl_bt_advertiser_start(advertising_set_handle, advertiser_user_data, advertiser_non_connectable); }
Сделал маяк... Правда, я данные о температуре решил запихнуть поглубже, а не в названии. Взял структуру как в последнем воркшопе и температуру передаю в том поле, где в воркшопе передавалась энкриптнутая строка. Поначалу у меня мой клиент адвертисменты видел, но EFR Connect при сканировании BT устройств его не показывал, пока не сообразил, что раз я укоротил строку, то и длина manuf_len тоже уменьшилась. Вторая проблема была в том, что я в плату Thunderboard Sense вставил батарейку CR2032 и... ничего. Тестером смотрю, что напряжение на ней упало до 1.7в. Тут я тоже подумал, что значительное потребление было из-за того, что я вставил отладочную распечатку. Удалил из компонентов IO USART, app_log, app_assert, OTA DFU. Теперь, вроде напряжение не просаживается и от батарейки работает. Правда нет идей как измерить потребление. На приобретение SLWSTK еще не созрел.
typedef struct{ uint8_t flags_len;// 0 Length of the Flags field uint8_t flags_type;// 1 Type of the Flags field uint8_t flags;// 2 Flags uint8_t name_len;// **3** Length of the Name field uint8_t name_type;// Type of the Name field uint8_t name[ADVERTISING_NAME_SIZE];// Name uint8_t manuf_len;// 4 Length of the Manufacturer ID field uint8_t manuf_type;// 5 Type of the Manufacturer ID field uint8_t company_LO;// 6 Manufacturer ID lower byte uint8_t company_HI;// 7 Manufacturer ID higher byte uint8_t adv_data[CIPHER_MSG_SIZE];// 8 User data field }custom_adv_t;
switch (SL_BT_MSG_ID(evt->header)){ case sl_bt_evt_system_boot_id:
// Extract unique ID from BT Address. sl_bt_system_get_identity_address(&address,&address_type);
// Pad and reverse unique ID to get System ID. system_id[0]= address.addr[5]; system_id[1]= address.addr[4]; system_id[2]= address.addr[3]; system_id[3]= 0xFF; system_id[4]= 0xFE; system_id[5]= address.addr[2]; system_id[6]= address.addr[1]; system_id[7]= address.addr[0];
Если проект для имеющейся у меня демо платы, пришлите его мне и я измерю его токопотребление с помощью WSTK. Из серии Thunderboard у меня есть Thunderboard Sense 2 и Thunderboard BG22, но не старая Thunderboard Sense. Желательно получить .sls файл, а не только HEX, чтобы я смог подправить его если нужно.
Проект вот. Он под плату на которой сейчас идёт воркшоп BRD4184A Thunderboard Sense. Пробовал мультиметром измерить - в самом начале было неколько сотен микроампер, а потом только всплески единиц микроампер. Ну, я задал вещать с периодом от 2.5 до 5 секунд. Тут я думаю всё в порядке. А вот в функции запроса температуры от датчика раз в минуту - там у сеня есть нехорошее подозрение, так как применил встроенную функцию и за неё не уверен. Но тут погонял пару дней с батарейкой - на холостом ходу на батарейке 2,97, в схеме 2,85, а через некоторое время 2,75в. Но и батарейка СR2032 не первой свежести.
Сейчас этот форум просматривают: pin1000 и гости: 51
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения