App Inventor Arduino Bluetooth: crea la tua app Android con HC-05
Controlla Arduino via Bluetooth con App Inventor: collegamento HC-05, ListPicker, blocchi per pulsanti e cursori, LED, relè, servo, sensori, APK e Android 12.
Controllare Arduino via Bluetooth con App Inventor è il modo più rapido per creare la tua app Android senza scrivere una sola riga di Java o Kotlin. In MIT App Inventor, lo strumento gratuito del MIT che funziona nel browser, trascini sullo schermo pulsanti, cursori ed etichette di testo e stabilisci cosa fanno con blocchi che si incastrano come tessere di un puzzle. Il telefono si collega a un modulo Bluetooth HC-05 cablato ad Arduino; ogni tocco nell'app arriva ad Arduino come un breve comando di testo, e Arduino rimanda al telefono le letture dei sensori per la stessa strada.
Alla fine di questa guida avrai un'app di controllo remoto che funziona davvero: un ListPicker che si collega all'HC-05 da un elenco, pulsanti che accendono e spengono un LED e un relè, due cursori (Slider) per la luminosità del LED e l'angolo del servo, ed etichette che mostrano in tempo reale temperatura e umidità di un DHT11. Vedrai passo dopo passo il codice Arduino, ogni blocco di App Inventor, la creazione dell'APK e la soluzione al problema dei permessi Bluetooth arrivato con Android 12. Se non hai mai usato l'HC-05, dai prima un'occhiata alla guida al modulo Bluetooth HC-05.

Che cos'è App Inventor e come comunica con Arduino?
MIT App Inventor è un ambiente gratuito per lo sviluppo di app creato dal Massachusetts Institute of Technology (MIT); accedi con il tuo account Google e lavori direttamente nel browser. Ha due viste: nel Designer prepari l'aspetto dell'app trascinando i componenti, mentre nell'editor Blocks scrivi con blocchi colorati regole come «quando si preme il pulsante, fai questo». Per provare l'app senza installarla si usa l'app MIT AI2 Companion del Play Store; a lavoro finito, un clic genera un file APK installabile.
Per il Bluetooth il lavoro lo fa il componente BluetoothClient. Usa il profilo SPP (Serial Port Profile, profilo di porta seriale) del Bluetooth classico, che è proprio quello dell'HC-05. Il risultato è un cavo seriale invisibile tra telefono e Arduino: quando l'app invia «L1» con il blocco SendText, Arduino legge quei due caratteri con bt.read(), come se li avessi scritti nel Monitor seriale. Tutto ciò che Arduino invia con bt.print() arriva al blocco ReceiveText del telefono. Su Arduino non serve alcuna libreria Bluetooth specifica.
| Livello | Compito | In questo progetto |
|---|---|---|
| Interfaccia (Designer) | Componenti visibili sullo schermo | ListPicker, 5 Button, 2 Slider, Label |
| Logica (Blocks) | Blocchi che reagiscono agli eventi | Eventi Click, PositionChanged e Timer |
| BluetoothClient | Connessione SPP, invio e ricezione dei dati | Connect, SendText, ReceiveText |
| HC-05 | Ponte tra Bluetooth e UART | 9600 baud, PIN 1234 |
| Arduino | Interpreta i comandi, pilota le uscite, legge il sensore | LED, relè, servo, DHT11 |
Materiale necessario
| Materiale | Quantità | Nota |
|---|---|---|
| Arduino Uno (o Nano) | 1 | Codice e pin sono uguali su entrambi |
| Modulo Bluetooth HC-05 o HC-06 | 1 | Bluetooth classico; l'HM-10 (BLE) non funziona in questo modo |
| Resistenze da 1 kΩ e 2 kΩ | 1 ciascuna | Partitore di tensione per il pin RXD dell'HC-05 (va bene anche 2,2 kΩ) |
| LED e resistenza da 220 Ω | 1 ciascuno | LED con luminosità regolabile (D5, PWM) |
| Modulo relè da 5 V | 1 | Per accendere carichi come una lampada o un ventilatore |
| Servomotore SG90 | 1 | Controllo da 0 a 180° con un cursore |
| Sensore di temperatura e umidità DHT11 | 1 | Dati da mostrare sul telefono |
| Telefono Android | 1 | L'HC-05 non funziona con l'iPhone |
| Breadboard e cavetti jumper | — | Una GND comune è indispensabile |





