Guias · 19 min de leitura

MIT App Inventor com Arduino via Bluetooth: crie um app Android para o HC-05

Controle o Arduino por Bluetooth com o MIT App Inventor: ligação do HC-05, ListPicker, blocos de botão e slider, LED, relé, servo, dados de sensor, APK e correção para o Android 12.

Controlar o Arduino por Bluetooth com o App Inventor é o jeito mais rápido de criar seu próprio app Android sem escrever uma única linha de Java ou Kotlin. No MIT App Inventor, gratuito e que roda no navegador, você arrasta botões, sliders e rótulos de texto para a tela e define o que eles fazem com blocos que se encaixam como peças de quebra-cabeça. O celular se conecta a um módulo Bluetooth HC-05 ligado ao Arduino; cada toque no app viaja até o Arduino como um comando de texto curto, e o Arduino envia as leituras dos sensores de volta ao celular do mesmo jeito.

Ao fim deste guia você terá um app de controle remoto que funciona de verdade: um ListPicker que se conecta ao HC-05 a partir de uma lista, botões que ligam e desligam um LED e um relé, dois sliders que ajustam o brilho do LED e o ângulo do servo, e rótulos que mostram a temperatura e a umidade ao vivo de um DHT11. Passo a passo, você vai ver o código do Arduino, cada bloco do App Inventor, a geração do APK e a correção para o problema de permissão de Bluetooth que surgiu no Android 12. Se você nunca usou o HC-05, dê antes uma olhada rápida no guia módulo Bluetooth HC-05.

Módulo Bluetooth HC-05
O HC-05 é um módulo de Bluetooth clássico (SPP), exatamente o que o componente BluetoothClient do App Inventor usa; ele cria uma ligação serial sem fio entre o celular e o Arduino.

O que é o App Inventor e como ele conversa com o Arduino?

O MIT App Inventor é um ambiente gratuito de desenvolvimento de apps criado pelo Massachusetts Institute of Technology (MIT); você entra com sua conta Google e usa tudo no navegador. Ele tem duas telas: no Designer você monta o app arrastando componentes, e no editor de Blocks (blocos) você escreve regras como “quando o botão for clicado, faça isto” com blocos coloridos. Para testar o app sem instalar, você usa o app MIT AI2 Companion da Play Store; quando terminar, um único clique gera um arquivo APK instalável.

No lado do Bluetooth, quem faz o trabalho é o componente BluetoothClient. Ele usa o SPP (Serial Port Profile) do Bluetooth clássico, que é exatamente o que o HC-05 fala. O resultado é um cabo serial invisível entre o celular e o Arduino: quando o app envia “L1” com o bloco SendText, o Arduino lê esses dois caracteres com bt.read(), como se você os tivesse digitado no Monitor Serial. Tudo o que o Arduino envia com bt.print() chega ao bloco ReceiveText do celular. Não é preciso nenhuma biblioteca específica de Bluetooth no Arduino.

CamadaFunçãoNeste projeto
Interface (Designer)Componentes mostrados na telaListPicker, 5 Buttons, 2 Sliders, Labels
Lógica (Blocks)Blocos que reagem a eventosEventos Click, PositionChanged e Timer
BluetoothClientConexão SPP, envio e recebimento de dadosConnect, SendText, ReceiveText
HC-05Ponte entre Bluetooth e UART9600 baud, PIN 1234
ArduinoInterpreta comandos, aciona saídas, lê o sensorLED, relé, servo, DHT11

Do que você vai precisar

PeçaQtd.Observação
Arduino Uno (ou Nano)1Mesmo código e mesmos pinos nos dois
Módulo Bluetooth HC-05 ou HC-061Bluetooth clássico; o HM-10 (BLE) não funciona deste jeito
Resistores de 1 kΩ e 2 kΩ1 de cadaDivisor de tensão para o pino RXD do HC-05 (2,2 kΩ também serve)
LED e resistor de 220 Ω1 de cadaLED com brilho ajustável (D5, PWM)
Módulo relé 5 V1Para ligar cargas como uma lâmpada ou uma ventoinha
Servo motor SG901Controle de 0–180° com um slider
Sensor de temperatura e umidade DHT111Dados para mostrar no celular
Celular Android1O HC-05 não funciona com iPhone
Protoboard e jumpers—Um GND comum é obrigatório
Arduino Uno
Arduino Uno
Módulo Bluetooth HC-06
Módulo Bluetooth HC-06
Servo SG90
Servo SG90
Sensor DHT11
Sensor DHT11
LED
LED

