uniapp蓝牙连接+打印

项目需求,做了低耗蓝牙打印功能,总结下如何实现功能的。下面是需要的api,刚开始写的时候有点多绕来绕去还是有点头疼哈哈,这个有个很大的问题就是不支持蓝牙版本4.0以下的版本。需要兼容另找其他办法。对于版本低的就会出现一些莫名其妙的问题。
1.uni.openBluetoothAdapter 初始化蓝牙模块
2.uni.onBluetoothDeviceFound 监听寻找到新设备的事件
3.uni.getBluetoothAdapterState 获取本机蓝牙适配器状态
4.uni.stopBluetoothDevicesDiscovery 停止搜寻附近的蓝牙设备
5.uni.createBLEConnection 初始化蓝牙连接
6.uni.getBLEDeviceServices 获取蓝牙设备服务
7.uni.getBLEDeviceCharacteristics 获取蓝牙设备某个服务中所有特征值(characteristic)
8.uni.writeBLECharacteristicValue 向低功耗蓝牙设备特征值中写入二进制数据
9.uni.startBluetoothDevicesDiscovery 开始搜寻附近的蓝牙设备

首先需要初始化蓝牙模块,如果没有初始化后面都不会在执行。然后再搜索周围设备。再把搜索到的设备存入数组,方便渲染。

          <view class="uni-list-cell uni-list-cell-pd auto-switch-cell">
                  <view class="uni-list-cell-db"  @tap="switchBluetooth">蓝牙初始化</view>
          </view>
      <list class="bt-list" v-if="devData.length!=0">
        <div v-for="(item,index) in devData" :key="index" class="bt-item">
              <view class="singleLeft">
                <div  class="dev-single-name">
                    <div class="single">
                        <div :class="{'cgreen': item.RSSI >= 1}" style=" height: 8rpx;"> </div>
                        <div :class="{'cgreen': item.RSSI >= 2}" style=" height: 12rpx;"> </div>
                        <div :class="{'cgreen': item.RSSI >= 3}" style=" height: 15rpx;"> </div>
                        <div :class="{'cgreen': item.RSSI >= 4}" style=" height: 19rpx;"> </div>
                        <div :class="{'cgreen': item.RSSI >= 5}" style=" height: 22rpx;"> </div>
                    </div>
                      <div class="dev-name">
                        <div>{{item.name}}</div>
                        <div>{{item.deviceId}}</div>
                      </div>
                </div>
              </view>
            <view class="singleRight">
                <button plain type="primary" class="link-btn" @click="linkBLE(item,item.deviceId, item.name)" v-if="BTPrintStatus !== item.deviceId">选择</button>
                <button plain type="primary" class="link-btn activelink" @click="closeBLE(item.deviceId)" v-if="BTPrintStatus === item.deviceId">取消</button>
            </view>
        </div>
      </list>
