一、前言

最近需求做扫码支付,但是扫码的扫码头是串口的,需要进行开发才能调起扫码头扫码,头疼了一段时间,在度娘上搜索了一波,做了下总结,也写了一个demo,顺便写一篇博文记录一下。

二、串口管理类

*** 串口管理类* <p>* 触发扫码:7E 00 08 01 00 02 01 AB CD* 停止扫码:7E 00 08 01 00 02 00 AB CD* <p>* <p>* 一次读码延时(默认5秒)* 10秒:7E 00 08 01 00 06 64 AB CD* 15秒:7E 00 08 01 00 06 96 AB CD* 20秒:7E 00 08 01 00 06 C8 AB CD* 无限长:7E 00 08 01 00 06 00 AB CD* 保存:7E 00 09 01 00 00 00 AB CD (如果不保存,断电后不能生效)** @author Freak* @date 2019/8/12.*/
public class SerialPortManager {private static final String TAG = "SerialPortManager";private SerialReadThread mReadThread;private OutputStream mOutputStream;private HandlerThread mWriteThread;private Scheduler mSendScheduler;private static class InstanceHolder {public static SerialPortManager sManager = new SerialPortManager();}public static SerialPortManager instance() {return InstanceHolder.sManager;}private SerialPort mSerialPort;private SerialPortManager() {}/*** 打开串口** @param device* @return*/public SerialPort open(Device device) {return open(device.getPath(), device.getBaudrate());}/*** 打开串口** @param devicePath* @param baudrateString* @return*/public SerialPort open(String devicePath, String baudrateString) {if (mSerialPort != null) {close();}try {File device = new File(devicePath);int baurate = Integer.parseInt(baudrateString);mSerialPort = new SerialPort(device, baurate, 0);mReadThread = new SerialReadThread(mSerialPort.getInputStream());mReadThread.start();mOutputStream = mSerialPort.getOutputStream();mWriteThread = new HandlerThread("write-thread");mWriteThread.start();mSendScheduler = AndroidSchedulers.from(mWriteThread.getLooper());return mSerialPort;} catch (Throwable tr) {LogUtil.e("打开串口失败", tr);close();return null;}}/*** 关闭串口*/public void close() {if (mReadThread != null) {mReadThread.close();}if (mOutputStream != null) {try {mOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if (mWriteThread != null) {mWriteThread.quit();}if (mSerialPort != null) {mSerialPort.close();mSerialPort = null;}}/*** 发送数据** @param datas* @return*/private void sendData(byte[] datas) throws Exception {mOutputStream.write(datas);}/*** (rx包裹)发送数据** @param datas* @return*/private Observable<Object> rxSendData(final byte[] datas) {return Observable.create(new ObservableOnSubscribe<Object>() {@Overridepublic void subscribe(ObservableEmitter<Object> emitter) throws Exception {try {sendData(datas);emitter.onNext(new Object());} catch (Exception e) {LogUtil.e("发送:" + ByteUtil.bytes2HexStr(datas) + " 失败", e);if (!emitter.isDisposed()) {emitter.onError(e);return;}}emitter.onComplete();}});}/*** 发送命令包*/@SuppressLint("CheckResult")public void sendCommand(final String command) {// TODO: 2018/3/22LogUtil.i("发送命令:" + command);byte[] bytes = ByteUtil.hexStr2bytes(command);rxSendData(bytes).subscribeOn(mSendScheduler).subscribe(new Consumer<Object>() {@Overridepublic void accept(Object o) throws Exception {LogManager.instance().post(new SendMessage(command));}});}
}

二、读写线程类

/*** 串口读线程** @author Freak* @date 2019/8/12.*/
public class SerialReadThread extends Thread {private static final String TAG = "SerialReadThread";private BufferedInputStream mInputStream;public SerialReadThread(InputStream is) {mInputStream = new BufferedInputStream(is);}@Overridepublic void run() {byte[] received = new byte[1024];int size;LogUtil.e("开始读线程");while (true) {if (Thread.currentThread().isInterrupted()) {break;}try {int available = mInputStream.available();if (available > 0) {size = mInputStream.read(received);if (size > 0) {onDataReceive(received, size);}} else {// 暂停一点时间,免得一直循环造成CPU占用率过高SystemClock.sleep(1);}} catch (IOException e) {LogUtil.e("读取数据失败", e);}//Thread.yield();}LogUtil.e("结束读进程");}/*** 处理获取到的数据** @param received* @param size*/private void onDataReceive(byte[] received, int size) {// TODO: 2018/3/22 解决粘包、分包等String hexStr = ByteUtil.bytes2HexStr(received, 0, size);LogManager.instance().post(new ReceiveMessage(hexStr));LogUtil.e("扫码结果--》"+ hexStr);}/*** 停止读线程*/public void close() {try {mInputStream.close();} catch (IOException e) {LogUtil.e("异常", e);} finally {super.interrupt();}}
}

三、串口读写字节数据处理类

/*** @author Freak* @date 2019/8/12.*/
public class ByteUtil {//public static void main(String[] args) {//    byte[] bytes = {//        (byte) 0xab, 0x01, 0x11//    };//    String hexStr = bytes2HexStr(bytes);//    System.out.println(hexStr);//    System.out.println(hexStr2decimal(hexStr));//    System.out.println(decimal2fitHex(570));//    String adc = "abc";//    System.out.println(str2HexString(adc));//    System.out.println(bytes2HexStr(adc.getBytes()));//}/*** 字节数组转换成对应的16进制表示的字符串** @param src* @return*/public static String bytes2HexStr(byte[] src) {StringBuilder builder = new StringBuilder();if (src == null || src.length <= 0) {return "";}char[] buffer = new char[2];for (int i = 0; i < src.length; i++) {buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);buffer[1] = Character.forDigit(src[i] & 0x0F, 16);builder.append(buffer);}return builder.toString().toUpperCase();}/*** 十六进制字节数组转字符串** @param src    目标数组* @param dec    起始位置* @param length 长度* @return*/public static String bytes2HexStr(byte[] src, int dec, int length) {byte[] temp = new byte[length];System.arraycopy(src, dec, temp, 0, length);return bytes2HexStr(temp);}/*** 16进制字符串转10进制数字** @param hex* @return*/public static long hexStr2decimal(String hex) {return Long.parseLong(hex, 16);}/*** 把十进制数字转换成足位的十六进制字符串,并补全空位** @param num* @return*/public static String decimal2fitHex(long num) {String hex = Long.toHexString(num).toUpperCase();if (hex.length() % 2 != 0) {return "0" + hex;}return hex.toUpperCase();}/*** 把十进制数字转换成足位的十六进制字符串,并补全空位** @param num* @param strLength 字符串的长度* @return*/public static String decimal2fitHex(long num, int strLength) {String hexStr = decimal2fitHex(num);StringBuilder stringBuilder = new StringBuilder(hexStr);while (stringBuilder.length() < strLength) {stringBuilder.insert(0, '0');}return stringBuilder.toString();}public static String fitDecimalStr(int dicimal, int strLength) {StringBuilder builder = new StringBuilder(String.valueOf(dicimal));while (builder.length() < strLength) {builder.insert(0, "0");}return builder.toString();}/*** 字符串转十六进制字符串** @param str* @return*/public static String str2HexString(String str) {char[] chars = "0123456789ABCDEF".toCharArray();StringBuilder sb = new StringBuilder();byte[] bs = null;try {bs = str.getBytes("utf8");} catch (Exception e) {e.printStackTrace();}int bit;for (int i = 0; i < bs.length; i++) {bit = (bs[i] & 0x0f0) >> 4;sb.append(chars[bit]);bit = bs[i] & 0x0f;sb.append(chars[bit]);}return sb.toString();}/*** 把十六进制表示的字节数组字符串,转换成十六进制字节数组** @param* @return byte[]*/public static byte[] hexStr2bytes(String hex) {int len = (hex.length() / 2);byte[] result = new byte[len];char[] achar = hex.toUpperCase().toCharArray();for (int i = 0; i < len; i++) {int pos = i * 2;result[i] = (byte) (hexChar2byte(achar[pos]) << 4 | hexChar2byte(achar[pos + 1]));}return result;}/*** 把16进制字符[0123456789abcde](含大小写)转成字节** @param c* @return*/private static int hexChar2byte(char c) {switch (c) {case '0':return 0;case '1':return 1;case '2':return 2;case '3':return 3;case '4':return 4;case '5':return 5;case '6':return 6;case '7':return 7;case '8':return 8;case '9':return 9;case 'a':case 'A':return 10;case 'b':case 'B':return 11;case 'c':case 'C':return 12;case 'd':case 'D':return 13;case 'e':case 'E':return 14;case 'f':case 'F':return 15;default:return -1;}}/*** 十六进制字符串转换成十进制数字  耗性能,计算慢,调用次数少可以忽略不计** @param hex* @return*/public static String hexToString(String hex) {StringBuilder sb = new StringBuilder();for (int count = 0; count < hex.length() - 1; count += 2) {//分离字符串,两位一组String output = hex.substring(count, (count + 2));//十六进制到十进制int decimal = Integer.parseInt(output, 16);//将小数点转换为字符sb.append((char) decimal);}return sb.toString();}/*** 十六进制字符串转换成十进制数字 相对上面的快些** @param hex* @return*/public static String hexToString1(String hex) {StringBuilder sb = new StringBuilder();char[] hexData = hex.toCharArray();for (int count = 0; count < hexData.length - 1; count += 2) {int firstDigit = Character.digit(hexData[count], 16);int lastDigit = Character.digit(hexData[count + 1], 16);int decimal = firstDigit * 16 + lastDigit;sb.append((char) decimal);}return sb.toString();}/*** 十六进制字符串转换成十进制数字** @param str* @return*/public static String hexToString3(final String str) {return new String(new BigInteger(str, 16).toByteArray());}
}

四、使用方法

4.1、初始化

