ガイド · 読了まで23分

MIT App InventorでArduinoをBluetooth操作:HC-05用のAndroidアプリを作る

MIT App InventorでArduinoをBluetooth操作します。HC-05の配線、ListPicker、ボタンとスライダーのブロック、LED、リレー、サーボ、センサーデータ、APK、Android 12の対策まで解説します。

App InventorでArduinoをBluetooth操作するのは、JavaやKotlinを1行も書かずに自分のAndroidアプリを作る最も手早い方法です。MITが提供する無料のブラウザベースのMIT App Inventorでは、ボタン、スライダー、テキストラベルを画面にドラッグし、パズルのピースのようにはまるブロックでそれぞれの動作を決めます。スマホはArduinoにつないだHC-05 Bluetoothモジュールに接続します。アプリでのタップはすべて短いテキストのコマンドとしてArduinoに届き、Arduinoも同じ方法でセンサーの値をスマホに送り返します。

このガイドを終えるころには、実際に動くリモコンアプリができあがります。リストからHC-05に接続するListPicker、LEDとリレーをオン・オフするボタン、LEDの明るさとサーボの角度を決める2つのスライダー、そしてDHT11の温度と湿度をリアルタイムに表示するラベルです。Arduinoのコード、App Inventorのすべてのブロック、APKのビルド、Android 12で生じたBluetoothの権限の問題の対策を、ステップごとに見ていきます。HC-05を使ったことがなければ、先にHC-05 Bluetoothモジュールのガイドにざっと目を通してください。

HC-05 Bluetoothモジュール
HC-05はクラシックBluetooth(SPP)のモジュールで、App InventorのBluetoothClientコンポーネントがまさに通信する相手です。スマホとArduinoの間に無線のシリアル通信路を作ります。

App Inventorとは?Arduinoとどう通信する?

MIT App Inventorは、マサチューセッツ工科大学(MIT)が作った無料のアプリ開発環境です。Googleアカウントでログインし、ブラウザで使います。画面は2つあります。Designer(デザイナー)ではコンポーネントをドラッグしてアプリの画面を組み立て、Blocks(ブロック)エディターでは「ボタンがクリックされたらこうする」といったルールを色とりどりのブロックで書きます。インストールせずにアプリを試すには、PlayストアのMIT AI2 Companionアプリを使います。完成したら、1クリックでインストール可能なAPKファイルができます。

Bluetoothの部分はBluetoothClientコンポーネントが担当します。これはクラシックBluetoothのSPP(シリアルポートプロファイル)を使い、HC-05が話すのもまさにこれです。その結果、スマホとArduinoの間に目に見えないシリアルケーブルができます。アプリがSendTextブロックで「L1」を送ると、Arduinoはその2文字をbt.read()で読みます。シリアルモニタに打ち込んだのと同じです。Arduinoがbt.print()で送ったものは、すべてスマホのReceiveTextブロックに届きます。Arduino側にBluetooth専用のライブラリは必要ありません。

層役割このプロジェクトでは
インターフェース(Designer)画面に表示するコンポーネントListPicker、Button 5個、Slider 2個、Label
ロジック(Blocks)イベントに反応するブロックClick、PositionChanged、Timerのイベント
BluetoothClientSPP接続、データの送受信Connect、SendText、ReceiveText
HC-05BluetoothとUARTの橋渡し9600ボー、PIN 1234
Arduinoコマンドを解釈し、出力を動かし、センサーを読むLED、リレー、サーボ、DHT11

必要なもの

部品数量備考
Arduino Uno(またはNano)1どちらも同じコードとピン
HC-05またはHC-06 Bluetoothモジュール1クラシックBluetooth。HM-10(BLE)はこの方法では使えない
1 kΩと2 kΩの抵抗各1HC-05のRXDピン用の分圧回路(2.2 kΩでも可)
LEDと220 Ωの抵抗各1明るさを変えられるLED(D5、PWM)
5 Vリレーモジュール1ランプやファンなどの負荷を切り替える
SG90サーボモーター1スライダーで0〜180°を制御
DHT11温湿度センサー1スマホに表示するデータ
Androidスマホ1HC-05はiPhoneでは使えない
ブレッドボードとジャンパーワイヤー—共通GNDは必須
Arduino Uno
Arduino Uno
HC-06
HC-06
SG90
SG90
DHT11
DHT11
LED
LED

配線:HC-05、LED、リレー、サーボ、DHT11

