В предыдущей статье я кратко описал как начать работать с nRF52832. Для полноценной работы периферийного устройства необходимо реализовать функции сопряжения и подключения "по кнопке", как многие серийные Bluetooth устройства. Этот путь был тернист и долог, этой статьей постараюсь его облегчить.

После нескольких часов безуспешных попыток заставить nRF52832 работать так, как я хочу, хотелось бросить эту затею и никогда к ней не возвращаться
После нескольких часов безуспешных попыток заставить nRF52832 работать так, как я хочу, хотелось бросить эту затею и никогда к ней не возвращаться

В качестве основы я возьму пример пульсоксиметра \examples\ble_peripheral\ble_app_hrs. Скопировал этот проект как в предыдущей статье. Так как проект получится сложным, то в конце статьи привожу репозиторий на гит с данным проектом.

В данных примерах весь функционал лежит в main, и читаемость кода сильно страдает. Для начала я вынес все функции по работе с BLE в отдельную библиотеку еще в прошлом проекте. Так что копирую папку в корень проекта и прописываю путь в препроцессоре.

Заходим в свойства проекта (options), переключаем конфигурацию на Common. Во вкладке Preprocessor открываем User Include Directories и добавляем папку с библиотекой. При добавлении строки важно не оставить в конце строки пробел, иначе при сборке можно получить прекрасное в духе "Task failed succsessfully".

В дереве проекта добавляем библиотеку

Чтобы подключить библиотеку остается в main добавить строку

#include "ble_stack_config.h"

Появилась куча ошибок, связанных с тем, что многие функции оказались продублированы. Так же я добавил свою версию BLE_NUS библиотеки, чтобы BLE UART устройство было совместимо с модулями HM-10. При добавлении BLE_NUS необходимо включить данную библиотеку в sdk_config.h

// BLE_NUS_ENABLED - ble_nus - Nordic UART Service
//==========================================================
#ifndef BLE_NUS_ENABLED
#define BLE_NUS_ENABLED 1
#endif

Чтобы запустить дебаг терминал, а не пользоваться UART, нужно в том же файле включить

//==========================================================
// NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend
//==========================================================
#ifndef NRF_LOG_BACKEND_RTT_ENABLED
#define NRF_LOG_BACKEND_RTT_ENABLED 1
#endif

Продолжаем продолжать

После всех подготовительных манипуляций с проектом перейдем к основной теме данной статьи. Чтобы осуществить управление подключениями, в данном SDK предусмотрено сопряжение по списку разрешенных устройств (whitelist). При отсутствии записей о сопряженных устройствах микроконтроллер стартует в режиме видимости всем (Fast advertising), о чем уведомляет нас в терминале.

После подключения и затем отключения ведущего устройства микроконтроллер перешел в режим подключения только по списку. Для того, чтобы появилась возможность подключиться к другому ведущему устройству, надо микроконтроллер перевести снова в режим видимости для всех. Я вынес данный функционал в отдельную функцию

void adv_disable_whitelist(void)
{
  ret_code_t err_code;
  if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
  {   
    if (m_advertising.adv_evt != BLE_ADV_EVT_IDLE)//check if adv idle restart advertising
    {
        err_code = ble_advertising_restart_without_whitelist(&m_advertising);
        if (err_code != NRF_ERROR_INVALID_STATE)
        {
          APP_ERROR_CHECK(err_code);
        }
    }
    else 
    {
        sd_ble_gap_adv_stop(m_advertising.adv_handle);
        advertising_start (false);
        err_code = ble_advertising_restart_without_whitelist(&m_advertising);
        if (err_code != NRF_ERROR_INVALID_STATE)
        {
          APP_ERROR_CHECK(err_code);
        }
    }
  }
}

При вызове этой функции проводится проверка, что в данный момент не установлено подключение к ведущему. Затем проверяется рассылка рекламных пакетов. Если она отключена (m_advertising.adv_evt == BLE_ADV_EVT_IDLE), то необходимо перезапустить рассылку рекламных пакетов. Затем функцией ble_advertising_restart_without_whitelist(&m_advertising);включается режим Fast advertising.

Так же неплохо научиться выключать доступность устройства для всех. Оказалось, что для этого недостаточно остановить рассылку пакетов и запустить снова. Ответ на эту задачу кроется в недрах функции ble_advertising_restart_without_whitelist(&m_advertising);

Этот функционал так же вынес в отдельную функцию:

void adv_enable_whitelist(void)
{
  ret_code_t err_code;
   if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
   {

      sd_ble_gap_adv_stop(m_advertising.adv_handle);


      m_advertising.whitelist_temporarily_disabled = false;
      m_advertising.whitelist_in_use               = true;
      m_advertising.adv_params.filter_policy       = BLE_GAP_ADV_FP_FILTER_BOTH;
      
      ret_code_t err_code;
        whitelist_set(PM_PEER_ID_LIST_SKIP_NO_ID_ADDR);

        err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);

        APP_ERROR_CHECK(err_code);//перезапуск приглашений уже по списку
   }
}

Так же сначала проверяется отсутствие подключению к ведущему, затем останавливается рассылка рекламных пакетов. Производится модификация полей структуры для работы по списку подключений. Дальнейшие действия повторяют первоначальную инициализацию рассылки рекламных пакетов, то есть включение списка подключений (whitelist_set) и запуск рассылки рекламных пакетов.

Проверка состояния подключения необходима, чтобы не случилась фатальная ошибка Softdevice или его недокументированное поведение.

Теперь добавлю вызов данных функций при нажатии на кнопку 0 с простым автоматом на статичной переменной. Для этого в файле ble_stack_config.c в функции void bsp_event_handler(bsp_event_t event) добавлю:

static uint8_t adv_statement;
...
      case BSP_EVENT_KEY_0:
              NRF_LOG_INFO("BSP_EVENT_KEY_0 Event");
              if (adv_statement == 0)
               {
                adv_disable_whitelist();
                adv_statement = 1;
               }
              else 
              {
                adv_statement = 0;
                adv_enable_whitelist();
              }
            break;

Запускаю отладку. При попытке подключиться другим ведущим устройством (или тем же, но удалив информацию о сопряжении) ведомое устройство не будет отображаться в списке доступных устройств. Станет же оно видимым после нажатия на кнопку и известит в терминал о смене состояния.

Чтобы микроконтроллер мог общаться с внешним миром я добавил программный таймер как в предыдущей статье, который мигает светодиодом и считает цифры.

В данном проекте число сопряженных устройств установлено 8. Для большинства задач этого будет более чем достаточно, но на случай заполнения отведенной памяти необходимо предусмотреть стирание всех подключений. Для этого надо остановить рассылку рекламных пакетов и вызвать функцию delete_bonds(). Стирать подключения буду при нажатии на кнопку 2:

case BSP_EVENT_KEY_2:
      NRF_LOG_INFO("BSP_EVENT_KEY_2 Event");
          if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
          {   
            sd_ble_gap_adv_stop(m_advertising.adv_handle);
            delete_bonds();
          }
      break;

В терминале после нажатия кнопки 2 появились сообщения о стирании всех подключений.

В эту короткую статью я впихнул (несколько скомкано и недостаточно подробно) путь, который занял у меня не один десяток часов. Надеюсь, что данный проект на GitHub поможет вам освоить данный функционал гораздо быстрее. Задавайте вопросы, если вдруг возникли сложности.

Комментарии (0)