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. 解決方法

  1. 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"/>。考慮日后上線產品的維護,果斷放棄。

  2. android-serialport-api

    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類似的報錯

    解決方法:

    1. 關于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"
           }
       }
      
    2. 關于無法獲取port的報錯

      一開始我懷疑是我的so沒有加載成功,后來斷點發現無法從sharePreference中獲取到設置的 Device和baudrates。修改SerialPortPreferencesActivity如下:

       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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容