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

PieceWhat it does
AddressesAndNamesThe list of already-paired devices: address, a space, the name
Connect(address)Opens the connection. True if it worked
IsConnectedTrue 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.TouchUpEvents: the button was pressed / released
Clock.TimerInterval, Clock.TimerA timer that fires every n milliseconds
ListPicker.Elements, BeforePicking, AfterPicking, SelectionA list the user picks from

The first seven belong to BluetoothClient. Buttons and the ListPicker: the user-interface reference.

Build it

  1. 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.
  2. 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.
  3. Fill the list. In ListPicker.BeforePicking: set ListPicker.Elements to BluetoothClient.AddressesAndNames.
  4. Connect. In ListPicker.AfterPicking: call BluetoothClient.Connect with ListPicker.Selection. If it returns true, set the ListPicker’s text to “Connected”.
  5. 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.
  6. 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.
  7. Stop button. In its Click: call SendText with S.
  8. 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: -1

The 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 →