The best tools to make your project dreams come true

Login or Signup
USD


By All About Circuits

How to Use Your Android to Communicate with Your Arduino

Courtesy of All About Circuits

How to Use Your Android to Communicate with Your Arduino

In this project, we show you how to send texts from your Android phone to your Arduino

You Will Need

  • Android Phone - The phone used needs to support USB Host Mode (i.e OTG Support). While most Android devices running Android 3.1+ support this, you can check by using the USB Host Diagnostics App from the Play Store.
  • Arduino - Any version will do, but we used an Uno R3
  • Arduino USB Cable
  • USB OTG Cable - This component connects the USB cable of your Arduino to the micro-USB port of your Android phone
  • Android Studio - You will need to have this installed and setup for this project. To make app development easier, Android Studio contains predictions and code generation.

 

Main Components of an Android App

The 3 major files in an Android app are:

• MainActivity.java - Where the Java code goes. This file controls the way the app will function.

• Activity_main.xml - Contains the layout of the app, such as the components, widget-like buttons, TextViews etc.

• AndroidManifest.xml - Where users define when the app must start, which permissions it requires, and which hardware it needs to access.

While there are a plethora of other files, they are all linked together with the help of these major three.

Where the user interacts with their phone is described as an activity. Activities contain widgets like text fields, buttons, images, etc., all of which help with information transfers. For this project, I will use the Main Activity, which takes the user’s input to send to the Arduino and display the received text.

The Layout

EditText Widget and Buttons

The same layout for the USB app and the Bluetooth app can be used. The layout is simple, with the minimum required widgets to test your connection between the two devices.

As you can see in the image above, there is an EditText widget and buttons to start and end the connection, transmit the data, and clear the TextView (which is the empty space below the buttons). All received data is displaying in the TextView.

Here's a sample of the XML. Since the code to add buttons is similar, they have been left out. A link to the complete code is available at the end of this article.

Copy Code
                  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

   xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"

   android:layout_height="match_parent"
  
   android:paddingLeft="@dimen/activity_horizontal_margin"

   android:paddingRight="@dimen/activity_horizontal_margin"

   android:paddingTop="@dimen/activity_vertical_margin"

   android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

   <EditText

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:id="@+id/editText"

       android:layout_alignParentTop="true"

       android:layout_alignParentRight="true"

       android:layout_alignParentEnd="true"

       android:layout_alignParentLeft="true"

       android:layout_alignParentStart="true" />

   <Button

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:text="Begin"

       android:id="@+id/buttonStart"

       android:layout_below="@+id/editText"

       android:layout_alignParentLeft="true"

       android:layout_alignParentStart="true"

       android:onClick="onClickStart"/>

   <TextView

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:id="@+id/textView"

       android:layout_below="@+id/buttonSend"

       android:layout_alignParentLeft="true"

       android:layout_alignParentStart="true"

       android:layout_alignRight="@+id/editText"

       android:layout_alignEnd="@+id/editText"

       android:layout_alignParentBottom="true" />

</RelativeLayout>

Here, we used a RelativeLayout which mean every widget is arranged with respect to its neighboring widget. This can be recreated easily with the Design Tab, which enables you to drag and drop widgets wherever you would like to see them. Each button needs a command when clicked, which you can organize in OnClick. The line below will allow you to specify the name of the method in the XML of the button:

Copy Code
android:onClick="onClickMethod"

After you enter that, hover on this line to get this menu to pop up on the left-hand side:

Click Create OnClick

Click Create 'OnClick...' which will allow you to automatically inject code for the OnClick method in MainActivity.java. This step needs to be repeated for each button.

The USB Serial Library

The UsbSerial library by Github user felHR85 is a great library to set up a serial connection automatically, because it is one of the only relevant libraries still being updated. As a bonus, it is relatively easy to setup and use. Adding this library to your project requires you to download the latest JAR file from Github. Then, you can move it to the 'libs' folder in the project's directory. In the file explorer of Android Studio, you can right-click the JAR and select "Add as Library".

The Program Flow

The Program Flow

The outline above shows how to proceed. Each activity has an onCreate() method, or the run when you create each activity. Any code you set to run at the beginning must be placed inside of this. Take note that reading from the device is asynchronous, so it will continuously run in the background, receiving data as soon as possible.

Opening a Connection

You’ll need to define the onClick method for the Begin button.

First off, we’ll define the onClick method for the Begin button which, when clicked, should search for all connected devices and then check if the vendor ID of the Arduino matches that of a connected device. If devices are found, permission is requested from the user. All USB slave devices have a vendor and product ID, used to identify what drivers should be used for it. An Arduino’s vendor ID is always 0x2341 or 9025.

Copy Code
                  public void onClickStart(View view) {

       HashMap usbDevices = usbManager.getDeviceList();
       if (!usbDevices.isEmpty()) {
           boolean keep = true;
           for (Map.Entry entry : usbDevices.entrySet()) {
               device = entry.getValue();
               int deviceVID = device.getVendorId();
               if (deviceVID == 0x2341)//Arduino Vendor ID
               {
                   PendingIntent pi = PendingIntent.getBroadcast(this, 0,
                    new Intent(ACTION_USB_PERMISSION), 0);
                   usbManager.requestPermission(device, pi);
                   keep = false;
               } else {
                   connection = null;
                   device = null;
               }

               if (!keep)
                   break;
           }
       }
   }
               

