С ростом популярности фитнес-браслетов, умных часов и других носимых устройств широкое распространение получает использование стандартов связи Bluetooth 5 / Bluetooth Low Energy. BLE помогает нам обмениваться данными на короткие расстояния с очень малым энергопотреблением, что очень важно для устройств с питанием от батареек, таких как носимые устройства. Он также помогает нам настраивать беспроводные ячеистые сети (Mesh сети) BLE, эта функция удобна для устройств домашней автоматизации, где несколько устройств должны взаимодействовать друг с другом в замкнутой среде.

На нашем сайте вы можете посмотреть следующие статьи, связанные с использованием Bluetooth Low Energy (BLE):
- сравнение технологий Bluetooth Classic и Bluetooth Low Energy (BLE);
- использование Bluetooth Low Energy в ESP32 для соединения с фитнес браслетом;
- передача данных на смартфон с помощью Arduino, модуля NRF24L01 и Bluetooth (BLE).
В этой статье мы рассмотрим интересную и популярную плату разработки nRF52 DK для измерения температуры и влажности с помощью BLE. По умолчанию профили измерения окружающей среды BLE поддерживают широкий диапазон параметров окружающей среды, но в этом руководстве мы ограничимся только значениями температуры и влажности. Это решение подключается к смартфону по Bluetooth с низким энергопотреблением и регулярно обновляет данные о параметрах окружающей среды, таких как температура и влажность. Мы будем использовать датчик DHT11, и измерение температуры будет осуществляться с разрешением 0,01 градуса Цельсия, а измерение влажности — с разрешением 0,01%.
Комплект разработчика nRF52
nRF52DK — это полноценная платформа для прототипирования приложений Bluetooth Low Energy и беспроводного Интернета вещей 2,4 ГГц. Комплект разработчика поддерживает различные стандартные инструменты Nordic, включая среды разработки с открытым исходным кодом, GCC и коммерческие интегрированные среды разработки, такие как Keil, IAR и Segger Embedded Studio и др. Nordic также предоставляет полнофункциональный комплект разработчика программного обеспечения для nRF52, включающий полную поддержку nRF52DK.

nRF52DK работает на микроконтроллере nRF52832 ARM Cortex-M4F, который имеет 512 КБ флэш-памяти и 64 КБ статического ОЗУ. nRF52DK имеет встроенный отладчик Segger J-Link на плате, который обеспечивает более простую и быструю отладку без внешних/дополнительных отладочных устройств JTAG. Он также включает в себя совместимый с Arduino Uno Rev3 разъем, который поддерживает сопряжение аналоговых и цифровых входов с микропроцессором, а также стандартные протоколы связи, такие как I2C (Inter-Integrated Circuit), SPI (Serial Peripheral Interface) и UART (Universal Asynchronous Receiver and Transmitter). Этот комплект разработки разработан со встроенной антенной на печатной плате, которая обеспечивает беспроводную связь малого радиуса действия с использованием Bluetooth Low Energy для подключения к смартфонам, ноутбукам и планшетам.
Встроенная студия Segger
Для программирования платы разработки мы будем использовать Segger Embedded Studio с nRF52. Segger Embedded Studio — это мощная интегрированная среда разработки (IDE) на C/C++, специально предназначенная для разработки встраиваемых систем. Она представляет собой комплексное решение, содержащее всё необходимое для программирования, разработки и отладки встраиваемых систем на языке C. Оно включает в себя полный рабочий процесс для программирования и разработки встраиваемых систем, включая управление проектами, редактор и отладчик с поддержкой устройств ARM Cortex. Эта мощная и простая в использовании IDE предоставляется бесплатно для клиентов из стран Северной Европы с полной лицензией без каких-либо ограничений по размеру кода. IDE можно загрузить по ссылке ниже.
Скачать Segger Embedded Studio
DHT11 с nRF52DK
DHT11 — полнофункциональный датчик температуры и влажности с резистивным компонентом измерения влажности и компонентом измерения температуры NTC-типа. Он обеспечивает превосходное качество, быстрый отклик и экономичность. По умолчанию все датчики DHT11 калибруются в лабораторных условиях, что обеспечивает их исключительную точность и надежность. Датчик использует однопроводной последовательный интерфейс. Другие его характеристики приведены ниже.