Ligação: HC-05, LED, relé, servo e DHT11

A placa do HC-05 tem um regulador, então o pino VCC aceita 5 V, mas os pinos de dados usam lógica de 3,3 V. O Arduino lê sem problemas o sinal de 3,3 V que sai do pino TXD do módulo; já o sinal TX de 5 V do Arduino deve chegar ao pino RXD por meio de um divisor de tensão: D11 → 1 kΩ → RXD, RXD → 2 kΩ → GND. Isso coloca 5 × 2 / 3 ≈ 3,3 V no RXD. Ligamos o HC-05 em D10 e D11 com SoftwareSerial em vez dos pinos seriais de hardware do Uno (0 e 1), para que você possa carregar o código com o módulo conectado e ainda usar o Monitor Serial para depurar. Você pode conferir o resistor do LED com a calculadora de resistor para LED.

LigaçãoArduino UnoESP32Raspberry Pi Pico
VCC do HC-055VNão precisa (Bluetooth embutido)VBUS (5 V)
GND do HC-05GND—GND
TXD do HC-05D10 (RX da SoftwareSerial)—GP1 (RX da UART0)
RXD do HC-05D11, por divisor de 1 kΩ / 2 kΩ—GP0 (TX da UART0), sem divisor
LED (+ 220 Ω)D5 (PWM)GPIO 2 (LED da placa)GP15
IN do módulo reléD7GPIO 26GP14
Sinal do servo (laranja)D9— (dá para adicionar com ESP32Servo)GP13
Dados do DHT11D2GPIO 4GP16
Respeite três regras: todos os GNDs (Arduino, HC-05, relé, servo e qualquer fonte externa) precisam estar ligados entre si. Nunca coloque 5 V sem divisor no pino RXD do HC-05; o módulo pode aguentar por um tempo, mas pode falhar com o uso. Um servo em movimento pode puxar por um instante várias centenas de miliampères; se o Arduino estiver na USB, esse pico derruba a tensão, o HC-05 perde a conexão ou o Arduino reinicia. Alimentar o servo e o relé por uma fonte de 5 V separada resolve isso na raiz.

Protocolo de comunicação: o que o celular vai enviar?

Antes de escrever o código, decida qual “língua” o celular e o Arduino vão falar. Neste projeto, cada comando é uma letra, seguida de um número e, por fim, de um caractere de nova linha (\n, ASCII 10). A nova linha é muito importante: os dados Bluetooth podem chegar em pedaços, e quando “S1” e “35” chegam separados, o Arduino espera a nova linha e trata “S135” como um só comando. Como os comandos são texto legível, você também pode testá-los à mão com qualquer app de terminal Bluetooth.

ComandoDireçãoSignificado
L1 / L0Celular → ArduinoAcender o LED no máximo / apagar
P0 … P255Celular → ArduinoBrilho do LED (valor do analogWrite)
R1 / R0Celular → ArduinoAcionar / soltar o relé
S0 … S180Celular → ArduinoÂngulo do servo (graus)
24.5;41Arduino → celularTemperatura (°C) e umidade (%), separadas por ponto e vírgula

Em vez de texto, o App Inventor também pode enviar um único byte (0–255) com o bloco Send1ByteNumber, que o Arduino lê direto como número. É curto e rápido, mas só serve para um slider: se dois sliders e quatro botões enviarem bytes, o Arduino não consegue saber se um 90 que chegou é um ângulo do servo ou um nível de brilho. O formato letra + número + nova linha custa alguns bytes a mais, mas a 9600 baud passam cerca de 960 caracteres por segundo, então você nunca vai notar a diferença.

O código do Arduino

