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 advertising BLE.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 again delay(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.

ссылка на оригинал статьи https://habr.com/ru/post/492404/


Комментарии

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

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