本篇我们将讲解蓝牙打印机和wifi云打印机的连接与数据发送,下一篇讲解ESC/POS命令集

一、蓝牙打印机连接

1.蓝牙权限

2.初始化配置

3.发现设备

4.连接设备

4.1 作为Client连接

5.数据传输

二、wifi云打印机连接

2.1 添加设备

2.2 删除打印机

2.3 查询打印机列表

2.4 发送数据到打印机


一、蓝牙打印机连接

打印机的蓝牙连接方式是基于传统的蓝牙连接方式,手机作为客户端,打印机作为服务端。

我们先上效果图:

1.蓝牙权限

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- If your app targets Android 9 or lower, you can declare ACCESS_COARSE_LOCATION instead. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED ||             ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSION);
}

BLUETOOTH权限允许用户请求连接,接受连接和传输数据等,BLUETOOTH_ADMIN权限允许应用启动设备发现或操纵蓝牙设置。如果应用的目标版本是Android 9或者更低的版本,ACCESS_COARSE_LOCATION权限允许蓝牙扫描收集用户的位置信息,返回的是一个模糊的位置信息,此信息可能来自用户自己的设备,以及在商店和交通设施等位置使用蓝牙信标。Android 10开始,要使用蓝牙扫描位置信息需要申请ACCESS_FINE_LOCATION权限,返回的是精确的位置信息,除此之外,还需要开启GPS功能才行,不然不能搜索和连接其他蓝牙设备。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {showToast("请您先开启gps,否则蓝牙不可用");return;}
}

2.初始化配置

初始化设备本身的蓝牙适配器BluetoothAdapter,有两种方式:

//方式一:
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
//方式二:
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();

整个系统只有一个蓝牙适配器,全局只有一个实例,如果返回null,则代表设备不支持蓝牙,如果设备支持蓝牙,再接着检查蓝牙是否打开:

