因为这篇帖子讲的是需要通过Android程序向继电器发送串口命令来控制继电器的开关,继而来控制led灯的开关,所以这篇帖子看起来比较枯燥。
串口开发首先要有串口的开发环境,此处用的是aar包。
下载地址: 串口开发环境
//串口
api(name: 'serialport-1.0.1', ext: 'aar')
将以上arr库下载下来之后,导入项目的libs包下,然后在app的build.gradle文件中添加以下代码,即可通过代码让Android 程序向继电器发送串口命令:
完成之后点击Sysn Now,在开始正式开发之前,一定要认真阅读Android开发板和继电器的开发说明,弄清开发板上所有的串口所对应的位置,同时搞清继电器所使用的波特率(如果串口名称或者波特率对应不上的话,使用Android程序向继电器发送串口命令可能会没有反应)。
以下是打开串口对应的串口的代码:
/**
* 打开串口,接收,发送数据数据
* 通过串口,接收单片机发送来的数据
*/
public boolean openSerialPort(String port_name,int port_clound) {
try {
serialPort = new SerialPort(new File(port_name), port_clound, 0);
//调用对象SerialPort方法,获取串口中"读和写"的数据流
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
isStart = true;
} catch (IOException e) {
e.printStackTrace();
}
getSerialPort();
return isStart;
}
上边的方法,首先看参数,port_name就是开发板上的对应的串口的名称,向继电器板发送命令就是通过该串口发送的,串口的名称一定要用开发板上的串口名称并且与串口所对应的位置一致,否则可能报错SecurityException。
port_clound是继电器的波特率,一定要和继电器的波特率对应好,不然可能会没有反应。
然后根据串口的名称和波特率创建SerialPort的实例,然后创建输入流(用来接收继电器返回的数据,一般时候也用不到,你只要看着灯亮了,关了就好了,还在乎继电器返回的数据干嘛~),输出流(用来向继电器发送串口命令),改变串口打开的状态isStart = true,同时调用了getSerialPort(),接下来看一下getSerialPort()里边的东西;
private void getSerialPort() {
mReceiveThread = new ReceiveThread();
mReceiveThread.start();
}
这里只是开启了一个ReceiveThread线程,看一下这个线程里边作了什么。
/**
* 接收串口数据的线程
*/
private class ReceiveThread extends Thread {
@Override
public void run() {
super.run();
while (isStart) {
if (inputStream == null) {
return;
}
byte[] readData = new byte[1024];
try {
int size = inputStream.read(readData);
if (size > 0) {
String readString = DataUtils.ByteArrToHex(readData, 0, size);
if (!StringUtils.isEmpty(readString)) {
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注释也说的很明白了,就是用来接收继电器返回的的数据用的,while (isStart) 说明只要你打开了串口,就开启一个死循环,在死循环里输入流一直在读取继电器返回来的数据。
有打开串口的方法,肯定就要有关闭串口的方法,接下来看一下关闭串口的方法。
/**
* 关闭串口
* 关闭串口中的输入输出流
*/
public void closeSerialPort() {
Log.i("test", "关闭串口");
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
isStart = false;
} catch (IOException e) {
e.printStackTrace();
}
}
这里只是将输入流和输出进行关闭,并改变串口的打开状态:isStart = false; 终止ReceiveThread线程中的循环。
接下来看一下看一下最重要的,向继电器发送串口命令。
/**
* 发送数据
* 通过串口,发送数据到继电器
*
* @param data 要发送的数据
*/
public void sendSerialPort(String data) {
try {
byte[] sendData = DataUtils.HexToByteArr(data);
// byte[] sendData = data.getBytes();
outputStream.write(sendData);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
其实也很简单就是通过DataUtils.HexToByteArr(data);将String类型的串口命令转换为字节数组,通过输出流将串口命令字节数组进行发送。
到这有的猿(媛)就要问了,那String类型的串口命令你怎么知道发送什么啊,别急,看下边:
串口命令可以通过工具进行获取:串口命令获取工具
使用串口线将你的电脑和开发板进行连接,连接之后打开串口命令获取工具
刚打开的页面是这样的,输入开发板的串口名称,之后将对应的串口名称输入进去,点击打开串口按钮,之后会提示打开串口是否成功,对应的位置的两个线进行短路(短路之后你给串口发送什么串口命令,返回什么串口命令显示在左边的白色框内),然后点击Open按钮发送打开继电器的命令,如果你短路成功的话继电器命令会显示在左边白色框内,点击close按钮和点击Open按钮同理。
以上代码均出自:
/**
* Created by tehike
* Time : 2018/11/29.10:55
* data : 开关读写串口的工具类
*/
public class SerialPortUtil {
private SerialPort serialPort = null;
private InputStream inputStream = null;
private OutputStream outputStream = null;
private ReceiveThread mReceiveThread = null;
private boolean isStart = false;
private static SerialPortUtil portUtil;
//单例模式获取工具类的实例
public static SerialPortUtil getPortUtil() {
if (portUtil == null) {
portUtil = getInstance();
}
return portUtil;
}
/**
* 创建实例时加同步锁
*
* @return 返回当前实例
*/
private synchronized static SerialPortUtil getInstance() {
if (portUtil == null) {
portUtil = new SerialPortUtil();
}
return portUtil;
}
/**
* 打开串口,接收,发送数据数据
* 通过串口,接收单片机发送来的数据
*/
public boolean openSerialPort(String port_name,int port_clound) {
try {
serialPort = new SerialPort(new File(port_name), port_clound, 0);
// 调用对象SerialPort方法,获取串口中"读和写"的数据流
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
isStart = true;
} catch (IOException e) {
e.printStackTrace();
}
getSerialPort();
return isStart;
}
/**
* 关闭串口
* 关闭串口中的输入输出流
*/
public void closeSerialPort() {
Log.i("test", "关闭串口");
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
isStart = false;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送数据
* 通过串口,发送数据到单片机
*
* @param data 要发送的数据
*/
public void sendSerialPort(String data) {
try {
byte[] sendData = DataUtils.HexToByteArr(data);
// byte[] sendData = data.getBytes();
outputStream.write(sendData);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private void getSerialPort() {
mReceiveThread = new ReceiveThread();
mReceiveThread.start();
}
/**
* 接收串口数据的线程
*/
private class ReceiveThread extends Thread {
@Override
public void run() {
super.run();
while (isStart) {
if (inputStream == null) {
return;
}
byte[] readData = new byte[1024];
try {
int size = inputStream.read(readData);
if (size > 0) {
String readString = DataUtils.ByteArrToHex(readData, 0, size);
if (!StringUtils.isEmpty(readString)) {
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
最后附上DataUtils工具类的代码:
public class DataUtils {
//-------------------------------------------------------
// 判断奇数或偶数,位运算,最后一位是1则为奇数,为0是偶数
public static int isOdd(int num) {
return num & 1;
}
//-------------------------------------------------------
//Hex字符串转int
public static int HexToInt(String inHex) {
return Integer.parseInt(inHex, 16);
}
public static String IntToHex(int intHex){
return Integer.toHexString(intHex);
}
//-------------------------------------------------------
//Hex字符串转byte
public static byte HexToByte(String inHex) {
return (byte) Integer.parseInt(inHex, 16);
}
//-------------------------------------------------------
//1字节转2个Hex字符
public static String Byte2Hex(Byte inByte) {
return String.format("%02x", new Object[]{inByte}).toUpperCase();
}
//-------------------------------------------------------
//字节数组转转hex字符串
public static String ByteArrToHex(byte[] inBytArr) {
StringBuilder strBuilder = new StringBuilder();
for (byte valueOf : inBytArr) {
strBuilder.append(Byte2Hex(Byte.valueOf(valueOf)));
strBuilder.append(" ");
}
return strBuilder.toString();
}
//-------------------------------------------------------
//字节数组转转hex字符串,可选长度
public static String ByteArrToHex(byte[] inBytArr, int offset, int byteCount) {
StringBuilder strBuilder = new StringBuilder();
int j = byteCount;
for (int i = offset; i < j; i++) {
strBuilder.append(Byte2Hex(Byte.valueOf(inBytArr[i])));
}
return strBuilder.toString();
}
//-------------------------------------------------------
//转hex字符串转字节数组
public static byte[] HexToByteArr(String inHex) {
byte[] result;
int hexlen = inHex.length();
if (isOdd(hexlen) == 1) {
hexlen++;
result = new byte[(hexlen / 2)];
inHex = "0" + inHex;
} else {
result = new byte[(hexlen / 2)];
}
int j = 0;
for (int i = 0; i < hexlen; i += 2) {
result[j] = HexToByte(inHex.substring(i, i + 2));
j++;
}
return result;
}
/**
* 按照指定长度切割字符串
*
* @param inputString 需要切割的源字符串
* @param length 指定的长度
* @return
*/
public static List<String> getDivLines(String inputString, int length) {
List<String> divList = new ArrayList<>();
int remainder = (inputString.length()) % length;
// 一共要分割成几段
int number = (int) Math.floor((inputString.length()) / length);
for (int index = 0; index < number; index++) {
String childStr = inputString.substring(index * length, (index + 1) * length);
divList.add(childStr);
}
if (remainder > 0) {
String cStr = inputString.substring(number * length, inputString.length());
divList.add(cStr);
}
return divList;
}
/**
* 计算长度,两个字节长度
*
* @param val value
* @return 结果
*/
public static String twoByte(String val) {
if (val.length() > 4) {
val = val.substring(0, 4);
} else {
int l = 4 - val.length();
for (int i = 0; i < l; i++) {
val = "0" + val;
}
}
return val;
}
/**
* 校验和
*
* @param cmd 指令
* @return 结果
*/
public static String sum(String cmd) {
List<String> cmdList = DataUtils.getDivLines(cmd, 2);
int sumInt = 0;
for (String c : cmdList) {
sumInt += DataUtils.HexToInt(c);
}
String sum = DataUtils.IntToHex(sumInt);
sum = DataUtils.twoByte(sum);
cmd += sum;
return cmd.toUpperCase();
}
}
一定要注意串口名称以及位置,和串口的波特率的问题。