HC-05の基板にはレギュレーターがあるのでVCCピンには5 Vを入れられますが、データピンは3.3 Vロジックです。モジュールのTXDピンからの3.3 Vの信号はArduinoで問題なく読めます。しかしArduinoの5 VのTX信号は、分圧回路を通してRXDピンに届けるべきです。D11 → 1 kΩ → RXD、RXD → 2 kΩ → GNDとつなぎます。これでRXDには5 × 2 / 3 ≈ 3.3 Vがかかります。HC-05はUnoのハードウェアシリアルのピン(0と1)ではなく、SoftwareSerialでD10とD11につなぎます。こうすればモジュールをつないだままコードを書き込め、デバッグ用にシリアルモニタも使えます。LEDの抵抗はLED抵抗計算ツールで確認できます。

接続Arduino UnoESP32Raspberry Pi Pico
HC-05 VCC5V不要(Bluetooth内蔵)VBUS(5 V)
HC-05 GNDGND—GND
HC-05 TXDD10(SoftwareSerialのRX)—GP1(UART0のRX)
HC-05 RXDD11、1 kΩ / 2 kΩの分圧回路を通す—GP0(UART0のTX)、分圧回路なし
LED(+ 220 Ω)D5(PWM)GPIO 2(オンボードLED)GP15
リレーモジュールのIND7GPIO 26GP14
サーボの信号(オレンジ)D9—(ESP32Servoで追加可能)GP13
DHT11のデータD2GPIO 4GP16
3つのルールに注意してください。すべてのGND(Arduino、HC-05、リレー、サーボ、外部電源)は共通につなぐ必要があります。HC-05のRXDピンには分圧回路なしで5 Vを絶対に入れないでください。しばらくは耐えても、いずれ壊れることがあります。動いているサーボは一瞬で数百ミリアンペアを引くことがあります。ArduinoがUSBで動いていると、この突入電流で電圧が下がり、HC-05の接続が切れたりArduinoがリセットされたりします。サーボとリレーを別の5 V電源から給電すれば、根本的に解決します。

通信プロトコル:スマホは何を送る?

コードを書く前に、スマホとArduinoが話す「言葉」を決めます。このプロジェクトでは、どのコマンドも文字、続いて数字、最後に改行文字(\n、ASCII 10)でできています。改行はとても重要です。Bluetoothのデータは分割されて届くことがあり、「S1」と「35」が別々に届いても、Arduinoは改行を待ってから「S135」を1つのコマンドとして処理します。コマンドは読めるテキストなので、どんなBluetoothターミナルアプリでも手で試せます。

コマンド方向意味
L1 / L0スマホ → ArduinoLEDを全点灯 / 消灯
P0 … P255スマホ → ArduinoLEDの明るさ(analogWriteの値)
R1 / R0スマホ → Arduinoリレーをオン / オフ
S0 … S180スマホ → Arduinoサーボの角度(度)
24.5;41Arduino → スマホ温度(°C)と湿度(%)、セミコロンで区切る

App Inventorはテキストの代わりに、Send1ByteNumberブロックで1バイト(0〜255)を送ることもでき、Arduinoはそれを直接数値として読めます。短くて速いのですが、スライダー1つだけの場合にしか向きません。スライダー2つとボタン4つがすべてバイトを送ると、届いた90がサーボの角度なのか明るさなのかArduinoには判別できないからです。文字 + 数字 + 改行の形式は数バイト多くかかりますが、9600ボーなら1秒に約960文字を送れるので、違いに気づくことはありません。

Arduinoのコード