// 打开蓝牙
function switchBluetooth(e) {
    if (e.mp.detail) {
        // 初始化蓝牙
        uni.openBluetoothAdapter({
            success: res => {
                startSearchBTDev()
            },
            fail: err => {
                showModal('提示', '请打开手机蓝牙')
            }
        })
    } else {
        // 断开蓝牙模块
        uni.closeBluetoothAdapter({
            success: res => {
                console.log('关闭蓝牙模块成功', res)
            },
            fail: err => {
                console.log('关闭蓝牙模块失败', err)
            }
        })
    }
}
// 开始搜索蓝牙列表
function startSearchBTDev() {
    this.devData = [] // 清空列表
    onListenerBTDevFound(this)
    store.commit('setAllBluthData', this.devData)//添加所有打印机数据
    // 获取本机蓝牙适配器状态
    getBTAdapterState().then(res => {   
        let available = res.available // 蓝牙适配器是否可用
        let discovering = res.discovering // 是否处于搜索状态
        if (res.available) {
            if (res.discovering) {
                this.stopDisBisBTDev()//停止搜寻设备
            }
            //搜索蓝牙
            //开始搜寻附近的蓝牙外围设备
            console.log("开始搜寻附近的蓝牙外围设备")
            //开始搜寻附近的蓝牙外围设备。此操作比较耗费系统资源,请在搜索并连接到设备后调用
            //uni.stopBluetoothDevicesDiscovery 方法停止搜索。
            // 扫描附近设备
            startDisBTDev().then(res => {
                console.log('扫描成功', res)
                // 开始监听新设备
            }).catch(err => {
                uni.showToast({ title: err })
            })
        } else {
            console.log('本机蓝牙不可用')
        }
    }
}
// 获取本机蓝牙适配器状态
function getBTAdapterState (fn) {
    return new Promise((resolve, reject) => {
      uni.getBluetoothAdapterState({
        success: res => {
          resolve(res)
        },
        fail: err => {
          console.log('获取适配器状态失败' + err)
          reject(BTStatus[err.errCode])
        }
      })
    })
  }
//监听寻找到新设备的事件
function onListenerBTDevFound (that) {
    uni.onBluetoothDeviceFound(res => {
      // console.log('监听到的设备', res)
      let dev = res.devices[0]
      // name和localName都存在
      if (dev.name && dev.localName) {
        let arr = that.devData.filter(item => item.deviceId === dev.deviceId)
        if (arr.length > 0) {
          // 说明存在相同设备,要进行RSSI更新
          let n = that.devData.indexOf(arr[0])
          // 转换信号
          let rssi = Math.floor(max(0, dev.RSSI + 100) / 10)
          if (rssi <= 0) {
            // 无信号删除
            that.devData.splice(n, 1)
          } else {
            // 有信号更新
            that.devData[n].RSSI = rssi
          }
        } else {
          // 新设备,直接push
          that.devData.push(dev)
        }
      }
      that.devData.forEach((currentValue, index,)=>{
          that.devData[index].services=[]
      })
    })
  }

// 开始搜寻附近的蓝牙设备
// 注意,该操作比较耗费系统资源,请在搜索并连接到设备后调用 stop 方法停止搜索。
function startDisBTDev () {
    return new Promise((resolve, reject) => {
      uni.startBluetoothDevicesDiscovery({
        // services: ['FEE7'] uuid列表
        // interval: 500, // 间隔500毫秒
        allowDuplicatesKey: true, // 允许重复上报, RSSI不同
        success: res => {
          resolve(res)
        },
        fail: err => {
          console.log('开始搜索失败' + err)
          reject(BTStatus[err.errCode])
        }
      })
    })
  }

蓝牙连接的时间有时效性,2min中过后会自动断开,然后导致打印数据输入不了,所以我在需要打印的时候再进行对设备连接,这样做可以在其他页面上也可以使用打印功能,并成功保持连接。

      <div style="width: 200rpx; background: #4EB331; color: white; border-radius: 8rpx;padding: 20rpx 0;text-align: center; margin: 10rpx auto; font-size: 26rpx;" v-show="deviceName" @click="testPrint">
        测试蓝牙打印
      </div>
let tmpOrder = ''
let tmpSkus = ''

async function testPrint() {
    let order = {
        orderSeq: '测试打印',
        buyerComment: '测试打印'
    }
    let orderSkus = []
    try {
        store.commit('Loading', true)
        let { data } = await data
        order = data[0]
        console.log('测试打印testPrint-order', order)
        orderSkus = await orderSkus
        console.log('测试打印testPrint-orderSkus', orderSkus)
    } catch (e) {
        console.log(e)
    } finally {
        store.commit('Loading', false)
    }
    // 请求订单
    // 请求sku
    startPrint(order, orderSkus, () => {
        console.log('-----------打印完成-----------')
        uni.showToast({ title: '打印完成' })
    }, err => {
        console.log('-----------打印完成-----------')
        uni.showToast({ title: err })
    })
}

// 初始化蓝牙准备打印
export function startPrint(order, skus, fn, errFn) {
    tmpOrder = order
    tmpSkus = skus
    console.log('tmp', tmpOrder, tmpSkus)
    let _deviceId = uni.getStorageSync('deviceId')
    console.log('deviceId', _deviceId)
    createBLE(_deviceId, fn, errFn)
    // printFormat(fn, errFn)
}
//初始化蓝牙连接
function createBLE(_deviceId, fn, errFn) {
    uni.createBLEConnection({
        deviceId: _deviceId,
        success(res) {
            console.log('createBLEConnection', res)
            if (res.errMsg == "createBLEConnection:ok") {
                //这个地方使用了setTimeout等待一秒种再去获取,直接获取我们可能出现获取不到的情况
                setTimeout(() => {
                    console.log('to----getBLEServices')
                    getBLEServices(_deviceId, fn, errFn)//获取蓝牙设备服务
                }, 2000)
            } else {
                console.log(res, '获取蓝牙设备服务失败')
            }
        },
        fail(res) {
            console.log('fail', res)
        }
    })
}

//获取蓝牙设备服务
function getBLEServices(_deviceId, fn, errFn) {
    let deviceId = _deviceId
    console.log("获取蓝牙设备所有服务(service)。---------------", _deviceId)
    uni.getBLEDeviceServices({
        // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
        deviceId,
        success(res) {
            console.log('getBLEDeviceServices--res', res)
            let serviceId = ""
            console.log('res.services.length', res.services)
            //有的低版本设备连接会出错,但是重新连接就可以链接上,所以在这里先断开连接再重新连接该设备
            if (res.services.length == 0) {
                console.log('res.services.length == 0')
                uni.closeBLEConnection({
                    deviceId,
                    success(res) {
                        console.log('断开与设备的连接', res)
                        //重新连接
                        setTimeout(() => {
                            createBLE(deviceId, fn, errFn)
                        }, 1000)

                    }
                })
                return
            }
            for (var s = 0; s < res.services.length; s++) {
                console.log(res.services[s].uuid)
                let serviceId = res.services[s].uuid
                uni.getBLEDeviceCharacteristics({
                    // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
                    deviceId,
                    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
                    serviceId: serviceId,
                    success(res) {
                        var re = JSON.parse(JSON.stringify(res))
                        console.log('re.characteristics.length', re.characteristics.length)
                        for (var c = 0; c < re.characteristics.length; c++) {
                            //这里的characteristics有多个值,且write值不一样,所以要循环判定
                            if (re.characteristics[c].properties.write == true) {
                                let uuid = re.characteristics[c].uuid
                                //进行ID匹配
                                let arr2222 = store.state.allBluthData
                                console.log('store.state.allBluthData', arr2222)
                                for (var index in arr2222) {
                                    if (arr2222[index].deviceId == deviceId) {
                                        console.log('arr2222[index].services', arr2222[index].services)
                                        arr2222[index].services.push({
                                            serviceId: serviceId,
                                            characteristicId: uuid
                                        })
                                        console.log('arr2222//进行ID匹配', arr2222)
                                        break
                                    }
                                }

                                let arr = arr2222.filter((currentValue, index) => {
                                    return arr2222[index].deviceId === _deviceId
                                })
                                store.commit('commitBTDevCharact', arr)
                                console.log('store.state.BTDevCharact', store.state.BTDevCharact)
                                console.log('与对应设备建立链接', arr2222)
                            }
                        }
                        printFormat(fn, errFn)
                    },
                    fail(res) {
                        console.log('getBLEDeviceCharacteristics', res)
                    }
                })
            }
        },
        fail(res) {
            console.log(res)
        },
    })
}

async function printFormat(fn, errFn) {
    try {
        console.log('推送打印数据')
        toast('开始打印')
        pos.ClearQueue()
        // 京西菜市
        pos.PrintBigTitle('京西菜市')
        pos.PrintJumpLines(1)
        pos.PrintMiddleText('手机买菜上京西')
        pos.PrintMiddleText('极速到家送惊喜')
        pos.PrintMiddleText('--------------------------------')
        pos.PrintJumpLines(1)
        // 下单时间
        pos.PrintText(`下单时间:${timeFormat2(tmpOrder.orderCreatedAt)}`)
        // 预计送达
        pos.PrintText(`预计送达:${expectTime()}`)
        // 订单号
        pos.PrintText(`订单编号:${tmpOrder.vendorOrderID}`)
        pos.PrintJumpLines(1)
        // #1
        pos.PrintBigLeftTitle(`${vendorID2()}#${tmpOrder.orderSeq + 1}`)
        pos.PrintJumpLines(1)
        pos.PrintBarcode(`${tmpOrder.vendorOrderID}`)
        if (tmpOrder.vendorID === 3) pos.PrintBigLeftTitle(`饿百取货码: ${tmpOrder.vendorOrderID2.slice(tmpOrder.vendorOrderID2.length - 4)}`)
        pos.PrintJumpLines(1)
        // 客户信息
        pos.PrintText(`客户: ${tmpOrder.consigneeName}`)
        pos.PrintText(`电话: ${tmpOrder.consigneeMobile}`)
        pos.PrintText(`地址: ${tmpOrder.consigneeAddress}`)
        pos.PrintJumpLines(1)
        // 客户备注
        pos.PrintText(`客户备注:`)
        pos.PrintBigLeftTitle(`${tmpOrder.buyerComment}`)
        pos.PrintJumpLines(1)
        // 商品明细
        pos.PrintText(`商品明细:`)
        pos.PrintText(`品名   数量                     `)
        pos.PrintMiddleText('--------------------------------')
        tmpSkus.forEach(item => {
            let comparePrice = item.salePrice / 100
            pos.PrintText(`${item.skuName}`)
            pos.PPP(`x${item.count}`, ` `, ` `)
        })

        pos.PrintJumpLines(1)
        // 总计
        pos.PrintNameValue(`共${tmpOrder.skuCount}种${tmpOrder.goodsCount}件商品`, '')
        pos.PrintJumpLines(1)
        pos.PrintMiddleText('--------------------------------')
        // 质量问题联系
        pos.PrintTitle('商品质量问题,请联系:')
        pos.PrintJumpLines(1)
        pos.PrintTitle(`${store.state.storeInfo.name}:${store.state.storeInfo.tel1}`)
        pos.PrintJumpLines(1)
        pos.PrintText('更多信息请关注官方微信: 京西菜市')
        pos.PrintJumpLines(1)
        pos.PrintQRcode2('http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk')
        pos.PrintJumpLines(2)
        pos.PrintMiddleText('--------------------------------')
        pos.PrintMiddleText('--------------------------------')
        pos.PrintJumpLines(2)
        // 打印完成回调
        console.log('打印即将完成')
        pos.DaYin(async () => {
            uni.closeBluetoothAdapter({
                success: res => {
                    console.log('断开蓝牙适配器成功', res)
                },
                fail: err => {
                    console.log('断开蓝牙适配器失败', err)
                },
                complete: async () => {
                    state.commit('setblueToothCheckedglobal')
                    console.log('完成了')
                    toast('打印完成')
                    tmpOrder = ''
                    tmpSkus = ''
                    fn && fn()
                }
            })
        })
    } catch (e) {
        errFn && errFn()
    }
}

function DaYin(fn) {
    if (isPrint) return;
    DaYin2(fn);
}
var isPrint = false;
function DaYin2(fn) {
    //打印的时候不能把数据全部打印,要分开打印,不然会报错
    if (QueueWrite.length > 0) {
        isPrint = true;
        var shuzu = QueueWrite.shift();
        lanyaposgo(shuzu, fn);
    } else {
        isPrint = false;
        fn && fn()
    }
}

function lanyaposgo(text, fn) {
    uni.writeBLECharacteristicValue({
        deviceId: store.state.BTDevCharact[0].deviceId,
        serviceId: store.state.BTDevCharact[0].services[0].serviceId,
        characteristicId: store.state.BTDevCharact[0].services[0].characteristicId,
        value: text.buffer,
        success: function (res) {
            DaYin2(fn);
        },
        fail: function (res) {
            isPrint = false;
            console.log("打印错误:", res);
            console.log("打印数据:", "/" + text + "/");
        }
    });
}

最后是效果图以及打印输出的结果


微信图片_20200904152830.jpg
微信图片_20200904152840.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342