Руководства · 15 мин чтения

MIT App Inventor и Arduino по Bluetooth: Android-приложение для HC-05

Управление Arduino по Bluetooth через MIT App Inventor: подключение HC-05, ListPicker, блоки кнопок и ползунков, светодиод, реле, сервопривод, данные датчика, APK и решение для Android 12.

Управлять Arduino по Bluetooth через App Inventor — самый быстрый способ сделать своё Android-приложение, не написав ни строчки на Java или Kotlin. В бесплатной браузерной среде MIT App Inventor от MIT кнопки, ползунки и текстовые надписи перетаскиваются на экран, а их поведение задаётся блоками, которые соединяются, как кусочки пазла. Телефон подключается к Bluetooth-модулю HC-05, соединённому с Arduino; каждое касание в приложении уходит в Arduino короткой текстовой командой, а Arduino тем же путём отправляет на телефон показания датчиков.

К концу этого руководства у тебя будет по-настоящему рабочее приложение-пульт: ListPicker, который подключается к HC-05 из списка, кнопки для включения и выключения светодиода и реле, два ползунка (Slider) для яркости светодиода и угла сервопривода и надписи с живыми показаниями температуры и влажности от DHT11. Шаг за шагом разберём код Arduino, каждый блок App Inventor, сборку APK и решение проблемы с разрешением Bluetooth, которая появилась в Android 12. Если HC-05 тебе пока незнаком, сначала загляни в руководство Bluetooth-модуль HC-05.

Bluetooth-модуль HC-05
HC-05 — модуль классического Bluetooth (SPP), именно с таким работает компонент BluetoothClient в App Inventor; он создаёт беспроводной последовательный канал между телефоном и Arduino.

Что такое App Inventor и как он общается с Arduino?

MIT App Inventor — бесплатная среда разработки приложений, созданная Массачусетским технологическим институтом (MIT); вход выполняется через аккаунт Google, а работа идёт прямо в браузере. В ней два режима: в Designer (дизайнер) внешний вид приложения собирается перетаскиванием компонентов, а в редакторе Blocks (блоки) правила вроде «когда нажата кнопка, сделать это» записываются цветными блоками. Чтобы попробовать приложение без установки, используется приложение MIT AI2 Companion из Play Store; когда работа закончена, один щелчок создаёт устанавливаемый APK-файл.

За Bluetooth отвечает компонент BluetoothClient. Он использует профиль SPP (Serial Port Profile, профиль последовательного порта) классического Bluetooth — ровно тот, на котором работает HC-05. В итоге между телефоном и Arduino появляется невидимый последовательный кабель: когда приложение отправляет «L1» блоком SendText, Arduino читает эти два символа через bt.read(), будто их набрали в мониторе порта. Всё, что Arduino отправляет через bt.print(), приходит в блок ReceiveText на телефоне. Никакой специальной Bluetooth-библиотеки на стороне Arduino не нужно.

УровеньЗадачаВ этом проекте
Интерфейс (Designer)Компоненты на экранеListPicker, 5 кнопок (Button), 2 ползунка (Slider), надписи (Label)
Логика (Blocks)Блоки, реагирующие на событияСобытия Click, PositionChanged и Timer
BluetoothClientSPP-соединение, отправка и приём данныхConnect, SendText, ReceiveText
HC-05Мост между Bluetooth и UART9600 бод, PIN 1234
ArduinoРазбирает команды, управляет выходами, читает датчикСветодиод, реле, сервопривод, DHT11

Что понадобится

ДетальКол-воПримечание
Arduino Uno (или Nano)1Код и пины одинаковы для обеих плат
Bluetooth-модуль HC-05 или HC-061Классический Bluetooth; HM-10 (BLE) так работать не будет
Резисторы 1 kΩ и 2 kΩпо 1Делитель напряжения для пина RXD модуля HC-05 (подойдёт и 2,2 kΩ)
Светодиод и резистор 220 Ωпо 1Светодиод с регулировкой яркости (D5, PWM)
Модуль реле на 5 V1Для включения нагрузок вроде лампы или вентилятора
Сервопривод SG901Управление 0–180° ползунком
Датчик температуры и влажности DHT111Данные для вывода на телефон
Телефон на Android1HC-05 не работает с iPhone
Макетная плата и перемычки—Общая земля (GND) обязательна
Arduino Uno
Arduino Uno
HC-06
HC-06
SG90
SG90
DHT11
DHT11
Светодиод
Светодиод

Схема подключения: HC-05, светодиод, реле, сервопривод и DHT11

На плате HC-05 есть стабилизатор, поэтому пин VCC принимает 5 V, но пины данных работают с логикой 3,3 V. Сигнал 3,3 V с пина TXD модуля Arduino читает без проблем, а вот сигнал TX Arduino на 5 V нужно подавать на пин RXD через делитель напряжения: D11 → 1 kΩ → RXD, RXD → 2 kΩ → GND. Так на RXD приходит 5 × 2 / 3 ≈ 3,3 V. HC-05 подключаем к D10 и D11 через SoftwareSerial, а не к аппаратным последовательным пинам Uno (0 и 1): так код можно загружать, не отсоединяя модуль, а монитор порта остаётся свободным для отладки. Резистор для светодиода можно проверить в калькуляторе резистора для светодиода.

