android串口通信——电子扫描枪
我们这里开始介绍电子扫描枪(串口的),在开发中我们可能用到电子扫描枪这么一个玩意。比如,我们在做一个可以说扫描条码的app的时候,就会用到,这种情况一般都是运行在终端上,往往不是我们手机上,那么我们来看看扫描枪这个玩意吧。串口的调用我们是基于android-serialport-api的,如有疑问,请看 android串口通信——android-serialport-api
下面介绍的只是其中一种,具体使用的是哪一种看具体的文档。
指令 | 说明 |
---|---|
02820383 | 扫描指令 |
06 | 成功的标志(这个是一个坑,实际在用的时候,有可能只返回06,就不返回数据了,所以不能把它当做唯一成功的标志) |
15 | 失败的标志 |
0d0a | 结束标志,如果一个返回的数据中,是个字符串结尾,那么说明数据读取结束 |
当我们需要扫描条码时候,发送 02820383 指令即可启动扫描枪扫描条码,然后我们根据上面的状态说明,来操作放回的数据。特此说明,串口的数据可能不是一次读完的,有时一个数据可能分好几次来读才读完,有时候一次就读完了,由此应该知道我们结束标志是多么的重要了。 所以我们在读完时,需要不数据先保存下来,等到读取完了了,再返回数据。
主要说明:
1.读取数据的线程我们必须只能使用一个线程,不然会造成数据读取不正确。
2.我们发送和读取到得数据都是十六进制。
3.读取到数据是ascii值,我们需要自己转换为对应的值。
package com.qiwenming.scandemo.utils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import android_serialport_api.SerialPort;
import android_serialport_api.SerialPortFinder;
import com.qiwenming.scandemo.MainActivity;
import com.qiwenming.scandemo.bean.SerialPortSendData;
/**
* @author qiwenming
* @date 2015-11-2 下午2:00:12
* @ClassName ScanUtils
* @Package com.qiwenming.scandemo.utils
* @Description: 串口工作类
*/
public class ScanUtils {
protected static OutputStream mOutputStream=null;
private static InputStream mInputStream = null;
private ReadThread mReadThread;
public SerialPortFinder mSerialPortFinder = new SerialPortFinder();
private SerialPort mSerialPort;
private Context context;
public static ReadThread thread = null;
/**
* @author qiwenming
* @creation 2015-6-18 下午4:38:54
* @instruction 读取类
*/
private class ReadThread extends Thread {
private ScanReciverListener listener;
private SerialPortSendData sendData;
public ReadThread() {
}
public void setDataListener(SerialPortSendData sendData, ScanReciverListener listener) {
this.listener = listener;
this.sendData = sendData;
}
@Override
public void run() {
thread = this;
StringBuffer sb = new StringBuffer();
super.run();
while (!isInterrupted()) {
int size;
try {
byte[] buffer = new byte[64];
if (mInputStream == null)
return;
size = mInputStream.read(buffer);
if (size > 0) { // 读取数据 数据c
//str 就是我们读取到数据
String str = StringUtils.bytesToHexString(buffer, size).trim().toLowerCase();
sb.append(str);
String msg = sb.toString();
//如果我们存储的数据,也就是读取到得数据包含了 结束的标志,那么进入这个if
if (msg.contains(sendData.stopStr)) {
//把成功的标志替换为 ""
msg = msg.replaceFirst("("+sendData.okStr+")+", "");
//查找到结束位标志的位置 然后窃取我们的数据(不包含结束位)
int end1 = msg.indexOf(sendData.stopStr);
if (end1 > 0) msg = msg.substring(0, end1 + 1);
//查找到结束位标志的位置 然后窃取我们的数据(不包含结束位)有的时候我们可能出现两种结束位
int end2 = msg.indexOf(sendData.stopStr1);
if (end2 > 0) msg = msg.substring(0, end2 + 1);
final String data = msg;
Log.i("onDataReceived_stop", data);
Log.i("onDataReceived_stop_ascii",StringUtils.convertHexToString(data)+"--------"+Thread.currentThread().getId());
closeDevice();
if (null == context)
return;
((MainActivity) context)
.runOnUiThread(new Runnable() {
@Override
public void run() {
thread = null;
listener.onReceived(data);
}
});
}
//包含了 失败的字符标志
if (sb.toString().matches("\\w+" + sendData.failStr + "\\w+")) {
closeDevice();
if (null == context)
return;
((MainActivity) context)
.runOnUiThread(new Runnable() {
@Override
public void run() {
thread = null;
listener.onFail(sendData.failStr);
}
});
}
}
} catch (Exception e) {
listener.onErr(e);
return;
}
}
}
}
/**
* 发送数据
*
* @param context
* @param devStr
* 地址
* @param baudRate
* 波特率
* @param msg
* 发送的消息
*/
public void toSend(Context context, SerialPortSendData sendData,
ScanReciverListener listener) {
this.context = context;
//检查设备地址
if ("".equals(sendData.path) || "/dev/tty".equals(sendData.path)) {
Toast.makeText(context, "设备地址不能为空", 0).show();
return;
}
//检查指令
if ("".equals(sendData.commandStr)) {
Toast.makeText(context, "指令不能为空", 0).show();
return;
}
//获取输入输出流
try {
mSerialPort = getSerialPort(sendData.path, sendData.baudRate);
mOutputStream = mSerialPort.getOutputStream();
mInputStream = mSerialPort.getInputStream();
//使用一个线程来读取数据
if(thread==null){
thread = new ReadThread();
thread.setDataListener(sendData, listener);
thread.start();
}
else{
thread.setDataListener(sendData, listener);
}
} catch (SecurityException e) {
} catch (IOException e) {
} catch (InvalidParameterException e) {
}
// 上面是获取设置而已 下面这个才是发送指令
byte[] text = StringUtils.hexStringToBytes(sendData.commandStr);
try {
mOutputStream.write(text);
mOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取到串口通信的一个实例
*
* @param path
* @param baudrate
* @return
* @throws SecurityException
* @throws IOException
* @throws InvalidParameterException
*/
public SerialPort getSerialPort(String path, int baudrate)
throws SecurityException, IOException, InvalidParameterException {
/* Check parameters */
if ((path.length() == 0) || (baudrate == -1)) {
throw new InvalidParameterException();
}
/* Open the serial port */
mSerialPort = new SerialPort(new File(path), baudrate, 0);// 打开这个串口
return mSerialPort;
}
/**
* 关闭设备
*/
public void closeDevice() {
if (mReadThread != null)
mReadThread.interrupt();
closeSerialPort();
// mSerialPort = null;
}
/**
* 关闭串口
*/
public void closeSerialPort() {// 关闭窗口
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
try {
if(mInputStream!=null){
mInputStream.close();
mInputStream = null;
}
if(mOutputStream !=null){
mOutputStream.close();
mOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @author qiwenming
* @creation 2015-7-20 上午10:16:54
* @instruction 接受回调类
*/
public interface ScanReciverListener {
/**
* 接受以后的处理方法
*
* @param string
*/
public abstract void onReceived(String receviceStr);
/**
* 出错
*
* @param string
*/
public abstract void onFail(String fialStr);
/**
* 出现异常
*
* @param e
*/
public abstract void onErr(Exception e);
}
/**
* @author qiwenming
* @creation 2015-7-20 下午2:34:28
* @instruction 这个是我们用于存储读取的数据
*/
public class RecevedData {
public ReturnType returnType;
/**
* 数据
*/
public String receviedData;
}
/**
* @author qiwenming
* @creation 2015-7-20 下午2:36:21
* @instruction 使用辨识返回的数据的
*/
public enum ReturnType {
ERR, // 错误
OK, // OK
Exception
}
}
package com.qiwenming.scandemo;
import java.util.Arrays;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import android_serialport_api.SerialPortFinder;
import com.qiwenming.scandemo.bean.SerialPortSendData;
import com.qiwenming.scandemo.constant.CommandConstants;
import com.qiwenming.scandemo.dialog.OneColumDialog;
import com.qiwenming.scandemo.dialog.OneColumDialog.SelectListener;
import com.qiwenming.scandemo.utils.ScanUtils;
import com.qiwenming.scandemo.utils.ScanUtils.ScanReciverListener;
import com.qiwenming.scandemo.utils.StringUtils;
/**
* @author qiwenming
* @date 2015-11-2 下午1:38:37
* @ClassName MainActivity
* @Package com.qiwenming.scandemo
* @Description: 扫描
*/
public class MainActivity extends Activity {
/**
* 内容
*/
private TextView contentTv;
/**
* 硬件地址
*/
private String address = "";
/**
* 波特率
*/
private int baudRate = 0;
private SerialPortFinder serailFinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contentTv = (TextView)findViewById(R.id.tv_content);
serailFinder = new SerialPortFinder();
}
/**
* 扫描
* @param view
*/
public void toScan(View view){
//1.判断有没有硬件地址
//2.判断有木有波特率
//3.组装指令
//4.发送指令
//5.数据处理
//1.判断有没有硬件地址
if("".equals(address)){
setDeviceAddress();
return;
}
//2.判断有木有波特率
if(baudRate==0){
setDeviceBautRate();
return;
}
//3.组装指令
//4.发送指令
SerialPortSendData sendData = new SerialPortSendData(address, baudRate, CommandConstants.SCAN_START,
CommandConstants.SCAN_RSP_OK, CommandConstants.SCAN_RSP_FAIL,
CommandConstants.SCAN_STOP, true);
ScanUtils scanDevice = new ScanUtils();
scanDevice.toSend(this, sendData, new ScanReciverListener() {
//5.数据处理
@Override
public void onReceived(String receviceStr) {
String scanNo = StringUtils.convertHexToString(receviceStr);
contentTv.setText(scanNo);
Log.i("toScan_recevied", scanNo);
}
@Override
public void onFail(String string) {
Toast.makeText(MainActivity.this, "扫描失败,请重新扫描", 0).show();
}
@Override
public void onErr(Exception e) {
Toast.makeText(MainActivity.this, "扫描失败,请重新扫描", 0).show();
}
});
}
/**
* 显示地址
* @param v
*/
public void setDeviceAddress(){
OneColumDialog dialog = new OneColumDialog(this, getAllDevicesPath(),new SelectListener() {
@Override
public void selected(int position, String value) {
address = value;
toScan(null);
}
});
dialog.show();
dialog.setTile("设置硬件地址");
}
/**
* 获取全部窗口地址
*
* @return
*/
public List getAllDevicesPath() {
return Arrays.asList(serailFinder.getAllDevicesPath());
}
/**
* 设置波特率
*/
public void setDeviceBautRate(){
OneColumDialog dialog = new OneColumDialog(this, getAllBautRate(),new SelectListener() {
@Override
public void selected(int position, String value) {
baudRate = Integer.parseInt(value);
toScan(null);
}
});
dialog.show();
dialog.setTile("设置波特率");
}
/**
* 获取全部 波特率
*
* @return
*/
public List getAllBautRate() {
return Arrays.asList(getResources().getStringArray(R.array.baudrates));
}
}