Arduino, HM-10 и App Inventor 2
Это руководство представляет собой полное введение в использование модуля HM-10 Bluetooth Low Energy с App Inventor 2. Материал выходит за рамки базовых начальных уроков и рассматривает практические шаблоны реализации.
«Хотя я использую Arduino, принципы будут одинаковыми для любого другого микропроцессора или для использования HM-10 самостоятельно.»
BLE (Bluetooth Low Energy)
BLE принципиально отличается от классического Bluetooth. Вместо поддержания постоянных соединений BLE использует нечастые небольшие пакеты данных для работы с низким энергопотреблением. Он не был разработан для постоянных соединений или передачи больших объёмов данных.
Существуют два режима связи:
Broadcaster + Observer (Вещатель + Наблюдатель): вещатель отправляет периодические рекламные пакеты, не зная, слушает ли кто-то
Central + Peripheral (Центральный + Периферийный): центральное устройство инициирует соединения с периферийными устройствами, аналогично классическому Bluetooth
Архитектура BLE основана на сервисах и характеристиках. Сервисы группируют связанные характеристики, а характеристики содержат фактические значения данных. Каждый имеет уникальный идентификатор UUID.
HM-10
HM-10 — это недорогой последовательный BLE-модуль от Jinan Huamao с UART-слоем, упрощающим интеграцию с Arduino. Однако это удобство ограничивает доступ к функциональности BLE.
Пользовательский сервис и характеристики HM-10 по умолчанию:
ПОЛЬЗОВАТЕЛЬСКИЙ СЕРВИС
UUID:
0000FFE0-0000-1000-8000-00805F9B34FB
ПОЛЬЗОВАТЕЛЬСКИЕ ХАРАКТЕРИСТИКИ
UUID:
0000FFE1-0000-1000-8000-00805F9B34FB(ЧТЕНИЕ/ЗАПИСЬ/УВЕДОМЛЕНИЕ)UUID:
0000FFE2-0000-1000-8000-00805F9B34FB(только ЗАПИСЬ)
Характеристика FFE1 активна по умолчанию; FFE2 требует активации перед использованием.
App Inventor 2
App Inventor 2 использует визуальное программирование Blockly вместо текстового кода, что делает разработку Android-приложений более доступной. Новое BLE-расширение в настоящее время находится в бета-версии, но обеспечивает стабильную работу для большинства приложений.
«Новое BLE-расширение является частью AI2 IOT, и подробности об этом можно найти на сайте AI2 IOT.»
Шаблон BaseConnect предоставляет основу для сканирования и подключения к BLE-устройствам, хотя перед использованием рекомендуется обновить BLE-расширение.
Пример 1: Включение и выключение светодиода (базовый)
Схема подключения
Схема подключения:
Arduino D8 (AltSoftSerial RX) к HM-10 TX
Arduino D9 (AltSoftSerial TX) через делитель напряжения к HM-10 RX
Arduino D2 через резистор 330 Ом к светодиоду
Делитель напряжения (1 КОм и 2 КОм) понижает 5 В до 3.3 В, защищая вывод RX HM-10.
Скетч Arduino — Часть 1
// Arduino, HM-10, App Inventor 2
//
// Example Project Part 1: Turn an LED on and off basic
// By Martyn Currey. www.martyncurrey.com
#include <AltSoftSerial.h>
AltSoftSerial ASSserial;
byte LEDPin = 2;
char c=' ';
void setup()
{
Serial.begin(9600);
Serial.print("Sketch: "); Serial.println(__FILE__);
Serial.print("Uploaded: "); Serial.println(__DATE__);
Serial.println(" ");
ASSserial.begin(9600);
Serial.println("ASSserial started at 9600");
Serial.println(" ");
pinMode(LEDPin, OUTPUT);
}
void loop()
{
if (ASSserial.available())
{
c = ASSserial.read();
Serial.println(c);
// ASCII code for 0 is dec 48
// ASCII code for 1 is dec 49
if ( c== 48) { digitalWrite(LEDPin, LOW); }
if ( c== 49) { digitalWrite(LEDPin, HIGH); }
}
}
Скетч отслеживает последовательный ввод на наличие символов «0» (выключить светодиод) или «1» (включить светодиод). Любые другие символы игнорируются.
Разработка Android-приложения
Начиная с шаблона BaseConnect, улучшения включают:
Улучшения интерфейса:
Увеличен размер текста ListView до 48 пикселей для удобства чтения
Элементы переименованы для ясности
Добавлены кнопки управления светодиодом
Управление Bluetooth:
Проверка включённости Bluetooth перед сканированием
Отображение уведомления, если Bluetooth отключён
Проверка подключения устройства перед отправкой команд
Структура кода:
Добавлены глобальные переменные для UUID:
Сервис:
0000FFE0-0000-1000-8000-00805F9B34FBХарактеристика:
0000FFE1-0000-1000-8000-00805F9B34FB
Реализация кнопки-переключателя:
Сканирование/остановка сканирования объединены в одну кнопку
Подключение/отключение объединены в одну кнопку
Текст кнопки указывает текущее состояние
Усовершенствованный поток управления
Улучшенное приложение обеспечивает:
Корректную обработку ошибок при отключённом Bluetooth
Предотвращение ошибок при нажатии кнопок без правильного состояния
Удобную обратную связь через изменение текста кнопок
Пример 2: Двустороннее управление светодиодом
В этом разделе добавляется физическая кнопка к Arduino, обеспечивая двунаправленную связь.
Добавление к схеме
К предыдущей схеме добавляется:
Arduino D3 с подтягивающим резистором 10 КОм к кнопке
Скетч Arduino — Часть 2
// Arduino, HM-10, App Inventor 2
//
// Example Project Part 2: Turn an LED on and off 2 way control 01
// By Martyn Currey. www.martyncurrey.com
#include <AltSoftSerial.h>
AltSoftSerial ASSserial;
const byte LEDPin = 2;
const byte SwitchPin = 3;
boolean LED_State = false;
boolean switch_State = false;
boolean oldswitch_State = false;
char c=' ';
void setup()
{
Serial.begin(9600);
Serial.print("Sketch: "); Serial.println(__FILE__);
Serial.print("Uploaded: "); Serial.println(__DATE__);
Serial.println(" ");
ASSserial.begin(9600);
Serial.println("AltSoftSerial started at 9600");
Serial.println(" ");
pinMode(LEDPin, OUTPUT);
digitalWrite(LEDPin,LOW);
pinMode(SwitchPin, INPUT);
}
void loop()
{
checkSwitch();
checkRecievedData();
}
void checkSwitch()
{
boolean state1 = digitalRead(SwitchPin);
delay(1);
boolean state2 = digitalRead(SwitchPin);
delay(1);
boolean state3 = digitalRead(SwitchPin);
if ((state1 == state2) && (state1==state3))
{
switch_State = state1;
if ( (switch_State == HIGH) && (oldswitch_State == LOW) )
{
LED_State = ! LED_State;
if ( LED_State == HIGH)
{
digitalWrite(LEDPin,HIGH);
ASSserial.print("1" );
Serial.println("Sent - 1");
}
else
{
digitalWrite(LEDPin,LOW);
ASSserial.print("0");
Serial.println("Sent - 0");
}
}
oldswitch_State = switch_State;
}
}
void checkRecievedData()
{
if (ASSserial.available())
{
c = ASSserial.read();
Serial.println(c);
if ( c== 48) { digitalWrite(LEDPin, LOW); LED_State = LOW; }
if ( c== 49) { digitalWrite(LEDPin, HIGH); LED_State = HIGH; }
}
}
Скетч теперь включает:
Устранение дребезга кнопки через тройное считывание
Функцию переключения для физической кнопки
Передачу состояния в приложение при нажатии кнопки
Обновления приложения для двунаправленного управления
Регистрация для приёма строк:
Добавление вызова RegisterForStrings в событии BluetoothLE1.Connected
Отмена регистрации перед отключением через UnregisterForValues
Обработчик приёма данных:
Обработка входящих строк в событии StringsReceived
Проверка, что данные являются списком, перед обработкой
Цикл по элементам списка (обычно один символ)
Процедуры обновления кнопки:
Создание отдельных процедур для изменения состояния:
LED_BUTTON_ON: устанавливает текст кнопки «ON», фон зелёный
LED_BUTTON_OFF: устанавливает текст кнопки «OFF», фон красный
Двунаправленная система теперь обеспечивает:
Кнопка приложения отражает фактическое состояние светодиода
Управление физической кнопкой видно в интерфейсе приложения
Оба метода управления обновляют состояние приложения
Пример 3: Сложные коды управления
В этом разделе простые односимвольные коды заменяются структурированными многосимвольными командами.
Новый формат кодов управления
Вместо «0» и «1» используется:
L10 — L для LED, 1 для светодиода №1, 0 для выключения
L11 — L для LED, 1 для светодиода №1, 1 для включения
Формат инкапсуляции:
Arduino получает: [L10] и [L11]
Arduino отправляет: [L,1,0] и [L,1,1]
Маркеры начала/конца ([]) гарантируют полный приём сообщения, несмотря на возможную фрагментацию данных при передаче.
Скетч Arduino — Часть 3
// Arduino, HM-10, App Inventor 2
//
// Example Project Part 3: Complex control codes
// By Martyn Currey. www.martyncurrey.com
#include <AltSoftSerial.h>
AltSoftSerial ASSserial;
const byte maxDataLength = 20;
char receivedChars[21];
boolean newCommand = false;
const byte LEDPin = 2;
const byte SwitchPin = 3;
boolean LED_State = false;
boolean switch_State = false;
boolean oldswitch_State = false;
void setup()
{
Serial.begin(9600);
Serial.print("Sketch: "); Serial.println(__FILE__);
Serial.print("Uploaded: "); Serial.println(__DATE__);
Serial.println(" ");
ASSserial.begin(9600);
Serial.println("AltSoftSerial started at 9600");
Serial.println(" ");
pinMode(LEDPin, OUTPUT);
digitalWrite(LEDPin,LOW);
pinMode(SwitchPin, INPUT);
}
void loop()
{
checkSwitch();
recvWithStartEndMarkers();
if (newCommand) { processCommand(); }
}
void checkSwitch()
{
boolean state1 = digitalRead(SwitchPin);
delay(1);
boolean state2 = digitalRead(SwitchPin);
delay(1);
boolean state3 = digitalRead(SwitchPin);
if ((state1 == state2) && (state1==state3))
{
switch_State = state1;
if ( (switch_State == HIGH) && (oldswitch_State == LOW) )
{
LED_State = ! LED_State;
if ( LED_State == HIGH)
{
ASSserial.print("[L,1,1]" );
digitalWrite(LEDPin,HIGH);
Serial.println("Sent - [L,1,1]");
}
else
{
ASSserial.print("[L,1,0]");
digitalWrite(LEDPin,LOW);
Serial.println("Sent - [L,1,0]");
}
}
oldswitch_State = switch_State;
}
}
void processCommand()
{
if (strcmp ("L10",receivedChars) == 0)
{
digitalWrite(LEDPin,LOW);
LED_State = LOW;
Serial.println("LED1 LOW");
}
else if (strcmp ("L11",receivedChars) == 0)
{
digitalWrite(LEDPin,HIGH);
LED_State = HIGH;
Serial.println("LED1 HIGH");
}
receivedChars[0] = '\0';
newCommand = false;
}
void recvWithStartEndMarkers()
{
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '[';
char endMarker = ']';
char rc;
if (ASSserial.available() > 0)
{
rc = ASSserial.read();
if (recvInProgress == true)
{
if (rc != endMarker)
{
receivedChars[ndx] = rc;
ndx++;
if (ndx > maxDataLength) { ndx = maxDataLength; }
}
else
{
receivedChars[ndx] = '\0';
recvInProgress = false;
ndx = 0;
newCommand = true;
}
}
else if (rc == startMarker) { recvInProgress = true; }
}
}
Функция recvWithStartEndMarkers() (адаптирована из поста Robin2 на форуме Arduino) захватывает полные сообщения между маркерами.
Обновления приложения для сложных кодов
Изменения управления кнопками:
Замена простых символьных кодов на форматированные команды: кнопка OFF отправляет [L10], кнопка ON отправляет [L11].
Управление буфером:
Создание глобальной переменной BT_receivedData_Buffer для накопления входящих данных.
Процедура TrimToStartMarker:
Удаляет любые символы перед первым «[» для поддержания целостности буфера при поступлении неполных сообщений.
Пример 4: Изменение обработки кодов
Этот раздел расширяет обработку команд, делая код более масштабируемым.
Пример 5: 3 светодиода и 3 переключателя
Файлы для скачивания
Важно
BLE-расширение, включённое в примеры, устарело и больше не функционирует полностью. Скачайте последнюю версию BLE-расширения с http://iot.appinventor.mit.edu/assets/resources/edu.mit.appinventor.ble-20181124.aix и загрузите его для замены старой версии.
Примечание
Оригинальная статья: Arduino, HM-10 and App Inventor 2 (Martyn Currey)