ПодключениеArduino UnoESP32Raspberry Pi Pico
HC-05 VCC5VНе нужен (встроенный Bluetooth)VBUS (5 V)
HC-05 GNDGND—GND
HC-05 TXDD10 (SoftwareSerial RX)—GP1 (UART0 RX)
HC-05 RXDD11, через делитель 1 kΩ / 2 kΩ—GP0 (UART0 TX), без делителя
Светодиод (+ 220 Ω)D5 (PWM)GPIO 2 (встроенный светодиод)GP15
IN модуля релеD7GPIO 26GP14
Сигнал сервопривода (оранжевый)D9— (можно добавить через ESP32Servo)GP13
Данные DHT11D2GPIO 4GP16
Запомни три правила: все GND (Arduino, HC-05, реле, сервопривода и внешнего источника, если он есть) должны быть соединены вместе. Никогда не подавай на пин RXD модуля HC-05 5 V без делителя: модуль может какое-то время это выдерживать, но со временем выйдет из строя. Сервопривод при движении может кратковременно потреблять несколько сотен миллиампер; если Arduino питается от USB, этот бросок тока просаживает напряжение, HC-05 разрывает соединение или Arduino перезагружается. Питание сервопривода и реле от отдельного источника 5 V решает проблему в корне.

Протокол обмена: что будет отправлять телефон?

Прежде чем писать код, нужно решить, на каком «языке» будут говорить телефон и Arduino. В этом проекте каждая команда — это буква, за ней число и в конце символ перевода строки (\n, ASCII 10). Перевод строки очень важен: данные по Bluetooth могут приходить по частям, и если «S1» и «35» приходят отдельно, Arduino ждёт перевода строки и обрабатывает «S135» как одну команду. Команды — это читаемый текст, поэтому их можно попробовать и вручную в любом приложении-терминале для Bluetooth.

КомандаНаправлениеЗначение
L1 / L0Телефон → ArduinoВключить светодиод на полную яркость / выключить
P0 … P255Телефон → ArduinoЯркость светодиода (значение analogWrite)
R1 / R0Телефон → ArduinoВключить / отпустить реле
S0 … S180Телефон → ArduinoУгол сервопривода (в градусах)
24.5;41Arduino → телефонТемпература (°C) и влажность (%) через точку с запятой

Вместо текста App Inventor может отправить один байт (0–255) блоком Send1ByteNumber, и Arduino прочитает его сразу как число. Это коротко и быстро, но подходит только для одного ползунка: если байты отправляют два ползунка и четыре кнопки, Arduino не поймёт, что означает пришедшее 90 — угол сервопривода или уровень яркости. Формат «буква + число + перевод строки» занимает на несколько байт больше, но на скорости 9600 бод в секунду проходит около 960 символов, так что разницы не заметишь.

Код для Arduino