As bibliotecas Servo e SoftwareSerial vêm com o Arduino IDE. Para o DHT11, instale a DHT sensor library (Adafruit) pelo Gerenciador de Bibliotecas; quando ele perguntar sobre as dependências, escolha Instalar tudo para adicionar também a Adafruit Unified Sensor. Como não usamos os pinos 0 e 1, não é preciso desconectar o HC-05 para carregar o código.

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}
  • Buffer de linha: o loop() acrescenta cada caractere que chega à variável komut (comando); quando chega \n, ele passa a linha para komutIsle() e limpa o buffer. Tudo o que passar de 16 caracteres é descartado, então dados lixo não conseguem encher a memória.
  • trim(): alguns apps de terminal também acrescentam \r no fim da linha; o trim() remove esse caractere junto com os espaços, então “L1\r” continua sendo reconhecido como “L1”.
  • Letra e número: charAt(0) dá a letra do comando e substring(1).toInt() o número depois dela. constrain() limita o valor a uma faixa segura; mesmo que o celular envie 300 por engano, o servo nunca é empurrado além de 180°.
  • Envio sem bloqueio: o sensor é lido a cada 2 segundos usando millis(). Como não há delay(), os comandos que chegam nesse meio-tempo são tratados sem atraso; veja millis ou delay para os detalhes.
  • print('\n') em vez de println: o println() termina a linha com \r\n. O App Inventor só usa \n como delimitador, então o \r grudaria no fim do valor de umidade; enviar só \n mantém limpa a interpretação no celular.
  • Relé ativo em LOW: a maioria dos módulos relé liga quando o IN vai para LOW. Se o seu liga com HIGH, basta trocar os valores das constantes ROLE_ACIK (ligado) e ROLE_KAPALI (desligado).
Antes de passar para o App Inventor, teste o lado do Arduino sozinho: conecte-se ao HC-05 com um app como o Serial Bluetooth Terminal da Play Store, digite “L1” e envie; o LED deve acender e L1 deve aparecer no Monitor Serial. Se linhas como “24.5;41” passarem a cada 2 segundos, os dados também estão voltando. Se algo der errado depois, você vai saber na hora se o defeito está no app ou no circuito.

Desenhando a interface no App Inventor (Designer)

  1. Acesse ai2.appinventor.mit.edu no navegador, entre com sua conta Google e crie um projeto chamado BlueGraysRemote em Projects > Start new project (Projetos > Iniciar novo projeto).
  2. No grupo Layout da Palette (paleta), arraste um HorizontalArrangement; coloque dentro dele um ListPicker (Text: Conectar), um Button (Text: Desconectar) e um Label (Text: Não conectado).
  3. Adicione mais dois HorizontalArrangements abaixo: coloque os botões Ligar LED e Desligar LED no primeiro, e Ligar relé e Desligar relé no segundo. Definir a Width (largura) dos botões como Fill parent deixa mais fácil tocá-los.
  4. Adicione dois Sliders: para o brilho, MinValue 0, MaxValue 255, ThumbPosition 0; para o servo, MinValue 0, MaxValue 180, ThumbPosition 90. Coloque acima de cada um um Label dizendo o que ele faz.
  5. Adicione mais dois Labels para temperatura e umidade e aumente o FontSize deles para cerca de 20.
  6. No grupo Connectivity (conectividade), adicione um BluetoothClient; no painel Properties, defina DelimiterByte como 10 e marque NoLocationNeeded.
  7. No grupo Sensors (sensores), adicione um Clock; defina TimerInterval como 200 e desmarque TimerEnabled. Vamos ligar o temporizador com blocos quando a conexão estiver feita.
ComponenteNome (Rename)Configuração principal
ListPickerLP_ConnectText: Conectar
Button × 5BTN_Disconnect, BTN_LedOn, BTN_LedOff, BTN_RelayOn, BTN_RelayOffWidth: Fill parent (preencher)
SliderSLD_BrightnessMinValue 0, MaxValue 255
SliderSLD_ServoMinValue 0, MaxValue 180, ThumbPosition 90
LabelLBL_Status, LBL_Temp, LBL_HumFontSize 20 (para os valores)
BluetoothClientBluetoothClient1DelimiterByte 10, NoLocationNeeded marcado
ClockClock1TimerInterval 200, TimerEnabled desmarcado

Conectando ao HC-05 com um ListPicker

