最近公司接了一个税控项目,要通过Andorid机串口链接打印机把发票打出来,那么串口通信就是大头了。这里记录一下过程。。。

这里主要有几个坑:

  • 串口没有读写权限:
  1. 找到Andorid的sdk中platform-tools目录下的adb给添加到环境变量中,这样就能方便使用
  2. 直接adb devices 是否能查看到当前连接的设备
  3. 满足第2步的情况下,adb shell 进入控制台
  4. root过的机子可以直接su 获取到权限
  5. chmod 777 /dev/ttyHSL1
  6. 如何还不行,那就可能是防火墙的关系 setenforce 0
  7. 这只是应急处理,每次重启后都要经过这个操作
  8. 后来就是直接和硬件沟通,把这个串口的权限给我开放了出来,谁都可以调用
  • 串口开放失败:这应该就是找对应打印机的那个串口(ttyHSL1这个是项目中链接打印机的串口,关于哪个串口是链接的,这我也是一个个试过来的,可以有工具类找到所有对应的串口,然后再一个个尝试一下)

这里贴一点代码

还有要说明一下,当中的 boolean chmod777 = chmod777(device) 对于我而言根本就没有用到,我是串口权限直接就获取到了,当没有权限的时候才执行chmod777(device),首先会通过 Process su = Runtime.getRuntime().exec("su"),获取root,关于这一步在硬件那边还没有开放的时候,我执行到这一步,但是却始终执行这句话就报错,手机也root过了,我很奇怪,以至于通过root来开放权限,各种方法也都尝试过,放在system/app下,也依旧不管用,这个问题有待解决。。。