Circuito: collegamento di HC-05, LED, relè, servo e DHT11
La scheda dell'HC-05 ha un regolatore, quindi il pin VCC accetta 5 V, ma i pin dati lavorano con logica a 3,3 V. Arduino legge senza problemi il segnale a 3,3 V del pin TXD del modulo; il segnale TX a 5 V di Arduino, invece, deve arrivare al pin RXD attraverso un partitore di tensione: D11 → 1 kΩ → RXD, RXD → 2 kΩ → GND. Così su RXD arrivano 5 × 2 / 3 ≈ 3,3 V. Colleghiamo l'HC-05 a D10 e D11 con SoftwareSerial invece che ai pin seriali hardware dell'Uno (0 e 1): in questo modo puoi caricare il codice con il modulo collegato e continuare a usare il Monitor seriale per il debug. Puoi verificare la resistenza del LED con il calcolatore di resistenza per LED.
| Collegamento | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| HC-05 VCC | 5V | Non serve (Bluetooth integrato) | VBUS (5 V) |
| HC-05 GND | GND | — | GND |
| HC-05 TXD | D10 (RX di SoftwareSerial) | — | GP1 (UART0 RX) |
| HC-05 RXD | D11, tramite partitore 1 kΩ / 2 kΩ | — | GP0 (UART0 TX), senza partitore |
| LED (+ 220 Ω) | D5 (PWM) | GPIO 2 (LED sulla scheda) | GP15 |
| IN del modulo relè | D7 | GPIO 26 | GP14 |
| Segnale del servo (arancione) | D9 | — (si può aggiungere con ESP32Servo) | GP13 |
| Dati del DHT11 | D2 | GPIO 4 | GP16 |
Protocollo di comunicazione: cosa invierà il telefono?
Prima di scrivere il codice bisogna decidere quale «lingua» parleranno telefono e Arduino. In questo progetto ogni comando è formato da una lettera, seguita da un numero e infine da un carattere di a capo (\n, ASCII 10). L'a capo è fondamentale: i dati Bluetooth possono arrivare a pezzi, e se «S1» e «35» arrivano separati, Arduino aspetta l'a capo ed elabora «S135» come un unico comando. Visto che i comandi sono testo leggibile, puoi provarli anche a mano con qualsiasi app terminale Bluetooth.
| Comando | Direzione | Significato |
|---|---|---|
| L1 / L0 | Telefono → Arduino | Accendere il LED al massimo / spegnerlo |
| P0 … P255 | Telefono → Arduino | Luminosità del LED (valore di analogWrite) |
| R1 / R0 | Telefono → Arduino | Eccitare / rilasciare il relè |
| S0 … S180 | Telefono → Arduino | Angolo del servo (gradi) |
| 24.5;41 | Arduino → telefono | Temperatura (°C) e umidità (%), separate da un punto e virgola |
Invece del testo, App Inventor può anche inviare un singolo byte (0–255) con il blocco Send1ByteNumber, che Arduino legge direttamente come numero. È un metodo breve e veloce, ma adatto a un solo cursore: se due cursori e quattro pulsanti inviano byte, Arduino non può capire se un 90 in arrivo sia un angolo del servo o una luminosità. Il formato lettera + numero + a capo occupa qualche byte in più, ma a 9600 baud passano circa 960 caratteri al secondo, quindi non noterai alcuna differenza.
Il codice per Arduino
Le librerie Servo e SoftwareSerial sono incluse nell'IDE di Arduino. Per il DHT11 installa DHT sensor library (Adafruit) dal Gestore librerie; quando ti chiede delle dipendenze, scegli Install all per aggiungere anche Adafruit Unified Sensor. Poiché non usiamo i pin 0 e 1, non serve scollegare l'HC-05 durante il caricamento.
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 di riga: loop() aggiunge ogni carattere ricevuto alla variabile komut (comando); quando arriva \n passa la riga a komutIsle() e svuota il buffer. Oltre i 16 caratteri il resto viene scartato, così i dati spazzatura non riempiono la memoria.
- trim(): Alcune app terminale aggiungono anche \r a fine riga; trim() lo elimina insieme agli spazi, quindi «L1\r» viene comunque riconosciuto come «L1».
- Lettera e numero: charAt(0) restituisce la lettera del comando e substring(1).toInt() il numero che la segue. constrain() limita il valore a un intervallo sicuro: anche se il telefono inviasse per errore 300, il servo non verrebbe mai spinto oltre 180°.
- Invio non bloccante: Il sensore viene letto ogni 2 secondi con millis(). Dato che non c'è delay(), i comandi che arrivano nel frattempo vengono gestiti senza ritardo; dettagli in millis() o delay().
- print('\n') invece di println: println() chiude la riga con \r\n. App Inventor usa solo \n come delimitatore, quindi il \r resterebbe attaccato alla fine del valore di umidità; inviare solo \n mantiene pulita la suddivisione sul telefono.
- Relè attivo basso: La maggior parte dei moduli relè si attiva quando IN va a LOW. Se il tuo si attiva con HIGH, basta scambiare i valori delle costanti ROLE_ACIK (acceso) e ROLE_KAPALI (spento).
Progettare l'interfaccia in App Inventor (Designer)
- Apri ai2.appinventor.mit.edu nel browser, accedi con il tuo account Google e crea un progetto chiamato BlueGraysRemote con Projects > Start new project.
- Dal gruppo Layout della Palette trascina un HorizontalArrangement e mettici dentro un ListPicker (Text: Connetti), un Button (Text: Disconnetti) e un Label (Text: Non connesso).
- Aggiungi sotto altri due HorizontalArrangement: nel primo i pulsanti Accendi LED e Spegni LED, nel secondo Accendi relè e Spegni relè. Impostando Width su Fill parent, i pulsanti si premono più facilmente.
- Aggiungi due Slider: per la luminosità MinValue 0, MaxValue 255, ThumbPosition 0; per il servo MinValue 0, MaxValue 180, ThumbPosition 90. Sopra ciascuno metti un Label che ne spieghi la funzione.
- Aggiungi altri due Label per temperatura e umidità e porta il loro FontSize a circa 20.
- Dal gruppo Connectivity aggiungi un BluetoothClient; nel pannello Properties imposta DelimiterByte a 10 e spunta NoLocationNeeded.
- Dal gruppo Sensors aggiungi un Clock; imposta TimerInterval a 200 e togli la spunta a TimerEnabled. Attiveremo il timer con i blocchi una volta stabilita la connessione.
| Componente | Nome (Rename) | Impostazione importante |
|---|---|---|
| ListPicker | LP_Connect | Text: Connetti |
| 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 (per i valori) |
| BluetoothClient | BluetoothClient1 | DelimiterByte 10, NoLocationNeeded spuntato |
| Clock | Clock1 | TimerInterval 200, TimerEnabled disattivato |
Collegarsi all'HC-05 con un ListPicker
Per prima cosa associa l'HC-05 dalle impostazioni Bluetooth del telefono (il PIN di solito è 1234, altrimenti 0000). Questo passaggio è indispensabile: BluetoothClient1.AddressesAndNames elenca solo i dispositivi associati e non cerca quelli nuovi nelle vicinanze. Ogni elemento è un testo con prima l'indirizzo MAC e poi il nome del dispositivo, come «98:D3:31:F5:2A:10 HC-05». Puoi passare l'intero testo al blocco Connect; App Inventor scarta da solo tutto ciò che segue il primo spazio. Il blocco restituisce true se la connessione riesce e false se fallisce.
- Nell'evento when LP_Connect.BeforePicking inserisci set LP_Connect.Elements to BluetoothClient1.AddressesAndNames. L'elenco si riempie di nuovo con i dispositivi associati ogni volta che si apre.
- Nell'evento when LP_Connect.AfterPicking aggiungi un blocco if e collega alla sua condizione call BluetoothClient1.Connect address: LP_Connect.Selection.
- Nella parte then dell'if imposta LBL_Status.Text su «Connesso» e avvia la lettura dei dati con set Clock1.TimerEnabled to true; nella parte else scrivi «Connessione fallita».
- Nell'evento when BTN_Disconnect.Click esegui prima set Clock1.TimerEnabled to false, poi call BluetoothClient1.Disconnect e infine imposta LBL_Status.Text su «Non connesso». Fermare prima il timer evita una lettura e un messaggio di errore dopo la chiusura della connessione.
Inviare dati: pulsanti e cursori
Per inviare basta un solo blocco: call BluetoothClient1.SendText text. Quando scrivi «L1\n» in un blocco di testo, App Inventor trasforma il \n finale in un carattere di a capo. Inviare senza connessione causa l'errore 515, perciò è buona abitudine racchiudere ogni invio in un controllo if BluetoothClient1.IsConnected. Per un cursore unisci il numero alla lettera e all'a capo con un blocco join: join("S", round(thumbPosition), "\n").
| Evento (event) | Testo inviato | Risultato su Arduino |
|---|---|---|
| BTN_LedOn.Click | L1\n | Il LED si accende al massimo |
| BTN_LedOff.Click | L0\n | Il LED si spegne |
| BTN_RelayOn.Click | R1\n | Il relè scatta e la lampada collegata si accende |
| BTN_RelayOff.Click | R0\n | Il relè si rilascia |
| SLD_Brightness.PositionChanged | join("P", round(thumbPosition), "\n") | analogWrite(5, valore) |
| SLD_Servo.PositionChanged | join("S", round(thumbPosition), "\n") | servo.write(angolo) |
Mentre trascini un cursore con il dito, l'evento PositionChanged scatta decine di volte al secondo e thumbPosition restituisce valori decimali come 87,4. Se invii a ogni evento, lo stesso angolo parte più e più volte, il buffer dell'HC-05 si riempie e il servo si muove a scatti e in ritardo. La soluzione è semplice: crea una variabile global lastAngle; invia solo se il valore arrotondato con round(thumbPosition) è diverso da essa, poi aggiorna la variabile. In questo modo i dati partono solo quando l'angolo cambia davvero. Applica lo stesso trucco al cursore della luminosità con global lastBrightness.
Ricevere dati: mostrare i valori del sensore sul telefono
Arduino invia una riga «24.5;41» ogni 2 secondi. In App Inventor non esiste un evento che scatti da solo all'arrivo dei dati, quindi usiamo il componente Clock per controllare a intervalli regolari se ci sono dati nuovi. Avendo impostato DelimiterByte a 10 nel Designer, se passi -1 come numberOfBytes al blocco ReceiveText, questo legge fino al carattere di a capo e restituisce la riga senza \n. Un timer da 200 ms controlla molto più spesso di quanto arrivino i dati (ogni 2000 ms), quindi nel buffer non si accumula nulla.
- Inserisci una condizione if BluetoothClient1.IsConnected dentro when Clock1.Timer.
- Aggiungi al suo interno una seconda condizione: call BluetoothClient1.BytesAvailableToReceive > 0.
- Salva la riga ricevuta in una variabile locale con initialize local line to call BluetoothClient1.ReceiveText numberOfBytes: -1.
- Trasforma la riga in una lista con il blocco split text line at ";": «24.5;41» diventa la lista di due elementi [24.5, 41]. Conserva il risultato in una variabile locale chiamata parts.
- if length of list parts = 2, imposta LBL_Temp.Text su join("Temperatura: ", select list item parts index 1, " °C") e LBL_Hum.Text su join("Umidità: ", select list item parts index 2, " %"). Il controllo sul numero di elementi evita errori dovuti a righe ricevute a metà o danneggiate.
Il problema dei permessi Bluetooth da Android 12 in poi
Con Android 12 (API 31) i permessi Bluetooth sono cambiati. Il vecchio permesso BLUETOOTH, concesso automaticamente all'installazione, è stato sostituito da BLUETOOTH_CONNECT e BLUETOOTH_SCAN, che l'utente deve approvare mentre l'app è in esecuzione; nelle impostazioni del telefono compaiono come permesso Dispositivi nelle vicinanze. Se non è stato concesso, App Inventor apre la finestra del permesso al primo tentativo, ma interrompe il blocco in esecuzione. Il risultato sono sintomi ben noti: l'elenco si apre vuoto al primo tocco su Connetti, compare il messaggio «Error 908: The permission ... has been denied» (con BLUETOOTH_CONNECT o BLUETOOTH_SCAN come nome) oppure, se l'utente ha rifiutato una volta, l'app non lo chiede mai più.
- Nell'evento when Screen1.Initialize controlla la versione di Android: se select list item (split Screen1.PlatformVersion at ".") index 1 ≥ 12, passa a call Screen1.AskForPermission il valore BluetoothConnect dall'elenco Permission. Su Android 11 e precedenti questi permessi non esistono e chiederli provoca anch'esso l'errore 908, quindi il controllo della versione è importante.
- Nell'evento when Screen1.PermissionGranted, se il permesso concesso è BLUETOOTH_CONNECT (contains text: permissionName, piece: "CONNECT"), chiedi subito dopo BluetoothScan. AskForPermission accetta un solo permesso alla volta, quindi bisogna concatenarli così.
- Nell'evento when Screen1.PermissionDenied mostra con un Notifier un messaggio con il percorso «Impostazioni > App > BlueGraysRemote > Autorizzazioni > Dispositivi nelle vicinanze > Consenti». Gestire questo evento mostra all'utente una spiegazione chiara invece di una finestra di errore in inglese.
- Lascia spuntato NoLocationNeeded sul BluetoothClient nel Designer; questa impostazione comunica ad Android che l'app non usa il Bluetooth per determinare la posizione.
- Prova sempre anche con l'APK: i permessi dell'AI2 Companion appartengono all'app Companion, mentre il tuo APK chiede i propri permessi dopo l'installazione.
| Errore | Significato | Soluzione |
|---|---|---|
| Error 507: Unable to connect. Is the device turned on? | Impossibile collegarsi al modulo | Controlla l'alimentazione dell'HC-05, l'associazione e che non sia collegato a un altro telefono; se non va ancora, togli la spunta a Secure nel BluetoothClient e riprova |
| Error 515: Not connected to a Bluetooth device. | Invio o lettura senza connessione | Racchiudi i blocchi di invio e del Clock in un controllo IsConnected |
| Error 516: Unable to write: Broken pipe | La connessione è caduta a metà (alimentazione persa, fuori portata) | Ferma il Clock, chiama Disconnect e ricollegati; alimenta a parte servo e relè |
| Error 908: The permission … has been denied | Manca il permesso Dispositivi nelle vicinanze su Android 12+ | Chiedilo con i blocchi o concedilo a mano nelle Impostazioni |
| L'elenco si apre vuoto | Modulo non associato o permesso appena richiesto | Associa l'HC-05 dalle impostazioni del telefono e, dopo aver concesso il permesso, tocca di nuovo Connetti |
Provare l'app e creare l'APK
Durante lo sviluppo non serve installare un APK dopo ogni modifica. Installa l'app MIT AI2 Companion dal Play Store; con computer e telefono sulla stessa rete Wi-Fi, scegli Connect > AI Companion in App Inventor e inquadra il QR code o inserisci il codice di 6 caratteri che compare sullo schermo. Ogni modifica fatta nel Designer o nei blocchi appare subito sul telefono. Quando l'app è pronta, crea il file installabile così:
- Seleziona Screen1 nel Designer e compila AppName (il nome mostrato sul telefono), Icon (un PNG quadrato), VersionCode e VersionName.
- Dal menu in alto scegli Build > Android App (.apk). La compilazione di solito richiede uno o due minuti.
- Al termine si apre una finestra con un QR code e un pulsante di download. Puoi inquadrare il QR code con il telefono per scaricare direttamente il file, oppure salvare l'APK sul computer e copiarlo sul telefono via cavo. Il link del QR code vale solo per poco tempo (circa 2 ore).
- Quando apri l'APK sul telefono, Android ti chiede di consentire al browser o al file manager di installare app sconosciute. Se Play Protect mostra un avviso, puoi proseguire con l'opzione per installare comunque nei dettagli; questo avviso è frequente con le app esterne al Play Store con una firma sconosciuta.
- Al primo avvio dell'app consenti il permesso Dispositivi nelle vicinanze, tocca Connetti e scegli l'HC-05 dall'elenco.
La stessa app con un ESP32, senza HC-05