Primeiro pareie o HC-05 nas configurações de Bluetooth do celular (o PIN normalmente é 1234, senão 0000). Esse passo é essencial: BluetoothClient1.AddressesAndNames só lista dispositivos pareados e não procura dispositivos novos por perto. Cada item é um texto com o endereço MAC primeiro e o nome do dispositivo depois, como “98:D3:31:F5:2A:10 HC-05”. Você pode passar o texto inteiro ao bloco Connect; o App Inventor descarta sozinho tudo o que vem depois do primeiro espaço. O bloco retorna true se a conexão der certo e false se falhar.

  1. No evento when LP_Connect.BeforePicking, coloque set LP_Connect.Elements to BluetoothClient1.AddressesAndNames. A lista é preenchida de novo com os dispositivos pareados toda vez que se abre.
  2. No evento when LP_Connect.AfterPicking, adicione um bloco if e encaixe call BluetoothClient1.Connect address: LP_Connect.Selection na condição dele.
  3. Na parte then do if, defina LBL_Status.Text como “Conectado” e comece a ler os dados com set Clock1.TimerEnabled to true; na parte else, escreva “Falha na conexão”.
  4. No evento when BTN_Disconnect.Click, primeiro set Clock1.TimerEnabled to false, depois call BluetoothClient1.Disconnect e, por fim, defina LBL_Status.Text como “Não conectado”. Parar o temporizador primeiro evita uma leitura e uma mensagem de erro depois que a conexão é fechada.
Depois de conectado, o LED do HC-05 para de piscar rápido e passa a dar duas piscadas curtas a cada 2 segundos. Isso mostra o estado da conexão sem olhar o app; se o LED voltar a piscar rápido, a conexão caiu.

Enviando dados: botões e sliders

Enviar exige um único bloco: call BluetoothClient1.SendText text. Quando você digita “L1\n” em um bloco de texto, o App Inventor transforma o \n final em um caractere de nova linha. Enviar sem estar conectado causa o erro 515, então envolver cada envio em uma verificação if BluetoothClient1.IsConnected é um bom hábito. Para um slider, você junta o número à letra e à nova linha com um bloco join: join("S", round(thumbPosition), "\n").

EventoTexto enviadoResultado no Arduino
BTN_LedOn.ClickL1\nO LED acende com brilho máximo
BTN_LedOff.ClickL0\nO LED apaga
BTN_RelayOn.ClickR1\nO relé clica e a lâmpada ligada nele acende
BTN_RelayOff.ClickR0\nO relé solta
SLD_Brightness.PositionChangedjoin("P", round(thumbPosition), "\n") (letra + valor + nova linha)analogWrite(5, valor)
SLD_Servo.PositionChangedjoin("S", round(thumbPosition), "\n") (letra + ângulo + nova linha)servo.write(ângulo)

Enquanto você arrasta um slider com o dedo, o evento PositionChanged dispara dezenas de vezes por segundo e thumbPosition retorna valores fracionários como 87.4. Se você enviar a cada evento, o mesmo ângulo sai várias vezes, o buffer do HC-05 enche e o servo se move aos trancos e atrasado. A solução é simples: crie uma variável chamada global lastAngle; envie só se o valor arredondado com round(thumbPosition) for diferente dela e depois atualize a variável. Assim, os dados só saem quando o ângulo realmente muda. Aplique o mesmo truque ao slider de brilho com global lastBrightness.

Recebendo dados: mostrando os valores do sensor no celular

O Arduino envia uma linha “24.5;41” a cada 2 segundos. O App Inventor não tem um evento que dispara sozinho quando os dados chegam, então usamos o componente Clock para verificar “chegou dado novo?” em intervalos regulares. Como definimos DelimiterByte como 10 no Designer, passar -1 como numberOfBytes ao bloco ReceiveText faz ele ler até o caractere de nova linha e retornar a linha sem o \n. Um temporizador de 200 ms verifica com muito mais frequência do que os dados chegam (a cada 2000 ms), então nada se acumula no buffer.

  1. Coloque uma condição if BluetoothClient1.IsConnected dentro de when Clock1.Timer.
  2. Adicione uma segunda condição dentro dela: call BluetoothClient1.BytesAvailableToReceive > 0.
  3. Guarde a linha que chegou em uma variável local com initialize local line to call BluetoothClient1.ReceiveText numberOfBytes: -1.
  4. Transforme a linha em uma lista com o bloco split text line at ";": “24.5;41” vira a lista de dois itens [24.5, 41]. Guarde o resultado em uma variável local chamada parts.
  5. if length of list parts = 2, defina LBL_Temp.Text como join("Temperatura: ", select list item parts index 1, " °C") e LBL_Hum.Text como join("Umidade: ", select list item parts index 2, " %"). Verificar a quantidade de itens impede que linhas recebidas pela metade ou corrompidas causem erros.