  • 打开串口
SerialPortManager.instance().open(mDevice)
  • 发送串口命令
SerialPortManager.instance().sendCommand(text);

串口命令:

  • 触发扫码:7E 00 08 01 00 02 01 AB CD  成功返回:02 00 00 01 00 33 31 
  • 停止扫码:7E 00 08 01 00 02 00 AB CD  成功返回:02 00 00 01 00 33 31 

读码时长设置命令:

10秒:7E 00 08 01 00 06 64 AB CD
15秒:7E 00 08 01 00 06 96 AB CD
20秒:7E 00 08 01 00 06 C8 AB CD、
无限长:7E 00 08 01 00 06 00 AB CD
保存:7E 00 09 01 00 00 00 AB CD (如果不保存,断电后不能生效)
  • 设置波特率为9600: 7E 00 08 02 00 2A 39 01 A7 EA        成功返回:02 00 00 02 39 01 C1 4C
  • 将设置保存到内部Flash: 7E 00 09 01 00 00 00 DE C8     成功返回:02 00 00 01 00 33 31
  • 查询波特率 :7E 00 07 01 00 2A 02 D8 0F                         成功返回:02 00 00 02 39 01 C1 4C
  • 恢复出厂设置 :7E 00 09 01 00 00 FF C0 38                      成功返回:02 00 00 01 00 33 31

重点是串口扫码数据的读写和解析,串口扫码数据获取的数据,是hex编码的byte数组,要进行hex编码的校验和转换成十进制字符串;具体的可以看ByteUtil工具类。

硬件系列(九)--------串口扫码头数据读写相关推荐

