1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Android 蓝牙/wifi云打印 ESC/POS热敏打印机打印(连接篇)

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

时间:2023-05-29 16:30:30

相关推荐

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

本篇我们将讲解蓝牙打印机和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连接

首先需要获取一个BluetoothDevice对象。获取方式如前文介绍的,通过调用startDiscovery()并监听广播获得,也可以通过查询已配对的设备获得。通过BluetoothDevice.createInsecureRfcommSocketToServiceRecord(UUID)得到BluetoothSocket对象。通过BluetoothSocket.connect()建立连接异常处理以及连接关闭

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 添加设备

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

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

@POST@FormUrlEncodedObservable<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 = "/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@FormUrlEncodedObservable<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 = "/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@FormUrlEncodedObservable<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 = "/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@FormUrlEncodedObservable<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 = "/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>11080001</gpBarCode>\n" +"<gpWord Align=0 Bold=0 Wsize=0 Hsize=0 Reverse=0 Underline=0>订单编号:11080001</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>/r/kHV3b67EXPMjreoM9yCC</gpQRCode>\n" +"<gpCut/>";return retrofit.create(IWifi.class).sendMsg(url, reqTime, securityCode, memberCode, deviceID, mode, msgDetail);}

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

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

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

github地址:/zoujin6649/PrinterDemo

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。