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。强依赖。考虑日后上线产品的维护,果断放弃。

  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 UART 串口通信_第1张图片
android_menu.png
Android UART 串口通信_第2张图片
android_talk.png
Android UART 串口通信_第3张图片
pc-comm.png
Android UART 串口通信_第4张图片
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

你可能感兴趣的:(Android UART 串口通信)