if (bluetoothAdapter == null || !getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {showToast("当前设备不支持蓝牙");finish();return;
} else {if (!bluetoothAdapter.isEnabled()) {//请求开启蓝牙Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableIntent, REQUEST_ENABLE_BLE);} else {setPairingDevice();handler.postDelayed(new Runnable() {@Overridepublic void run() {scanDevice();}}, 1000);}
}

成功打开蓝牙后就会回调到onActivityResult()中。除了这种主动的打开蓝牙,还可以监听BluetoothAdapter.ACTION_STATE_CHANGED广播,每当蓝牙状态发生变化时,此广播包含的值BluetoothAdapter.EXTRA_STATE,它包含新的蓝牙状态,可能的值:BluetoothAdapter.STATE_OFF和BluetoothAdapter.STATE_ON。

3.发现设备

设备发现是一个扫描过程,它会搜索局部区域内已开启蓝牙功能的设备,并请求与每台设备相关的某些信息。如果设备已开启可检测行,它会通过共享一些信息(例如设备名称、类及其唯一的MAC地址)来响应发现请求。扫描是一个耗时的过程,我们需要在异步执行,并且监听发现设备的广播。

Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();//获取已配对的设备
handler.postDelayed(new Runnable() {@Overridepublic void run() {if (bluetoothAdapter.isDiscovering()) {bluetoothAdapter.cancelDiscovery();}bluetoothAdapter.startDiscovery();}
}, 1000);
private BroadcastReceiver discoveryReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();BluetoothDevice bluetoothDevice =     intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (TextUtils.isEmpty(action) || bluetoothDevice == null) {return;}switch (action) {case BluetoothAdapter.ACTION_DISCOVERY_STARTED:Log.e("TAG", "正在搜索附近的蓝牙设备");break;case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:Log.e("TAG", "搜索结束");break;case BluetoothDevice.ACTION_ACL_CONNECTED:Log.e("TAG", "与" + bluetoothDevice.getName() + "蓝牙已连接");break;case BluetoothDevice.ACTION_ACL_DISCONNECTED:Log.e("TAG", "与" + bluetoothDevice.getName() + "蓝牙连接已结束");break;case BluetoothDevice.ACTION_FOUND:Log.e("TAG", "发现了新设备");if (bluetoothDevice.getBondState() != BluetoothDevice.BOND_BONDED) {//Add}break;case BluetoothAdapter.ACTION_STATE_CHANGED:int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);switch (blueState) {case BluetoothAdapter.STATE_OFF:showToast("蓝牙已关闭");finish();break;case BluetoothAdapter.STATE_ON:showToast("蓝牙已开启");Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();//获取已配对的设备handler.postDelayed(new Runnable() {@Overridepublic void run() {if (bluetoothAdapter.isDiscovering()) {bluetoothAdapter.cancelDiscovery();}bluetoothAdapter.startDiscovery();}}, 1000);break;}break;}}
};

注意:startDiscovery()只能扫描到那些状态被设为可发现的设备。安卓设备默认不可发现,要改变设备为可发现的状态,需要如下请求:

//无功能状态,查询扫描和页面扫描都无效,该状态下蓝牙模块既不能扫描其他设备,也不可见
//请求开启可见
Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoveryIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoveryIntent, REQUEST_DISCOVERABLE_BLE);

注意:startDiscovery()是一个特别耗费资源的操作,所以为了避免资源浪费,需要及时的调用cancelDiscovery()来释放资源。比如在进行设备连接之前,一定要先调用cancelDiscovery()

4.连接设备

蓝牙设备的连接和网络连接的模型十分相似,都是Client-Server模式,都通过一个socket来进行数据传输。作为一个Android设备,存在以下三种情况:

1.只作为Client端发起连接
2.只作为Server端等待别人发起建立连接的请求
3.同时作为Client和Server

因为我们这篇文章主要为介绍连接热敏打印机的做铺垫,所以这里我们只讲Android设备作为Client建立连接的情况。因为打印机也不可能主动跟Android设备建立连接,所以打印机必然是作为Server端被连接。

4.1 作为Client连接

  1. 首先需要获取一个BluetoothDevice对象。获取方式如前文介绍的,通过调用startDiscovery()并监听广播获得,也可以通过查询已配对的设备获得。
  2. 通过BluetoothDevice.createInsecureRfcommSocketToServiceRecord(UUID)得到BluetoothSocket对象。
  3. 通过BluetoothSocket.connect()建立连接
  4. 异常处理以及连接关闭
private class ConnectThread extends Thread {private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device) {BluetoothSocket tmp = null;mmDevice = device;try {// 通过 BluetoothDevice 获得 BluetoothSocket 对象tmp = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));} catch (IOException e) { }mmSocket = tmp;}@Overridepublic void run() {// 建立连接前记得取消设备发现mBluetoothAdapter.cancelDiscovery();try {// 耗时操作,所以必须在主线程之外进行mmSocket.connect();} catch (IOException connectException) {//处理连接建立失败的异常try {mmSocket.close();} catch (IOException closeException) { }return;}doSomething(mmSocket);}//关闭一个正在进行的连接public void cancel() {try {mmSocket.close();} catch (IOException e) { }}
}

Client发起连接时传入的UUID必须要和Server端设置的一样,都在就会报错。

如是像我们连接热敏打印机的这种情况,因为一些常见的蓝牙服务协议已有约定的UUID,比如我们连接热敏打印机是基于SPP串口通信协议,其对应的UUID是"00001101-0000-1000-8000-00805F9B34FB"。

5.数据传输

经过前面4步的操作,两个蓝牙设备已连接,准备就绪,现在就是利用Socket获得InputStream输入流和OutputStream输出流来进行数据得收发。

由于我们是与热敏打印机连接,所以我们只需要给打印机发送我们需要打印得内容。

private class ConnectedThread extends Thread {private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket) {mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;//通过 socket 得到 InputStream 和 OutputStreamtry {tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch (IOException e) { }mmInStream = tmpIn;mmOutStream = tmpOut;}public void run() {byte[] buffer = new byte[1024]; // buffer store for the streamint bytes; // bytes returned from read()//不断的从 InputStream 取数据while (true) {try {bytes = mmInStream.read(buffer);mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();} catch (IOException e) {break;}}}//向 Server 写入数据public void write(byte[] bytes) {try {mmOutStream.write(bytes);} catch (IOException e) { }}public void cancel() {try {mmSocket.close();} catch (IOException e) { }}
}

二、wifi云打印机连接

wifi打印机的连接方式跟蓝牙连接方式一样,都是Client-Server模式,通过一个socket来进行数据传输。由于手里还没有传统的wifi打印机,所以无法验证。但是公司有一台wifi云打印机,通过云服务器连接,调用WebAPI来连接发送数据实现打印。

我们先看一下效果图:

2.1 添加设备

先关注佳博科技的微信公众号,登录佳博云平台http://cloud.poscom.cn/,注册云平台账号,获取API集成所需的商户编号和API密钥。下面是网页端的云平台界面:

我们也可以直接在云平台的终端管理里面去添加终端设备,这里我们就调用API的方式在我们自己的APP中向云服务添加一台我们的打印终端。详细接口文档可以参考:http://cloud.poscom.cn/index.php?catid=18

@POST
@FormUrlEncoded
Observable<CommonResponse> addDevice(@Url String url,@Field("reqTime") String reqTime,@Field("securityCode") String securityCode,@Field("memberCode") String memberCode,@Field("deviceID") String deviceID,@Field("devName") String devName);
public Observable<CommonResponse> addDevice(String deviceID, String devName) {Retrofit retrofit = RetrofitUtils.getGsonRetrofit();String url = "http://api.poscom.cn/apisc/adddev";String memberCode = "商户编号";String reqTime = String.valueOf(System.currentTimeMillis());String apiKey = "API key";String securityCode = Md5Utils.md5(memberCode + reqTime + apiKey + deviceID);return retrofit.create(IWifi.class).addDevice(url, reqTime, securityCode, memberCode, deviceID, devName);
}

注意:securityCode安全校验码,是用API密钥和规定的参数进行MD5运算的结果,注意顺序不能乱。另外,如果云服务器检测到该设备ID已存在,将提示设备已存在,需要先删除再添加。

2.2 删除打印机

@POST
@FormUrlEncoded
Observable<CommonResponse> deleteDevice(@Url String url,@Field("reqTime") String reqTime,@Field("securityCode") String securityCode,@Field("memberCode") String memberCode,@Field("deviceID") String deviceID);
public Observable<CommonResponse> deleteDevice(String deviceID) {Retrofit retrofit = RetrofitUtils.getGsonRetrofit();String url = "http://api.poscom.cn/apisc/deldev";String memberCode = "商户编号";String reqTime = String.valueOf(System.currentTimeMillis());String apiKey = "API key";String securityCode = Md5Utils.md5(memberCode + reqTime + apiKey + deviceID);return retrofit.create(IWifi.class).deleteDevice(url, reqTime, securityCode, memberCode, deviceID);
}

注意:securityCode安全校验码的MD5运算顺序。

2.3 查询打印机列表

@POST
@FormUrlEncoded
Observable<ListDeviceResponse> getListDevices(@Url String url,@Field("reqTime") String reqTime,@Field("memberCode") String memberCode,@Field("securityCode") String securityCode);
public Observable<ListDeviceResponse> getListDevices() {Retrofit retrofit = RetrofitUtils.getGsonRetrofit();String url = "http://api.poscom.cn/apisc/listDevice";String memberCode = "商户编号";String reqTime = String.valueOf(System.currentTimeMillis());String apiKey = "API key";String securityCode = Md5Utils.md5(memberCode + reqTime + apiKey);return retrofit.create(IWifi.class).getListDevices(url, reqTime, memberCode, securityCode);
}

注意:securityCode安全校验码的MD5运算顺序。

2.4 发送数据到打印机

@POST
@FormUrlEncoded
Observable<CommonResponse> sendMsg(@Url String url,@Field("reqTime") String reqTime,@Field("securityCode") String securityCode,@Field("memberCode") String memberCode,@Field("deviceID") String deviceID,@Field("mode") String mode,//model 2-3,2自由格式打印,推荐,3十六进制命字符串打印@Field("msgDetail") String msgDetail);
public Observable<CommonResponse> sendMsg(String deviceID) {Retrofit retrofit = RetrofitUtils.getGsonRetrofit();String url = "http://api.poscom.cn/apisc/sendMsg";String memberCode = "商户编号";String reqTime = String.valueOf(System.currentTimeMillis());String apiKey = "API key";String securityCode = Md5Utils.md5(memberCode + deviceID + reqTime + apiKey);String mode = "2";String msgDetail = "<gpLogo/><gpWord Align=1 Bold=1 Wsize=2 Hsize=2 Reverse=0 Underline=0>发货单</gpWord>\n" +"<gpBarCode Align=1 Type=7 Width=2 Height=80 Position=0>201811080001</gpBarCode>\n" +"<gpWord Align=0 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>订单编号:201811080001</gpWord>\n" +"<gpWord Align=0 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>买家姓名:张三</gpWord>\n" +"<gpWord Align=0 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>买家手机:18666666666</gpWord>\n" +"<gpWord Align=0 Bold=1 Wsize=0 Hsize=1 Reverse=0 Underline=0>买家留言:发顺丰,尽快发货,谢谢</gpWord>\n" +"<gpWord Align=0 Bold=1 Wsize=0 Hsize=1 Reverse=0 Underline=0>卖家备注:发顺丰,优先处理</gpWord>\n" +"<gpWord Align=0 Bold=1 Wsize=0 Hsize=1 Reverse=0 Underline=0>买就送信息:送U盘</gpWord>\n" +"<gpWord Align=0 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>--------------------------------</gpWord>\n" +"<gpTR4 Type=0><td>宝贝名称</td><td>单价</td><td>数量</td><td>价格</td></gpTR4>\n" +"<gpTR4 Type=0><td>佳博GP-CH421D云打印机</td><td>1180</td><td>1</td><td>1180</td></gpTR4>\n" +"<gpTR4 Type=0><td>佳博GP-5890XIII云打印机</td><td>480</td><td>1</td><td>480</td></gpTR4>\n" +"<gpTR4 Type=0><td>佳博G3-350V云打印机</td><td>980</td><td>1</td><td>980</td></gpTR4>\n" +"<gpTR4 Type=0><td>100x150热敏标签纸300</td><td>36</td><td>10</td><td>360</td></gpTR4>\n" +"<gpTR4 Type=0><td>58毫米热敏卷纸100米</td><td>48</td><td>5</td><td>240</td></gpTR4>\n" +"<gpTR4 Type=0><td>80毫米热敏卷纸100米</td><td>42</td><td>10</td><td>420</td></gpTR4>\n" +"<gpWord Align=0 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>-------------------------------- </gpWord>\n" +"<gpWord Align=2 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>合计:3660元</gpWord>\n" +"<gpWord Align=2 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>优惠:-198元</gpWord>\n" +"<gpWord Align=2 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>邮费:  30元</gpWord>\n" +"<gpWord Align=2 Bold=1 Wsize=1 Hsize=1 Reverse=0 Underline=0>实收:3492元 </gpWord>\n" +"<gpCut/>\n" +"<gpWord Align=1 Bold=1 Wsize=1 Hsize=1 Reverse=0 Underline=0>扫码关注佳博</gpWord>\n" +"<gpQRCode Align=1 Size=9 Error=M>http://weixin.qq.com/r/kHV3b67EXPMjreoM9yCC</gpQRCode>\n" +"<gpCut/>";return retrofit.create(IWifi.class).sendMsg(url, reqTime, securityCode, memberCode, deviceID, mode, msgDetail);
}

佳博票据云打印格式详情可以参考http://cloud.poscom.cn/index.php?id=152。

注意:securityCode安全校验码的MD5运算顺序不能乱。mode打印信息的类型,mode为2是自由格式打印,如上文格式,mode为3是十六进制命令集或十六进制字符串打印。

最后附上打印出来的效果图:

github地址:https://github.com/zoujin6649/PrinterDemo

Android 蓝牙/wifi云打印 ESC/POS热敏打印机打印(连接篇)相关推荐

  1. Android 蓝牙/wifi云打印机 ESC/POS热敏打印机打印(ESC/POS指令篇)

    上一篇主要介绍了如何通过蓝牙打印机和wifi云打印机的连接与数据发送,这一篇,我们就介绍向打印机发送打印指令,来打印字符和图片. 由于公司暂且买了两台打印机,一台佳博GP-58MIII,一台GP-SH ...

  2. Android 蓝牙连接 ESC/POS 热敏打印机打印(ESC/POS指令篇)

    上一篇 主要介绍了如何通过蓝牙连接到打印机.这一篇,我们就介绍如何向打印机发送打印指令,来打印字符和图片. =====================2017.05.09 更新============ ...

  3. android 蓝牙打印机(ESC/POS 热敏打印机),打印菜单小票和图片,对蓝牙配对和连接打印功能进行了封装,让你超快实现蓝牙打印功能

    BluetoothPrint 项目地址:liuGuiRong18/BluetoothPrint  简介:android 蓝牙打印机(ESC/POS 热敏打印机),打印菜单小票和图片,对蓝牙配对和连接打 ...

  4. Android sockot连接打印机EPSON ESC/POS指令打印

    sockot连接打印机EPSON ESC/POS指令打印 接了一个需求,需要用Android pad连接打印机进行打印,以前倒是没接触过,这次在网上找了下资料,简单实现了下需求.在这记录下相关代码以及 ...

  5. 小票打印ESC/POS命令集

    搜集的ESC/POS命令 前言 做过两次票据打印的工作了,第一次做完的时候没有做大规模的整理,结果第二次做的时候命令都忘的差不多了,不得不重新查对应资料,再重新做整理. ESC/POS 该打印控制命令 ...

  6. Android在针式打印机上通过ESC/P指令打印二维码

    Android在针式打印机上通过ESC/P指令打印二维码 前言 正文 前言 最近有个需求就是在rk3399上通过usb口打印发票单,各大品牌的针式打印机几乎都没有Android平台的SDK,后查找相关 ...

  7. 安卓开发ESC/POS打印机打印

    ESC/POS打印机打印 主要记录一下主要代码 一.设置文字对齐: mWriter.write(0x1b); mWriter.write(0x61); mWriter.write(alignment) ...

  8. Android进阶——安卓调用ESC/POS打印机打印

    前言 前一段时间由于工作需要,要研究一下安卓程序调用打印机打印小票,并且要求不能使用蓝牙调用,研究了一下,可以利用socket连接,来实现打印功能.写了个Demo,分享一下. 工具:一台打印机(芯烨X ...

  9. iOS 连接打印机 ESC/POS 指令打印 打印图片二维码

    最近公司给商户做的App 允许App把卖出的商品信息通过打印机 打印标签 所以了解了一下iOS 和 打印机 之间的交互 (Ps:用的不是UIPrinter 那个扫面打印机 发送信息打印的那个框架) 主 ...

最新文章

  1. 【PHP】常用日期函数
  2. 牛客网 对称平方数【回文数的判断 两个vector是否相等】
  3. activiti 工作流_一文让你读懂什么是Activiti工作流
  4. 幻像类型提高了编译时的安全性
  5. python的内存回收机制_关于python的变量使用回收机制
  6. LabVIEW: 无法执行该VI。
  7. 卖程序的小女孩(转)
  8. stm32 ISP串口下载
  9. 纯CSS打造圆角Table效果
  10. Oracle结构组成
  11. 如何禁用不需要的HTTP方法
  12. CMake中链接库的顺序问题
  13. 802d简明调试手册_西门子数控系统828D简明调试手册.pdf
  14. 中国历史人物传记数据库 CBDB 若干表简介
  15. Spring的AOP的基于AspectJ注解开发——Spring的JDBC的模板的使用——Spring的事务管理
  16. 应用之星教你制作高下载量的App
  17. 解决百度云管家导入未完成下载任务
  18. 单片机并行口开发——双数码管显示
  19. 怎么解决网页中播放视频没有声音?
  20. 不错的google搜索地址

热门文章

  1. matlab pid buck,BUCK电路闭环PID控制系统的MATLAB仿真.doc
  2. dinfo-oec hadoop部署方案
  3. php web项目目录结构图,目录结构
  4. 07年12月大学英语四级考试B卷答案(新东方版)
  5. 安装AE报错131,Ae安装时报错误代码131
  6. MyBatis Plus整合p6spy控制台打印美化格式的sql语句
  7. 推荐几种占位图生成工具,包含dummyimage、placehold和placekitten等
  8. Your “Flamingo“ is My “Bird”:Fine-Grained or Not
  9. 生成LaTeX使用的eps格式图片
  10. plc编程和python的联系_通过Python与西门子PLC通信