最近在尝试写一个安卓版的无人机地面站,参考了DroidPlanner的代码,发现了一个很好用的串口通信组件usb-serial-for-android,免root权限,不需要NDK,不过只支持Android 3.1+。
github地址:https://github.com/mik3y/usb-serial-for-android
关于用法README.md中已经介绍得很详尽了,可以简单归结为以下几点:
1. 把usb-serial-for-android复制到工程中,Eclipse和Android Studio的处理方式各不相同,具体步骤可参考这里;
2. 复制device_filter.xml到工程的res/xml目录(没有则自行新建);
3. 配置AndroidManifest.xml;
1 <activity 2 android:name="..." 3 ...> 4 <intent-filter> 5 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> 6 </intent-filter> 7 <meta-data 8 android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" 9 android:resource="@xml/device_filter" /> 10 </activity>
4. 编写代码。
1 // Find all available drivers from attached devices. 2 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 3 List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager); 4 if (availableDrivers.isEmpty()) { 5 return; 6 } 7 8 // Open a connection to the first available driver. 9 UsbSerialDriver driver = availableDrivers.get(0); 10 UsbDeviceConnection connection = manager.openDevice(driver.getDevice()); 11 if (connection == null) { 12 // You probably need to call UsbManager.requestPermission(driver.getDevice(), ..) 13 return; 14 } 15 16 // Read some data! Most have just one port (port 0). 17 UsbSerialPort port = driver.getPorts().get(0); 18 try { 19 port.open(connection); 20 port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); 21 22 byte buffer[] = new byte[16]; 23 int numBytesRead = port.read(buffer, 1000); 24 Log.d(TAG, "Read " + numBytesRead + " bytes."); 25 } catch (IOException e) { 26 // Deal with error. 27 } finally { 28 port.close(); 29 }
以下是我修改过的简单的例子(基于Android Studio 2.2.3)。
MainActivity.java
1 package com.stshdz.drone.shdrone; 2 3 import android.content.Context; 4 import android.hardware.usb.UsbDeviceConnection; 5 import android.hardware.usb.UsbManager; 6 import android.os.Handler; 7 import android.os.Message; 8 import android.support.v7.app.AppCompatActivity; 9 import android.os.Bundle; 10 import android.view.View; 11 import android.widget.TextView; 12 13 import com.hoho.android.usbserial.driver.UsbSerialDriver; 14 import com.hoho.android.usbserial.driver.UsbSerialPort; 15 import com.hoho.android.usbserial.driver.UsbSerialProber; 16 17 import java.io.IOException; 18 import java.util.List; 19 20 public class MainActivity extends AppCompatActivity { 21 UsbSerialPort port = null; 22 23 class MyHandler extends Handler { 24 @Override 25 public void handleMessage(Message msg) { 26 switch (msg.what) { 27 case 0x11: 28 TextView tv_display = (TextView)findViewById(R.id.tv_display); 29 Bundle bundle = msg.getData(); 30 tv_display.setText(tv_display.getText() + bundle.getString("text")); 31 break; 32 default: 33 break; 34 } 35 } 36 } 37 38 39 private MyHandler myHandler = new MyHandler(); 40 41 @Override 42 protected void onCreate(Bundle savedInstanceState) { 43 super.onCreate(savedInstanceState); 44 getSupportActionBar().hide(); 45 setContentView(R.layout.activity_main); 46 47 findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() { 48 @Override 49 public void onClick(View v) { 50 try { 51 if(port != null){ 52 port.write("Hello\r\n".getBytes(), 1000); 53 } 54 } catch (IOException e) { 55 e.printStackTrace(); 56 } 57 } 58 }); 59 60 new Thread(new Runnable() { 61 @Override 62 public void run() { 63 64 // Find all available drivers from attached devices. 65 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 66 List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager); 67 if (availableDrivers.isEmpty()) { 68 return; 69 } 70 71 // Open a connection to the first available driver. 72 UsbSerialDriver driver = availableDrivers.get(0); 73 UsbDeviceConnection connection = manager.openDevice(driver.getDevice()); 74 if (connection == null) { 75 // You probably need to call UsbManager.requestPermission(driver.getDevice(), ..) 76 return; 77 } 78 79 // Read some data! Most have just one port (port 0). 80 port = driver.getPorts().get(0); 81 try { 82 port.open(connection); 83 port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); 84 85 while(true) { 86 byte buffer[] = new byte[16]; 87 int numBytesRead = port.read(buffer, 1000); 88 89 if(numBytesRead > 0){ 90 Message message = new Message(); 91 message.what = 0x11; 92 Bundle bundle = new Bundle(); 93 bundle.putString("text", new String(buffer, "UTF-8")); 94 message.setData(bundle); 95 myHandler.sendMessage(message); 96 } 97 } 98 99 } catch (IOException e) { 100 // Deal with error. 101 } finally { 102 try { 103 port.close(); 104 } catch (IOException e) { 105 e.printStackTrace(); 106 } 107 } 108 } 109 }).start(); 110 } 111 }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.stshdz.drone.shdrone.MainActivity"> <Button android:id="@+id/btn_send" android:text="send" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_display" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="没有数据." /> </LinearLayout>
xml/device_filter.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 <!-- 0x0403 / 0x6001: FTDI FT232R UART --> 4 <usb-device vendor-id="1027" product-id="24577" /> 5 6 <!-- 0x0403 / 0x6015: FTDI FT231X --> 7 <usb-device vendor-id="1027" product-id="24597" /> 8 9 <!-- 0x2341 / Arduino --> 10 <usb-device vendor-id="9025" /> 11 12 <!-- 0x16C0 / 0x0483: Teensyduino --> 13 <usb-device vendor-id="5824" product-id="1155" /> 14 15 <!-- 0x10C4 / 0xEA60: CP210x UART Bridge --> 16 <usb-device vendor-id="4292" product-id="60000" /> 17 18 <!-- 0x067B / 0x2303: Prolific PL2303 --> 19 <usb-device vendor-id="1659" product-id="8963" /> 20 21 <!-- 0x1a86 / 0x7523: Qinheng CH340 --> 22 <usb-device vendor-id="6790" product-id="29987" /> 23 </resources>
AndroidManifest.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.stshdz.drone.shdrone" > 4 5 <application 6 android:allowBackup="true" 7 android:icon="@mipmap/ic_launcher" 8 android:label="@string/app_name" 9 android:supportsRtl="true" 10 android:theme="@style/AppTheme" > 11 12 <activity android:name=".MainActivity" > 13 <intent-filter> 14 <action android:name="android.intent.action.MAIN" /> 15 16 <category android:name="android.intent.category.LAUNCHER" /> 17 </intent-filter> 18 19 <intent-filter> 20 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> 21 </intent-filter> 22 <meta-data 23 android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" 24 android:resource="@xml/device_filter" /> 25 </activity> 26 </application> 27 28 </manifest>
Android设备通过OTG数据线连接USB转TTL模块,模块的TX与RX引脚短接(自发自收),点击“SEND”即可看到效果。