>>直接查看第六步
package top.keepempty.serialportnormal;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.serialport.SerialPort;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import top.keepempty.sph.library.DataConversion;
/**
* Create on 2019/9/5
* author chtj
*/
public class SerialPortContrl {
private static final String TAG = "SerialPortContrl";
private Context context;//上下文
private SerialPort port = null;//串口控制
private OnComListener onComListener;//数据回调
private SerialPortEntity serialPortEntity;
private static boolean isOpen = false;//串口是否打开
//定时检查串口是否正常
private Handler handler = new Handler();
//是否正在操作,一般存在两个状态
//是否正在执行心跳包检测
private boolean isHeartBeatOperation = false;
//是否正在读写
private boolean isWriteReadOperation = false;
/**
* 注册串口相关的数据监听
*
* @param onComListener
*/
public void setOnComListener(OnComListener onComListener) {
this.onComListener = onComListener;
}
//初始化时默认开启读取线程
public SerialPortContrl(Context context, SerialPortEntity serialPortEntity) {
this.serialPortEntity = serialPortEntity;
this.context = context;
openHeartBeatCheck();
}
//心跳检测
//主要用于串口是否收发正常
//如果要使用该方法 需要设置 SerialPortEntity中 heartBeatComm和heartBeatFlag的值
public void openHeartBeatCheck() {
if (serialPortEntity != null && serialPortEntity.getHeartBeatEntity().getHeartBeatComm() != null && serialPortEntity.getHeartBeatEntity().getHeartBeatFlag() != 0) {
handler.postDelayed(heartBeatRunnable, 5000);
}
}
Runnable heartBeatRunnable=new Runnable() {
@Override
public void run() {
if (port != null) {
try {
if (!isWriteReadOperation) {//判断是否在执行写读操作
writeData(serialPortEntity.getHeartBeatEntity().getHeartBeatComm());
//改变为正在执行
isHeartBeatOperation = true;
boolean isNormal = false;
while (true) {
//查询可以读取到的字节数量
//读取超时的检查 设置为3秒
//3秒内无响应 则退出
readSize = port.getInputStream().available();
//Log.e(TAG, "readSize=" + readSize );
if (readSize <= 0) {
//当前未检查到数据
waitTime += 200;
Thread.sleep(200);
isNormal = false;
if (waitTime >= serialPortEntity.getTimeOut()) {
waitTime = 0;
bytes = null;
break;
}
} else {
//没有超时 获取到了数据
byte[] temporaryComm=new byte[readSize];
port.getInputStream().read(temporaryComm);
Log.e(TAG,"心跳包返回数据:"+DataConversion.encodeHexString(temporaryComm));
isNormal = true;
break;
}
}
//Log.e(TAG, "串口能正常收发");
if (onComListener != null) {
onComListener.comStatus(isNormal);
}
isHeartBeatOperation = false;
}else{
Log.e(TAG,"当前正在进行写读操作,所以暂时不检查串口是否正常");
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "heartBeatCheck: errMeg:" + e.getMessage());
}
}
handler.postDelayed(this, serialPortEntity.getHeartBeatEntity().getDelayMillis());
}
};
/**
* 打开串口
*/
public void openSerialPort() {
//串口状态为关闭时 才能去执行开启
if (!isOpen) {
try {
port = new SerialPort(new File(serialPortEntity.getCom()), serialPortEntity.getBaudrate(), 0);
Log.e(TAG, "串口打开成功 com=" + serialPortEntity.getCom() + ",baudrate=" + serialPortEntity.getBaudrate());
isOpen = true;
} catch (IOException e) {
e.printStackTrace();
isOpen = false;
Log.e(TAG, "串口访问失败!");
} catch (SecurityException e) {
e.printStackTrace();
isOpen = false;
Log.e(TAG, "请授予系统权限!");
} catch (Exception e) {
e.printStackTrace();
isOpen = false;
Log.e(TAG, "其他异常:" + e.getMessage());
}
if (onComListener != null) {
if (isOpen) {
onComListener.isOpen(true);
} else {
onComListener.isOpen(false);
}
}
}
}
/**
* 添加相关命令
*
* @param comm 单个命令
* @param flag 传进来的flag 读取|写入|超时|写入完成等回调的时候可以标识为当前
*/
public void setWriteRead(byte[] comm, int flag) {
List commList = new ArrayList<>();
commList.add(comm);
this.setWriteRead(commList, flag);
}
/**
* 添加相关命令
*
* @param commList 这次需要执行的命令集合
* @param flag 传进来的flag 读取|写入|超时|写入完成等回调的时候可以标识为当前
*/
public synchronized void setWriteRead(List commList, int flag) {
while (isHeartBeatOperation){
//如果正在操作心跳包检测
//则暂时等待
}
if(port==null){
if(onComListener!=null){
onComListener.comStatus(false);
return;
}
}
isWriteReadOperation = true;//操作中
if (commList != null && commList.size() > 0) {
//Iterator 方便删除数据 而不影响下标
Iterator it = commList.iterator();
//用于标识3次机会是否用完 并且是否成功写入和读取
//只有命令发送后在3次机会中成功至少一次的才能继续向下执行
boolean isSuccessful = true;
while (it.hasNext()) {
// 注意:!serialPortEntity.getFlagFilterArray().contains(flag)
//如果写入的命令存在一条以上时 执行完成一条后
//如果添加进来的flag不管上一条是否执行失败 继续向下执行
//否则只执行第一条就退出了
if (isSuccessful == false && !serialPortEntity.getFlagFilterArray().contains(flag)) {
break;
}
byte[] nowData = it.next();
int count = serialPortEntity.getRetriesCount();//数据写入之后的重试次数
while (--count >= 0) {
//每次减去1
//然后去检查是否成功获取数据
//否则循环n次 继续
//还是失败的话则升级失败
try {
Thread.sleep(250);
writeData(nowData);
Log.e(TAG, "写入命令>>>:" + DataConversion.encodeHexString(nowData));
onComListener.writeCommand(nowData, flag);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "errMeg=" + e.getMessage());
} finally {
try {
if (readInputStreaData(flag)) {
it.remove();
isSuccessful = true;
break;
} else {
isSuccessful = false;
Log.e(TAG, "读取异常,继续重发,剩余重发送次数:" + count);
//如果剩余次数小于等于0
if (count <= 0) {
if (serialPortEntity.getFlagFilterArray().contains(flag)) {
it.remove();
} else {
}
break;
}
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, ">>" + e.getMessage());
} finally {
Log.e(TAG, "剩余写入的命令数量=" + commList.size());
if (commList == null || commList.size() == 0) {
onComListener.writeComplet(flag);
}
}
}
}
}
//这里的判断 是防止可能命令只有一个
if (!isSuccessful) {
Log.e(TAG, "升级失败了~~~~");
}
}
//操作结束
isWriteReadOperation = false;
}
/**
* 检查是否存在数据
* 如果不存在数据就要做延时读取操作
* 这里的延时时间为 {waitTime}
*
* @return 存在数据true 不存在数据false
* @throws IOException
* @throws InterruptedException
*/
private boolean checkInputStreaData(int flag) throws IOException, InterruptedException {
while (true) {
//查询可以读取到的字节数量
//读取超时的检查 设置为3秒
//3秒内无响应 则退出
readSize = port.getInputStream().available();
//Log.e(TAG, "readSize=" + readSize );
if (readSize <= 0) {
//当前未检查到数据
waitTime += 200;
Thread.sleep(200);
isExistData = false;
if (waitTime >= serialPortEntity.getTimeOut()) {
waitTime = 0;
bytes = null;
//回调超时处理 通知UI
onComListener.isReadTimeOut(flag);
break;
}
} else {
isExistData = true;
break;
}
}
return isExistData;
}
private int count = 0;//当前数据记录到哪一个位置
private long datalength = -1;//接收到数据的总长度
private int waitTime = 0;//线程等待时间
private int readSize = 0;//目前数据流中的字节数量
private int readNum = -1;//读取到的一个字节
private byte[] bytes = null;//当前读取到数据
private boolean isExistData = false;//是否读取到了数据
byte[] dataLen = new byte[2];//记录命令中返回的两个字节长度
/**
* 读取数据流中的数据
* 这里需要进行自定义
* ①比如我这里存在两个数据头 0xAA和0x55 也就是对应(byte[0]下标count=0)和(byte[1]下标count=1) 那么就是要读两个字节出来判断
* ②然后就是获取data长度 我这里用了两个字节表示长度 byte[3]和byte[4] 这个要看协议是否一致 不过一般是一个字节
* ③获取到data长度后好需要加上一些固定长度 比如获取到的data为{0x12,0x13,0x14,0x15}长度为4 那么还需要加上另外数据位才能得到完整的总长度
* 例如返回的数据:AA55000004A001A3017F
* ①AA55为数据头 length=2
* ②00 为地址 length=1
* ③00 04 为data长度 length=2 这里为什么是04:因为我们的协议把④+⑤的长度算在了一起
* ④A0 为指令 length=1
* ⑤01 A3 01 为data内容 length=3
* ⑥7F 为crc校验值 length=1
* 注:
* 所以除了data之外的其他固定长度为6
* 再加上data的长度为4
* 所以总长度产生的数据为AA 55 00 00 04 A0 01 A3 01 7F
*
*/
private boolean readInputStreaData(int flag) {
try {
while (true) {
boolean isBreak = false;//是否需要中断
bytes = new byte[256];
if (checkInputStreaData(flag)) {
count = 0;
//读取缓存中的第一个字节 看是否与第一个数据头对应
readNum = port.getInputStream().read();
bytes[count] = (byte) readNum;
if (bytes[0] == -86) {//检查第一个数据头AA
count = 1;
if (checkInputStreaData(flag)) {
readNum = port.getInputStream().read();
bytes[count] = (byte) readNum;
if (bytes[1] == 85) {//检查第二个数据头 55
//Log.e(TAG, "两个数据头正确!");
while (true) {
if (checkInputStreaData(flag)) {
if ((readNum = port.getInputStream().read()) != -1) {
count++;
bytes[count] = (byte) readNum;
//Log.e(TAG, "byte[count]=" + bytes[count] + ",count=" + count + ",datalength=" + datalength+",bytes="+DataConversion.encodeHexString(bytes));
if (count == 3) {
dataLen[0] = bytes[count];
}
if (count == 4) {
//找到数据长度
//长度包括--指令和数据
//数据头1 数据头1 地址1 长度2 指令1 数据(...) 效验1
//除了数据长度以外 其他的数据不变长度为6
//所以这里得到了一个数据的总长度
//用于去判断数据是否接收完成
dataLen[1] = bytes[count];
String dataLenHex = DataConversion.encodeHexString(dataLen);
if (dataLenHex.length() % 2 == 1) {
dataLenHex = "0" + dataLenHex;//高位补0
}
datalength = DataConversion.hexToDec(dataLenHex) + 6;
Log.e(TAG, "数据包长度=" + datalength);
}
//这里是判断指令位是否正确
int msgNum = -1;
if (count == 5) {//查看属于哪一种命令
switch (bytes[5]) {
case -96://A0 检测升级
msgNum = 0;
break;
case -95://A1 进入升级
msgNum = 1;
break;
case -94://A2 数据写入
msgNum = 2;
break;
case -93://A3 数据写入成功
msgNum = 3;
break;
default://指令错误
msgNum = -1;
bytes = null;
count = 0;
//Log.e(TAG, "当前命令属于 现在需要将count清空,并且重置一些数据");
break;
}
//Log.e(TAG, "当前命令属于:" + message);
if (msgNum == -1) {
break;//跳出循环 开始进行下一轮
}
}
if (datalength == count + 1) {
//Log.e(TAG, "数据校验完整");
isBreak = true;
//Log.e(TAG,"数据接收完成了噢,完整的数据为0:count"+count);
break;
}
}
} else {
isBreak = true;
break;
}
}
}
} else {
break;
}
}
} else {
break;
}
if (isBreak) {
//这里是由于数据读取完成 或者因为读取过程中超时
//所以需要退出
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
boolean isSuccessful = false;
if (onComListener != null) {
if (bytes != null && bytes.length > 0) {
//Log.e(TAG, "数据接收完成了噢,完整的数据为1:" + DataConversion.encodeHexString(bytes));
byte[] newbytes = new byte[(int) datalength];
System.arraycopy(bytes, 0, newbytes, 0, (int) datalength);
onComListener.readCommand(newbytes, flag);
isSuccessful = true;//正常情况下到这里都为true
//Log.e(TAG, "数据接收完成了噢,完整的数据为2:" + DataConversion.encodeHexString(newbytes));
} else {
isSuccessful = false;
onComListener.readCommand(null, flag);
}
}
return isSuccessful;
}
}
/**
* 写数据
*
* @param command
*/
private synchronized void writeData(byte[] command) {
port.write(command);
}
/**
* 关闭串口和线程
*/
public void closeSerialPort() {
if (port != null) {
port.close();
port=null;
}
//取消心跳检测
if (heartBeatRunnable != null) {
Log.e(TAG, "心跳包周期检测已关闭");
handler.removeCallbacks(heartBeatRunnable);
}
//设置为关闭状态
isOpen = false;
if (onComListener != null) {
onComListener.isOpen(false);
}
}
}
package top.keepempty.serialportnormal;
/**
* Create on 2019/9/5
* author chtj
*/
public interface OnComListener {
//命令写入成功
void writeCommand(byte[] comm, int flag);
//获取到了数据流中的数据
void readCommand(byte[] comm, int flag);
//命令已全部写完
void writeComplet(int flag);
//执行超时
void isReadTimeOut(int flag);
//是否已经打开
void isOpen(boolean isOpen);
//得到串口状态 正常TRUE 异常FALSE
void comStatus(boolean isNormal);
}
package top.keepempty.serialportnormal;
import java.util.List;
/**
* Create on 2019/9/18
* author chtj
*/
public class SerialPortEntity {
private String com;//串口号
private int baudrate;//波特率
private int timeOut;//读取超时时间
private int retriesCount;//重试次数 默认为0次
private HeartBeatEntity heartBeatEntity;//心跳包相关
//如果写入的命令存在一条以上时 执行完成一条后
//如果添加进来的flag不管上一条是否执行失败 继续向下执行
//否则只执行第一条就退出了
private List flagFilterArray;
public SerialPortEntity() {
}
public SerialPortEntity(String com, int baudrate, int timeOut, int retriesCount, List flagFilterArray) {
this.com = com;
this.baudrate = baudrate;
this.timeOut = timeOut;
this.retriesCount = retriesCount;
this.flagFilterArray = flagFilterArray;
}
public SerialPortEntity(String com, int baudrate, int timeOut, int retriesCount, HeartBeatEntity heartBeatEntity, List flagFilterArray) {
this.com = com;
this.baudrate = baudrate;
this.timeOut = timeOut;
this.retriesCount = retriesCount;
this.heartBeatEntity = heartBeatEntity;
this.flagFilterArray = flagFilterArray;
}
public String getCom() {
return com;
}
public void setCom(String com) {
this.com = com;
}
public int getBaudrate() {
return baudrate;
}
public void setBaudrate(int baudrate) {
this.baudrate = baudrate;
}
public int getTimeOut() {
return timeOut;
}
public void setTimeOut(int timeOut) {
this.timeOut = timeOut;
}
public int getRetriesCount() {
return retriesCount;
}
public void setRetriesCount(int retriesCount) {
this.retriesCount = retriesCount;
}
public HeartBeatEntity getHeartBeatEntity() {
return heartBeatEntity;
}
public void setHeartBeatEntity(HeartBeatEntity heartBeatEntity) {
this.heartBeatEntity = heartBeatEntity;
}
public List getFlagFilterArray() {
return flagFilterArray;
}
public void setFlagFilterArray(List flagFilterArray) {
this.flagFilterArray = flagFilterArray;
}
}
package top.keepempty.serialportnormal;
/**
* Create on 2019/9/25
* author chtj
*/
public class HeartBeatEntity {
private byte[] heartBeatComm;//执行的心跳包命令
private int heartBeatFlag;//执行心跳包的标志
private int delayMillis;//多少周期执行一次心跳包检测毫秒
public HeartBeatEntity(byte[] heartBeatComm, int heartBeatFlag, int delayMillis) {
this.heartBeatComm = heartBeatComm;
this.heartBeatFlag = heartBeatFlag;
this.delayMillis = delayMillis;
}
public byte[] getHeartBeatComm() {
return heartBeatComm;
}
public void setHeartBeatComm(byte[] heartBeatComm) {
this.heartBeatComm = heartBeatComm;
}
public int getHeartBeatFlag() {
return heartBeatFlag;
}
public void setHeartBeatFlag(int heartBeatFlag) {
this.heartBeatFlag = heartBeatFlag;
}
public int getDelayMillis() {
if(delayMillis==0){
delayMillis=1*60*1000;//如果忘记设置心跳包的执行周期,将设置为1分钟
}
return delayMillis;
}
public void setDelayMillis(int delayMillis) {
this.delayMillis = delayMillis;
}
}
package top.keepempty.serialportnormal;
/**
* Create on 2019/9/25
* author chtj
*/
public class FlagManager {
public static final int FLAG_HEARtBEAT=0x110;//心跳包定时检测标志
public static final int FLAG_CHECK_UPDATE=0x111;//例如:这是检查更新的标志
}
package top.keepempty.serialportnormal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import com.serialport.SerialPortFinder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import top.keepempty.R;
import top.keepempty.sph.library.DataConversion;
import top.keepempty.util.DataCehck;
public class SerialPortNormalAty extends AppCompatActivity implements View.OnClickListener {
public static final String TAG = "SerialPortNormalAty";
TextView tvResult;//返回的结果
EditText etCommand;//命令
Spinner sp_com,sp_burate;//串口列表,波特率列表
SerialPortContrl serialPortContrl=null;//串口控制类
List list_serialcom=null;//串口地址
String[] arrays_burate;//波特率
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_serialport_normal);
//初始化控件
tvResult = findViewById(R.id.tvResult);
tvResult.setMovementMethod(ScrollingMovementMethod.getInstance());
etCommand = findViewById(R.id.etCommand);
sp_com = findViewById(R.id.sp_com);
sp_burate = findViewById(R.id.sp_burate);
//获取所有串口地址
SerialPortFinder mSerialPortFinder = new SerialPortFinder();
String[] entryValues = mSerialPortFinder.getAllDevicesPath();
list_serialcom= Arrays.asList(entryValues);
//获取所有的波特率 可在R.array.burate 中手动添加需要的波特率
arrays_burate = getResources().getStringArray(R.array.burate);
//添加到适配器中显示
ArrayAdapter arr_adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, list_serialcom);
sp_com.setAdapter(arr_adapter);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_close://关闭串口
if(serialPortContrl!=null){
serialPortContrl.closeSerialPort();
}
break;
case R.id.btn_init://初始化和开启串口
//获得当前选择串口和波特率
String com=sp_com.getSelectedItem().toString();
int baudrate=Integer.parseInt(sp_burate.getSelectedItem().toString());
//参数设置
List flagFilterList=new ArrayList<>();
flagFilterList.add(FlagManager.FLAG_CHECK_UPDATE);
//①未开启心跳包
//SerialPortEntity serialPortEntity=new SerialPortEntity(com,baudrate,6000,3,flagFilterList);
//②心跳包参数设置
HeartBeatEntity heartBeatEntity=new HeartBeatEntity(new byte[]{(byte) 0xAA, 0x55, 00, 0, 0x01, (byte) 0xA0, (byte) 0xBF},FlagManager.FLAG_HEARtBEAT,15*1000);
SerialPortEntity serialPortEntity=new SerialPortEntity(com,baudrate,6000,3,heartBeatEntity,flagFilterList);
//初始化数据
serialPortContrl=new SerialPortContrl(this,serialPortEntity);
//注册监听
serialPortContrl.setOnComListener(new OnComListener() {
@Override
public void writeCommand(byte[] comm, int flag) {
String writeData="writeCommand>>> comm="+DataConversion.encodeHexString(comm)+",flag="+flag;
Log.e(TAG,writeData);
Message message=handler.obtainMessage();
message.obj=writeData;
handler.sendMessage(message);
}
@Override
public void readCommand(byte[] comm, int flag) {
String readData="readCommand>>> comm="+DataConversion.encodeHexString(comm)+",flag="+flag;
Log.e(TAG,readData);
Message message=handler.obtainMessage();
message.obj=readData;
handler.sendMessage(message);
}
@Override
public void writeComplet(int flag) {
String writeSuccessful="writeComplet>>> flag="+flag;
Log.e(TAG,writeSuccessful);
Message message=handler.obtainMessage();
message.obj=writeSuccessful;
handler.sendMessage(message);
}
@Override
public void isReadTimeOut(int flag) {
String readTimeOut="isReadTimeOut>>> flag="+flag;
Log.e(TAG,readTimeOut);
Message message=handler.obtainMessage();
message.obj=readTimeOut;
handler.sendMessage(message);
}
@Override
public void isOpen(boolean isOpen) {
String comStatus=isOpen?"isOpen>>>串口打开!":"isOpen>>>串口关闭";
Log.e(TAG,comStatus);
Message message=handler.obtainMessage();
message.obj=comStatus;
handler.sendMessage(message);
}
@Override
public void comStatus(boolean isNormal) {
String comStatus=isNormal?"comStatus>>>串口正常!":"comStatus>>>串口异常";
Log.e(TAG,comStatus);
Message message=handler.obtainMessage();
message.obj=comStatus;
handler.sendMessage(message);
}
});
//开启串口
serialPortContrl.openSerialPort();
break;
case R.id.btn_test_send://发送命令
new Thread(new Runnable() {
@Override
public void run() {
//这里只是一个示例
//这里时多个命令发送
/*List bytesList = new ArrayList<>();
for (int i = 0; i <=15; i++) {
byte[] bytes = new byte[]{(byte) 0xAA, 0x55, (byte) i, 0, 0x01, (byte) 0xA0};
byte crcNum = DataCehck.calcCrc8(bytes);//获得校验值
//复制到新的数组并把校验值填写到最后一位
byte[] newbytes = new byte[bytes.length + 1];
System.arraycopy(bytes, 0, newbytes, 0, 6);
newbytes[newbytes.length - 1] = crcNum;//填充校验值
//Log.e(TAG, "检测升级>>>计算校验值后得command[" + i + "]=" + DataConversion.encodeHexString(newbytes));
bytesList.add(newbytes);
}
if (bytesList.size() > 0) {
//传进去的flag 读取|写入|超时|写入完成等回调的时候可以标识为当前
serialPortContrl.setWriteRead(bytesList, FlagManager.FLAG_CHECK_UPDATE);
} else {
Log.e(TAG, "没有任何需要检测升级的 checkUpdate bytesList.size()=" + bytesList.size());
}*/
//单个命令发送
String hexComm=etCommand.getText().toString().trim();
byte[] comm=DataConversion.decodeHexString(hexComm);
serialPortContrl.setWriteRead(comm, FlagManager.FLAG_CHECK_UPDATE);
}
}).start();
break;
case R.id.btn_clear://清除结果
tvResult.setText("");
break;
}
}
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvResult.append("\n\r"+msg.obj.toString());
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if(serialPortContrl!=null){
serialPortContrl.closeSerialPort();
}
}
}