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.

Что такое 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 |
| BluetoothClient | SPP-соединение, отправка и приём данных | Connect, SendText, ReceiveText |
| HC-05 | Мост между Bluetooth и UART | 9600 бод, PIN 1234 |
| Arduino | Разбирает команды, управляет выходами, читает датчик | Светодиод, реле, сервопривод, DHT11 |
Что понадобится
| Деталь | Кол-во | Примечание |
|---|---|---|
| Arduino Uno (или Nano) | 1 | Код и пины одинаковы для обеих плат |
| Bluetooth-модуль HC-05 или HC-06 | 1 | Классический Bluetooth; HM-10 (BLE) так работать не будет |
| Резисторы 1 kΩ и 2 kΩ | по 1 | Делитель напряжения для пина RXD модуля HC-05 (подойдёт и 2,2 kΩ) |
| Светодиод и резистор 220 Ω | по 1 | Светодиод с регулировкой яркости (D5, PWM) |
| Модуль реле на 5 V | 1 | Для включения нагрузок вроде лампы или вентилятора |
| Сервопривод SG90 | 1 | Управление 0–180° ползунком |
| Датчик температуры и влажности DHT11 | 1 | Данные для вывода на телефон |
| Телефон на Android | 1 | HC-05 не работает с iPhone |
| Макетная плата и перемычки | — | Общая земля (GND) обязательна |





