android串口通信——电子扫描枪
16lz
2021-01-23
android串口通信——电子扫描枪
我们这里开始介绍电子扫描枪(串口的),在开发中我们可能用到电子扫描枪这么一个玩意。比如,我们在做一个可以说扫描条码的app的时候,就会用到,这种情况一般都是运行在终端上,往往不是我们手机上,那么我们来看看扫描枪这个玩意吧。串口的调用我们是基于android-serialport-api的,如有疑问,请看 android串口通信——android-serialport-api
一、扫描枪基础
1.基础指令
下面介绍的只是其中一种,具体使用的是哪一种看具体的文档。
指令 | 说明 |
---|---|
02820383 | 扫描指令 |
06 | 成功的标志(这个是一个坑,实际在用的时候,有可能只返回06,就不返回数据了,所以不能把它当做唯一成功的标志) |
15 | 失败的标志 |
0d0a | 结束标志,如果一个返回的数据中,是个字符串结尾,那么说明数据读取结束 |
2.基本操作说明
当我们需要扫描条码时候,发送 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)); }}
更多相关文章
- Android 判断用户2G/3G/4G移动数据网络
- Android Studio中使用adb shell查看数据库
- 共享全局数据 android
- Android 数据Parcel序列化过程源码分析
- Android中Activity之间的数据传递和Intent使用
- 【Android 开发】: Android客户端与服务端之间使用GSON交互数据