Библиотеки Servo и SoftwareSerial входят в Arduino IDE. Для DHT11 установи через «Менеджер библиотек» (Library Manager) библиотеку DHT sensor library (Adafruit); когда появится вопрос о зависимостях, выбери «Установить все» (Install all), чтобы добавить и Adafruit Unified Sensor. Пины 0 и 1 не используются, поэтому при загрузке кода отключать HC-05 не нужно.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2#include <Servo.h>
3#include <DHT.h>
4
5SoftwareSerial bt(10, 11);       // RX, TX → HC-05 TXD, RXD (bölücü üzerinden / via divider)
6Servo servo;
7DHT dht(2, DHT11);               // DHT11 veri pini D2 / data pin D2
8
9const int LED_PIN = 5;           // PWM destekli pin / PWM-capable pin
10const int ROLE_PIN = 7;
11const int SERVO_PIN = 9;
12const int ROLE_ACIK = LOW;       // çoğu röle modülü LOW ile çeker / most relay modules are active LOW
13const int ROLE_KAPALI = HIGH;
14
15String komut = "";               // gelen satırın tamponu / buffer for the incoming line
16unsigned long sonGonderim = 0;
17
18void komutIsle(String k) {
19  k.trim();                                  // boşluk ve \r temizle / strip spaces and \r
20  if (k.length() == 0) return;
21  Serial.println(k);                         // hata ayıklama / debugging
22  char tur = k.charAt(0);                    // komut harfi / command letter
23  int deger = k.substring(1).toInt();        // harften sonraki sayı / number after the letter
24
25  switch (tur) {
26    case 'L':                                // L1 / L0 → LED aç, kapat / LED on, off
27      analogWrite(LED_PIN, deger == 1 ? 255 : 0);
28      break;
29    case 'P':                                // P0..P255 → parlaklık / brightness
30      analogWrite(LED_PIN, constrain(deger, 0, 255));
31      break;
32    case 'R':                                // R1 / R0 → röle / relay
33      digitalWrite(ROLE_PIN, deger == 1 ? ROLE_ACIK : ROLE_KAPALI);
34      break;
35    case 'S':                                // S0..S180 → servo açısı / servo angle
36      servo.write(constrain(deger, 0, 180));
37      break;
38  }
39}
40
41void setup() {
42  pinMode(LED_PIN, OUTPUT);
43  pinMode(ROLE_PIN, OUTPUT);
44  digitalWrite(ROLE_PIN, ROLE_KAPALI);       // açılışta röle kapalı / relay off at start
45  servo.attach(SERVO_PIN);
46  servo.write(90);                           // orta konum / centre position
47  dht.begin();
48  Serial.begin(9600);                        // Seri Monitör / Serial Monitor
49  bt.begin(9600);                            // HC-05 veri modu hızı / HC-05 data mode baud rate
50}
51
52void loop() {
53  // 1) Telefondan gelen komutları satır satır oku / read commands from the phone line by line
54  while (bt.available()) {
55    char c = bt.read();
56    if (c == '\n') {                         // satır bitti / line complete
57      komutIsle(komut);
58      komut = "";
59    } else if (komut.length() < 16) {        // aşırı uzun satırı kes / cap overlong lines
60      komut += c;
61    }
62  }
63
64  // 2) 2 saniyede bir sensör verisini gönder / send sensor data every 2 seconds
65  if (millis() - sonGonderim >= 2000) {
66    sonGonderim = millis();
67    float t = dht.readTemperature();
68    float h = dht.readHumidity();
69    if (!isnan(t) && !isnan(h)) {
70      bt.print(t, 1);                        // örnek / example: 24.5
71      bt.print(';');
72      bt.print(h, 0);                        // örnek / example: 41
73      bt.print('\n');                        // println değil, \r gitmesin / not println, no \r
74    }
75  }
76}
  • Буфер строки: loop() добавляет каждый пришедший символ в переменную komut (команда); когда приходит \n, строка передаётся в komutIsle(), а буфер очищается. Всё, что длиннее 16 символов, отбрасывается, поэтому мусорные данные не забьют память.
  • trim(): некоторые терминальные приложения добавляют в конец строки ещё и \r; trim() убирает его вместе с пробелами, так что «L1\r» всё равно распознаётся как «L1».
  • Буква и число: charAt(0) даёт букву команды, а substring(1).toInt() — число после неё. constrain() зажимает значение в безопасном диапазоне: даже если телефон по ошибке пришлёт 300, сервопривод никогда не будет загнан дальше 180°.
  • Отправка без блокировки: датчик читается каждые 2 секунды с помощью millis(). Поскольку delay() нет, команды, приходящие в это время, обрабатываются без задержки; подробнее — в статье millis вместо delay.
  • print('\n') вместо println: println() завершает строку символами \r\n. App Inventor использует как разделитель только \n, поэтому \r прилипал бы к концу значения влажности; отправка одного \n сохраняет разбор на телефоне чистым.
  • Реле с активным LOW: большинство модулей реле включаются, когда на IN подаётся LOW. Если твой модуль включается от HIGH, просто поменяй местами значения констант ROLE_ACIK (вкл) и ROLE_KAPALI (выкл).
Прежде чем переходить к App Inventor, проверь сторону Arduino отдельно: подключись к HC-05 через приложение вроде Serial Bluetooth Terminal из Play Store, набери «L1» и отправь; светодиод должен загореться, а в мониторе порта должно появиться L1. Если каждые 2 секунды бегут строки вроде «24.5;41», обратный поток данных тоже работает. Если позже что-то пойдёт не так, сразу будет понятно, где ошибка — в приложении или в схеме.

Проектирование интерфейса в App Inventor (Designer)

  1. Открой в браузере ai2.appinventor.mit.edu, войди через аккаунт Google и создай проект BlueGraysRemote через Projects › Start new project.
  2. Из группы Layout в палитре (Palette) перетащи HorizontalArrangement; положи в него ListPicker (Text: Подключить), Button (Text: Отключить) и Label (Text: Не подключено).
  3. Ниже добавь ещё два HorizontalArrangement: в первый положи кнопки «Светодиод вкл» и «Светодиод выкл», во второй — «Реле вкл» и «Реле выкл». Если задать кнопкам Width = Fill parent, по ним будет легче попадать пальцем.
  4. Добавь два Slider: для яркости MinValue 0, MaxValue 255, ThumbPosition 0; для сервопривода MinValue 0, MaxValue 180, ThumbPosition 90. Над каждым поставь Label с подписью, за что он отвечает.
  5. Добавь ещё два Label для температуры и влажности и увеличь у них FontSize примерно до 20.
  6. Из группы Connectivity добавь BluetoothClient; на панели Properties (свойства) установи DelimiterByte равным 10 и отметь NoLocationNeeded.
  7. Из группы Sensors добавь Clock; задай TimerInterval 200 и сними отметку TimerEnabled. Таймер включим блоками, когда соединение будет установлено.
КомпонентИмя (Rename)Важная настройка
ListPickerLP_ConnectText: Подключить
Button × 5BTN_Disconnect, BTN_LedOn, BTN_LedOff, BTN_RelayOn, BTN_RelayOffWidth: Fill parent
SliderSLD_BrightnessMinValue 0, MaxValue 255
SliderSLD_ServoMinValue 0, MaxValue 180, ThumbPosition 90
LabelLBL_Status, LBL_Temp, LBL_HumFontSize 20 (для значений)
BluetoothClientBluetoothClient1DelimiterByte 10, NoLocationNeeded отмечен
ClockClock1TimerInterval 200, TimerEnabled выкл