L'ESP32 classico integra sia il Bluetooth classico sia il BLE. La libreria BluetoothSerial presenta l'ESP32 al telefono come dispositivo SPP, quindi puoi usare l'app appena creata senza cambiare nulla. Associa nelle impostazioni Bluetooth del telefono il dispositivo «BlueGrays-ESP32», sceglilo dall'elenco nell'app ed è fatta. Attenzione: ESP32-S3, ESP32-C3 ed ESP32-C6 supportano solo il BLE; non hanno BluetoothSerial e in App Inventor serve l'estensione BluetoothLE. Per lavorare con il BLE consulta la guida ESP32 Bluetooth e 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}Il codice capisce gli stessi comandi della versione per Uno e invia i dati nello stesso formato «temperatura;umidità»; ignora i comandi S perché non c'è alcun servo collegato. La libreria Servo di Arduino non funziona sull'ESP32; per aggiungere un servo installa la libreria ESP32Servo e usa le stesse chiamate attach() e write(), per esempio sul GPIO 13. Alimenta il modulo relè dal pin VIN (5 V); se il suo ingresso IN non commuta in modo affidabile con il segnale a 3,3 V dell'ESP32, scegli un modulo relè compatibile con 3,3 V. Maggiori dettagli in Che cos'è un modulo relè?
Con il Raspberry Pi Pico e MicroPython