Se você passar -1 ao ReceiveText mas esquecer de definir DelimiterByte como 10 (o padrão é 0), o bloco fica esperando um byte 0 que nunca chega e o app parece travado. E se o Arduino enviar a linha com println(), o caractere \r acaba no fim do segundo valor; é por isso que o código envia só '\n'.
Não pare em mostrar os valores como texto: você pode deixar LBL_Temp.TextColor vermelho quando a temperatura passar de 30 °C, abrir um alerta com o componente Notifier ou desenhar em um Canvas um gráfico de linha das últimas leituras.

O problema de permissão de Bluetooth no Android 12 e posteriores

As permissões de Bluetooth mudaram com o Android 12 (API 31). A antiga permissão BLUETOOTH, concedida automaticamente na instalação, foi substituída por BLUETOOTH_CONNECT e BLUETOOTH_SCAN, que o usuário precisa aprovar com o app em execução; nas configurações do celular, elas aparecem como a permissão Dispositivos por perto. Se ela não tiver sido concedida, o App Inventor abre a janela de permissão na primeira tentativa, mas interrompe o bloco que estava rodando. O resultado é um conjunto de sintomas conhecidos: a lista abre vazia na primeira vez que você toca em Conectar, aparece a mensagem “Error 908: The permission ... has been denied” (citando BLUETOOTH_CONNECT ou BLUETOOTH_SCAN) ou, se o usuário recusou uma vez, o app nunca mais pergunta.

  1. No evento when Screen1.Initialize, verifique a versão do Android: se select list item (split Screen1.PlatformVersion at ".") index 1 ≥ 12, passe ao call Screen1.AskForPermission o valor BluetoothConnect da lista Permission. Essas permissões não existem no Android 11 e anteriores, e pedi-las lá também gera o erro 908, então a verificação da versão é importante.
  2. No evento when Screen1.PermissionGranted, se a permissão concedida for BLUETOOTH_CONNECT (contains text: permissionName, piece: "CONNECT"), peça em seguida a BluetoothScan. O AskForPermission só aceita uma permissão por vez, então você as encadeia assim.
  3. No evento when Screen1.PermissionDenied, use um Notifier para mostrar uma mensagem com o caminho “Configurações > Apps > BlueGraysRemote > Permissões > Dispositivos por perto > Permitir”. Tratar esse evento mostra ao usuário uma explicação clara em vez de uma caixa de erro crua.
  4. Mantenha NoLocationNeeded marcado no BluetoothClient, no Designer; ele informa ao Android que o app não usa o Bluetooth para descobrir a localização do usuário.
  5. Teste sempre também com o APK: as permissões do AI2 Companion pertencem ao app Companion, enquanto o seu APK pede as próprias permissões depois de instalado.
ErroSignificadoSolução
Error 507: Unable to connect. Is the device turned on? (não foi possível conectar)Não conseguiu se conectar ao móduloConfira a alimentação do HC-05, se ele não está conectado a outro celular e o pareamento; se ainda falhar, desmarque Secure no BluetoothClient e tente de novo
Error 515: Not connected to a Bluetooth device. (sem conexão)Envio ou leitura sem conexãoEnvolva os blocos de envio e do Clock em uma verificação IsConnected
Error 516: Unable to write: Broken pipe (conexão interrompida)A conexão caiu no meio (falta de energia, fora do alcance)Pare o Clock, chame Disconnect e reconecte; dê ao servo e ao relé uma alimentação própria
Error 908: The permission … has been denied (permissão negada)Sem a permissão Dispositivos por perto no Android 12+Peça-a com blocos ou conceda-a manualmente nas Configurações
A lista abre vaziaMódulo não pareado, ou a permissão acabou de ser pedidaPareie o HC-05 nas configurações do celular; depois de conceder a permissão, toque em Conectar de novo

Testando o app e gerando o APK