ServoライブラリとSoftwareSerialライブラリはArduino IDEに付属しています。DHT11用には、ライブラリマネージャからDHT sensor library(Adafruit)をインストールします。依存ライブラリについて聞かれたら「全てをインストール」を選び、Adafruit Unified Sensorも追加します。0番と1番ピンを使わないので、書き込みの際にHC-05を外す必要はありません。

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2#include <Servo.h>
3#include <DHT.h>
4
5SoftwareSerial bt(10, 11);       // RX, TX → HC-05 TXD, RXD (bölücü üzerinden / via divider)
6Servo servo;
7DHT dht(2, DHT11);               // DHT11 veri pini D2 / data pin D2
8
9const int LED_PIN = 5;           // PWM destekli pin / PWM-capable pin
10const int ROLE_PIN = 7;
11const int SERVO_PIN = 9;
12const int ROLE_ACIK = LOW;       // çoğu röle modülü LOW ile çeker / most relay modules are active LOW
13const int ROLE_KAPALI = HIGH;
14
15String komut = "";               // gelen satırın tamponu / buffer for the incoming line
16unsigned long sonGonderim = 0;
17
18void komutIsle(String k) {
19  k.trim();                                  // boşluk ve \r temizle / strip spaces and \r
20  if (k.length() == 0) return;
21  Serial.println(k);                         // hata ayıklama / debugging
22  char tur = k.charAt(0);                    // komut harfi / command letter
23  int deger = k.substring(1).toInt();        // harften sonraki sayı / number after the letter
24
25  switch (tur) {
26    case 'L':                                // L1 / L0 → LED aç, kapat / LED on, off
27      analogWrite(LED_PIN, deger == 1 ? 255 : 0);
28      break;
29    case 'P':                                // P0..P255 → parlaklık / brightness
30      analogWrite(LED_PIN, constrain(deger, 0, 255));
31      break;
32    case 'R':                                // R1 / R0 → röle / relay
33      digitalWrite(ROLE_PIN, deger == 1 ? ROLE_ACIK : ROLE_KAPALI);
34      break;
35    case 'S':                                // S0..S180 → servo açısı / servo angle
36      servo.write(constrain(deger, 0, 180));
37      break;
38  }
39}
40
41void setup() {
42  pinMode(LED_PIN, OUTPUT);
43  pinMode(ROLE_PIN, OUTPUT);
44  digitalWrite(ROLE_PIN, ROLE_KAPALI);       // açılışta röle kapalı / relay off at start
45  servo.attach(SERVO_PIN);
46  servo.write(90);                           // orta konum / centre position
47  dht.begin();
48  Serial.begin(9600);                        // Seri Monitör / Serial Monitor
49  bt.begin(9600);                            // HC-05 veri modu hızı / HC-05 data mode baud rate
50}
51
52void loop() {
53  // 1) Telefondan gelen komutları satır satır oku / read commands from the phone line by line
54  while (bt.available()) {
55    char c = bt.read();
56    if (c == '\n') {                         // satır bitti / line complete
57      komutIsle(komut);
58      komut = "";
59    } else if (komut.length() < 16) {        // aşırı uzun satırı kes / cap overlong lines
60      komut += c;
61    }
62  }
63
64  // 2) 2 saniyede bir sensör verisini gönder / send sensor data every 2 seconds
65  if (millis() - sonGonderim >= 2000) {
66    sonGonderim = millis();
67    float t = dht.readTemperature();
68    float h = dht.readHumidity();
69    if (!isnan(t) && !isnan(h)) {
70      bt.print(t, 1);                        // örnek / example: 24.5
71      bt.print(';');
72      bt.print(h, 0);                        // örnek / example: 41
73      bt.print('\n');                        // println değil, \r gitmesin / not println, no \r
74    }
75  }
76}
  • 行バッファ:loop()は届いた文字を1つずつkomut(コマンド)変数に追加し、\nが届くとその行をkomutIsle()に渡してバッファをクリアします。16文字を超えた分は捨てるので、ゴミのデータでメモリがいっぱいになることはありません。
  • trim():ターミナルアプリの中には行末に\rも付けるものがあります。trim()はそれを空白と一緒に取り除くので、「L1\r」も「L1」として認識されます。
  • 文字と数字:charAt(0)でコマンドの文字を、substring(1).toInt()でその後ろの数字を取り出します。constrain()は値を安全な範囲に収めます。スマホが誤って300を送っても、サーボが180°を超えて動かされることはありません。
  • 処理を止めない送信:センサーはmillis()を使って2秒ごとに読みます。delay()がないので、その間に届いたコマンドも遅れずに処理されます。詳しくはmillisとdelayの違いを参照してください。
  • printlnではなくprint('\n'):println()は行を\r\nで終えます。App Inventorは\nだけを区切りに使うので、\rが湿度の値の末尾に残ってしまいます。\nだけを送れば、スマホでの解析がきれいに保たれます。
  • アクティブLOWのリレー:多くのリレーモジュールはINがLOWになるとオンになります。HIGHでオンになるモジュールなら、ROLE_ACIK(オン)とROLE_KAPALI(オフ)の定数の値を入れ替えるだけです。
App Inventorに進む前に、Arduino側だけでテストしてください。PlayストアのSerial Bluetooth TerminalのようなアプリでHC-05に接続し、「L1」と入力して送ります。LEDが点き、シリアルモニタにL1が表示されるはずです。「24.5;41」のような行が2秒ごとに流れてくれば、データの返送もできています。あとで何か問題が起きても、原因がアプリにあるのか回路にあるのかすぐにわかります。