Il Raspberry Pi Pico standard non ha il Bluetooth, quindi colleghi l'HC-05 ai pin UART0 (GP0 e GP1). Poiché i pin del Pico lavorano a 3,3 V, stavolta il partitore non serve; alimenta il modulo dal pin VBUS, che porta i 5 V dell'USB. Pico W e Pico 2 W hanno il Bluetooth integrato, ma MicroPython su queste schede supporta solo il BLE; per dialogare con il BluetoothClient classico di App Inventor, l'HC-05 resta la strada più semplice. Il codice seguente capisce gli stessi comandi, muove il servo con un PWM a 50 Hz e invia i dati nello stesso formato; il modulo dht è già incluso nel firmware MicroPython del 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)Idee di progetti con App Inventor e Arduino
- Macchinina controllata via Bluetooth: Associa i pulsanti di direzione alle lettere F, B, L e R e muovi i motori con un L298N; tutti i passaggi sono nella guida costruire una macchinina robot con Arduino.
- Pannello per la casa intelligente: Controlla lampada, presa e ventilatore da un'unica schermata con un modulo relè a quattro canali; per qualche idea guarda il progetto di casa intelligente con Arduino.
- Lampada con selettore di colore: Regola separatamente rosso, verde e blu di un LED RGB con tre cursori.
- Braccio robotico: Aggiungi un cursore per ogni giunto e muovi i servomotori dal telefono.
- Monitoraggio della serra: Mostra sul telefono i dati del DHT11 e del sensore di umidità del terreno e aggiungi un pulsante che avvia la pompa quando il terreno è secco.
- Comando vocale: Con il componente SpeechRecognizer di App Inventor crea un'app che invia L1 quando dici «accendi la luce».
Errori comuni e soluzioni
- L'app si collega ma il LED non si accende: I cavi TXD e RXD dell'HC-05 sono invertiti, oppure i pin di SoftwareSerial nel codice non corrispondono al circuito. Il TXD del modulo deve andare all'RX di Arduino (D10).
- I comandi non vengono eseguiti: Nel blocco di testo manca il \n; Arduino aspetta l'a capo. Controlla se i comandi in arrivo compaiono nel Monitor seriale.
- Caratteri senza senso: La velocità dell'HC-05 è diversa da quella di bt.begin(). Il modulo esce di fabbrica a 9600 baud; se è stata cambiata con i comandi AT, adatta il codice.
- Il servo trema o la connessione cade: Il servo fa crollare la linea a 5 V di Arduino; alimentalo con una sorgente a 5 V separata e collega insieme le GND. Un leggero tremolio può dipendere anche da SoftwareSerial, che blocca per un attimo gli interrupt mentre riceve; come soluzione definitiva usa un Arduino Mega o un ESP32, che hanno porte seriali hardware libere. Ogni tanto il servo può anche scattare per un attimo a un angolo sbagliato: la libreria DHT disattiva gli interrupt per circa 5 ms a ogni lettura e un comando che arriva proprio in quel momento può corrompersi; il comando successivo del cursore rimette tutto a posto.
- I valori sul telefono arrivano in ritardo: L'intervallo del Clock è più lungo di quello di invio di Arduino e le righe si accumulano. Tieni TimerInterval più breve dell'intervallo di invio.
- L'app si blocca: DelimiterByte è rimasto a 0, oppure Arduino non chiude le righe con \n; ReceiveText(-1) aspetta un delimitatore che non arriva mai.
- Su iPhone non funziona: L'HC-05 usa il Bluetooth classico, che iOS non consente. Per l'iPhone serve un modulo BLE (HM-10) o un ESP32.
Unire App Inventor e Arduino è il modo più divertente per imparare insieme sviluppo di app ed elettronica. Una volta impostati connessione e protocollo, lo stesso schema diventa il telecomando di una macchinina, di una casa intelligente o di una serra; cambiano solo le lettere inviate dai pulsanti. Nell'app BlueGrays trovi passo dopo passo, con schemi e codice, i progetti per accendere un LED dal telefono via Bluetooth e per la macchinina Bluetooth con L298N.
Domande frequenti
Un'app App Inventor con Bluetooth funziona su iPhone?
Con un HC-05 o un HC-06 no. Questi moduli usano il Bluetooth classico (SPP) e iOS non permette alle app comuni di aprire questo tipo di connessione seriale. Per comunicare con un iPhone devi passare a un modulo BLE come l'HM-10 o a un ESP32; per i progetti con HC-05 un telefono Android è la scelta più semplice.
Perché la lista Bluetooth è vuota in App Inventor?
AddressesAndNames elenca solo i dispositivi già associati al telefono, quindi associa prima l'HC-05 dalle impostazioni Bluetooth con il PIN 1234. Da Android 12 in poi la lista si apre vuota anche la prima volta se il permesso Dispositivi nelle vicinanze non è stato concesso; concedilo e tocca di nuovo Connetti. Assicurati anche che il Bluetooth sia attivo.
Come risolvere l'errore 507 di App Inventor?
L'errore 507 significa «Unable to connect. Is the device turned on?»: il telefono non è riuscito ad aprire la connessione con il modulo. Controlla che l'HC-05 sia alimentato, che il suo LED lampeggi velocemente e che non sia collegato a un altro telefono. Se il problema persiste, elimina l'associazione e ripetila, oppure prova a disattivare la proprietà Secure del BluetoothClient.
Come si crea l'APK con App Inventor?
Apri il progetto e fai clic su Build > Android App (.apk) nel menu in alto. Al termine della compilazione inquadra il QR code con il telefono oppure scarica l'APK sul computer e copialo sul telefono. Durante l'installazione dovrai consentire l'installazione di app sconosciute.
App Inventor può collegarsi a un ESP32 via Bluetooth?
Sì. Con la libreria BluetoothSerial l'ESP32 classico si comporta come un dispositivo SPP e il BluetoothClient di App Inventor vi si collega come a un HC-05. ESP32-S3 ed ESP32-C3 supportano solo il BLE, quindi su queste schede serve l'estensione BluetoothLE.
Posso pubblicare la mia app App Inventor su Google Play?
Sì. L'opzione Android App Bundle (.aab) del menu Build genera un file da caricare su Google Play; per farlo serve un account sviluppatore Google Play. Gli aggiornamenti devono essere firmati con la stessa chiave, quindi salva il tuo keystore con Projects > Export keystore.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
LED lampeggiante
Il primissimo ciao dell'elettronica: accendere e spegnere un solo LED. L'Arduino accende e spegne il LED a intervalli brevissimi, così la luce lampeggia veloce. Vedi nel modo più semplice come la sche