Você não precisa instalar um APK a cada mudança enquanto desenvolve. Instale o app MIT AI2 Companion da Play Store; com o computador e o celular na mesma rede Wi-Fi, escolha Connect > AI Companion no App Inventor e leia o QR code ou digite o código de 6 caracteres que aparece na tela. Cada mudança que você faz no Designer ou nos blocos aparece na hora no celular. Quando o app estiver pronto, gere o arquivo instalável assim:

  1. Selecione Screen1 no Designer e preencha AppName (o nome que aparece no celular), Icon (um PNG quadrado), VersionCode e VersionName.
  2. No menu de cima, escolha Build > Android App (.apk). A geração normalmente leva um ou dois minutos.
  3. Quando terminar, aparece uma janela com um QR code e um botão de download. Você pode ler o QR code com o celular para baixar o arquivo direto ou salvar o APK no computador e copiá-lo para o celular pela USB. O link do QR só vale por pouco tempo (cerca de 2 horas).
  4. Quando você abrir o APK no celular, o Android vai pedir para permitir que o navegador ou o gerenciador de arquivos instale apps desconhecidos. Se o Play Protect mostrar um aviso, você pode continuar com a opção de instalar mesmo assim nos detalhes; esse aviso é comum para apps de fora da Play Store com uma assinatura desconhecida.
  5. Na primeira vez que abrir o app, aprove a permissão Dispositivos por perto, toque em Conectar e escolha o HC-05 na lista.
Faça backup do projeto com Projects > Export selected project (.aia) to my computer; você pode compartilhar o arquivo .aia com um amigo ou levá-lo para outra conta. Se quiser publicar o app no Google Play, use Build > Android App Bundle (.aab) e guarde bem sua chave de assinatura com Projects > Export keystore: as atualizações precisam ser assinadas com a mesma chave. Não se esqueça de aumentar o VersionCode em 1 a cada atualização.

O mesmo app com um ESP32, sem precisar de HC-05

Placa de desenvolvimento ESP32
O ESP32 clássico tem Bluetooth clássico embutido; o BluetoothClient do App Inventor se conecta a ele do mesmo jeito que a um HC-05.

O ESP32 clássico tem Bluetooth clássico e BLE embutidos. A biblioteca BluetoothSerial apresenta o ESP32 ao celular como um dispositivo SPP, então você pode usar o app que acabou de criar sem mudar nada. Pareie o dispositivo chamado “BlueGrays-ESP32” nas configurações de Bluetooth do celular, escolha-o na lista do app e pronto. Atenção: o ESP32-S3, o ESP32-C3 e o ESP32-C6 só suportam BLE; eles não têm BluetoothSerial, e no lado do App Inventor você precisa da extensão BluetoothLE. Para trabalhar com BLE, veja o guia Bluetooth e BLE no ESP32.

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}

O código entende os mesmos comandos da versão do Uno e envia os dados no mesmo formato “temperatura;umidade”; ele ignora os comandos S porque não há servo ligado. A biblioteca Servo do Arduino não funciona no ESP32; para adicionar um servo, instale a biblioteca ESP32Servo e use as mesmas chamadas attach() e write() em, por exemplo, o GPIO 13. Alimente o módulo relé pelo pino VIN (5 V); se a entrada IN dele não acionar de forma confiável com o sinal de 3,3 V do ESP32, escolha um módulo relé compatível com 3,3 V. Para os detalhes, veja O que é um módulo relé?

Com o Raspberry Pi Pico e MicroPython

Raspberry Pi Pico
O Raspberry Pi Pico trabalha em 3,3 V, então se liga direto ao pino RXD do HC-05, sem divisor de tensão.

O Raspberry Pi Pico padrão não tem Bluetooth, então você liga o HC-05 nos pinos da UART0 (GP0 e GP1). Como os pinos do Pico trabalham em 3,3 V, desta vez não é preciso divisor de tensão; alimente o módulo pelo pino VBUS, que traz os 5 V da USB. O Pico W e o Pico 2 W têm Bluetooth embutido, mas o MicroPython só suporta BLE neles; para conversar com o BluetoothClient clássico do App Inventor, o HC-05 continua sendo o caminho mais fácil. O código abaixo entende os mesmos comandos, aciona o servo com PWM de 50 Hz e envia os dados no mesmo formato; o módulo dht já vem embutido no firmware MicroPython do 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)

