Интро
Уже сравнительно давно микроконтроллер ESP32 стал неким стандартом для множества DIY проектов. И действительно, возможность работы с Wi-Fi, Bluetooth, встроенная энергонезависимая память, а так же большое число выводов позволяют сделать массу интересных проектов.
В этой статье, мы поговорим про управление конроллером с помощью BLE используя доступные в AppStore приложения. А в следующей,
На основе непосредственно контроллера, существует множество плат, с различными интегрируемыми модулями: экраном, модулем для карт памяти и тд. Одной из самых удобных для новичков, которым интересно именно программирование, а не возня с кучей проводков — платформа M5Stack.

Это устройство, в базовой комплектации, включает в себя LCD-дисплей, кардридер, аккумулятор, и три кнопки. Кроме этого, к нему существует множество модулей, подключаемых по принципу «бутерброда».
Не смотря на то, что в данной статье программирование микроконтроллера будет в Arduino IDE, его так же можно программировать с помощью MicroPython, а так же собственной блоковой среды разработки — UiFlow.
Инструкцию по настройке ArduinoIDE, для работы с M5Stack можно найти здесь.
Немного про то, что же такое BLE
BLE — Bluetooth Low Energy, это протокол, который, в отличии от обычного Bluetooth, постоянно находится в спящем режиме, кроме тех случаев, когда идёт непосредственная передача данных. За счёт этого, тратится примерно в 100 раз меньше энергии, что очень важно при использовании встраиваемых устройств с аккумуляторами.
Когда мы используем протокол BLE, мы работаем с сервисами и характеристиками. Это очень напоминает протокол MQTT, где есть топики. У каждого сервиса, есть как минимум одна характеристика. Каждый сервис это просто объем информации: например состояние датчика. Характеристика включает в себя описание себя (что она умеет: читать, писать, является Broadcast-ом) и непосредственно значение.
Каждый элемент, с которым мы работаем: сервис, характеристика должен иметь свой UUID (Universally unique identifier).
Подробнее про BLE можно почитать тут.
В Ардуино, система работа с BLE примерно такая: есть сервер — это само устройство, у сервера есть сервис, а у сервиса уже есть характеристики, в которые и поступают данные. У каждого из них могут быть Callback функции.
Выводим сообщение на экран
Чтобы работать с BLE и M5Stack из Arduino IDE необходимо подключить библиотеки.
#include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h> #include <M5Stack.h>
Затем сгенерировать как минимум два UUID: один для сервиса, а один для характеристики. Для генерации можно использовать UUID generator.
#define SERVICE_UUID "9a8ca9ef-e43f-4157-9fee-c37a3d7dc12d" // ID сервиса #define ELEM_UUID "cc46b944-003e-42b6-b836-c4246b8f19a0" // ID характеристики #define DEVINFO_UUID (uint16_t)0x180a #define DEVINFO_MANUFACTURER_UUID (uint16_t)0x2a29 #define DEVINFO_NAME_UUID (uint16_t)0x2a24 #define DEVINFO_SERIAL_UUID (uint16_t)0x2a25 #define DEVICE_MANUFACTURER "M5Stack" // Имя "Производителя" #define DEVICE_NAME "M5Stack_BLE" // Имя устройства
Создадим сервер и сервис.
// Создание Callback функции для сервера. class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { // Обработка подключения телефона к устройству M5.Lcd.println("Connected"); }; void onDisconnect(BLEServer* pServer) { // Обработка отключения M5.Lcd.println("Disconnected"); } }; // Код размещённый ниже - помещается в функцию setup() String devName = DEVICE_NAME; String chipId = String((uint32_t)(ESP.getEfuseMac() >> 24), HEX); devName += '_'; devName += chipId; BLEDevice::init(devName.c_str()); // Инициализация девайса BLEServer *pServer = BLEDevice::createServer(); // Создание сервера pServer->setCallbacks(new MyServerCallbacks()); // Подключение Callback-а BLEService *pService = pServer->createService(SERVICE_UUID); // Cоздание сервиса
Для создания какой-либо характеристики необходимо сделать следующее:
- Объявить характеристику.
BLECharacteristic *pElem; - Объявить класс Callback Функция onWrite вызывается, когда в данную характеристику поступает информация. В нашем случае, информация просто выводится на экран.
class ElemCallbacs : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string value1 = pCharacteristic->getValue(); M5.Lcd.clear(BLACK); M5.Lcd.setCursor(3, 35); M5.Lcd.print("Get value: "); M5.Lcd.println(value1.c_str()); } }; - И добавить новую характеристику к уже существующему сервису, с указанием свойств. Это тоже делается внутри функции setup()
pElem = pService->createCharacteristic(ELEM_UUID,BLECharacteristic::PROPERTY_READ| BLECharacteristic::PROPERTY_WRITE); pElem->setCallbacks(new ElemCallbacs());
После добавления всех характеристик, важно не забыть запустить сервис, а после этого запустить Advertising.
pService->start(); // ----- Advertising BLEAdvertising *pAdvertising = pServer->getAdvertising(); BLEAdvertisementData adv; adv.setName(devName.c_str()); pAdvertising->setAdvertisementData(adv); BLEAdvertisementData adv2; adv2.setCompleteServices(BLEUUID(SERVICE_UUID)); pAdvertising->setScanResponseData(adv2); pAdvertising->start();
Полностью исходный код представлен на гитхабе.
Тестируем
Для тестирования была использована программа BLE scanner. В ней можно выбрать устройство, выбрать характеристику и отправить на неё данные в текстовом или байтовом формате.
При запуске необходимо выбрать наше устройство, затем выбрать ту характеристику, с которой мы хотим работать и отправить в неё информацию.

А что же происходит на устройстве? При загрузке на нём отображается такие сообщения:

После подключения телефона — добавляется соответствующая строка:

А когда отправляется сообщение — экран выглядит так:

Заключение
Используя предложенный пример, можно достаточно легко сделать собственный функционал. А с учётом возможностей M5Stack, часть проектов не потребует никакого дополнительного оборудования.
P.S. Пользователи Android, не обижайтесь, что я не сказал про вас ни слова. У вас тоже есть возможность работы с BLE и даже приложение BLE Scanner доступно в PlayMarket.