App Inventorで画面を設計する(Designer)

  1. ブラウザでai2.appinventor.mit.eduを開き、Googleアカウントでログインして、Projects > Start new project(新しいプロジェクトを始める)でBlueGraysRemoteという名前のプロジェクトを作ります。
  2. パレットのLayout(レイアウト)グループからHorizontalArrangementをドラッグし、その中にListPicker(Text: Connect)、Button(Text: Disconnect)、Label(Text: Not connected)を入れます。
  3. その下にHorizontalArrangementをさらに2つ追加します。1つ目にはLED OnとLED Offのボタンを、2つ目にはRelay OnとRelay Offのボタンを入れます。ボタンのWidthをFill parentにすると押しやすくなります。
  4. Sliderを2つ追加します。明るさ用はMinValue 0、MaxValue 255、ThumbPosition 0、サーボ用はMinValue 0、MaxValue 180、ThumbPosition 90にします。それぞれの上に、何をするものかを書いたLabelを置きます。
  5. 温度と湿度用にLabelをさらに2つ追加し、FontSizeを20ほどに大きくします。
  6. Connectivity(接続)グループからBluetoothClientを追加し、PropertiesパネルでDelimiterByteを10にして、NoLocationNeededにチェックを入れます。
  7. Sensors(センサー)グループからClockを追加し、TimerIntervalを200にして、TimerEnabledのチェックを外します。タイマーは接続ができてからブロックでオンにします。
コンポーネント名前(Rename)主な設定
ListPickerLP_ConnectText: Connect
Button × 5BTN_Disconnect、BTN_LedOn、BTN_LedOff、BTN_RelayOn、BTN_RelayOffWidth: Fill parent
SliderSLD_BrightnessMinValue 0、MaxValue 255
SliderSLD_ServoMinValue 0、MaxValue 180、ThumbPosition 90
LabelLBL_Status、LBL_Temp、LBL_HumFontSize 20(値の表示用)
BluetoothClientBluetoothClient1DelimiterByte 10、NoLocationNeededにチェック
ClockClock1TimerInterval 200、TimerEnabledはオフ

ListPickerでHC-05に接続する

まずスマホのBluetooth設定でHC-05とペアリングします(PINは通常1234、だめなら0000)。この手順は欠かせません。BluetoothClient1.AddressesAndNamesはペアリング済みのデバイスしか一覧に出さず、近くの新しいデバイスは検索しないからです。各項目は「98:D3:31:F5:2A:10 HC-05」のように、先頭にMACアドレス、続いてデバイス名が並ぶテキストです。このテキストをそのままConnectブロックに渡せます。App Inventorは最初の空白より後ろを自動で無視します。ブロックは接続に成功するとtrue、失敗するとfalseを返します。

  1. when LP_Connect.BeforePickingイベントに、set LP_Connect.Elements to BluetoothClient1.AddressesAndNamesを置きます。リストを開くたびに、ペアリング済みのデバイスで一覧が更新されます。
  2. when LP_Connect.AfterPickingイベントにifブロックを追加し、条件にcall BluetoothClient1.Connect address: LP_Connect.Selectionをはめ込みます。
  3. ifのthenの部分でLBL_Status.Textを「接続済み」にし、set Clock1.TimerEnabled to trueでデータの読み取りを始めます。elseの部分には「接続に失敗しました」と書きます。
  4. when BTN_Disconnect.Clickイベントでは、まずset Clock1.TimerEnabled to false、次にcall BluetoothClient1.Disconnectを呼び、最後にLBL_Status.Textを「未接続」にします。先にタイマーを止めておくと、接続を閉じたあとに読み取りが走ってエラーメッセージが出るのを防げます。
接続されると、HC-05のLEDは速い点滅をやめ、2秒ごとに短く2回光るようになります。これでアプリを見なくても接続状態がわかります。LEDが速い点滅に戻ったら、接続が切れています。

データを送る:ボタンとスライダー

送信に必要なブロックはcall BluetoothClient1.SendText textの1つだけです。テキストブロックに「L1\n」と入力すると、App Inventorは末尾の\nを改行文字に変えます。未接続のまま送るとエラー515になるので、送信はすべてif BluetoothClient1.IsConnectedの確認で囲むのがよい習慣です。スライダーの場合は、joinブロックで文字と数字と改行をつなげます。join("S", round(thumbPosition), "\n")です。

イベント送るテキストArduinoでの結果
BTN_LedOn.ClickL1\nLEDが最大の明るさで点く
BTN_LedOff.ClickL0\nLEDが消える
BTN_RelayOn.ClickR1\nリレーがカチッとオンになり、つないだランプが点く
BTN_RelayOff.ClickR0\nリレーがオフになる
SLD_Brightness.PositionChangedjoin("P", round(thumbPosition), "\n")analogWrite(5, 値)
SLD_Servo.PositionChangedjoin("S", round(thumbPosition), "\n")servo.write(角度)

指でスライダーをドラッグしている間、PositionChangedイベントは1秒に何十回も発生し、thumbPositionは87.4のような小数を返します。イベントのたびに送ると、同じ角度が何度も送られ、HC-05のバッファがいっぱいになり、サーボがカクカクと遅れて動きます。解決法は簡単です。global lastAngleという変数を作り、round(thumbPosition)で丸めた値がそれと違うときだけ送り、そのあと変数を更新します。こうすれば角度が本当に変わったときだけデータが送られます。明るさのスライダーにもglobal lastBrightnessで同じ工夫をします。