  1. S3C2440 GPS串口配置以及数据读写

    S3C2440 GPS串口配置以及数据读写 参考文章:http://www.cnblogs.com/jason-lu/articles/3173988.html       http://www.cn ...

  2. 补习系列(14)-springboot redis 整合-数据读写

    目录 一.简介 二.SpringBoot Redis 读写 A. 引入 spring-data-redis B. 序列化 C. 读写样例 三.方法级缓存 四.连接池 小结 一.简介 在 补习系列(A3 ...

  3. 踏莎行·术 - NIO系列4:TCP服务数据读写

    注:本文适合对象需对java NIO API的使用及异步事件模型(Reactor模式)有一定程度的了解,主要讲述使用java原生NIO实现一个TCP服务的过程及细节设计. 上文讲到当客户端完成与服务端 ...

  4. C语言串口编程收发数据 并实现AT指令的收发 可变参数控制串口属性 树莓派4G模块

    文章目录 一. Linux下串口编程的流程 1.打开串口 2. 串口初始化 2.1 常用函数总览 2.2 初始化 3. 串口的读写 4. 串口关闭 二.代码--串口编程实现自发自收 三.可变参数控制串 ...

  5. STC15系列单片机通过串口多字节数据读写EEPROM操作

    STC15系列单片机通过串口多字节数据读写EEPROM操作

  6. Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  7. 学习 STM32之九轴姿态传感器(BWT901CL)串口通信读取数据

    由于个人应用到3轴传感器,所以买了直接买了一个9轴的,用于学习STM32Core平台串口2连接维特智能串口Normal协议,然后通过串口1直接打印数据,接收传感器数据和与传感器进行通信:需要看产品文档 ...

  8. STM32F0系列串口DMA收发数据

    关于STM32F0系列串口DMA收发数据详解 这里用的库函数版本,芯片型号为stm32f030c8t6.在用到串口DMA时,要按以下几个步骤进行. 1.确定使用的串口号,这里,我用的是usart2,对 ...

  9. HX530系列串口通信高频RFID读写器|读卡器串口调试工具测试读卡操作说明

    本文将重点介绍HX530系列串口通信高频RFID读写器|读卡器串口调试工具测试读卡操作说明. 1.打开SDK 中的Demo,使用串口工具,如下图 2.在串口调试工具设置相关参数 2.1.波特率:960 ...

最新文章

  1. np.reshape()用法
  2. ubuntu server中的一些小知识(个人收集贴)
  3. db2 某个字段排序_sql字段排序 rank over
  4. QT的QSortFilterProxyModel类的使用
  5. android九宫格密码源码,Android九宫格解锁的实现
  6. 前端页面预览word_jquery.media.js实现前端页面预览
  7. Windows下安装Python模块时环境配置
  8. 电脑无法启动故障的10种解决方法
  9. .net 从txt中读取行数据_【VBA项目】从指定文件中读取数据并绘制图表
  10. Eclipse如何连接SQL Server 2005数据库
  11. di-tech2016_2016年Tech最佳愚人节笑话
  12. 计算机专业实践报告立题依据,开题报告立题依据 .doc
  13. 曾经的大学德育论文,致敬天津理工大学
  14. BPR的几种经典方法(转)
  15. 《CSAPP》(第3版)答案(第三章)(一)
  16. 苹果手机怎么发语音短信?
  17. java+vue+SSM215的病人跟踪治疗信息管理系统毕业设计
  18. 智能指针(shared_ptr、unique_ptr、weak_ptr)的使用
  19. 移动硬盘插入提示需要格式化RAW_超级硬盘数据恢复格式化数据还原免注册版,回011获取...
  20. ipad手写笔哪个牌子好用?便宜的ipad触控笔

热门文章

  1. 计算机课程教学措施,计算机课程教学改革措施3篇.doc
  2. css在div块添加背景图片为何不显示呢?
  3. 2023年全球最受欢迎投资移民计划排名:欧洲的吸引力占据主导地位 | 美通社头条...
  4. js 只允许输入大写字母
  5. 粗糙集理论---研究现状
  6. NO.174 地下城游戏
  7. 在模型推理时合并BN和Conv层
  8. 升级鸿蒙的条件,鸿蒙os该怎么取得升级条件
  9. 计算机游戏设计师要学什么软件,游戏设计需要学什么技术和软件
  10. 【电气专业知识问答】问:发电机失磁异步运行故障如何处理?