Подключение к HC-05 через ListPicker

Сначала выполни сопряжение с HC-05 в настройках Bluetooth телефона (PIN обычно 1234, иначе 0000). Этот шаг обязателен: BluetoothClient1.AddressesAndNames показывает только сопряжённые устройства и не ищет новые поблизости. Каждый элемент списка — текст, где сначала идёт MAC-адрес, а затем имя устройства, например «98:D3:31:F5:2A:10 HC-05». Весь этот текст можно передать в блок Connect: App Inventor сам отбросит всё после первого пробела. Блок возвращает true, если соединение установлено, и false, если нет.

  1. В событие when LP_Connect.BeforePicking поставь блок set LP_Connect.Elements to BluetoothClient1.AddressesAndNames. Тогда список будет заново заполняться сопряжёнными устройствами при каждом открытии.
  2. В событие when LP_Connect.AfterPicking добавь блок if и вставь в его условие call BluetoothClient1.Connect address: LP_Connect.Selection.
  3. В части then блока if задай LBL_Status.Text значение «Подключено» и запусти чтение данных блоком set Clock1.TimerEnabled to true; в части else напиши «Не удалось подключиться».
  4. В событии when BTN_Disconnect.Click сначала set Clock1.TimerEnabled to false, затем call BluetoothClient1.Disconnect и в конце задай LBL_Status.Text значение «Не подключено». Если сначала остановить таймер, после разрыва связи не будет ни попытки чтения, ни сообщения об ошибке.
После подключения светодиод HC-05 перестаёт часто мигать и вместо этого дважды коротко вспыхивает каждые 2 секунды. Так состояние соединения видно, даже не глядя в приложение; если светодиод снова начинает часто мигать, связь потеряна.

Отправка данных: кнопки и ползунки

Для отправки нужен всего один блок: call BluetoothClient1.SendText text. Если ввести в текстовый блок «L1\n», App Inventor превратит \n в конце в символ перевода строки. Отправка без соединения вызывает ошибку 515, поэтому оборачивать каждую отправку в проверку if BluetoothClient1.IsConnected — хорошая привычка. Для ползунка число соединяется с буквой и переводом строки блоком join: join("S", round(thumbPosition), "\n").

СобытиеОтправляемый текстРезультат на Arduino
BTN_LedOn.ClickL1\nСветодиод горит на полную яркость
BTN_LedOff.ClickL0\nСветодиод гаснет
BTN_RelayOn.ClickR1\nРеле щёлкает и включается, подключённая лампа загорается
BTN_RelayOff.ClickR0\nРеле отпускает
SLD_Brightness.PositionChangedjoin("P", round(thumbPosition), "\n")analogWrite(5, значение)
SLD_Servo.PositionChangedjoin("S", round(thumbPosition), "\n")servo.write(угол)

Пока ползунок тянут пальцем, событие PositionChanged срабатывает десятки раз в секунду, а thumbPosition возвращает дробные значения вроде 87,4. Если отправлять при каждом событии, один и тот же угол уходит снова и снова, буфер HC-05 переполняется, а сервопривод двигается рывками и с опозданием. Решение простое: создай переменную global lastAngle и отправляй, только если значение, округлённое через round(thumbPosition), от неё отличается, а затем обновляй переменную. Так данные уходят, только когда угол действительно меняется. Тот же приём примени к ползунку яркости с переменной global lastBrightness.

Приём данных: показания датчика на телефоне

Arduino отправляет строку «24.5;41» каждые 2 секунды. В App Inventor нет события, которое само срабатывает при поступлении данных, поэтому компонентом Clock через равные промежутки проверяем, «есть ли новые данные?». Так как в Designer мы задали DelimiterByte равным 10, блок ReceiveText с -1 в numberOfBytes читает до символа перевода строки и возвращает строку без \n. Таймер на 200 мс проверяет гораздо чаще, чем приходят данные (каждые 2000 мс), поэтому в буфере ничего не накапливается.

  1. Внутрь when Clock1.Timer поставь условие if BluetoothClient1.IsConnected.
  2. Добавь внутрь него второе условие: call BluetoothClient1.BytesAvailableToReceive > 0.
  3. Сохрани пришедшую строку в локальную переменную блоком initialize local line to call BluetoothClient1.ReceiveText numberOfBytes: -1.
  4. Преврати строку в список блоком split text line at ";": «24.5;41» становится списком из двух элементов [24.5, 41]. Результат сохрани в локальной переменной parts.
  5. if length of list parts = 2, задай LBL_Temp.Text значение join("Температура: ", select list item parts index 1, " °C"), а LBL_Hum.Text — значение join("Влажность: ", select list item parts index 2, "%") (второй элемент списка). Проверка числа элементов не даёт строкам, пришедшим наполовину или с искажениями, вызывать ошибки.