データを受け取る:センサーの値をスマホに表示する

Arduinoは2秒ごとに「24.5;41」という行を送ります。App Inventorにはデータが届いたときに自動で発生するイベントがないので、Clockコンポーネントを使って一定間隔で「新しいデータはある?」と確認します。DesignerでDelimiterByteを10にしてあるので、ReceiveTextブロックのnumberOfBytesに-1を渡すと、改行文字まで読み、\nを除いた行を返します。200 msのタイマーはデータが届く間隔(2000 msごと)よりずっと頻繁に確認するので、バッファに何もたまりません。

  1. when Clock1.Timerの中にif BluetoothClient1.IsConnectedの条件を置きます。
  2. その中に2つ目の条件call BluetoothClient1.BytesAvailableToReceive > 0を追加します。
  3. initialize local line to call BluetoothClient1.ReceiveText numberOfBytes: -1で、届いた行をローカル変数に入れます。
  4. split text line at ";"ブロックで行をリストに変えます。「24.5;41」は2項目のリスト[24.5, 41]になります。結果はpartsというローカル変数に入れます。
  5. if length of list parts = 2なら、LBL_Temp.Textをjoin("温度: ", select list item parts index 1, " °C")に、LBL_Hum.Textをjoin("湿度: ", select list item parts index 2, " %")にします。項目数を確認すると、途中までしか届かなかった行や壊れた行でエラーが出るのを防げます。
ReceiveTextに-1を渡したのにDelimiterByteを10にし忘れると(初期値は0)、ブロックは来ることのない0のバイトを待ち続け、アプリが固まったように見えます。また、Arduinoがprintln()で行を送ると、\r文字が2つ目の値の末尾に付いてしまいます。そのためコードは'\n'だけを送っています。
値をテキストで表示するだけで終わらせる必要はありません。温度が30 °Cを超えたらLBL_Temp.TextColorを赤にしたり、Notifierコンポーネントで警告を表示したり、Canvasに最近の値の折れ線グラフを描いたりできます。

Android 12以降のBluetoothの権限の問題

Android 12(API 31)でBluetoothの権限が変わりました。インストール時に自動で許可されていた古いBLUETOOTH権限は、アプリの実行中にユーザーが承認しなければならないBLUETOOTH_CONNECTとBLUETOOTH_SCANに置き換えられました。スマホの設定では付近のデバイスの権限として表示されます。許可されていないと、App Inventorは最初の試行で権限のダイアログを開きますが、実行中だったブロックは止まってしまいます。その結果、よくある症状が出ます。初めてConnectをタップしたときにリストが空で開く、「Error 908: The permission ... has been denied」(権限が拒否された)というメッセージが出る(BLUETOOTH_CONNECTかBLUETOOTH_SCANの名前が入る)、あるいはユーザーが一度拒否すると二度と聞かれなくなる、といった症状です。

  1. when Screen1.InitializeイベントでAndroidのバージョンを確認します。select list item (split Screen1.PlatformVersion at ".") index 1 ≥ 12なら、call Screen1.AskForPermissionにPermissionリストのBluetoothConnectを渡します。これらの権限はAndroid 11以前には存在せず、そこで要求するとやはりエラー908になるので、バージョンの確認が重要です。
  2. when Screen1.PermissionGrantedイベントでは、許可された権限がBLUETOOTH_CONNECTなら(contains text: permissionName, piece: "CONNECT")、続けてBluetoothScanを要求します。AskForPermissionは一度に1つの権限しか受け取らないので、このようにつなげていきます。
  3. when Screen1.PermissionDeniedイベントでは、Notifierを使って「設定 > アプリ > BlueGraysRemote > 権限 > 付近のデバイス > 許可」という手順を示すメッセージを表示します。このイベントを処理すれば、生のエラーボックスの代わりにわかりやすい説明をユーザーに見せられます。
  4. DesignerでBluetoothClientのNoLocationNeededにはチェックを入れたままにします。これでアプリがユーザーの位置を知るためにBluetoothを使わないことをAndroidに伝えます。
  5. 必ずAPKでもテストしてください。AI2 Companionの権限はCompanionアプリのものであり、自分のAPKはインストール後に独自に権限を要求します。
エラー意味解決法
Error 507: Unable to connect. Is the device turned on?(接続できない)モジュールに接続できなかったHC-05の電源、ほかのスマホに接続されていないか、ペアリングを確認する。それでもだめならBluetoothClientのSecureのチェックを外して再試行する
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とペアリングする。権限を許可したら、もう一度Connectをタップする