Технические характеристики DHT11:
- Диапазон влажности: 20–90 % относительной влажности;
- Диапазон температур: 0–50 градусов Цельсия;
- Точность измерения влажности: ±5% относительной влажности;
- Точность измерения температуры: ±2℃.
Временная диаграмма DHT11:

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

Я использую соединительный модуль для подключения датчика к плате, поэтому моя окончательная сборка выглядит так.

Схема взаимодействия с DHT11
На приведенной ниже блок-схеме показана логическая последовательность программы, которую мы будем использовать для связи между nRF52DK и DHT11.

Формат данных:

Как работать с Bluetooth Low Energy (BLE)?
Чтобы понять, как использовать функцию BLE, нам нужно понять несколько основных терминов, которые объясняются ниже.
Общий профиль доступа (GAP)
Профиль общего доступа (Generic Access Profile) полностью отвечает за установление соединения между периферийным устройством BLE и центральным устройством. GAP также обеспечивает различные процедуры, включая сканирование/обнаружение устройств, установление соединения на канальном уровне, завершение соединения, подтверждение связи с функциями безопасности и полноценную настройку устройства. GAP работает в следующих состояниях устройства:
| Состояния GAP | Описание |
| Standby | Начальное состояние устройства после сброса |
| Advertiser | Объявление устройства с данными, помогающими при сканировании инициатора |
| Scanner | Получает объявление и отправляет запрос на сканирование передатчику объявления |
| Initiator | Отправляет запрос на соединение для установления связи |
| Slave / Master | При подключении устройство выступает в роли ведомого, если передает объявление, и ведущего, если инициатор. |
Как видим, каждое состояние по своему уникально.
Уровень профиля общих атрибутов (GATT)
GATT — это сокращение от Generic Attribute Profile Layer, которое отвечает за обмен данными между двумя устройствами BLE (периферийным и центральным). Обмен данными характеризуется характеристиками, которые обеспечивают передачу и хранение данных. Устройство BLE выполняет две различные функции в процессе обмена данными, перечисленные ниже:
- Сервер GATT содержит информацию о характеристиках, которая будет использоваться для чтения и записи. В нашем руководстве это датчик DHT11 и устройство. Комплект — это наш сервер GATT.
- Клиент GATT считывает и записывает данные с/на сервер GATT. Смартфон также является клиентом GATT, который считывает и записывает данные на нашу сенсорную плату.
Bluetooth SIG
Специальная группа по интересам Bluetooth (SIG) — организация по стандартизации, которая следит за разработкой стандартов Bluetooth и лицензированием технологий Bluetooth. Группа SIG не производит и не продаёт никакие продукты Bluetooth. Она определяет спецификации и стандартизацию Bluetooth. Она определяет уникальный идентификатор для профиля Bluetooth с низким энергопотреблением и соответствующие характеристики. Спецификации профиля GATT можно найти по ссылке ниже.
На основе спецификации GATT, приведенной по ссылке выше, мы собрали уникальные идентификаторы, необходимые для нашего проекта, которые представлены в таблице ниже.
| Профиль / характеристики | UUID |
| GAP (общий доступ) | 0x1800 |
| GATT (общий атрибут) | 0x1801 |
| ESS (датчики окружающей среды) | 0x181A |
| Температура | 0x2A6E |
| Влажность | 0x2A6F |
Далее уже переходим к соответствующим диаграммам.
Диаграмма обслуживания/характеристик BLE