Temperatura e umidità con DHT11
Quanti gradi ci sono nella tua stanza, e l'aria è umida o secca? Il sensore DHT11 misura temperatura e umidità, e l'Arduino scrive i valori su uno schermo LCD a due righe, aggiornandoli ogni paio di s

Controllare un LED dal telefono tramite Bluetooth
Una piccola prova per accendere e spegnere le luci della tua stanza dal telefono. Il modulo Bluetooth passa all'Arduino le lettere che invii dal telefono; una lettera accende il LED rosso, un'altra qu

Auto controllata via Bluetooth con L298N
Trasforma il telefono in un telecomando e guida la tua auto robot. L'Arduino riceve tramite un modulo Bluetooth i comandi di un'app di controllo remoto sul telefono e fa girare le ruote con un driver

Utilizzo del servomotore
Il braccio di un piccolo servomotore ruota a turno in tre posizioni: tutto all'inizio, esattamente a metà e tutto alla fine, aspettando due secondi a ogni fermata. L'Arduino dice al motore a quale ang

Utilizzo dei LED RGB
Dentro un solo LED si nascondono tre colori: il LED RGB si accende rosso, poi verde, poi blu, ogni colore acceso per mezzo secondo e spento per mezzo secondo, e poi il giro ricomincia. Ti bastano un L

Lampadina 220V con pulsante (Push)
Finché tieni premuto il pulsante, una vera lampadina da 220 volt resta accesa, e si spegne appena lo lasci. Il piccolo segnale dell'Arduino non può accendere la lampadina direttamente, quindi in mezzo

Comunicazione unidirezionale tra 2 Arduino
Fai parlare due Arduino con un solo filo: premi il pulsante sull'uno e il LED dell'altro si accende, premi di nuovo e si spegne. La scheda che invia segnala la pressione con un breve messaggio, e quel
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 160 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

