Хабрахабр

Flutter + arduino nano 33 BLE sense = очень простой BLE sensor

В этой статье я хочу рассказать как сделать очень простую bluetooth метеостанцию (куда уж без нее 🙂 ) и написать мобильное приложение на Flutter для нее.

В начале рассмотрим сам сенсор.

Для повторения понадобится плата Arduino nano 33 BLE sense
Плата построена на nrf52840. Устанавливаем ее через менеджер плат в ардуино.

Сразу же установим необходимые библиотеки:

Эти библиотеки необходимы для сенсоров, которые уже распаяны на самой плате.

Немного теории, а дальше рассмотрим практическую реализацию.

Основная идея была в том, чтобы не делать подключаемое устройство, а реализовать широковещательные посылки с включением в них всей необходимой информации.
Использовался режим обычной bluetooth метки, но с модификацией ManufacturerData. Этот пакет можно передавать в каждой advertise посылке.
Общий размер advertise посылки 31 байт. Сюда входит вся необходимая информация: имя устройства, системные данные, пользовательские данные. В чистом виде пользователю в ManufacturerData остается около 20 байт. Этого хватает на передачу данных метеостанции.
Преимущества такого способа передачи данных в более низком энергопотреблении, отсутствии необходимости держать постоянный коннект с устройством, широковещательная рассылка. Такое сообщение может поймать неограниченное количество приемников в радиусе приема.
ManufacturerData устанавливается перед стартом адвертайзинга.

А теперь практическая часть:
В коде ардуино указываем тип работы BLE части и задаем стартовый ManufacturerData
Также для удобства я указываю имя устройства, так его проще искать в приложении.

BLE.setLocalName("nrf52840.ru");BLE.setConnectable(false);byte data[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};BLE.setManufacturerData(data, 8);// start advertisingBLE.advertise();

Начальные данные заданы как набор байтов, чтобы заполнить массив.
Теперь в основном коде делаем остановку advertising, останавливаем радио производим замер необходимых данных и заполняем ManufacturerData уже реальными данными, после этого запускаем вещание обратно. Эта операция производится каждые 2 сек.

BLE.stopAdvertise();// read all the sensor values---------------------читаем данные с сенсоров и записываем их значения---------------------byte data[8] = { 0x00, 0x01, t1, t2, h1, h2, p1, p2}; // t - температура (2 байта), h - влажность (2 байта), p - давление (2 байта)BLE.setManufacturerData(data, 8);BLE.advertise();// wait 2 second to print againdelay(2000);

На этом работа с датчиком закончена. Вещать датчик будет каждые 100мс и каждые 2сек обновлять данные на актуальные. Получился очень простой по коду и реализации метео сенсор.

Теперь рассмотрим мобильное приложение

Сразу сделаю оговорку: я не разработчик мобильных приложений.
Для работы я использовал VSCode с плагином Flutter. Эта среда кажется проще, чем Android Studio, как мне показалось. Для работы с BLE использовалась библиотека Flutter_blue, которая сильно упростила подключение устройства.
Логика работы приложения тоже достаточно простая. Наш сенсор вещает в режиме обычного Beacon, поэтому нужно выполнить всего пару действий:

  1. Просканировать эфир — найти устройство с заданным именем,
  2. Разобрать его ManufacturerData, чтобы отобразить данные на экране.

Давайте посмотрим как это сделано.
После старта приложения регулярно запускается таймер, который сканирует Bluetooth устройства каждые 10 сек в течение 2 сек. Чаще и дольше сканировать смысла нет, расход батареи увеличится, а датчик вещает вообще каждые 100мс.

DeviceScanner() { _subscribeToScanEvents(); _timer = new Timer.periodic(const Duration(seconds: 10), startScan); } void startScan(Timer timer) { FlutterBlue.instance.startScan(timeout: Duration(seconds: 2));}

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

 void _subscribeToScanEvents() { FlutterBlue.instance.scanResults.listen((scanResults) { for (ScanResult scanResult in scanResults) { if (scanResult.device.name.toString() == "nrf52840.ru") { final int rssi = scanResult.rssi; final String name = scanResult.device.name; final String mac = scanResult.device.id.toString(); final double temp = scanResult.advertisementData.manufacturerData[256] [0] + scanResult.advertisementData.manufacturerData[256][1] * 0.01; final double humm = scanResult.advertisementData.manufacturerData[256] [2] + scanResult.advertisementData.manufacturerData[256][3] * 0.01; final double press = scanResult.advertisementData.manufacturerData[256][4] + scanResult.advertisementData.manufacturerData[256][5] * 0.01; final SensorData sensorData = new SensorData( name: name, rssi: rssi, mac: mac, temperature: temp, humidity: humm, pressure: press); _streamController.add(sensorData); print( 'Manufacturer data ${scanResult.advertisementData.manufacturerData}'); FlutterBlue.instance.stopScan(); } print( '${scanResult.device.name} found! mac: ${scanResult.device.id} rssi: ${scanResult.rssi}'); } }); }

Небольшой нюанс. Bluetooth библиотека для Flutter может показаться странной, данные она получает в виде массива int, а в ардуино мы формируем посылку из байтов, поэтому в сенсоре я формировал посылку чтобы упростить ее анализ. Температура 25.85 градусов разбивается на два значения 25 и 85, которые отправляются в виде отдельных byte значений, и точно так же собираются обратно.

В конечном результате получилось вот такое приложение.

Исходный код проекта можно скачать с Github

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»