Android UART 串列埠通訊
1. 需求
最近有專案需要實現windows機器和Android開發版進行UART串列埠通訊,經過3天查詢嘗試,特記錄一下最終方案,希望之後的同行少走彎路,最後在git上回開源我最終的方案希望大家支援。
2. 環境
Android 3.0.1
Gradle 4.1
ARM開發版 : RK3399
PC機器:Win10
開發機器:MAC 10.13.3
3. 解決方法
-
Android Things
Android Things 谷歌於2018年5月釋出1.0正式版,讓開發者可以使用Android開發工具開發嵌入式裝置。需要比較高的Android API支援 ( >24 )。很多樹莓派3用這個做開發,普遍應用於物聯網領域。
因為是官方的原因,感覺應該靠譜些,且整合方便,Git上參考了 https://github.com/androidthings/sample-uartloopback 這個專案。但是,每個卵用。簡直白折騰。首先 需要API 27以上 的裝置,現在手上的開發版很少有Android 8.0以上版本的。(為此,還刷了韌體,做了嘗試)最大的坑是:這個專案最後基本安裝不到國內的Android 系統上,原因: 需要Google Service 。強依賴
<uses-library android:name="com.google.android.things"/>
。考慮日後上線產品的維護,果斷放棄。 -
android-serialport-api,是兩個Eclipse專案,匯入進去之後,設定好裝置和波特律之後直接就可以使用,也看到別人分享的專案經驗,這個可用。兩個專案分別是編譯底層JNI專案 android-sercd ,另一個側重根據JNI的API實現Java端功能。如果只實現功能就看 android-serialport-api 缺點:專案比較老,需要JNI編譯
經過嘗試匯入庫中已編譯好的so,執行可以跑通UART通訊。但遇到了一下幾個‘坑’ 匯入JNI的時候注意事項就不說了,包名這些的都不能改。
-
Run 使用已有的so的專案時候會報錯
dlopen failed: "has text relocations"
-
遇到無法獲取port,請重新配置Serial port類似的報錯
解決方法:
-
關於 dlopen failed: "has text relocations" 的簡單解決辦法,降級targetSdkVersion 23一下。Android 6.0 機制的問題。根本解決版本使用高版本的NDK重新編譯so來解決,後文中我重新編譯了一把。這裡是gradle檔案修改的關鍵地方。
compileSdkVersion 22 defaultConfig { applicationId "com.attrsc.braincs.androidserialport" minSdkVersion 21 targetSdkVersion 22 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" ndk { abiFilters "armeabi-v7a" } }
-
關於無法獲取port的報錯
一開始我懷疑是我的so沒有載入成功,後來斷點發現無法從sharePreference中獲取到設定的 Device和baudrates。修改
SerialPortPreferences
Activity如下:public class SerialPortPreferences extends PreferenceActivity { private Application mApplication; private SerialPortFinder mSerialPortFinder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mApplication = (Application) getApplication(); mSerialPortFinder = mApplication.mSerialPortFinder; addPreferencesFromResource(R.xml.serial_port_preferences); final SharedPreferences sp = getSharedPreferences("android_serialport_api.sample_preferences", MODE_PRIVATE); // Devices final ListPreference devices = (ListPreference)findPreference("DEVICE"); String[] entries = mSerialPortFinder.getAllDevices(); String[] entryValues = mSerialPortFinder.getAllDevicesPath(); devices.setEntries(entries); devices.setEntryValues(entryValues); devices.setSummary(devices.getValue()); devices.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { preference.setSummary((String)newValue); sp.edit().putString("DEVICE",(String) newValue).apply();//此處新增 return true; } }); // Baud rates final ListPreference baudrates = (ListPreference)findPreference("BAUDRATE"); baudrates.setSummary(baudrates.getValue()); baudrates.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { preference.setSummary((String)newValue); sp.edit().putString("BAUDRATE",(String) newValue).apply();//此處新增 return true; } }); } } ```
-
4. 終極解決方案
先上圖

android_menu.png

android_talk.png

pc-comm.png

pc-receive.png
由於 android-serialport-api 專案中的so使用較old的ndk編譯,所以在對於Android 6.0 以上版本相容的時候會報錯 dlopen failed: "has text relocations"
。且使用的mk進行編譯,特升級為用cmake編譯。
升級
android-serialport-api
-
ndk 17.0.4xxx jni編譯
-
cmake 編譯鏈
-
EClipse專案-> Android Studio專案
專案結構:
. ├── AndroidSerialLibrary.iml ├── androidserial │├── CMakeLists.txt │├── androidserial.iml │├── build │├── build.gradle │├── libs │├── proguard-rules.pro │└── src ├── app │├── app.iml │├── build │├── build.gradle │├── libs │├── proguard-rules.pro │└── src ├── build │└── android-profile ├── build.gradle ├── gradle │└── wrapper ├── gradle.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle
app對應原專案中的各個Activity, androidserial 是module 對應編譯之前的so,還有API的封裝。可以直接引用androidserial,呼叫方法參考app目錄下的activity。
注意
關於許可權!
當接入開發板後如果發現 Error You do not have read/write permission to the serial port
需要root 許可權 ,在開發者模式中開啟root 許可權 adb和應用
使用一下命令開啟Android對串列埠的讀寫許可權
❯ adb shell rk3399_firefly_mid:/ $ su rk3399_firefly_mid:/ # chmod 777 /dev/ttyS4 rk3399_firefly_mid:/ # setenforce 0 rk3399_firefly_mid:/ #
setenforce 0
: 關閉防火牆,有人說關鍵是這,但是我的環境不用關閉,只要給許可權就可以
注意
關於ttyS1 - 6 ttyS1 - 6 對應的是 UART 串列埠1-6 一般都是一一對應的。這個具體要看一下開發板的說明。
記錄的比較糙,還請見諒,如有問題請留言,我看到後肯定回覆。專案主要看結構,剩下的都是複製黏貼的事。 git地址:https://github.com/braincs/AndroidSerialLibrary