SerialPortManager mSerialPortManager = new SerialPortManager();// 打开串口
boolean openSerialPort = mSerialPortManager.setOnOpenSerialPortListener(this).openSerialPort(new File("/dev/ttyHSL1"), 9600);
public class SerialPortManager extends SerialPort {private static final String TAG = SerialPortManager.class.getSimpleName();private FileInputStream mFileInputStream;private FileOutputStream mFileOutputStream;private FileDescriptor mFd;private OnOpenSerialPortListener mOnOpenSerialPortListener;private OnSerialPortDataListener mOnSerialPortDataListener;private HandlerThread mSendingHandlerThread;private Handler mSendingHandler;private SerialPortReadThread mSerialPortReadThread;/*** 打开串口** @param device   串口设备* @param baudRate 波特率* @return 打开是否成功*/public boolean openSerialPort(File device, int baudRate) {Log.i(TAG, "openSerialPort: " + String.format("打开串口 %s  波特率 %s", device.getPath(), baudRate));// 校验串口权限if (!device.canRead() || !device.canWrite()) {boolean chmod777 = chmod777(device);if (!chmod777) {Log.i(TAG, "openSerialPort: 没有读写权限");if (null != mOnOpenSerialPortListener) {mOnOpenSerialPortListener.onFail(device, OnOpenSerialPortListener.Status.NO_READ_WRITE_PERMISSION);}return false;}}try {mFd = open(device.getAbsolutePath(), baudRate, 0);mFileInputStream = new FileInputStream(mFd);mFileOutputStream = new FileOutputStream(mFd);Log.i(TAG, "openSerialPort: 串口已经打开 " + mFd);if (null != mOnOpenSerialPortListener) {mOnOpenSerialPortListener.onSuccess(device);}// 开启发送消息的线程startSendThread();// 开启接收消息的线程startReadThread();return true;} catch (Exception e) {e.printStackTrace();if (null != mOnOpenSerialPortListener) {mOnOpenSerialPortListener.onFail(device, OnOpenSerialPortListener.Status.OPEN_FAIL);}}return false;}/*** 关闭串口*/public void closeSerialPort() {if (null != mFd) {close();mFd = null;}// 停止发送消息的线程stopSendThread();// 停止接收消息的线程stopReadThread();if (null != mFileInputStream) {try {mFileInputStream.close();} catch (IOException e) {e.printStackTrace();}mFileInputStream = null;}if (null != mFileOutputStream) {try {mFileOutputStream.close();} catch (IOException e) {e.printStackTrace();}mFileOutputStream = null;}}/*** 添加打开串口监听** @param listener listener* @return SerialPortManager*/public SerialPortManager setOnOpenSerialPortListener(OnOpenSerialPortListener listener) {mOnOpenSerialPortListener = listener;return this;}/*** 开启发送消息的线程*/private void startSendThread() {// 开启发送消息的线程mSendingHandlerThread = new HandlerThread("mSendingHandlerThread");mSendingHandlerThread.start();// HandlermSendingHandler = new Handler(mSendingHandlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {byte[] sendBytes = (byte[]) msg.obj;if (null != mFileOutputStream && null != sendBytes && 0 < sendBytes.length) {try {mFileOutputStream.write(sendBytes);} catch (IOException e) {e.printStackTrace();}}}};}/*** 停止发送消息线程*/private void stopSendThread() {mSendingHandler = null;if (null != mSendingHandlerThread) {mSendingHandlerThread.interrupt();mSendingHandlerThread.quit();mSendingHandlerThread = null;}}/*** 开启接收消息的线程*/private void startReadThread() {mSerialPortReadThread = new SerialPortReadThread(mFileInputStream);mSerialPortReadThread.start();}/*** 停止接收消息的线程*/private void stopReadThread() {if (null != mSerialPortReadThread) {mSerialPortReadThread.release();}}/*** 发送数据** @param sendBytes 发送数据* @return 发送是否成功*/public boolean sendBytes(byte[] sendBytes) {if (null != mFd && null != mFileInputStream && null != mFileOutputStream) {if (null != mSendingHandler) {Message message = Message.obtain();message.obj = sendBytes;return mSendingHandler.sendMessage(message);}}return false;}}
public class SerialPort {static {System.loadLibrary("SerialPort");}private static final String TAG = SerialPort.class.getSimpleName();/*** 文件设置最高权限 777 可读 可写 可执行** @param file 文件* @return 权限修改是否成功*/boolean chmod777(File file) {if (null == file || !file.exists()) {// 文件不存在return false;}try {// 获取ROOT权限Process su = Runtime.getRuntime().exec("su");//Process su = new ProcessBuilder().command("su").redirectErrorStream(true).start();// 修改文件属性为 [可读 可写 可执行]String cmd = "chmod 777 " + file.getAbsolutePath() + "\n" + "exit\n";su.getOutputStream().write(cmd.getBytes());int flag = su.waitFor();if (0 == flag) {if (file.canRead() && file.canWrite() && file.canExecute()){return true;}}} catch (IOException | InterruptedException e) {// 没有ROOT权限e.printStackTrace();}return false;}// 打开串口protected native FileDescriptor open(String path, int baudRate, int flags);// 关闭串口protected native void close();
}
 /*** 发送字符串*/public void sendMsg(String args) throws UnsupportedEncodingException {byte[] sendContentBytes = args.getBytes("GBK");boolean sendBytes = mSerialPortManager.sendBytes(sendContentBytes);}/*** 发送指令*/public void sendOrder(byte[]  bytes) throws UnsupportedEncodingException {boolean sendBytes = mSerialPortManager.sendBytes(bytes);}

当中还需要打印二维码,通过实验,以下代码可行:

/*** 生成二维码Bitmap**/public static Bitmap createQRImage(String url,int width,int height){try{if (url == null || "".equals(url) || url.length() < 1){//判断URL合法性return null;}Hashtable<EncodeHintType, String> hints = new Hashtable<>();hints.put(EncodeHintType.CHARACTER_SET, "utf-8");//图像数据转换,使用了矩阵转换BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, width, height, hints);int[] pixels = new int[width * height];//下面这里按照二维码的算法,逐个生成二维码的图片,//两个for循环是图片横列扫描的结果for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){if (bitMatrix.get(x, y)){pixels[y * width + x] = 0xff000000;}else{pixels[y * width + x] = 0xffffffff;}}}//生成二维码图片的格式,使用ARGB_8888Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);bitmap.setPixels(pixels, 0, width, 0, 0, width, height);return bitmap;}catch (Exception e){e.printStackTrace();return  null;}}/* ************************************************************************** 假设一个240*240的图片,分辨率设为24, 共分10行打印* 每一行,是一个 240*24 的点阵, 每一列有24个点,存储在3个byte里面。* 每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色**************************************************************************//*** 把一张Bitmap图片转化为打印机可以打印的字节流** @param bmp* @return*/public static byte[] draw2PxPoint(Bitmap bmp) {//用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法//整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte,5:5455,3,5447,4,5427//但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销,//所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。int size = bmp.getWidth() * bmp.getHeight() / 8 + 1000;byte[] data = new byte[size];int k = 0;//设置行距为0的指令data[k++] = 0x1B;data[k++] = 0x33;data[k++] = 0x00;// 逐行打印for (int j = 0; j < bmp.getHeight() / 24f; j++) {//打印图片的指令data[k++] = 0x1B;data[k++] = 0x2A;data[k++] = 33;data[k++] = (byte) (bmp.getWidth() % 256); //nLdata[k++] = (byte) (bmp.getWidth() / 256); //nH//对于每一行,逐列打印for (int i = 0; i < bmp.getWidth(); i++) {//每一列24个像素点,分为3个字节存储for (int m = 0; m < 3; m++) {//每个字节表示8个像素点,0表示白色,1表示黑色for (int n = 0; n < 8; n++) {byte b = px2Byte(i, j * 24 + m * 8 + n, bmp);data[k] += data[k] + b;}k++;}}data[k++] = 10;//换行}//   long a=System.currentTimeMillis();byte[] data1 = new byte[k];System.arraycopy(data, 0, data1, 0, k);// long b=System.currentTimeMillis();//  System.out.println("结束字节:"+k+"---"+data.length+"耗时:"+(b-a));return data1;}/*** 灰度图片黑白化,黑色是1,白色是0** @param x   横坐标* @param y   纵坐标* @param bit 位图* @return*/public static byte px2Byte(int x, int y, Bitmap bit) {if (x < bit.getWidth() && y < bit.getHeight()) {byte b;int pixel = bit.getPixel(x, y);int red = (pixel & 0x00ff0000) >> 16; // 取高两位int green = (pixel & 0x0000ff00) >> 8; // 取中两位int blue = pixel & 0x000000ff; // 取低两位int gray = RGB2Gray(red, green, blue);if (gray < 128) {b = 1;} else {b = 0;}return b;}return 0;}/*** 图片灰度的转化*/private static int RGB2Gray(int r, int g, int b) {int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b);  //灰度转化公式return gray;}

其中找了不少的资料,在这里推荐 :

  • https://github.com/cepr/android-serialport-api  串口开发api
  • https://blog.csdn.net/itdo_just/article/details/80514116  关于如何将api运用到自己项目中
  • https://blog.csdn.net/qq_25817651/article/details/53135685  CMAKE的使用
  • https://github.com/GrassQing/CommonPrintProvider  工具类
  • https://blog.csdn.net/akunainiannian/article/details/8740007  串口操作

Andorid串口开发打印机相关推荐

  1. 串口开发 打印机 读卡器 遇到的问题

    对于串口开发的打印机和读卡器,再利用SerialPort开发时,注意事项: 1.端口被占用(可能是安装了硬件的驱动,导致占用端口),解决办法步骤:打开"控制面板"-->&qu ...

  2. 串口小票打印机调试命令

    小票打印机作为POS收银机的一个标准外设,在平时的实施过程不时也会出现无法打印的情况. 出现这种情况的原因有很多种,主要有安装不正确,打印机参数设置不正确,软件问题等.现以EPSON的 串口小票打印机 ...

  3. android studio scala插件,Scala 语言开发Andorid ,开发环境的搭建(一)

    Scala 语言开发Andorid ,开发环境的搭建 厌倦 Java 繁琐的语法,为了更优雅的开发 Android 程序,Scala 代替 Java 是一个不错的尝试. 开发前可以学习 Scala 的 ...

  4. Android USB串口开发

    因为第一次接触Android下的串口开发,在网上找了很多例子都不能满足自己的需要以及不能正常使用,于是结合网上的资源以及查阅资料,终于完成了关于这个串口的开发,在此记录下usb转串口通信开发的过程. ...

  5. 中移4G模块-ML302-OpenCpu开发-串口开发

    B站:https://space.bilibili.com/309103931 中移4G模块-ML302专栏:https://blog.csdn.net/qq_33259323/category_10 ...

  6. android 串口开发第二篇:利用jni实现android和串口通信

    一:串口通信简介 由于串口开发涉及到jni,所以开发环境需要支持ndk开发,如果未配置ndk配置的朋友,或者对jni不熟悉的朋友,请查看上一篇文章,android 串口开发第一篇:搭建ndk开发环境以 ...

  7. QT for Android串口开发

    QT for Android 思路 一.所用到的头文件 1.打开串口 2.配置串口 3.接收数据 3.发送数据 最近在搞QT for Android开发,在网上搜了下解决方案发现都是需要java库的支 ...

  8. orangepi——uart串口开发(TX,RX)

    基于wiringPi的串口开发 1.简介 串口是嵌入式常用的一种通信方式,串口协议涉及到波特率.奇偶校验位.数据位.停止位等.如何配置寄存器. 串行接口的简称,按照一位一位的顺序传输. wiringP ...

  9. Android程序调用串口开发硬件

    Android程序调用串口开发硬件 Android的串口程序 工程编辑 Android的串口程序   Adnroid开发串口程序与linux一致,本质也是对文件进行读写操作,不过为了能用java操作, ...

最新文章

  1. [Android Pro] 分析 Package manager has died
  2. JavaXml教程(六)使用JDOM解析XML文件
  3. SIGIR 2019 eBay高精度召回任务挑战赛冠军团队DeepBlueAI技术分享
  4. Linux缺少qt5core,关于qt5:无法运行Qt应用:找不到版本“ Qt_5”
  5. 将数据压缩到数据结构中
  6. HDU 1525 类Bash博弈
  7. 图论算法(四)--最小生成树的Kruskal [ 加边 ] 、Prim [ 加点 ] 的解法(JAVA)
  8. 用Python解决百马百瓦
  9. c++ fstream类详解
  10. Spring MVC 3.2+ @ResponseBody 导致的中文乱码处理
  11. 使用ntp协议同步时间,chronyc sources -v 同步时间
  12. java向flex传递 List
  13. 案例分享:Qt管道焊接参数条码打印系统(条码打印机TSC 244 Pro、打印条码、打印中文、打印字符、多张连续打印)
  14. 计算机语言工资排行,Python位居编程语言薪资排行榜前列!风变编程打造职场竞争力...
  15. peer channel create解析
  16. 母婴行业竟也可以免费送?两种案例让你全面了解新的赚钱模式!
  17. 人脸识别最低像素_人脸识别新利器:让你在50米内无处遁逃
  18. 心理学 | (1)焦虑症和恐惧症--一种认知的观点
  19. 2018 ACM-ICPC Syrian Collegiate Programming Contest
  20. android启动百度地图应用并开始导航,android打开外部地图导航(百度、高德、腾讯)...

热门文章

  1. Web APIs-事件流、事件委托、其他事件、元素尺寸与位置
  2. [PLT] 概念笔记
  3. 将数据集类标签数字化
  4. 做一个python的旅游系统_我把全国旅游数据用Python爬下来后发现,这个地方才是真正的旅游胜地...
  5. Linux中fork创建兄弟子进程,验证进程之间全局变量不共享,exec函数族
  6. 公安部规定婚车用“百年好合”遮牌扣12分-百年好合-婚车-扣分
  7. 一维空间最近点对问题
  8. CLUE-S|对1995年土地利用做二阶Logistic回归分析
  9. android 7.0 暗 主题,手机亮度调节超暗模式APP 7.0.3 安卓免ROOT
  10. shader中toggle的使用