Если передать в ReceiveText -1, но забыть задать DelimiterByte равным 10 (по умолчанию там 0), блок будет ждать нулевой байт, который никогда не придёт, и приложение будет выглядеть зависшим. А если Arduino отправляет строку через println(), в конце второго значения оказывается символ \r; именно поэтому код отправляет только '\n'.
Не ограничивайся выводом значений текстом: можно сделать LBL_Temp.TextColor красным, когда температура превышает 30 °C, открыть предупреждение компонентом Notifier или нарисовать на Canvas линейный график последних показаний.

Проблема с разрешением Bluetooth в Android 12 и новее

В Android 12 (API 31) разрешения Bluetooth изменились. Старое разрешение BLUETOOTH, которое выдавалось автоматически при установке, заменили BLUETOOTH_CONNECT и BLUETOOTH_SCAN, и их нужно подтверждать во время работы приложения; в настройках телефона они отображаются как разрешение «Устройства поблизости» (Nearby devices). Если оно не выдано, App Inventor при первой попытке открывает окно запроса разрешения, но останавливает выполнявшийся блок. Отсюда знакомые симптомы: при первом нажатии «Подключить» список открывается пустым, появляется сообщение «Error 908: The permission ... has been denied» (с названием BLUETOOTH_CONNECT или BLUETOOTH_SCAN), либо, если в разрешении однажды отказано, приложение больше никогда его не запрашивает.

  1. В событии when Screen1.Initialize проверь версию Android: если select list item (split Screen1.PlatformVersion at ".") index 1 ≥ 12, передай в call Screen1.AskForPermission значение BluetoothConnect из списка Permission. В Android 11 и более ранних версиях этих разрешений нет, и запрос там тоже вызывает ошибку 908, поэтому проверка версии важна.
  2. В событии when Screen1.PermissionGranted, если выдано разрешение BLUETOOTH_CONNECT (contains text: permissionName, piece: "CONNECT"), запроси следом BluetoothScan. AskForPermission принимает только одно разрешение за раз, поэтому разрешения приходится выстраивать в такую цепочку.
  3. В событии when Screen1.PermissionDenied покажи через Notifier сообщение с путём «Настройки › Приложения › BlueGraysRemote › Разрешения › Устройства поблизости › Разрешить». Если обработать это событие, пользователь увидит понятное объяснение вместо голого окна с ошибкой на английском.
  4. Держи NoLocationNeeded у BluetoothClient в Designer отмеченным: эта настройка сообщает Android, что приложение не использует Bluetooth для определения местоположения пользователя.
  5. Обязательно тестируй и через APK: разрешения AI2 Companion принадлежат самому приложению Companion, а твой APK запрашивает собственные разрешения после установки.
ОшибкаЗначениеРешение
Error 507: Unable to connect. Is the device turned on? (не удаётся подключиться)Не удалось подключиться к модулюПроверь питание HC-05, сопряжение и не подключён ли модуль к другому телефону; если не помогает, сними отметку Secure у BluetoothClient и попробуй снова
Error 515: Not connected to a Bluetooth device. (нет соединения с устройством)Отправка или чтение без соединенияОберни блоки отправки и Clock в проверку IsConnected
Error 516: Unable to write: Broken pipe (запись невозможна, канал разорван)Связь оборвалась посреди работы (пропало питание, модуль вне зоны действия)Останови Clock, вызови Disconnect и подключись заново; запитай сервопривод и реле от отдельного источника
Error 908: The permission … has been denied (в разрешении отказано)Нет разрешения «Устройства поблизости» в Android 12+Запроси его блоками или выдай вручную в настройках
Список открывается пустымМодуль не сопряжён, или разрешение только что запрошеноВыполни сопряжение с HC-05 в настройках телефона; после выдачи разрешения снова нажми «Подключить»

Тестирование приложения и сборка APK

Во время разработки не нужно устанавливать APK после каждого изменения. Установи на телефон приложение MIT AI2 Companion из Play Store; когда компьютер и телефон в одной сети Wi-Fi, выбери в App Inventor Connect › AI Companion и отсканируй QR-код или введи 6-символьный код с экрана. Каждое изменение в Designer или в блоках сразу появляется на телефоне. Когда приложение закончено, собери устанавливаемый файл так:

  1. Выбери Screen1 в Designer и заполни AppName (имя на телефоне), Icon (квадратный PNG), VersionCode и VersionName.
  2. В верхнем меню выбери Build › Android App (.apk). Сборка обычно занимает минуту-две.
  3. Когда сборка завершится, появится окно с QR-кодом и кнопкой скачивания. Можно отсканировать QR-код телефоном и скачать файл напрямую или сохранить APK на компьютер и скопировать на телефон по USB. Ссылка из QR-кода действует недолго (около 2 часов).
  4. Когда откроешь APK на телефоне, Android попросит разрешить браузеру или файловому менеджеру установку неизвестных приложений. Если Play Protect покажет предупреждение, можно продолжить через вариант «Всё равно установить» в подробностях; такое предупреждение обычно для приложений не из Play Store с незнакомой подписью.
  5. При первом запуске приложения подтверди разрешение «Устройства поблизости», нажми «Подключить» и выбери HC-05 из списка.