アプリのテストとAPKのビルド

開発中は、変更のたびにAPKをインストールする必要はありません。PlayストアからMIT AI2 Companionアプリをインストールし、パソコンとスマホを同じWi-Fiネットワークにつないで、App InventorでConnect > AI Companionを選び、QRコードを読み取るか画面に表示された6文字のコードを入力します。Designerやブロックでの変更がすべて、すぐにスマホに反映されます。アプリが完成したら、次の手順でインストール用のファイルを作ります。

  1. DesignerでScreen1を選び、AppName(スマホに表示される名前)、Icon(正方形のPNG)、VersionCode、VersionNameを入力します。
  2. 上のメニューからBuild > Android App (.apk)を選びます。ビルドには通常1〜2分かかります。
  3. 終わると、QRコードとダウンロードボタンのあるウィンドウが表示されます。スマホでQRコードを読み取って直接ダウンロードするか、APKをパソコンに保存してUSBでスマホにコピーします。QRのリンクは短時間(約2時間)しか有効ではありません。
  4. スマホでAPKを開くと、Androidはブラウザやファイルマネージャーに不明なアプリのインストールを許可するよう求めます。Playプロテクトが警告を出した場合は、詳細から「インストールする」を選んで続行できます。この警告は、Playストア以外から入手した見慣れない署名のアプリではよく出ます。
  5. アプリを初めて起動したら、付近のデバイスの権限を承認し、Connectをタップして、リストからHC-05を選びます。
Projects > Export selected project (.aia) to my computerでプロジェクトをバックアップしておきましょう。.aiaファイルは友達と共有したり、別のアカウントに移したりできます。Google Playでアプリを公開したい場合はBuild > Android App Bundle (.aab)を使い、Projects > Export keystoreで署名鍵を必ず安全に保管してください。アップデートは同じ鍵で署名しなければなりません。アップデートのたびにVersionCodeを1増やすのも忘れないでください。

HC-05不要、ESP32で同じアプリを使う

ESP32開発ボード
初代ESP32はクラシックBluetoothを内蔵しています。App InventorのBluetoothClientは、HC-05と同じようにESP32に接続できます。

初代ESP32はクラシックBluetoothとBLEの両方を内蔵しています。BluetoothSerialライブラリはESP32をSPPデバイスとしてスマホに見せるので、今作ったアプリをそのまま使えます。スマホのBluetooth設定で「BlueGrays-ESP32」というデバイスとペアリングし、アプリのリストから選べば完了です。注意点として、ESP32-S3、ESP32-C3、ESP32-C6はBLEにしか対応していません。BluetoothSerialはなく、App Inventor側ではBluetoothLE拡張機能が必要です。BLEの使い方はESP32のBluetoothとBLEのガイドを参照してください。

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

このコードはUno版と同じコマンドを理解し、同じ「温度;湿度」の形式でデータを送ります。サーボはつないでいないので、Sのコマンドは無視します。ArduinoのServoライブラリはESP32では動きません。サーボを追加するにはESP32Servoライブラリをインストールし、たとえばGPIO 13で同じattach()とwrite()を使います。リレーモジュールはVIN(5 V)ピンから給電します。ESP32の3.3 Vの信号でINが確実に切り替わらない場合は、3.3 V対応のリレーモジュールを選んでください。詳しくはリレーモジュールとは?を参照してください。

Raspberry Pi PicoとMicroPythonで使う

Raspberry Pi Pico
Raspberry Pi Picoは3.3 Vで動くので、HC-05のRXDピンに直接つなげます。分圧回路は不要です。

標準のRaspberry Pi PicoにはBluetoothがないので、HC-05をUART0のピン(GP0とGP1)につなぎます。Picoのピンは3.3 Vで動くので、今回は分圧回路は不要です。モジュールは、USBからの5 Vが来ているVBUSピンから給電します。Pico WとPico 2 WはBluetoothを内蔵していますが、MicroPythonではBLEにしか対応していません。App InventorのクラシックなBluetoothClientと通信するには、やはりHC-05が最も簡単です。下のコードは同じコマンドを理解し、50 HzのPWMでサーボを動かし、同じ形式でデータを送ります。dhtモジュールはPicoのMicroPythonファームウェアに組み込まれています。

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

