Andorid串口开发打印机
最近公司接了一个税控项目,要通过Andorid机串口链接打印机把发票打出来,那么串口通信就是大头了。这里记录一下过程。。。
这里主要有几个坑:
- 串口没有读写权限:
- 找到Andorid的sdk中platform-tools目录下的adb给添加到环境变量中,这样就能方便使用
- 直接adb devices 是否能查看到当前连接的设备
- 满足第2步的情况下,adb shell 进入控制台
- root过的机子可以直接su 获取到权限
- chmod 777 /dev/ttyHSL1
- 如何还不行,那就可能是防火墙的关系 setenforce 0
- 这只是应急处理,每次重启后都要经过这个操作
- 后来就是直接和硬件沟通,把这个串口的权限给我开放了出来,谁都可以调用
- 串口开放失败:这应该就是找对应打印机的那个串口(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串口开发打印机相关推荐
- 串口开发 打印机 读卡器 遇到的问题
对于串口开发的打印机和读卡器,再利用SerialPort开发时,注意事项: 1.端口被占用(可能是安装了硬件的驱动,导致占用端口),解决办法步骤:打开"控制面板"-->&qu ...
- 串口小票打印机调试命令
小票打印机作为POS收银机的一个标准外设,在平时的实施过程不时也会出现无法打印的情况. 出现这种情况的原因有很多种,主要有安装不正确,打印机参数设置不正确,软件问题等.现以EPSON的 串口小票打印机 ...
- android studio scala插件,Scala 语言开发Andorid ,开发环境的搭建(一)
Scala 语言开发Andorid ,开发环境的搭建 厌倦 Java 繁琐的语法,为了更优雅的开发 Android 程序,Scala 代替 Java 是一个不错的尝试. 开发前可以学习 Scala 的 ...
- Android USB串口开发
因为第一次接触Android下的串口开发,在网上找了很多例子都不能满足自己的需要以及不能正常使用,于是结合网上的资源以及查阅资料,终于完成了关于这个串口的开发,在此记录下usb转串口通信开发的过程. ...
- 中移4G模块-ML302-OpenCpu开发-串口开发
B站:https://space.bilibili.com/309103931 中移4G模块-ML302专栏:https://blog.csdn.net/qq_33259323/category_10 ...
- android 串口开发第二篇:利用jni实现android和串口通信
一:串口通信简介 由于串口开发涉及到jni,所以开发环境需要支持ndk开发,如果未配置ndk配置的朋友,或者对jni不熟悉的朋友,请查看上一篇文章,android 串口开发第一篇:搭建ndk开发环境以 ...
- QT for Android串口开发
QT for Android 思路 一.所用到的头文件 1.打开串口 2.配置串口 3.接收数据 3.发送数据 最近在搞QT for Android开发,在网上搜了下解决方案发现都是需要java库的支 ...
- orangepi——uart串口开发(TX,RX)
基于wiringPi的串口开发 1.简介 串口是嵌入式常用的一种通信方式,串口协议涉及到波特率.奇偶校验位.数据位.停止位等.如何配置寄存器. 串行接口的简称,按照一位一位的顺序传输. wiringP ...
- Android程序调用串口开发硬件
Android程序调用串口开发硬件 Android的串口程序 工程编辑 Android的串口程序 Adnroid开发串口程序与linux一致,本质也是对文件进行读写操作,不过为了能用java操作, ...
最新文章
- [Android Pro] 分析 Package manager has died
- JavaXml教程(六)使用JDOM解析XML文件
- SIGIR 2019 eBay高精度召回任务挑战赛冠军团队DeepBlueAI技术分享
- Linux缺少qt5core,关于qt5:无法运行Qt应用:找不到版本“ Qt_5”
- 将数据压缩到数据结构中
- HDU 1525 类Bash博弈
- 图论算法(四)--最小生成树的Kruskal [ 加边 ] 、Prim [ 加点 ] 的解法(JAVA)
- 用Python解决百马百瓦
- c++ fstream类详解
- Spring MVC 3.2+ @ResponseBody 导致的中文乱码处理
- 使用ntp协议同步时间,chronyc sources -v 同步时间
- java向flex传递 List
- 案例分享:Qt管道焊接参数条码打印系统(条码打印机TSC 244 Pro、打印条码、打印中文、打印字符、多张连续打印)
- 计算机语言工资排行,Python位居编程语言薪资排行榜前列!风变编程打造职场竞争力...
- peer channel create解析
- 母婴行业竟也可以免费送?两种案例让你全面了解新的赚钱模式!
- 人脸识别最低像素_人脸识别新利器:让你在50米内无处遁逃
- 心理学 | (1)焦虑症和恐惧症--一种认知的观点
- 2018 ACM-ICPC Syrian Collegiate Programming Contest
- android启动百度地图应用并开始导航,android打开外部地图导航(百度、高德、腾讯)...
热门文章
- Web APIs-事件流、事件委托、其他事件、元素尺寸与位置
- [PLT] 概念笔记
- 将数据集类标签数字化
- 做一个python的旅游系统_我把全国旅游数据用Python爬下来后发现,这个地方才是真正的旅游胜地...
- Linux中fork创建兄弟子进程,验证进程之间全局变量不共享,exec函数族
- 公安部规定婚车用“百年好合”遮牌扣12分-百年好合-婚车-扣分
- 一维空间最近点对问题
- CLUE-S|对1995年土地利用做二阶Logistic回归分析
- android 7.0 暗 主题,手机亮度调节超暗模式APP 7.0.3 安卓免ROOT
- shader中toggle的使用