The most reliable way to connect an Arduino to an Android app built in Android Studio is via a USB OTG (On-The-Go) cable paired with the usb-serial-for-android library. While older tutorials rely on Bluetooth (HC-05), Android 12 and later severely restrict background Bluetooth serial access, making USB OTG the definitive standard for low-latency, rock-solid hardware communication. This guide walks through a complete bidirectional serial build, targeting the exact CH340G driver hurdles that stall most developers.

Difficulty Rating: Intermediate (Requires Android Studio Koala/Meerkat, basic Kotlin, and physical USB-OTG hardware).
Time to Build: 45 minutes.

Project Spec Sheet & Hardware Requirements

Before opening Android Studio, verify your physical layer. The most common point of failure in Arduino-to-Android projects is using a charge-only USB cable that lacks the D+ and D- data lines.

ComponentExact Variant / SpecificationNotes & Pricing (2026)
MicrocontrollerArduino Nano V3 (ATmega328P)Must have the CH340G USB-UART chip (not FTDI). ~$6.00.
OTG AdapterUSB-C to USB-A OTGMust support data transfer. UGREEN or Anker branded. ~$7.00.
CableMini-USB to USB-A Data CableVerify data capability by plugging into a PC first. ~$4.00.
Sensor10kΩ Linear PotentiometerFor analog input testing. ~$1.00.
Actuator5mm Red LED + 220Ω ResistorFor digital output testing. ~$0.50.
⚠️ Power Warning: Standard Android USB-C ports limit OTG power output to 500mA. If your Arduino circuit draws more (e.g., driving motors or long LED strips), the phone will brownout or kill the USB bus. Use a powered USB hub between the phone and Arduino for high-current loads.

Pin Mapping & Physical Wiring

This build uses a simple bidirectional loop: the phone reads the potentiometer and writes to the LED. Wire the Nano V3 exactly as mapped below.

Arduino Nano V3 PinComponentWire Color (Suggested)Function
D2220Ω Resistor → LED AnodeOrangeDigital Output (LED Control)
GNDLED Cathode & Pot Pin 3BlackCircuit Ground
5VPot Pin 1RedVCC for Potentiometer
A0Pot Wiper (Pin 2)BlueAnalog Input (Sensor Data)

Android Studio Configuration & The CH340G Filter

The Arduino Nano V3 uses the CH340G USB-to-serial chip. Android does not natively map this chip to a standard serial port. You must use the usb-serial-for-android library and explicitly declare the CH340 Vendor ID (VID) and Product ID (PID) in your manifest.

Step 1: Add the library to your build.gradle.kts (Module level):

dependencies {
    implementation("com.github.mik3y:usb-serial-for-android:3.8.0")
}

Step 2: Create res/xml/device_filter.xml. The CH340G VID is 0x1A86 and PID is 0x7523. If you omit this, Android will silently ignore the Arduino when it is plugged in.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="6790" product-id="29987" /> <!-- 1A86:7523 in decimal -->
</resources>

Step 3: Add the intent filter to your AndroidManifest.xml inside your main <activity> tag:

<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" />

Complete Firmware & Android Studio Code

Below is the complete, compilable code for both sides of the bridge. The Arduino firmware targets the Nano V3 (ATmega328P) at 9600 baud.

Arduino Firmware (C++)

// Target: Arduino Nano V3 (ATmega328P)
// Baud: 9600
const int LED_PIN = 2;
const int POT_PIN = A0;

void setup() {
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  pinMode(POT_PIN, INPUT);
}

void loop() {
  // Read commands from Android
  if (Serial.available() > 0) {
    char cmd = Serial.read();
    if (cmd == '1') digitalWrite(LED_PIN, HIGH);
    else if (cmd == '0') digitalWrite(LED_PIN, LOW);
  }
  
  // Send sensor data to Android
  int sensorVal = analogRead(POT_PIN);
  Serial.println(sensorVal);
  delay(100); // 10Hz update rate
}

Android Studio Kotlin Implementation

This snippet handles USB permission requests, port initialization, and bidirectional I/O with proper error handling. For full Android USB Host API context, refer to the official documentation.

import android.content.Context
import android.hardware.usb.UsbManager
import com.hoho.android.usbserial.driver.UsbSerialPort
import com.hoho.android.usbserial.driver.UsbSerialProber
import java.io.IOException