App InventorとArduinoのプロジェクトアイデア

  • Bluetooth操作のカー:方向ボタンにF、B、L、Rの文字を割り当て、L298Nでモーターを動かします。すべての手順はArduinoのロボットカーのガイドにあります。
  • スマートホームのパネル:4チャンネルのリレーモジュールで、ランプ、コンセント、ファンを1つの画面から操作します。アイデアはArduinoのスマートホームプロジェクトを参照してください。
  • 色を選べるランプ:3つのスライダーでRGB LEDの赤、緑、青の値を別々に設定します。
  • ロボットアーム:関節ごとにスライダーを1つ追加し、スマホからサーボモーターを動かします。
  • 温室モニター:DHT11と土壌水分センサーの値をスマホに表示し、土が乾いたらポンプを動かすボタンを追加します。
  • 音声操作:App InventorのSpeechRecognizerコンポーネントを使い、「電気をつけて」と言うとL1を送るアプリを作ります。

よくある間違いと解決法

  • アプリは接続されるのにLEDが点かない:HC-05のTXDとRXDの線が逆になっているか、コードのSoftwareSerialのピンが回路と合っていません。モジュールのTXDはArduinoのRX(D10)につなぐ必要があります。
  • コマンドが無視される:テキストブロックに\nがありません。Arduinoは改行を待っています。届いたコマンドがシリアルモニタに表示されるか確認してください。
  • 文字化けする:HC-05のボーレートがbt.begin()と違います。モジュールは出荷時9600ボーです。ATコマンドで変更した場合は、コードを合わせてください。
  • サーボが震える、または接続が切れる:サーボがArduinoの5 Vラインを引き下げています。別の5 V電源から給電し、GNDを共通にしてください。わずかな震えは、SoftwareSerialが受信中に一瞬割り込みを止めることでも起きます。根本的に直すなら、予備のハードウェアシリアルポートがあるArduino MegaかESP32を使います。また、サーボがときどき一瞬違う角度に跳ねることもあります。DHTライブラリは読み取りのたびに約5 ms割り込みを止めるので、その瞬間に届いたコマンドが壊れることがあるためです。スライダーの次のコマンドで元に戻ります。
  • スマホの値が遅れて表示される:Clockの間隔がArduinoの送信間隔より長く、行がたまっています。TimerIntervalは送信間隔より短くしてください。
  • アプリが固まる:DelimiterByteが0のままか、Arduinoが行を\nで終えていません。ReceiveText(-1)は来ることのない区切りを待っています。
  • iPhoneで動かない:HC-05はクラシックBluetoothを使っており、iOSではそれが許可されていません。iPhoneにはBLEモジュール(HM-10)かESP32が必要です。
トラブルシューティングでは、つながりを2つに分けて考えます。まずSerial Bluetooth TerminalでArduino側を、次にApp Inventorのアプリをテストします。ターミナルでは動くのにアプリで動かなければ問題はブロックにあり、ターミナルでも動かなければ配線かArduinoのコードを確認します。ピン番号の再確認にはピン配置ツールを使ってください。

App InventorとArduinoの組み合わせは、モバイルアプリ開発と電子工作を1つのプロジェクトで学ぶ最も楽しい方法です。接続とプロトコルさえ作れば、同じ骨組みがカーにも、スマートホームにも、温室のリモコンにもなります。変わるのは、ボタンがどの文字を送るかだけです。BlueGraysアプリでは、BluetoothでのLED制御と、L298NのBluetooth操作カーのプロジェクトを、回路図とコード付きでステップごとに紹介しています。なお、日本国内では技適マークのない無線モジュールの使用が電波法で制限されているので、HC-05を選ぶときは技適の有無を確認してください。

よくある質問

App InventorのBluetoothアプリはiPhoneで動く?

HC-05やHC-06では動きません。これらのモジュールはクラシックBluetooth(SPP)を使っていて、iOSでは普通のアプリがこの種のシリアル接続を開くことが許可されていません。iPhoneと通信するには、HM-10のようなBLEモジュールかESP32に切り替える必要があります。HC-05のプロジェクトには、Androidスマホが最もトラブルの少ない選択です。

App InventorでBluetoothのデバイス一覧が空になるのはなぜ?

AddressesAndNamesは、すでにスマホとペアリングされたデバイスしか一覧に出しません。まずBluetoothの設定でPIN 1234を使ってHC-05とペアリングしてください。Android 12以降では、付近のデバイスの権限が許可されていないと、初回もリストが空で開きます。許可してからもう一度Connectをタップしてください。Bluetoothがオンになっていることも確認しましょう。

App Inventorのエラー507を直すには?

エラー507は「Unable to connect. Is the device turned on?」(接続できません。デバイスの電源は入っていますか?)という意味で、スマホがモジュールとの接続を開けなかったことを示します。HC-05に電源が来ているか、LEDが速く点滅しているか、ほかのスマホに接続されていないかを確認してください。それでも直らなければ、ペアリングを解除してやり直すか、BluetoothClientのSecureプロパティをオフにしてみてください。

App InventorでAPKを作るには?

