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項目,導入進去之后,設置好設備和波特律之后直接就可以使用,也看到別人分享的項目經驗,這個可用。具體講解可以參考這篇文章https://blog.csdn.net/qiwenmingshiwo/article/details/49557889,兩個項目分別是編譯底層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-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