fun connectToArduino(context: Context): UsbSerialPort? {
    val manager = context.getSystemService(Context.USB_SERVICE) as UsbManager
    val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
    
    if (availableDrivers.isEmpty()) {
        // Error: device_filter.xml is missing VID/PID or cable is charge-only
        return null 
    }
    
    val driver = availableDrivers[0]
    val connection = manager.openDevice(driver.device)
    
    if (connection == null) {
        // Error: User denied USB permission prompt
        return null
    }
    
    val port = driver.ports[0] // Most Arduinos only have 1 port
    try {
        port.open(connection)
        port.setParameters(9600, UsbSerialPort.DATABITS_8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
        
        // Turn on LED (Send '1')
        port.write(byteArrayOf('1'.code.toByte()), 1000)
        
        // Read Potentiometer (Blocking read for example)
        val buffer = ByteArray(8)
        val bytesRead = port.read(buffer, 1000)
        if (bytesRead > 0) {
            val sensorData = String(buffer, 0, bytesRead).trim()
            // Process sensorData
        }
    } catch (e: IOException) {
        // Handle physical disconnect during I/O
        port.close()
        return null
    }
    return port
}

Debugging: First Three Things to Check When It Fails

If your app crashes or fails to connect, look for this exact error string in your Android Studio Logcat:

java.lang.SecurityException: User has not given permission to device UsbDevice [name=/dev/bus/usb/001/002, vendor=6790, product=29987]

If you see that, or if findAllDrivers() returns an empty list, execute these three checks in order:

  1. Verify the Data Lines (Physical Layer): Swap the Mini-USB cable. 60% of OTG failures are caused by charge-only cables. Plug the Arduino into a PC; if the PC doesn't chime and assign a COM port, the cable lacks data wires.
  2. Check the Decimal VID/PID (Manifest Layer): The CH340G VID is 1A86 (Hex) which is 6790 (Decimal). Android XML requires decimal values. If you put 1A86 in the vendor-id tag, Android parses it as decimal 1710, and the filter fails silently.
  3. Clear App USB Permissions (OS Layer): If you previously clicked "Cancel" on the Android USB permission prompt and checked "Always open with this app", Android remembers the denial. Go to Android Settings → Apps → Your App → Set as Default → Clear Defaults, then force-stop the app and replug the OTG cable.

Extending and Simplifying the Build

How to Simplify: If dealing with CH340G drivers and XML filters is frustrating, switch your hardware to an Arduino Leonardo or Pro Micro (ATmega32U4). These boards feature native USB HID (Human Interface Device) capabilities. Android recognizes them natively as keyboards or gamepads without requiring the usb-serial-for-android library or custom VID/PID filters. You can read more about Arduino native USB in the official docs.

How to Extend: To add wireless telemetry without violating Android 12+ Bluetooth restrictions, replace the Nano with an ESP32-WROOM-32. Use the ESP32's BLE (Bluetooth Low Energy) GATT server. Android Studio's BLE API is fully supported in the background, allowing you to read the potentiometer via BLE characteristics while the phone screen is off.

Frequently Asked Questions

Can I use Bluetooth instead of USB OTG for Arduino Android Studio projects?

You can, but it is no longer recommended for new builds. Classic Bluetooth (using the HC-05/HC-06 modules) relies on the BluetoothSocket API, which Android 12 and later heavily restrict for background operation to prevent tracking. If you must go wireless, use an ESP32 with BLE (Bluetooth Low Energy), which is fully supported for background sensor polling in modern Android Studio environments.

Why does Android Studio not recognize my Arduino Uno R3?

The official Arduino Uno R3 uses the ATmega16U2 USB-to-Serial chip, which has a different VID/PID (VID: 0x2341, PID: 0x0043) than the CH340G found on clone Nanos. If your Uno isn't recognized, you must add the Uno's decimal VID (9025) and PID (67) to your device_filter.xml file alongside the CH340G values.

How do I keep the USB serial connection alive when the Android screen turns off?

Android aggressively kills background USB connections to save battery. To maintain the link, you must run your serial read/write loop inside an Android Foreground Service with a persistent notification. Furthermore, you need to acquire a PARTIAL_WAKE_LOCK via the PowerManager to prevent the CPU from sleeping, which would halt the USB host controller.