前言:
最近在总是看见有人在群里面问一些串口通信相关的问题,特别是对于我们这些做APP出生的程序员来说,初次接触串口通信的确是会遇到各种Bug,各种摸不着头脑。串口通信偏向嵌入式一点,是Android设备通过串口与其他设备进行通信的一种方式,本文介绍的Android纯串口的通信,并不是手机上的USB串口通信。
简介:
首先简述一下此项目应用,它是一个简单的物联网智能盒子,主要工作:是通过Android开发板上的串口进行数据读写操作。一块Android开发板外接一个单片机,Android上面跑有MQTT服务与后台服务器通信,Android机收到MQTT发布的不同信息后,将信息转换成不同的串口指令发给单片机,Android板收到对应的MQTT消息就做相应的界面处理并将MQTT消息转换成不同的串口指令发给单片机,然后单片机直接控制硬件设备工作。
一、什么是串口?
串行端口 (SerialPort)简称:串口,主要用于数据被逐位按顺序传送的通讯方式称为串口通讯(简单来讲就是按顺序一位一位地传输数据)。
常见的串口有25针和9针(遵循RS-232标准)
二、串口通信原理
串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。
串口用于ASCII码字符的传输。通信使用3根线完成,分别是地线(GND)、发送(TX)、接收(RX)。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
三、Android应用串口通信的实现
Android SDK并没有在Framework层实现封装关于串口通信的类库。但是,Android是基于Linux kernel 2.6上的,所以我们可以像在Linux系统上一样来使用串口。因为Framework层中并没有封装关于串口通信的类库,所以我们需要通过Android NDK来实现打开、读写串口,然后提供接口供JAVA本地调用。
这里可以参照Google已经给出了源码,地址在GitHub android-serialport-api
这是12年的代码,还是Eclipse工程,本文主要介绍如何在Android Studio中使用。这里先得配置好NDK环境
1:项目配置
首先看一下项目结构:创建了一个jni目录,jni目录用来放生成的头文件(.h文件)及Java本地方法的C代码实现类;
Java层的代码,Google已经给封装好了,主要的都在 SerialPort.java
第一步:拷贝创建 native方法的java类 SerialPort.java 到自己的项目中;
第二步:生成.h头文件; 生成方法有多种,这里使用 cd \app\src\main\java 进入到java目录下;
方式1: javah -classpath . -jni +全限定类名;
方式2:javah -d ../jni +全限定类名;(此方式会直接创建一个jni目录,然后把生成的.h文件存放在目录里面)
第三步:实现SerialPort.java类中的本地方法,这里直接拷贝Google写好的C实现,将c代码中的函数名与生成的头文件中的函数名保持一致
配置build.gradle
如需帮助请 + QQ:631934797
使用
串口作操都封装到SerialPortUtil类中
/**
* Created by Roy88 on 2017/6/18.
*
*串口工具类
*/
public classSerialPortUtil {
private static finalStringTAG= SerialPortUtil.class.getSimpleName();
public staticSerialPortserialPort=null;
public staticInputStreammInputStream=null;
public staticOutputStreammOutputStream=null;
public static booleanflag=false;
private staticBufferedReaderbr;
/**
* 打开串口
*/
public static voidopenSrialPort(String port, intbaudrate){
Log.i(TAG,"打开串口");
try{
serialPort=newSerialPort(newFile("/dev/"+ port),baudrate,0);
//获取打开的串口中的输入输出流,以便于串口数据的收发
mInputStream=serialPort.getInputStream();
mOutputStream=serialPort.getOutputStream();
flag=true;
//接收串口数据
receiveSerialPort();
}catch(IOException e) {
e.printStackTrace();
}
}
/**
* 接收串口数据
*/
public static voidreceiveSerialPort(){
newThread(newRunnable() {
@Override
public voidrun() {
//循环接收串口数据
while(flag) {
try{
if(mInputStream==null)return;
br=newBufferedReader(newInputStreamReader(mInputStream));
String str;
while((str =br.readLine()) !=null)
{
if(TextUtils.isEmpty(str))continue;
Log.i(TAG,"接收串口数据:"+ str);
if(String.valueOf(str.charAt(0)).equals("{") && str.substring(str.length() -1).equals("}")){
acceptAndNotify(str);
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* 区分收到的指令数据并分类分发
*
*@paramjsonBack收到的JSON指令
*/
private static voidacceptAndNotify(String jsonBack) {
if(jsonBack ==null||"".equals(jsonBack.trim()))
throw newIllegalArgumentException("JsonBack is illegal, please check args ... ");
JsonParser jsonParser =newJsonParser();
JsonObject json = (JsonObject) jsonParser.parse(jsonBack);
if(json ==null)
throw newJsonParseException("Json Parse error, please check args ... ");
String protocolResult = json.getAsJsonPrimitive("protocol").getAsString();
if(protocolResult ==null||"".equals(protocolResult))
throw newNumberFormatException("转化错误... ");
switch(protocolResult) {
case"coin_in":
// EventBus.getDefault().post(new SerialPortEvent(GsonUtil.parse(jsonBack, CoinAndRemoteCoinBean.class)));
break;
case"remote_coin_in":
// EventBus.getDefault().post(new SerialPortEvent(GsonUtil.parse(jsonBack, CoinAndRemoteCoinBean.class)));
break;
case"key_event":
// EventBus.getDefault().post(new SerialPortEvent(GsonUtil.parse(jsonBack, OrientationAndKeyBean.class)));
break;
}
}
/**
* 发送串口数据
*@paramdata要发送的数据
*/
public static voidsendSerialPort(String data){
Log.i(TAG,"发送串口数据:"+ data);
try{
byte[] sendData = data.getBytes();
mOutputStream.write(sendData);
mOutputStream.flush();
Log.i(TAG,"发送串口数据成功!");
}catch(IOException e) {
e.printStackTrace();
Log.i(TAG,"发送串口数据失败!");
}
}
/**
*关闭串口
*关闭串口中的输入输出流
*然后将flag的值设为flag,终止接收数据线程
*/
public static voidcloseSerialPort(){
Log.i(TAG,"关闭串口");
try{
if(serialPort!=null) {
serialPort.close();
}
if(mInputStream!=null) {
mInputStream.close();
}
if(mOutputStream!=null){
mOutputStream.close();
}
if(br!=null){
br.close();
}
flag=false;
}catch(IOException e) {
e.printStackTrace();
}
}
}
本文没有介绍关于jni、NDK的内容,因为上网有很讲这方面知识讲解。如有不了解的请自行百度、google。