Make It Connect · Chapter 29
Arduino Bluetooth controller in App Inventor
A hold-to-drive controller for the Chapter 28 car, made in MIT App Inventor, which is free, runs in the browser and builds a real Android app. The car’s sketch doesn’t change: the app sends the same single letters, F, B, L, R and S.
HM-10 or a BLE clone?
These blocks will never connect to it. App Inventor’s BluetoothClient speaks Classic Bluetooth only: an HC-05 or HC-06. For an HM-10, use the web-page remote, which needs no app, or MIT’s BluetoothLE extension (below).
No project file yet
The book promises a project file (.aia) to import. It isn’t here yet, because I haven’t built this app in the editor and driven a car with it, and I won’t put up a file that hasn’t done that. Until then, the blocks below are the whole app: they take about as long to place as a file takes to import, and you’ll know what each one does.
Every component name, method and event here was read in MIT’s component reference on 1 October 2026. The editor changes often, so this is the contract, not a click-path: if a tutorial’s screenshots look different, the names are what to look for.
The pieces you program against
| Piece | What it does |
|---|---|
| AddressesAndNames | The list of already-paired devices: address, a space, the name |
| Connect(address) | Opens the connection. True if it worked |
| IsConnected | True while connected. Check it before you send |
| SendText(text) | Turns the text into bytes and sends them |
| BytesAvailableToReceive() | How many bytes are waiting |
| ReceiveText(numberOfBytes) | Reads that many bytes; a negative number reads up to DelimiterByte |
| Disconnect() | Closes it |
| Button.TouchDown / Button.TouchUp | Events: the button was pressed / released |
| Clock.TimerInterval, Clock.Timer | A timer that fires every n milliseconds |
| ListPicker.Elements, BeforePicking, AfterPicking, Selection | A list the user picks from |
The first seven belong to BluetoothClient. Buttons and the ListPicker: the user-interface reference.
Build it
- Pair the module in Android’s own Bluetooth settings first (PIN 1234 or 0000). App Inventor can’t pair; it only lists devices that are already paired.
- Add the components: a ListPicker (text “Connect”), five Buttons (Forward, Back, Left, Right, Stop), a Label for replies, and the non-visible BluetoothClient and Clock.
- Fill the list. In ListPicker.BeforePicking: set ListPicker.Elements to BluetoothClient.AddressesAndNames.
- Connect. In ListPicker.AfterPicking: call BluetoothClient.Connect with ListPicker.Selection. If it returns true, set the ListPicker’s text to “Connected”.
- Remember which button is held. Make a global variable held, empty to start. In each driving button’s TouchDown: set held to its letter and call SendText with it at once. In its TouchUp: set held to empty and call SendText with S.
- Repeat while held. Set Clock.TimerInterval to 150. In Clock.Timer: if BluetoothClient.IsConnected and held is not empty, call SendText with held. This is the repeat that keeps the car’s fail-safe fed.
- Stop button. In its Click: call SendText with S.
- Build the app and install it on the phone (the Build menu makes an .apk), or use the MIT AI2 Companion app while you work.
The blocks, written out
Each “when” is an event block; everything indented under it goes inside it. Make the TouchDown and TouchUp pair once for each of the four driving buttons, with its own letter. The last two lines of Clock1.Timer show the Arduino’s replies: set BluetoothClient1.DelimiterByte to 10 (newline) so that -1 reads one whole line at a time.
initialize global held to ""
when ListPicker1.BeforePicking
set ListPicker1.Elements to
BluetoothClient1.AddressesAndNames
when ListPicker1.AfterPicking
if call BluetoothClient1.Connect
address: ListPicker1.Selection
set ListPicker1.Text to "Connected"
# the same pair for Back ("B"), Left ("L") and Right ("R")
when ForwardButton.TouchDown
set global held to "F"
call BluetoothClient1.SendText text: "F"
when ForwardButton.TouchUp
set global held to ""
call BluetoothClient1.SendText text: "S"
when StopButton.Click
call BluetoothClient1.SendText text: "S"
# Clock1: TimerInterval 150, TimerEnabled ticked
# BluetoothClient1: DelimiterByte 10
when Clock1.Timer
if BluetoothClient1.IsConnected
and not is empty global held
call BluetoothClient1.SendText
text: global held
if call BluetoothClient1
.BytesAvailableToReceive > 0
set ReplyLabel.Text to
call BluetoothClient1.ReceiveText
numberOfBytes: -1The test
You should see: with the car’s wheels in the air and the Serial Monitor open, holding Forward prints byte 70 lines about 150 ms apart; letting go prints byte 83. The wheels spin while you hold and stop when you let go.
- Instead: The list is empty.
- Meaning: The module isn’t paired in Android’s settings. It looks exactly like a broken app.
- Fix: Step 1, then open the list again.
- Instead: Connect fails on a module that is in the list.
- Meaning: Another phone or app holds the connection; a Classic module takes one at a time.
- Fix: Close the other app, or switch the other phone’s Bluetooth off.
- Instead: One byte 70, then the wheels stop after a second.
- Meaning: The Clock isn’t repeating the letter: it isn’t running, or held isn’t set in TouchDown.
- Fix: Tick Clock.TimerEnabled, and check step 5.
The HM-10 path: the BluetoothLE extension
Untested. For a BLE module, import MIT’s BluetoothLE extension (an .aix file from MIT’s extensions page). The shape is the same and the names change: you scan and connect instead of picking from a paired list, write with WriteStrings, and receive with RegisterForStrings and the StringsReceived event, using service FFE0 and characteristic FFE1. I haven’t built this version; the names are from MIT’s extension source, read on 1 October 2026.
For an HM-10 today, the web-page remote needs no app →
Make It Connect, the book this page belongs to →