Now, we need to define the BroadcastReceiver to receive the broadcast to ask for user permission. It also needs to be able to start the connection automatically when devices are connected and to close connections when it is disconnected.

Copy Code
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { //Broadcast Receiver to automatically start and stop the Serial connection.
       @Override
       public void onReceive(Context context, Intent intent) {
           if (intent.getAction().equals(ACTION_USB_PERMISSION)) {
               boolean granted =
               intent.getExtras().getBoolean(UsbManager.EXTRA_PERMISSION_GRANTED);
               if (granted) {
                   connection = usbManager.openDevice(device);
                   serialPort = UsbSerialDevice.createUsbSerialDevice(device, connection);
                   if (serialPort != null) {
                       if (serialPort.open()) { //Set Serial Connection Parameters.
                           setUiEnabled(true); //Enable Buttons in UI
                           serialPort.setBaudRate(9600);
                           serialPort.setDataBits(UsbSerialInterface.DATA_BITS_8);
                           serialPort.setStopBits(UsbSerialInterface.STOP_BITS_1);
                           serialPort.setParity(UsbSerialInterface.PARITY_NONE);
                           serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
                           serialPort.read(mCallback); //
                           tvAppend(textView,"Serial Connection Opened!\n");

                       } else {
                           Log.d("SERIAL", "PORT NOT OPEN");
                       }
                   } else {
                       Log.d("SERIAL", "PORT IS NULL");
                   }
               } else {
                   Log.d("SERIAL", "PERM NOT GRANTED");
               }
           } else if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
               onClickStart(startButton);
           } else if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
               onClickStop(stopButton);
           }
       };

A connection for a device with a vendor ID that matches yours can be initiated if the initial IF condition is satisfied and if the other user has granted permission. If a broadcast is received for a device attach or detach, you can manually call the onClick methods for the Start and Stop buttons. You can define a SerialPort by using the device as the connection and the arguments. If this definition is successful, you can open the SerialPort and set your parameters accordingly. The default parameters for an Uno are 8 data bits, 1 stop bit and the flow control if OFF. The baud rate can be 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200, but for this project, stick with the standard 9600.

Receiving Data from the Device

Take note of the line in the code fragment above that says serialPort.read(mCallback). Below, a reference of a callback is passed to the read function. This makes it so it will be automatically triggered when any incoming data is detected.

Copy Code
                  UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() { 
    //Defining a Callback which triggers whenever data is read.
       @Override
       public void onReceivedData(byte arg0) {
           String data = null;
           try {
               data = new String(arg0, "UTF-8");
               data.concat("/n");
               tvAppend(textView, data);
           } catch (UnsupportedEncodingException e) {
               e.printStackTrace();
           }
       }
   };

 

All data received is in the form of raw bytes, so it needs to be re-encoded into a readable format such as UTF-8. Once re-encoded, it can be appended to the TextView by a custom method called tvAppend(). This is due to the fact that any change to the UI can only occur on the UI thread. This callback runs as a background thread, so it does not affect the UI directly.

Copy Code
private void tvAppend(TextView tv, CharSequence text) { final TextView ftv = tv; final CharSequence ftext = text; runOnUiThread(new Runnable() { @Override public void run() { ftv.append(ftext); } }); }

Sending Data to the Device

Sending data is relatively easy when compared to reading data from the device. It is a simple function call with bytes of data which needs to be sent as the argument. This will be defined in the OnClick method of the Send Button.

Copy Code
serialPort.write(string.getBytes());

Closing the Connection

When you want to close the connection, simply close the SerialPort.

Copy Code
serialPort.close();

The Application Manifest

In the application manifest, make sure to state all extra permissions the app may require. For this project, the only one needed is permission to make the Android a USB host. In order to do this, add the following code:

Copy Code
<uses-feature android:name="android.hardware.usb.host" />

You can make it so the app will start automatically by adding an IntentFilter to the MainActivity. The IntentFilter triggers when any new device is attached. To explicitly specify the type of device by providing the vendor ID and/or product ID in an XML file.

Copy Code
<?xml version="1.0" encoding="utf-8"?> <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity>

Take note of the line "android:resource="@xml/device_filter". This line is in place to tell the compiler it can find the device properties in a file named device_filter in src/main/res/xml, so make a folder named "xml" in src/main/res and put the following in it:

Copy Code
<resources> <usb-device vendor-id="9025" /> <!-- Vendor ID of Arduino --> </resources>

Testing the App

Follow these steps to build and run the app on your smartphone. Once all these steps are completed, fire up the Arduino IDE to set up the Arduino to echo everything it receives on the serial port. Use the following code to do that:

Copy Code
void setup() { Serial.begin(9600); } void loop() { char c; if(Serial.available()) { c = Serial.read(); Serial.print(c); } }

Finally, the Arduino can be connected to the microUSB port using the OTG Cable. The app should auto-start. Now send some text and the same data will be echoed back!

Download the Code

Download the code for this project here.

Key Parts and Components

Add all Digi-Key Parts to Cart
  • 1050-1024-ND