串口通讯,对于没接触过这方面的朋友们,确实会感到头疼,不知道从何下手。
其实,串口通讯和服务器之间的通讯是一样的,都是传一些参数过去,然后返回一些数据回来。不过串口通讯管这些参数叫做指令,而这些指令是由硬件的通讯协议而定的,通讯协议不同,指令自然也不同。在我开发的这个项目里,兼容了四种硬件通讯协议,这四种协议各不相同,所以,那些指令就不在代码里面写出来了。
串口通讯,第一步要做的当然是打开串口,打开串口的方法如下:
首先在app下建立一个libs文件夹,把.so文件复制到libs下
用android studio的朋友们要记得在build.gradle 文件中添加这段 jniLibs.srcDirs = ['libs'] ,我有一次看见别人的代码里面可以不加这段,照样能运行,有知道的可以和大家分享一下。不过这些都不重要,只要能开串口就行。然后,在java的根目录下建一个包,注意是根目录,包名为:android_serialport_api,这个包名是固定的,不能少也不能多。然后把类SerialPort复制到包下面。
// JNI
private native static FileDescriptor open(String path, int baudrate, int flags);
public native void close();
static { System.loadLibrary("serial_port"); }
上面的第一个函数open是调用jni打开串口的方法,调用该方法的时候会返回一个FileDescriptor对象,通过该对象可以获取输入输出流。第二个close函数是关闭串口的方法,可以通过此方法关闭串口。这两个函数最好是能成对出现,在程序打开的时候把串口打开,程序退出了就把串口关闭,这样可以避免一些问题出现。第三个是加载.so文件里面的代码的,加载了之后串口才能用。
···
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
System.out.println("device======"+device.getAbsolutePath());
/* Check access permission /
if (!device.canRead() || !device.canWrite()) {
try {
/ Missing read/write permission, trying to chmod the file */
Process su = Runtime.getRuntime().exec("/system/bin/su");
String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
throw new SecurityException();
}
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException();
}
}
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
···
上面的这段代码中,su.getOutputStream().write(cmd.getBytes());这行是获取root权限的,打开串口是需要root权限的,如果不能获取root权限,串口也是打不开的,其他的就是一些判断了,在这就不做详细讲解了。
上面这些是开串口之前的准备,准备完毕后,我们来打开串口,打开串口就是调用open函数,调用open函数需要传三个参数,第一个参数path是串口名,比如:”/dev/ttyS0“,这些是根据实际接口来定的,第二个参数baudrate是波特率,一般都是9600,15200,这个需要根据硬件来定,第三个我就不清楚了,我都是传0,有知道的可以和大家分享一下。
···
/**
* 初始化串口
* @param lockerPortInterface
/
private void setSerialPort(LockerPortInterface lockerPortInterface){
this.sportInterface = lockerPortInterface;
try {
/ Check parameters /
if ((path.length() == 0) || (baudrate == -1)) {
throw new InvalidParameterException();
}
/ Open the serial port */
boxPort = new SerialPort(new File(path), baudrate, 0);
mOutputStreamBox = boxPort.getOutputStream();
mInputStreamBox = boxPort.getInputStream();
/* Create a serial rec buf thread */
mReadThreadBox = new ReadThreadBox();
// SerialPortState = true;
mReadThreadBox.start();
if (firstRegisterBox) {
if(mContext == null){
Log.e(TAG, "mContext nulll");
}
m_SerialRecBox = new SerialBroadcastReceiverBox(mContext);
m_SerialRecBox.registerAction();
firstRegisterBox = false;
Log.i(TAG, "----locker port--- 注册完毕");
}
lockerPortInterface.onLockerOutputStream(mOutputStreamBox);
} catch (SecurityException e) {
e.printStackTrace();
DisplayError(mContext,R.string.error_security);
} catch (IOException e) {
e.printStackTrace();
DisplayError(mContext,R.string.error_unknown);
} catch (InvalidParameterException e) {
e.printStackTrace();
DisplayError(mContext,R.string.error_configuration);
}
}
···
boxPort = new SerialPort(new File(path), baudrate, 0);这行代码是调用SerialPort的构造方法,通过他的构造方法去调用open函数,然后通过SerialPort对象来获取输入输出流。在这里解释一下,输入流是接收串口返回的数据,输出流是向串口发指令。 调用SerialPort的构造方法可能会发生三种异常,第一种异常(SecurityException)是串口无读写权限,抛出这种异常的话就说明你可能没有root权限,第二种异常(IOException )串口不能打开,可能就是你没有这个串口,第三种异常(InvalidParameterException)是传的参数有误,可能是你的波特率不对。我理解的就是这样的,不知道对不对。
这是我用模拟器测试的,只要有请求root权限的页面,并且抛的是IO异常,打开串口应该就没问题了。
最后就是向串口发指令,向串口发指令是用输出流向串口写入,至于具体的指令是什么,需要根据协议来定。发完指令之后串口会回数据结果给你,你需要接收这些数据来做业务,硬件不同,回数据结果的方式也不同,有些硬件是发完指令后把结果直接回给你,有些硬件是先给你一小部分,然后等1s再回另外一部分数据。
在我写的demo里面用的方法是针对硬件一次性把数据结果全部返回的方式,onLockerDataReceived直接用这个方法接收数据,这样就比较简单了。串口通讯说到这里就结束了。
最后贴上自己写的一个小demo地址:https://github.com/fm183/SerialportDemo.git,以供大家参考。