Схема подключения: 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 Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| HC-05 VCC | 5V | Не нужен (встроенный Bluetooth) | VBUS (5 V) |
| HC-05 GND | GND | — | GND |
| HC-05 TXD | D10 (SoftwareSerial RX) | — | GP1 (UART0 RX) |
| HC-05 RXD | D11, через делитель 1 kΩ / 2 kΩ | — | GP0 (UART0 TX), без делителя |
| Светодиод (+ 220 Ω) | D5 (PWM) | GPIO 2 (встроенный светодиод) | GP15 |
| IN модуля реле | D7 | GPIO 26 | GP14 |
| Сигнал сервопривода (оранжевый) | D9 | — (можно добавить через ESP32Servo) | GP13 |
| Данные DHT11 | D2 | GPIO 4 | GP16 |
Протокол обмена: что будет отправлять телефон?
Прежде чем писать код, нужно решить, на каком «языке» будут говорить телефон и 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;41 | Arduino → телефон | Температура (°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 не нужно.
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 (Designer)
- Открой в браузере ai2.appinventor.mit.edu, войди через аккаунт Google и создай проект BlueGraysRemote через Projects › Start new project.
- Из группы Layout в палитре (Palette) перетащи HorizontalArrangement; положи в него ListPicker (Text: Подключить), Button (Text: Отключить) и Label (Text: Не подключено).
- Ниже добавь ещё два HorizontalArrangement: в первый положи кнопки «Светодиод вкл» и «Светодиод выкл», во второй — «Реле вкл» и «Реле выкл». Если задать кнопкам Width = Fill parent, по ним будет легче попадать пальцем.
- Добавь два Slider: для яркости MinValue 0, MaxValue 255, ThumbPosition 0; для сервопривода MinValue 0, MaxValue 180, ThumbPosition 90. Над каждым поставь Label с подписью, за что он отвечает.
- Добавь ещё два Label для температуры и влажности и увеличь у них FontSize примерно до 20.
- Из группы Connectivity добавь BluetoothClient; на панели Properties (свойства) установи DelimiterByte равным 10 и отметь NoLocationNeeded.
- Из группы Sensors добавь Clock; задай TimerInterval 200 и сними отметку TimerEnabled. Таймер включим блоками, когда соединение будет установлено.
| Компонент | Имя (Rename) | Важная настройка |
|---|---|---|
| ListPicker | LP_Connect | Text: Подключить |
| Button × 5 | BTN_Disconnect, BTN_LedOn, BTN_LedOff, BTN_RelayOn, BTN_RelayOff | Width: Fill parent |
| Slider | SLD_Brightness | MinValue 0, MaxValue 255 |
| Slider | SLD_Servo | MinValue 0, MaxValue 180, ThumbPosition 90 |
| Label | LBL_Status, LBL_Temp, LBL_Hum | FontSize 20 (для значений) |
| BluetoothClient | BluetoothClient1 | DelimiterByte 10, NoLocationNeeded отмечен |
| Clock | Clock1 | TimerInterval 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, если нет.
- В событие when LP_Connect.BeforePicking поставь блок set LP_Connect.Elements to BluetoothClient1.AddressesAndNames. Тогда список будет заново заполняться сопряжёнными устройствами при каждом открытии.
- В событие when LP_Connect.AfterPicking добавь блок if и вставь в его условие call BluetoothClient1.Connect address: LP_Connect.Selection.
- В части then блока if задай LBL_Status.Text значение «Подключено» и запусти чтение данных блоком set Clock1.TimerEnabled to true; в части else напиши «Не удалось подключиться».
- В событии when BTN_Disconnect.Click сначала set Clock1.TimerEnabled to false, затем call BluetoothClient1.Disconnect и в конце задай LBL_Status.Text значение «Не подключено». Если сначала остановить таймер, после разрыва связи не будет ни попытки чтения, ни сообщения об ошибке.
Отправка данных: кнопки и ползунки
Для отправки нужен всего один блок: call BluetoothClient1.SendText text. Если ввести в текстовый блок «L1\n», App Inventor превратит \n в конце в символ перевода строки. Отправка без соединения вызывает ошибку 515, поэтому оборачивать каждую отправку в проверку if BluetoothClient1.IsConnected — хорошая привычка. Для ползунка число соединяется с буквой и переводом строки блоком join: join("S", round(thumbPosition), "\n").
| Событие | Отправляемый текст | Результат на Arduino |
|---|---|---|
| BTN_LedOn.Click | L1\n | Светодиод горит на полную яркость |
| BTN_LedOff.Click | L0\n | Светодиод гаснет |
| BTN_RelayOn.Click | R1\n | Реле щёлкает и включается, подключённая лампа загорается |
| BTN_RelayOff.Click | R0\n | Реле отпускает |
| SLD_Brightness.PositionChanged | join("P", round(thumbPosition), "\n") | analogWrite(5, значение) |
| SLD_Servo.PositionChanged | join("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 мс), поэтому в буфере ничего не накапливается.
- Внутрь when Clock1.Timer поставь условие if BluetoothClient1.IsConnected.
- Добавь внутрь него второе условие: call BluetoothClient1.BytesAvailableToReceive > 0.
- Сохрани пришедшую строку в локальную переменную блоком initialize local line to call BluetoothClient1.ReceiveText numberOfBytes: -1.
- Преврати строку в список блоком split text line at ";": «24.5;41» становится списком из двух элементов [24.5, 41]. Результат сохрани в локальной переменной parts.
- 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, "%") (второй элемент списка). Проверка числа элементов не даёт строкам, пришедшим наполовину или с искажениями, вызывать ошибки.
Проблема с разрешением 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), либо, если в разрешении однажды отказано, приложение больше никогда его не запрашивает.
- В событии when Screen1.Initialize проверь версию Android: если select list item (split Screen1.PlatformVersion at ".") index 1 ≥ 12, передай в call Screen1.AskForPermission значение BluetoothConnect из списка Permission. В Android 11 и более ранних версиях этих разрешений нет, и запрос там тоже вызывает ошибку 908, поэтому проверка версии важна.
- В событии when Screen1.PermissionGranted, если выдано разрешение BLUETOOTH_CONNECT (contains text: permissionName, piece: "CONNECT"), запроси следом BluetoothScan. AskForPermission принимает только одно разрешение за раз, поэтому разрешения приходится выстраивать в такую цепочку.
- В событии when Screen1.PermissionDenied покажи через Notifier сообщение с путём «Настройки › Приложения › BlueGraysRemote › Разрешения › Устройства поблизости › Разрешить». Если обработать это событие, пользователь увидит понятное объяснение вместо голого окна с ошибкой на английском.
- Держи NoLocationNeeded у BluetoothClient в Designer отмеченным: эта настройка сообщает Android, что приложение не использует Bluetooth для определения местоположения пользователя.
- Обязательно тестируй и через 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 или в блоках сразу появляется на телефоне. Когда приложение закончено, собери устанавливаемый файл так:
- Выбери Screen1 в Designer и заполни AppName (имя на телефоне), Icon (квадратный PNG), VersionCode и VersionName.
- В верхнем меню выбери Build › Android App (.apk). Сборка обычно занимает минуту-две.
- Когда сборка завершится, появится окно с QR-кодом и кнопкой скачивания. Можно отсканировать QR-код телефоном и скачать файл напрямую или сохранить APK на компьютер и скопировать на телефон по USB. Ссылка из QR-кода действует недолго (около 2 часов).
- Когда откроешь APK на телефоне, Android попросит разрешить браузеру или файловому менеджеру установку неизвестных приложений. Если Play Protect покажет предупреждение, можно продолжить через вариант «Всё равно установить» в подробностях; такое предупреждение обычно для приложений не из Play Store с незнакомой подписью.
- При первом запуске приложения подтверди разрешение «Устройства поблизости», нажми «Подключить» и выбери HC-05 из списка.
То же приложение с ESP32, без 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.
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 нет 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.
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.
Связка 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.
Что собрать после прочтения
Все проекты
Мигающий светодиод
Самое первое «привет» в электронике: включение и выключение одного светодиода. Arduino включает и выключает светодиод через короткие промежутки, поэтому свет быстро мигает. Ты самым простым способом в

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

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

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

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

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

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

Односторонняя связь между двумя Arduino
Ты заставляешь две Arduino разговаривать по одному проводу: нажми кнопку на одной, и на другой загорится светодиод, нажми снова — погаснет. Плата-передатчик сообщает о нажатии кнопки коротким сообщени
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