プロジェクトを開き、上のメニューでBuild > Android App (.apk)をクリックします。ビルドが終わったら、スマホでQRコードを読み取るか、APKをパソコンにダウンロードしてスマホにコピーします。インストールの際は、不明なアプリのインストールを許可する必要があります。

App InventorはESP32にBluetoothで接続できる?

はい。BluetoothSerialライブラリを使うと初代ESP32はSPPデバイスとして振る舞い、App InventorのBluetoothClientはHC-05と同じように接続できます。ESP32-S3とESP32-C3はBLEにしか対応していないので、これらのボードではBluetoothLE拡張機能が必要です。

App InventorのアプリをGoogle Playで公開できる?

はい。BuildメニューのAndroid App Bundle (.aab)を選ぶと、Google Playにアップロードできるファイルが作られます。Google Playのデベロッパーアカウントが必要です。アップデートは同じ鍵で署名しなければならないので、Projects > Export keystoreでキーストアをバックアップしておいてください。

全プロジェクトを見る
LEDの点滅
#1初級

LEDの点滅

電子工作の第一歩は、1個のLEDを点けたり消したりすることです。Arduinoで短い間隔で点灯と消灯を繰り返すと、LEDが素早く点滅します。ボードが光を制御する仕組みを最もシンプルな形で確かめ、これから取り組むプロジェクトの基礎を身につけます。

4.6(188)
1127
DHT11で温度と湿度を測定
#55中級

DHT11で温度と湿度を測定

部屋の温度は何度で、空気は湿っているでしょうか、それとも乾燥しているでしょうか?DHT11センサーが温度と湿度を測定し、Arduinoが2行のLCDディスプレイに値を表示して、数秒ごとに更新します。センサーに異常があればエラーメッセージが表示されます。自分だけの小さな気象観測装置を作る第一歩です。

4.7(47)
73
BluetoothでスマートフォンからLEDを操作
#34初級
プレミアム

BluetoothでスマートフォンからLEDを操作

スマートフォンで部屋の照明をつけたり消したりするための、小さな練習です。Bluetoothモジュールがスマートフォンから送られた文字をArduinoに伝えます。ある文字で赤いLEDが、別の文字で緑のLEDが点灯し、同じ文字を小文字で送ると消灯します。無線操作の仕組みを、ごくシンプルな形で学べます。

4.8(68)
89
L298Nで作るBluetooth操作の車
#80上級
プレミアム

L298Nで作るBluetooth操作の車

スマートフォンをリモコンにして、自作のロボットカーを操作できます。ArduinoはBluetoothモジュールを通じてスマートフォンのリモコンアプリから指令を受け取り、モータードライバーで車輪を回します。前進、後退、左右や斜めへの移動ができ、速度もスマートフォンから調整できます。停止ボタンを押すと、短いブレーキ動作でモーターがすぐに止まります。

5.0(38)
65
サーボモーターの使い方
#35初級
プレミアム

サーボモーターの使い方

小型サーボモーターのアームが、一方の端、中央、もう一方の端の3つの位置へ順番に回り、それぞれの位置で2秒間止まります。Arduinoが保持する角度をモーターに指示すると、モーターはその位置まで動いて止まります。ロボットアームや自動開閉するふたが動く仕組みを学べます。

4.7(48)
67
RGB LEDの使い方
#39初級
プレミアム

RGB LEDの使い方

1つのLEDに3つの色が入っています。RGB LEDが赤、緑、青の順に、それぞれ0.5秒点灯して0.5秒消灯し、このサイクルを繰り返します。ArduinoにRGB LEDを1つと抵抗を3つ接続するだけです。色とりどりの光を使うプロジェクトを、手軽に始められます。

4.9(73)
98
ボタンを押している間だけ220V電球を点灯
#19上級
プレミアム

ボタンを押している間だけ220V電球を点灯

ボタンを押している間は実際の220ボルトの電球が点灯し、離すとすぐに消灯します。Arduinoの小さな信号では電球を直接点灯できないため、電気で動くスイッチであるリレーを間に入れ、電球の回路を開閉します。ボードで家庭の照明を操作するための第一歩です。家庭用電源を扱う際は十分に注意し、不安がある場合は詳しい人に手伝ってもらってください。

4.4(30)
38
2台のArduinoで一方向通信
#57中級
プレミアム

2台のArduinoで一方向通信

2台のArduinoを1本のワイヤーで通信させます。一方のボタンを押すともう一方のLEDが点灯し、もう一度押すと消灯します。送信側のボードがボタンの押下を短いメッセージで伝え、受信側のボードはそのメッセージを受け取るとLEDの点灯と消灯を切り替えます。機器同士が情報を伝える仕組みを、最もシンプルな形で確かめられます。

4.4(37)
50