Ideias de projetos com App Inventor e Arduino

  • Carrinho controlado por Bluetooth: associe os botões de direção às letras F, B, L e R e acione os motores com um L298N; todos os passos estão no guia carrinho robô com Arduino.
  • Painel de casa inteligente: controle uma lâmpada, uma tomada e uma ventoinha em uma só tela com um módulo relé de quatro canais; para ideias, veja o projeto de casa inteligente com Arduino.
  • Luminária com seletor de cor: ajuste separadamente os valores de vermelho, verde e azul de um LED RGB com três sliders.
  • Braço robótico: adicione um slider por junta e comande os servo motores pelo celular.
  • Monitor de estufa: mostre no celular as leituras do DHT11 e do sensor de umidade do solo e adicione um botão que liga a bomba quando o solo secar.
  • Controle por voz: use o componente SpeechRecognizer do App Inventor para criar um app que envia L1 quando você diz “acender luz”.

Erros comuns e como resolvê-los

  • O app conecta, mas o LED não acende: os fios TXD e RXD do HC-05 estão trocados, ou os pinos da SoftwareSerial no código não correspondem ao circuito. O TXD do módulo tem que ir no RX do Arduino (D10).
  • Os comandos são ignorados: falta o \n no bloco de texto; o Arduino espera a nova linha. Confira se os comandos que chegam aparecem no Monitor Serial.
  • Caracteres embaralhados: o baud rate do HC-05 é diferente do bt.begin(). O módulo sai de fábrica com 9600 baud; se foi mudado com comandos AT, ajuste o código para combinar.
  • O servo treme ou a conexão cai: o servo está derrubando o barramento de 5 V do Arduino; alimente-o por uma fonte de 5 V separada e una os GNDs. Uma tremida leve também pode vir da SoftwareSerial bloqueando as interrupções por um instante enquanto recebe; para resolver de vez, use um Arduino Mega ou um ESP32, que têm portas seriais de hardware sobrando. De vez em quando o servo também pode pular por um instante para um ângulo errado: a biblioteca DHT desliga as interrupções por cerca de 5 ms a cada leitura, e um comando que chegue bem nessa hora pode se corromper; o próximo comando do slider corrige isso.
  • Os valores no celular ficam atrasados: o intervalo do Clock é maior que o intervalo de envio do Arduino, então as linhas se acumulam. Mantenha o TimerInterval menor que o intervalo de envio.
  • O app trava: o DelimiterByte ainda está em 0, ou o Arduino não termina as linhas com \n; o ReceiveText(-1) espera um delimitador que nunca chega.
  • Não funciona no iPhone: o HC-05 usa Bluetooth clássico, que o iOS não permite. Um iPhone precisa de um módulo BLE (HM-10) ou de um ESP32.
Para solucionar problemas, divida a cadeia em duas: primeiro teste o lado do Arduino com o Serial Bluetooth Terminal, depois o app do App Inventor. Se o terminal funciona e o app não, o problema está nos blocos; se o terminal também falha, confira a ligação ou o código do Arduino. Para conferir os números dos pinos, use a ferramenta pinagem.

Combinar App Inventor e Arduino é o jeito mais divertido de aprender desenvolvimento de apps e eletrônica em um só projeto. Depois de montar a conexão e o protocolo, o mesmo esqueleto pode virar o controle de um carrinho, de uma casa inteligente ou de uma estufa; a única coisa que muda é quais letras os botões enviam. No app BlueGrays você encontra, passo a passo e com esquemas do circuito e código, os projetos de controle de LED por Bluetooth e de carrinho controlado por Bluetooth com L298N.

Perguntas frequentes

Um app Bluetooth do App Inventor funciona no iPhone?

Não com um HC-05 ou HC-06. Esses módulos usam Bluetooth clássico (SPP), e o iOS não deixa apps comuns abrirem esse tipo de conexão serial. Para conversar com um iPhone, você precisa passar para um módulo BLE como o HM-10 ou para um ESP32; para projetos com HC-05, um celular Android é a escolha que dá menos dor de cabeça.

Por que a lista de dispositivos Bluetooth fica vazia no App Inventor?

O AddressesAndNames só lista dispositivos já pareados com o celular, então primeiro pareie o HC-05 nas configurações de Bluetooth usando o PIN 1234. No Android 12 e posteriores, a lista também abre vazia na primeira vez se a permissão Dispositivos por perto não tiver sido concedida; conceda-a e toque em Conectar de novo. Confira também se o Bluetooth está ligado.