Сделай резервную копию проекта через Projects › Export selected project (.aia) to my computer; файлом .aia можно поделиться с другом или перенести его в другой аккаунт. Если хочешь опубликовать приложение в Google Play, используй Build › Android App Bundle (.aab) и обязательно сохрани ключ подписи через Projects › Export keystore: обновления должны подписываться тем же ключом. И не забывай увеличивать VersionCode на 1 при каждом обновлении.

То же приложение с ESP32, без HC-05

Отладочная плата ESP32
В классическую ESP32 встроен классический Bluetooth; BluetoothClient в App Inventor подключается к ней так же, как к HC-05.

В классическую ESP32 встроены и классический Bluetooth, и BLE. Библиотека BluetoothSerial представляет ESP32 телефону как SPP-устройство, поэтому только что созданным приложением можно пользоваться без единого изменения. Выполни сопряжение с устройством «BlueGrays-ESP32» в настройках Bluetooth телефона, выбери его из списка в приложении — и всё. Внимание: ESP32-S3, ESP32-C3 и ESP32-C6 поддерживают только BLE; у них нет BluetoothSerial, а на стороне App Inventor нужно расширение BluetoothLE. О работе с BLE читай в руководстве ESP32: Bluetooth и BLE.

C++ (Arduino IDE)
1#include "BluetoothSerial.h"
2#include <DHT.h>
3
4BluetoothSerial SerialBT;
5DHT dht(4, DHT11);               // DHT11 veri pini GPIO 4 / data pin GPIO 4
6
7const int LED_PIN = 2;           // çoğu DevKit'te kart üstü LED / onboard LED on most DevKits
8const int ROLE_PIN = 26;
9
10String komut = "";
11unsigned long sonGonderim = 0;
12
13void setup() {
14  pinMode(ROLE_PIN, OUTPUT);
15  digitalWrite(ROLE_PIN, HIGH);              // aktif LOW röle kapalı / active-LOW relay off
16  dht.begin();
17  SerialBT.begin("BlueGrays-ESP32");         // telefonda görünecek ad / name shown on the phone
18}
19
20void loop() {
21  while (SerialBT.available()) {
22    char c = SerialBT.read();
23    if (c == '\n') {
24      komut.trim();
25      int deger = komut.substring(1).toInt();
26      if (komut.startsWith("L")) {
27        analogWrite(LED_PIN, deger == 1 ? 255 : 0);              // LED aç, kapat / LED on, off
28      } else if (komut.startsWith("P")) {
29        analogWrite(LED_PIN, constrain(deger, 0, 255));          // parlaklık / brightness
30      } else if (komut.startsWith("R")) {
31        digitalWrite(ROLE_PIN, deger == 1 ? LOW : HIGH);         // röle / relay
32      }
33      komut = "";
34    } else if (komut.length() < 16) {
35      komut += c;
36    }
37  }
38
39  if (millis() - sonGonderim >= 2000) {      // Uno koduyla aynı biçim / same format as the Uno
40    sonGonderim = millis();
41    float t = dht.readTemperature();
42    float h = dht.readHumidity();
43    if (!isnan(t) && !isnan(h)) {
44      SerialBT.print(t, 1);
45      SerialBT.print(';');
46      SerialBT.print(h, 0);
47      SerialBT.print('\n');
48    }
49  }
50}

Код понимает те же команды, что и версия для Uno, и отправляет данные в том же формате «температура;влажность»; команды S он игнорирует, потому что сервопривод не подключён. Библиотека Servo от Arduino на ESP32 не работает; чтобы добавить сервопривод, установи библиотеку ESP32Servo и используй те же вызовы attach() и write(), например на GPIO 13. Модуль реле питай от пина VIN (5 V); если его вход IN ненадёжно срабатывает от сигнала 3,3 V с ESP32, выбери модуль реле, совместимый с 3,3 V. Подробнее — в статье Что такое модуль реле?

С Raspberry Pi Pico и MicroPython

Raspberry Pi Pico
Raspberry Pi Pico работает от 3,3 V, поэтому подключается к пину RXD модуля HC-05 напрямую, без делителя напряжения.

В стандартной Raspberry Pi Pico нет Bluetooth, поэтому HC-05 подключается к пинам UART0 (GP0 и GP1). Пины Pico работают на 3,3 V, так что на этот раз делитель напряжения не нужен; питай модуль от пина VBUS, на который приходят 5 V от USB. В Pico W и Pico 2 W есть встроенный Bluetooth, но MicroPython поддерживает на них только BLE; для связи с классическим BluetoothClient из App Inventor самый простой путь — всё тот же HC-05. Код ниже понимает те же команды, управляет сервоприводом через PWM 50 Hz и отправляет данные в том же формате; модуль dht уже встроен в прошивку MicroPython для Pico.