BLE UUID
| UUID | 16-битное значение | 128-битный UUID |
| ESS Service | 0x181A | 0000181A-0000-0000-0000-00000000000 |
| Temp Char | 0x2A6E | 00002A6E-0000-0000-0000-00000000000 |
| Humidity Char | 0x2A6F |
00002A6F-0000-0000-0000-00000000000 |
Характеристики температуры
| Свойство | Описание |
| Единица | Градус Цельсия с разрешением 0,01 градуса |
| Формат | sint16 |
| UUID | 0x2A6E |
| Десятичная экспонента | 2 |
| Считывать | Обязательно |
Характеристики влажности
| Свойство | Описание |
| Единица | Процент с разрешением 0,01 процента |
| Формат | sint16 |
| UUID | 0x2A6F |
| Десятичная экспонента | 2 |
| Считывать | Обязательно |
Теперь перейдем непосредственно к программе.
Пояснение к программе nRF52 BLE
Мы будем использовать nRF5 SDK для программирования нашего комплекта разработчика nRF52. nRF5 SDK — это полнофункциональный комплект разработчика программного обеспечения, интегрированный с многочисленными профилями Bluetooth Low Energy, сериализатором GATT и поддержкой драйверов для всех периферийных устройств на SoC серии nRF5. Этот SDK помогает разработчикам создавать полнофункциональные, надёжные и безопасные приложения Bluetooth Low Energy на микроконтроллерах серий nRF52 и nRF51. Полную версию программы можно скачать здесь. Код программы поясняется ниже.
Настройте вывод данных DHT11 как вход на nrf52 с подтяжкой вверх. Состояние вывода должно быть высоким, чтобы убедиться, что nRF52 обеспечивает надлежащую подтяжку вывода данных DHT11.
|
1 2 3 4 5 6 |
/* set to input and check if the signal gets pulled up */ Data_SetInput(); DelayUSec(50); if(Data_GetVal()==0) { return DHT11_NO_PULLUP; } |
Сгенерируйте сигнал START от микроконтроллера nRF52 и проверьте наличие сигнала подтверждения.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/* send start signal */ Data_SetOutput(); Data_ClrVal(); DelayMSec(20); /* keep signal low for at least 18 ms */ Data_SetInput(); DelayUSec(50); /* check for acknowledge signal */ if (Data_GetVal()!=0) { /* signal must be pulled low by the sensor */ return DHT11_NO_ACK_0; } /* wait max 100 us for the ack signal from the sensor */ cntr = 18; while(Data_GetVal()==0) { /* wait until signal goes up */ DelayUSec(5); if (--cntr==0) { return DHT11_NO_ACK_1; /* signal should be up for the ACK here */ } } /* wait until it goes down again, end of ack sequence */ cntr = 18; while(Data_GetVal()!=0) { /* wait until signal goes down */ DelayUSec(5); if (--cntr==0) { return DHT11_NO_ACK_0; /* signal should be down to zero again here */ } } |
Теперь прочитайте 40 бит данных, которые содержат 2 байта температуры, 2 байта влажности и 1 байт контрольной суммы.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/* now read the 40 bit data */ i = 0; data = 0; loopBits = 40; do { cntr = 11; /* wait max 55 us */ while(Data_GetVal()==0) { DelayUSec(5); if (--cntr==0) { return DHT11_NO_DATA_0; } } cntr = 15; /* wait max 75 us */ while(Data_GetVal()!=0) { DelayUSec(5); if (--cntr==0) { return DHT11_NO_DATA_1; } } data <<= 1; /* next data bit */ if (cntr<10) { /* data signal high > 30 us ==> data bit 1 */ data |= 1; } if ((loopBits&0x7)==1) { /* next byte */ buffer[i] = data; i++; data = 0; } } while(--loopBits!=0); |
Проверьте данные с помощью контрольной суммы.
|
1 2 3 4 |
/* test CRC */ if ((uint8_t)(buffer[0]+buffer[1]+buffer[2]+buffer[3])!=buffer[4]) { return DHT11_BAD_CRC; } |
Сохранение значений температуры и влажность
|
1 2 3 |
/* store data values for caller */ humidity = ((int)buffer[0])*100+buffer[1]; temperature = ((int)buffer[2])*100+buffer[3]; |
Инициализируем службу регистрации nRF5 SDK. nRF52 SDK имеет интерфейс управления журналированием nrf_log и использует бэкенд по умолчанию для регистрации информации. Бэкенд по умолчанию будет представлять собой последовательный порт. Здесь мы также инициализируем как интерфейс управления nrf_log , так и бэкенды по умолчанию nrf_log .
|
1 2 3 |
ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT(); |
В SDK nRF52 реализована функциональность таймера приложения . Модуль таймера приложения позволяет создавать несколько экземпляров таймера на основе периферийного устройства RTC1. Здесь мы инициализируем модуль таймера приложения nRF5. В этом решении два таймера приложения используются для интервалов объявления и обновления данных.
|
1 2 |
ret_code_t err_code = app_timer_init(); APP_ERROR_CHECK(err_code); |
В комплект nRF52 SDK входит полнофункциональный модуль управления питанием, поскольку устройства BLE должны работать несколько месяцев от батарейки-таблетки. Управление питанием играет важнейшую роль в приложениях BLE. Модуль управления питанием nRF52 полностью выполняет эту функцию. Здесь мы инициализируем модуль управления питанием nRF5 SDK.
|
1 2 3 |
ret_code_t err_code; err_code = nrf_pwr_mgmt_init(); APP_ERROR_CHECK(err_code); |
nRF52 SDK содержит встроенный hex-файл прошивки Nordic Soft Device , включающий центральный и периферийный стек Bluetooth с низким энергопотреблением. Этот высококвалифицированный стек протоколов включает GATT, GAP, ATT, SM, L2CAP и канальный уровень. Ниже представлена последовательность инициализации, которая инициализировала радиостек nRF5 BLE (Nordic Soft Device).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ret_code_t err_code; err_code = nrf_sdh_enable_request(); APP_ERROR_CHECK(err_code); // Configure the BLE stack using the default settings. // Fetch the start address of the application RAM. uint32_t ram_start = 0; err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); APP_ERROR_CHECK(err_code); // Enable BLE stack. err_code = nrf_sdh_ble_enable(&ram_start); APP_ERROR_CHECK(err_code); // Register a handler for BLE events. NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); |
GAP отвечает за сканирование/обнаружение устройств, установление и завершение соединения, запуск функций безопасности и настройку. GAP поддерживает ключевые параметры соединения, такие как интервал соединения, задержка ведомого устройства, тайм-аут контроля и т. д. При этом инициализируются параметры соединения общего профиля доступа.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ret_code_terr_code; ble_gap_conn_params_tgap_conn_params; ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME)); APP_ERROR_CHECK(err_code); memset(&gap_conn_params, 0, sizeof(gap_conn_params)); gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; gap_conn_params.slave_latency = SLAVE_LATENCY; gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; err_code = sd_ble_gap_ppcp_set(&gap_conn_params); APP_ERROR_CHECK(err_code); |
GATT отвечает за обмен данными между периферийными и центральными устройствами BLE. Модуль GATT nRF52 полезен для согласования и отслеживания максимального размера ATT_MTU. Здесь мы инициализируем модуль общих атрибутов nRF52 SDK.
|
1 2 |
ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL); APP_ERROR_CHECK(err_code); |
GATT осуществляет передачу данных в виде сервисов и характеристик. Здесь мы инициализируем сервисы ГАТТ по анализу окружающей среды, включая инициализацию таких характеристик, как температура и влажность.
|
1 2 3 4 5 6 7 8 9 |
ret_code_terr_code; nrf_ble_qwr_init_t qwr_init = {0}; // Инициализируем модуль очереди записи. qwr_init.error_handler = nrf_qwr_error_handler; err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init); APP_ERROR_CHECK(err_code); m_ess.notif_write_handler = ble_ess_notif_write_handler; err_code = ble_ess_init(&m_ess); APP_ERROR_CHECK(err_code); |
Объявления играют важную роль в среде приложений BLE. Пакеты объявлений содержат информацию о типе адреса, типе объявления, данных в нем, данных производителя устройства и данных ответа на сканирование. nRF52 SDK включает в себя модуль объявлений. Здесь мы его инициализируем модуль с заданными параметрами.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
ret_code_terr_code; ble_advdata_t advdata; ble_advdata_t srdata; ble_uuid_t adv_uuids[] = {{ESS_UUID_SERVICE, BLE_UUID_TYPE_BLE}}; // Build and set advertising data. memset(&advdata, 0, sizeof(advdata)); advdata.name_type= BLE_ADVDATA_FULL_NAME; advdata.include_appearance = true; advdata.flags= BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; memset(&srdata, 0, sizeof(srdata)); srdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]); srdata.uuids_complete.p_uuids= adv_uuids; err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len); APP_ERROR_CHECK(err_code); err_code = ble_advdata_encode(&srdata, m_adv_data.scan_rsp_data.p_data, &m_adv_data.scan_rsp_data.len); APP_ERROR_CHECK(err_code); ble_gap_adv_params_t adv_params; // Set advertising parameters. memset(&adv_params, 0, sizeof(adv_params)); adv_params.primary_phy= BLE_GAP_PHY_1MBPS; adv_params.duration= APP_ADV_DURATION; adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; adv_params.p_peer_addr= NULL; adv_params.filter_policy= BLE_GAP_ADV_FP_ANY; adv_params.interval= APP_ADV_INTERVAL; err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &adv_params); APP_ERROR_CHECK(err_code); |
BLE-соединение будет обрабатываться и отслеживаться с использованием различных параметров, таких как задержка обновления параметров первого соединения, задержки последующих соединений, количество обновлений, функция обратного вызова обработчика событий соединения и обработчик событий обратного вызова ошибок соединения. Здесь мы инициализируем параметры установления BLE-соединения и обработчик событий обратного вызова для событий соединения и ошибок.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ret_code_terr_code; ble_conn_params_init_t cp_init; memset(&cp_init, 0, sizeof(cp_init)); cp_init.p_conn_params= NULL; cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; cp_init.next_conn_params_update_delay= NEXT_CONN_PARAMS_UPDATE_DELAY; cp_init.max_conn_params_update_count= MAX_CONN_PARAMS_UPDATE_COUNT; t_on_notify_cccd_handle= BLE_GATT_HANDLE_INVALID; cp_init.disconnect_on_fail= false; cp_init.evt_handler= on_conn_params_evt; cp_init.error_handler= conn_params_error_handler; err_code = ble_conn_params_init(&cp_init); APP_ERROR_CHECK(err_code); |
После завершения инициализации системы мы начинаем с отображения имени устройства BLE и информации о его возможностях. После этого периферийное устройство можно увидеть в списке сканирования BLE смартфона.
|
1 2 3 |
ret_code_terr_code; err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG); APP_ERROR_CHECK(err_code); |
Основной цикл выполняется с интервалом в 2 секунды, считывает температуру и влажность и передает данные на подключенное смарт-устройство с помощью функции чтения или уведомления.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
for (;;) { uint16_t temperature, humidity; DHTxx_ErrorCode dhtErrCode; idle_state_handle(); if(updtmrexp) { dhtErrCode = DHTxx_Read(&temperature, &humidity); if(dhtErrCode == DHT11_OK) { NRF_LOG_INFO("Temperature: %d Humidity: %d\n", temperature, humidity); if(temp_notif_enabled) { ble_ess_notify_temp(m_conn_handle, &m_ess, temperature); }else{ ble_ess_update_temp(&m_ess, temperature); } if(humid_notif_enabled) { ble_ess_notify_humid(m_conn_handle, &m_ess, humidity); }else{ ble_ess_update_humid(&m_ess, humidity); } } updtmrexp=false; } } |
Тестирование программы с использованием nRF Connect
nRF Connect — это мощный Bluetooth-инструмент с низким энергопотреблением, позволяющий сканировать и исследовать периферийные устройства с поддержкой BLE. nRF Connect для мобильных устройств поддерживает широкий спектр стандартных профилей Bluetooth SIG. С его помощью можно проверить работу программы. После установки приложения можно подключить плату nRF52 к телефону, выполнив поиск устройств BLE в приложении. В разделе «Окружающая среда» можно заметить обновление значений температуры и влажности, как показано на рисунках ниже.