Como resolvo o erro 507 do App Inventor?

O erro 507 significa “Unable to connect. Is the device turned on?”: o celular não conseguiu abrir uma conexão com o módulo. Confira se o HC-05 está alimentado, se o LED dele está piscando rápido e se ele não está conectado a outro celular. Se o problema continuar, remova o pareamento e pareie de novo, ou tente desligar a propriedade Secure do BluetoothClient.

Como gero um APK no App Inventor?

Abra o projeto e clique em Build > Android App (.apk) no menu de cima. Quando a geração terminar, leia o QR code com o celular ou baixe o APK no computador e copie-o para o celular. Durante a instalação, você vai precisar permitir a instalação de apps desconhecidos.

O App Inventor consegue se conectar a um ESP32 por Bluetooth?

Sim. Com a biblioteca BluetoothSerial, o ESP32 clássico se comporta como um dispositivo SPP, e o BluetoothClient do App Inventor se conecta a ele do mesmo jeito que a um HC-05. O ESP32-S3 e o ESP32-C3 só suportam BLE, então nessas placas você precisa da extensão BluetoothLE.

Posso publicar meu app do App Inventor no Google Play?

Sim. A opção Android App Bundle (.aab) do menu Build gera um arquivo que você pode enviar ao Google Play; você vai precisar de uma conta de desenvolvedor no Google Play. As atualizações precisam ser assinadas com a mesma chave, então faça backup do seu keystore com Projects > Export keystore.

Ver todos os projetos
LED piscante
#1Iniciante

LED piscante

O primeiro contato com a eletrônica: acender e apagar um único LED. O Arduino acende e apaga o LED em intervalos curtos, fazendo a luz piscar rapidamente. Você vê da forma mais simples como a placa co

4.6(188)
1127
Temperatura e umidade com DHT11
#55Intermediário

Temperatura e umidade com DHT11

Qual é a temperatura do seu quarto? O ar está úmido ou seco? O sensor DHT11 mede a temperatura e a umidade, e o Arduino mostra os valores em um display LCD de duas linhas, atualizando a cada poucos se

4.7(47)
73
Controle de LED pelo celular via Bluetooth
#34Iniciante
Premium

Controle de LED pelo celular via Bluetooth

Um pequeno ensaio para acender e apagar as luzes do seu quarto pelo celular. O módulo Bluetooth repassa ao Arduino as letras enviadas pelo celular; uma letra acende o LED vermelho, outra acende o verd

4.8(68)
89
Carrinho controlado por Bluetooth com L298N
#80Avançado
Premium

Carrinho controlado por Bluetooth com L298N

Transforme seu celular em um controle remoto e dirija seu próprio carrinho robô. O Arduino recebe os comandos de um aplicativo de controle remoto no celular por meio de um módulo Bluetooth e aciona as

5.0(38)
65
Uso de servo motor
#35Iniciante
Premium

Uso de servo motor

O braço de um pequeno servo motor gira para três posições em sequência: o início, o meio e o fim do percurso, esperando dois segundos em cada parada. O Arduino informa ao motor em qual ângulo ficar, e

4.7(48)
67
Uso de LED RGB
#39Iniciante
Premium

Uso de LED RGB

Três cores se escondem em um único LED: o LED RGB acende primeiro em vermelho, depois em verde e, por fim, em azul. Cada cor fica acesa por meio segundo e apagada por meio segundo antes de o ciclo rec

4.9(73)
98
Acender uma lâmpada de 220V com botão de pressão
#19Avançado
Premium

Acender uma lâmpada de 220V com botão de pressão

Enquanto você mantém o botão pressionado, uma lâmpada de 220 volts fica acesa e apaga assim que você solta. O sinal fraco do Arduino não consegue acender a lâmpada diretamente, então um relé, que é um

4.4(30)
38
Comunicação unidirecional entre 2 Arduinos
#57Intermediário
Premium

Comunicação unidirecional entre 2 Arduinos

Faça dois Arduinos conversarem por um único fio: ao pressionar o botão de um, o LED do outro acende; ao pressionar de novo, ele apaga. A placa transmissora informa que o botão foi pressionado com uma

4.4(37)
50