MicroPython
1from machine import UART, Pin, PWM
2import dht
3import time
4
5bt = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))   # GP0 → HC-05 RXD, GP1 ← HC-05 TXD
6led = PWM(Pin(15))
7led.freq(1000)
8role = Pin(14, Pin.OUT, value=1)                    # aktif LOW röle kapalı / active-LOW relay off
9servo = PWM(Pin(13))
10servo.freq(50)                                      # servo 50 Hz ister / servos need 50 Hz
11sensor = dht.DHT11(Pin(16))
12
13def komut_isle(k):
14    if not k:
15        return
16    tur, deger = k[0], int(k[1:] or 0)              # harf + sayı / letter + number
17    if tur == 'L':
18        led.duty_u16(65535 if deger == 1 else 0)
19    elif tur == 'P':
20        led.duty_u16(min(max(deger, 0), 255) * 257) # 0-255 → 0-65535
21    elif tur == 'R':
22        role.value(0 if deger == 1 else 1)
23    elif tur == 'S':
24        aci = min(max(deger, 0), 180)
25        servo.duty_ns(544000 + aci * 1856000 // 180)  # 544-2400 µs darbe / pulse
26
27tampon = b''
28son = time.ticks_ms()
29while True:
30    if bt.any():
31        tampon += bt.read()
32        while b'\n' in tampon:                      # tam satır geldi mi? / full line received?
33            satir, tampon = tampon.split(b'\n', 1)
34            try:
35                komut_isle(satir.decode().strip())
36            except ValueError:
37                pass                                # bozuk komutu atla / skip malformed command
38        if len(tampon) > 32:
39            tampon = b''                            # satır sonu hiç gelmedi / no newline arrived
40    if time.ticks_diff(time.ticks_ms(), son) >= 2000:
41        son = time.ticks_ms()
42        try:
43            sensor.measure()
44            bt.write('{:.1f};{}\n'.format(sensor.temperature(), sensor.humidity()))
45        except OSError:
46            pass                                    # sensör okunamadı / sensor read failed
47    time.sleep_ms(10)

Идеи проектов с App Inventor и Arduino

  • Машинка с управлением по Bluetooth: назначь кнопкам направления буквы F, B, L и R и управляй моторами через L298N; все шаги — в руководстве робот-машинка на Arduino.
  • Панель умного дома: управляй лампой, розеткой и вентилятором с одного экрана через четырёхканальный модуль реле; идеи — в статье умный дом на Arduino.
  • Лампа с выбором цвета: задавай красный, зелёный и синий каналы RGB-светодиода по отдельности тремя ползунками.
  • Рука-манипулятор: добавь по ползунку на каждый сустав и управляй сервоприводами с телефона.
  • Мониторинг теплицы: выводи на телефон показания DHT11 и датчика влажности почвы и добавь кнопку, которая включает насос, когда почва пересыхает.
  • Голосовое управление: с помощью компонента SpeechRecognizer в App Inventor сделай приложение, которое отправляет L1 по фразе «включи свет».

Частые ошибки и их решение

  • Приложение подключается, но светодиод не горит: провода TXD и RXD модуля HC-05 перепутаны, или пины SoftwareSerial в коде не совпадают со схемой. TXD модуля должен идти на RX Arduino (D10).
  • Команды игнорируются: в текстовом блоке не хватает \n, а Arduino ждёт перевода строки. Проверь, появляются ли входящие команды в мониторе порта.
  • Непонятные символы: скорость HC-05 не совпадает со скоростью в bt.begin(). С завода модуль работает на 9600 бод; если скорость меняли AT-командами, поправь код под неё.
  • Сервопривод дёргается или связь обрывается: сервопривод просаживает линию 5 V Arduino; запитай его от отдельного источника 5 V и соедини земли (GND). Лёгкое дрожание может появляться и из-за того, что SoftwareSerial при приёме ненадолго блокирует прерывания; для окончательного решения возьми Arduino Mega или ESP32, у которых есть свободные аппаратные последовательные порты. Изредка сервопривод может на мгновение прыгнуть на неверный угол: библиотека DHT при каждом чтении отключает прерывания примерно на 5 мс, и команда, пришедшая именно в этот момент, может исказиться; следующая команда ползунка всё исправит.
  • Значения на телефоне запаздывают: интервал Clock длиннее интервала отправки Arduino, и строки накапливаются. Делай TimerInterval короче интервала отправки.
  • Приложение зависает: DelimiterByte всё ещё 0, или Arduino не завершает строки символом \n; ReceiveText(-1) ждёт разделителя, который никогда не придёт.
  • Не работает на iPhone: HC-05 использует классический Bluetooth, а iOS его не разрешает. Для iPhone нужен BLE-модуль (HM-10) или ESP32.
При поиске неисправности дели цепочку надвое: сначала проверь сторону Arduino через Serial Bluetooth Terminal, затем приложение App Inventor. Если терминал работает, а приложение нет, проблема в блоках; если не работает и терминал, проверь подключение или код Arduino. Номера пинов удобно сверять в утилите распиновки.

Связка App Inventor и Arduino — самый увлекательный способ изучить разработку мобильных приложений и электронику в одном проекте. Когда соединение и протокол настроены, тот же каркас легко превращается в машинку, умный дом или пульт для теплицы; меняется только то, какие буквы отправляют кнопки. В приложении BlueGrays есть пошаговые проекты «Управление светодиодом по Bluetooth» и «Машинка на L298N с управлением по Bluetooth» со схемами подключения и кодом.

Частые вопросы

Работает ли Bluetooth-приложение из App Inventor на iPhone?

С HC-05 или HC-06 — нет. Эти модули используют классический Bluetooth (SPP), а iOS не позволяет обычным приложениям открывать такое последовательное соединение. Для связи с iPhone нужно перейти на BLE-модуль вроде HM-10 или на ESP32; для проектов с HC-05 телефон на Android — самый беспроблемный вариант.

Почему список Bluetooth-устройств в App Inventor пустой?

AddressesAndNames показывает только устройства, уже сопряжённые с телефоном, поэтому сначала выполни сопряжение с HC-05 в настройках Bluetooth с PIN-кодом 1234. В Android 12 и новее список при первом открытии тоже пустой, если не выдано разрешение «Устройства поблизости»; выдай его и снова нажми «Подключить». И проверь, что Bluetooth на телефоне включён.

Как исправить ошибку 507 в App Inventor?

Ошибка 507 означает «Unable to connect. Is the device turned on?» (не удаётся подключиться, включено ли устройство?): телефону не удалось открыть соединение с модулем. Проверь, что на HC-05 есть питание, его светодиод часто мигает и модуль не подключён к другому телефону. Если проблема остаётся, удали сопряжение и выполни его заново или попробуй отключить свойство Secure у BluetoothClient.

Как собрать APK в App Inventor?

Открой проект и выбери в верхнем меню Build › Android App (.apk). Когда сборка завершится, отсканируй QR-код телефоном или скачай APK на компьютер и скопируй на телефон. При установке понадобится разрешить установку неизвестных приложений.

Можно ли подключить App Inventor к ESP32 по Bluetooth?

Да. С библиотекой BluetoothSerial классическая ESP32 ведёт себя как SPP-устройство, и BluetoothClient из App Inventor подключается к ней так же, как к HC-05. ESP32-S3 и ESP32-C3 поддерживают только BLE, поэтому на этих платах нужно расширение BluetoothLE.

Можно ли опубликовать приложение из App Inventor в Google Play?

Да. Вариант Android App Bundle (.aab) в меню Build создаёт файл, который можно загрузить в Google Play; для этого нужен аккаунт разработчика Google Play. Обновления должны подписываться тем же ключом, поэтому сохрани резервную копию keystore через Projects › Export keystore.

Все проекты
Мигающий светодиод
#1Новичок

Мигающий светодиод

Самое первое «привет» в электронике: включение и выключение одного светодиода. Arduino включает и выключает светодиод через короткие промежутки, поэтому свет быстро мигает. Ты самым простым способом в

4.6(188)
1127
Температура и влажность с DHT11
#55Средний

Температура и влажность с DHT11

Насколько тепло в твоей комнате и влажный ли воздух или сухой? Датчик DHT11 измеряет температуру и влажность, а Arduino выводит значения на двухстрочный LCD-экран и обновляет их каждые пару секунд. Ес

4.7(47)
73
Управление светодиодом с телефона по Bluetooth
#34Новичок
Premium

Управление светодиодом с телефона по Bluetooth

Небольшая репетиция того, как включать и выключать свет в комнате с телефона. Bluetooth-модуль передаёт в Arduino буквы, отправленные с телефона; одна буква включает красный светодиод, другая — зелёны

4.8(68)
89
Машинка на L298N с управлением по Bluetooth
#80Продвинутый
Premium

Машинка на L298N с управлением по Bluetooth

Преврати телефон в пульт и управляй своей машинкой-роботом. Arduino получает команды из приложения-пульта на телефоне через Bluetooth-модуль и вращает колёса через драйвер моторов; машинка едет вперёд

5.0(38)
65
Работа с сервоприводом
#35Новичок
Premium

Работа с сервоприводом

Рычаг маленького сервопривода по очереди поворачивается в три положения: до упора в начало, ровно на середину и до упора в конец, задерживаясь на каждой остановке на две секунды. Arduino сообщает мото

4.7(48)
67
Работа с RGB-светодиодом
#39Новичок
Premium

Работа с RGB-светодиодом

В одном светодиоде прячутся три цвета: RGB-светодиод загорается красным, затем зелёным, затем синим, каждый цвет горит полсекунды и полсекунды не горит, а потом цикл начинается снова. Нужны всего один

4.9(73)
98
Включение лампы 220V кнопкой (удержание)
#19Продвинутый
Premium

Включение лампы 220V кнопкой (удержание)

Пока ты держишь кнопку, горит настоящая лампа на 220 вольт, а как только отпустишь — гаснет. Слабый сигнал Arduino не может зажечь лампу напрямую, поэтому между ними стоит реле — выключатель с электри

4.4(30)
38
Односторонняя связь между двумя Arduino
#57Средний
Premium

Односторонняя связь между двумя Arduino

Ты заставляешь две Arduino разговаривать по одному проводу: нажми кнопку на одной, и на другой загорится светодиод, нажми снова — погаснет. Плата-передатчик сообщает о нажатии кнопки коротким сообщени

4.4(37)
50