免费国产欧美国日产_少妇AV一区二区三区无码_蜜桃精品av无码喷奶水小说_jk18禁网站视频_精产国品一二三级产品区别_被夫の上司に犯波多野结衣_78m成人手机免费看_最爽最刺激18禁视频_偷偷色噜狠狠狠狠的777米奇

易優(yōu)GEO 重磅上線 ~ 一站式GEO優(yōu)化工具,讓豆包、文心一言、DeepSeek 在回答中主動推薦你的品牌,搶占AI流量入口!  點(diǎn)擊查看

小程序模板網(wǎng)

實(shí)戰(zhàn)分享,藍(lán)牙在小程序中的應(yīng)用!

發(fā)布時(shí)間:2018-01-03 09:38 所屬欄目:小程序開發(fā)教程

導(dǎo)語

藍(lán)牙在日常生活中廣泛使用的一項(xiàng)技術(shù),小程序給了我們前端工程師一個控制藍(lán)牙的方法,帶上你的設(shè)備,來看看怎么控制你的藍(lán)牙設(shè)備吧。

1. 背景介紹

藍(lán)牙是愛立信公司創(chuàng)立的一種無線技術(shù)標(biāo)準(zhǔn),為短距離的硬件設(shè)備提供低成本的通信規(guī)范。藍(lán)牙規(guī)范由藍(lán)牙技術(shù)聯(lián)盟(Bluetooth Special Interest Group,簡稱SIG)管理,在計(jì)算機(jī),手機(jī),傳真機(jī),耳機(jī),汽車,家用電器等等很多場景廣泛使用。藍(lán)牙具有以下一些特點(diǎn):

(1) 免費(fèi)使用:使用的工作頻段在2.4GHz的工科醫(yī)(ISM)頻段,無需申請?jiān)S可證。

(2) 功耗低:BLE4.0包含了一個低功耗標(biāo)準(zhǔn)(Bluetooth Low Energy),可以讓藍(lán)牙的功耗顯著降低

(3) 安全性高:藍(lán)牙規(guī)范提供了一套安全加密機(jī)制和授權(quán)機(jī)制,可以有效防范數(shù)據(jù)被竊取

(4) 傳輸率高:目前最新BLE4.0版本,理論傳輸速率可達(dá)3Mbit/s(實(shí)際肯定達(dá)不到),理論覆蓋范圍可達(dá)100米。

2.小程序藍(lán)牙介紹

小程序API提供了一套藍(lán)牙操作接口,所以作為我們前端開發(fā)人員可以更加方便的進(jìn)行藍(lán)牙設(shè)備開發(fā),而無需了解安卓和IOS的各種藍(lán)牙底層概念。小程序的藍(lán)牙操作大多都是通過異步調(diào)用來處理的,這里面就存在著一些坑,后面會詳細(xì)介紹。在使用小程序藍(lán)牙API之前有幾個概念或者說術(shù)語需要預(yù)先了解:

(1) 藍(lán)牙終端:我們常說的硬件設(shè)備,包括手機(jī),電腦等等。

(2) UUID:是由子母和數(shù)字組成的40個字符串的序號,根據(jù)硬件設(shè)備有關(guān)聯(lián)的唯一ID。

(3) 設(shè)備地址:每個藍(lán)牙設(shè)備都有一個設(shè)備地址deviceId,但是安卓和IOS差別很大,安卓下設(shè)備地址就是mac地址,但是IOS無法獲取mac地址,所以設(shè)備地址是針對本機(jī)范圍有效的UUID,所以這里需要注意,后面會介紹。

(4) 設(shè)備服務(wù)列表:每個設(shè)備都存在一些服務(wù)列表,可以跟不同的設(shè)備進(jìn)行通信,服務(wù)有一個serviceId來維護(hù),每個服務(wù)包含了一組特征值。

(5) 服務(wù)特征值:包含一個單獨(dú)的value值和0 –n個用來描述characteristic 值(value)的descriptors。一個characteristics可以被認(rèn)為是一種類型的,類似于一個類。

(6) ArrayBuffer:小程序中對藍(lán)牙數(shù)據(jù)的傳遞是使用ArrayBuffer的二進(jìn)制類型來的,所以在我們的使用過程中需要進(jìn)行轉(zhuǎn)碼。

3. API總覽

小程序?qū)λ{(lán)牙設(shè)備的操作有18個API

API名稱 說明
openBluetoothAdapter 初始化藍(lán)牙適配器,在此可用判斷藍(lán)牙是否可用
closeBluetoothAdapter 關(guān)閉藍(lán)牙連接,釋放資源
getBluetoothAdapterState 獲取藍(lán)牙適配器狀態(tài),如果藍(lán)牙未開或不可用,這里可用檢測到
onBluetoothAdapterStateChange 藍(lán)牙適配器狀態(tài)發(fā)生變化事件,這里可用監(jiān)控藍(lán)牙的關(guān)閉和打開動作
startBluetoothDevicesDiscovery 開始搜索設(shè)備,藍(lán)牙初始化成功后就可以搜索設(shè)備
stopBluetoothDevicesDiscovery 當(dāng)找到目標(biāo)設(shè)備以后需要停止搜索,因?yàn)樗阉髟O(shè)備是比較消耗資源的操作
getBluetoothDevices 獲取已經(jīng)搜索到的設(shè)備列表
onBluetoothDeviceFound 當(dāng)搜索到一個設(shè)備時(shí)的事件,在此可用過濾目標(biāo)設(shè)備
getConnectedBluetoothDevices 獲取已連接的設(shè)備
createBLEConnection 創(chuàng)建BLE連接
closeBLEConnection 關(guān)閉BLE連接
getBLEDeviceServices 獲取設(shè)備的服務(wù)列表,每個藍(lán)牙設(shè)備都有一些服務(wù)
getBLEDeviceCharacteristics 獲取藍(lán)牙設(shè)備某個服務(wù)的特征值列表
readBLECharacteristicValue 讀取低功耗藍(lán)牙設(shè)備的特征值的二進(jìn)制數(shù)據(jù)值
writeBLECharacteristicValue 向藍(lán)牙設(shè)備寫入數(shù)據(jù)
notifyBLECharacteristicValueChange 開啟藍(lán)牙設(shè)備notify提醒功能,只有開啟這個功能才能接受到藍(lán)牙推送的數(shù)據(jù)
onBLEConnectionStateChange 監(jiān)聽藍(lán)牙設(shè)備錯誤事件,包括異常斷開等等
onBLECharacteristicValueChange 監(jiān)聽藍(lán)牙推送的數(shù)據(jù),也就是notify數(shù)據(jù)

4. 主要流程

藍(lán)牙通信的一個正常流程是下面的圖示

(1) 開啟藍(lán)牙:調(diào)用openBluetoothAdapter來開啟和初始化藍(lán)牙,這個時(shí)候可以根據(jù)狀態(tài)判斷用戶設(shè)備是否支持藍(lán)牙

(2) 檢查藍(lán)牙狀態(tài):調(diào)用getBluetoothAdapterState來檢查藍(lán)牙是否開啟,如果沒有開啟可以在這里提醒用戶開啟藍(lán)牙,并且能在開啟后自動啟動下面的步驟

這里有一個坑:IOS里面藍(lán)牙狀態(tài)變化以后不能馬上開始搜索,否則會搜索不到設(shè)備,必須要等待2秒以上。

復(fù)制代碼
function connect(){
  wx.openBluetoothAdapter({
    success: function (res) {
    },
    fail(res){
    },
    complete(res){
      wx.onBluetoothAdapterStateChange(function(res) {
        if(res.available){
          setTimeout(function(){
            connect();
          },2000);
        }
      })
   //開始搜索  
    }
  })
}
復(fù)制代碼

 

(3) 搜索設(shè)備:startBluetoothDevicesDiscovery開始搜索設(shè)備,當(dāng)發(fā)現(xiàn)一個設(shè)備會觸發(fā)onBluetoothDeviceFound事件,首先看下標(biāo)準(zhǔn)API

由于IOS無法獲取Mac地址所以這里需要區(qū)分兩個場景

a) 安卓:安卓下可以根據(jù)Mac地址來搜索設(shè)備,或者跳過此步直接連接到設(shè)備。當(dāng)搜索到一個設(shè)備以后,可以在onBluetoothDeviceFound事件回調(diào)中判斷當(dāng)前設(shè)備的deviceID是否為指定的Mac地址

復(fù)制代碼
let mac = "XXXXXXXXXXXXXXX";
wx.startBluetoothDevicesDiscovery({
  services:[],
  success(res) {
    wx.onBluetoothDeviceFound(res=>{
        let devices = res.devices;
        for(let i = 0;i<devices.length;i++){
          if(devices[i].deviceId = mac){
            console.log("find");
            wx.stopBluetoothDevicesDiscovery({
              success:res=>console.log(res),
              fail:res=>console.log(res),
            })
          }
        }
    });

  },
  fail(res){
      console.log(res);
  }
})
復(fù)制代碼

 

b) IOS:IOS下獲取設(shè)備Mac地址的方法已經(jīng)被屏蔽,所以不存在mac地址,此時(shí)只能通過其他方式來判斷,比如在藍(lán)牙設(shè)備advertisData字段添加一些特別的信息來判斷等等,可以轉(zhuǎn)字符串來判斷,也可以直接用二進(jìn)制來判斷。

復(fù)制代碼
let id = "XXXXXXXXXXXXXXX",//設(shè)備標(biāo)識符
    deviceId = "";
wx.startBluetoothDevicesDiscovery({
  services:[],
  success(res) {
    wx.onBluetoothDeviceFound(res=>{
        var devices = res.devices;
        for(let i = 0;i<devices.length;i++){
          let advertisData = devices[i].advertisData;
          var data = arrayBufferToHexString(advertisData);//二進(jìn)制轉(zhuǎn)字符串
          if (!!data && data.indexOf(id) > -1) {
              console.log("find");
        deviceId = devices[i].deviceId;
          }
        }
    });    
  },
  fail(res){
      console.log(res);
  }
});
function arrayBufferToHexString(buffer) {
  let bufferType = Object.prototype.toString.call(buffer)
  if (buffer != '[object ArrayBuffer]') {
    return
  }
  let dataView = new DataView(buffer)

  var hexStr = '';
  for (var i = 0; i < dataView.byteLength; i++) {
    var str = dataView.getUint8(i);
    var hex = (str & 0xff).toString(16);
    hex = (hex.length === 1) ? '0' + hex : hex;
    hexStr += hex;
  }
****
  return hexStr.toUpperCase();
}
復(fù)制代碼

 

這里需要注意的是:如果知道m(xù)ac地址在安卓下可以直接略過搜索過程直接連接,如果不知道m(xù)ac地址或者是IOS場景下需要開啟搜索,由于搜索是比較消耗資源的動作,所以發(fā)現(xiàn)目標(biāo)設(shè)備以后一定要及時(shí)關(guān)閉搜索,以節(jié)省系統(tǒng)消耗。

(4) 搜索到設(shè)備以后,就是連接設(shè)備createBLEConnection:

(5) 連接成功以后就開始查詢設(shè)備的服務(wù)列表:getBLEDeviceServices,然后根據(jù)目標(biāo)服務(wù)ID或者標(biāo)識符來找到指定的服務(wù)ID

復(fù)制代碼
let deviceId = "XXXX";
wx.getBLEDeviceServices({
  deviceId: device_id,
  success: function (res) {        
    let service_id = "";
    for(let i = 0;i<res.services.length;i++){
      if(services[i].uuid.toUpperCase().indexOf("TEST") != -1){
        service_id = services[i].uuid;
        break;
      }
    }

    return service_id;
  },
  fail(res){
    console.log(res);
  }
})
復(fù)制代碼

 

這里有個坑的地方:如果是安卓下如果你知道設(shè)備的服務(wù)ID,你可以省去getBLEDeviceServices的過程,但是IOS下即使你知道了服務(wù)ID,也不能省去getBLEDeviceServices的過程,這是小程序里面需要注意的一點(diǎn)。

(6) 獲取服務(wù)特征值:每個服務(wù)都包含了一組特征值用來描述服務(wù)的一些屬性,比如是否可讀,是否可寫,是否可以開啟notify通知等等,當(dāng)你跟藍(lán)牙通信時(shí)需要這些特征值ID來傳遞數(shù)據(jù)。

getBLEDeviceCharacteristics方法返回了res參數(shù)包含了以下屬性:

characteristics包含了一組特征值列表

通過遍歷特征值對象來獲取想要的特征值ID

復(fù)制代碼
wx.getBLEDeviceCharacteristics({
  deviceId: device_id,
  serviceId: service_id,
  success: function (res) {
    let notify_id,write_id,read_id;
    for (let i = 0; i < res.characteristics.length; i++) {
      let charc = res.characteristics[i];
      if (charc.properties.notify) {
        notify_id = charc.uuid;           
      }
      if(charc.properties.write){
        write_id = charc.uuid;
      }
      if(charc.properties.write){
        read_id = charc.uuid;
      }
    }
  },
  fail(res){
    console.log(res); 
  }
})
復(fù)制代碼

 

這個例子就通過搜索特征值取到了 notify特征值ID,寫ID和讀取ID

(7) 獲取特征值ID以后就可以開啟notify通知模式,同時(shí)開啟監(jiān)聽特征值變化消息

復(fù)制代碼
wx.notifyBLECharacteristicValueChange({
  state: true,
  deviceId: device_id,
  serviceId: service_id,
  characteristicId:notify_id,
  complete(res) {
    wx.onBLECharacteristicValueChange(function (res) {
      console.log(arrayBufferToHexString(res.value));
    })
  },
  fail(res){
    console.log(res);
  }
})
復(fù)制代碼

 

(8) 一切都準(zhǔn)備好以后,就可以開始給藍(lán)牙發(fā)送消息,一旦藍(lán)牙有響應(yīng),就可以在onBLECharacteristicValueChange事件中得到消息并打印出來。

這里面有個坑:開啟notify以后并不能馬上發(fā)送消息,藍(lán)牙設(shè)備有個準(zhǔn)備的過程,需要在setTimeout中延遲1秒以上才能發(fā)送,否則會發(fā)送失敗

復(fù)制代碼
let buf = hexStringToArrayBuffer("test");
wx.writeBLECharacteristicValue({
  deviceId: device_id,
  serviceId: service_id,
  characteristicId:write_id,
  value: buf,
  success: function (res) {
    console.log(buf);
  },
  fail(res){
    console.log(res);
  }
})
function hexStringToArrayBuffer(str) {
  if (!str) {
    return new ArrayBuffer(0);
  }
  var buffer = new ArrayBuffer(str.length);
  let dataView = new DataView(buffer)
  let ind = 0;
  for (var i = 0, len = str.length; i < len; i += 2) {
    let code = parseInt(str.substr(i, 2), 16)
    dataView.setUint8(ind, code)
    ind++
  }
  return buffer;
}
復(fù)制代碼

 

(9) 所有都通信完畢后可以斷開連接:

復(fù)制代碼
wx.closeBLEConnection({
  deviceId: device_id,
  success(res) {
    console.log(res)
  },
  fail(res) {
    console.log(res)
  }
})
wx.closeBluetoothAdapter({
  success: function (res) {
    console.log(res)
  }
})
復(fù)制代碼

 

5. 完整例子

這里為了簡潔,把fail等異常處理已經(jīng)省去,主要流程就是設(shè)置設(shè)備ID和服務(wù)ID的過濾值,在開啟notify之后寫入測試消息,然后監(jiān)聽藍(lán)牙發(fā)送過來的消息,整個過程采用簡化處理,沒有使用事件通信來驅(qū)動,僅做參考。

復(fù)制代碼
let blueApi = {
  cfg:{
    device_info:"AAA",
    server_info:"BBB",
    onOpenNotify:null
  },
  blue_data:{
    device_id:"",
    service_id:"",
    write_id:""
  },
  setCfg(obj){
    this.cfg = Object.assign({},this.cfg,obj);
  },
  connect(){
    if(!wx.openBluetoothAdapter){
      this.showError("當(dāng)前微信版本過低,無法使用該功能,請升級到最新微信版本后重試。");
      return;
    }
    var _this = this;
    wx.openBluetoothAdapter({
      success: function (res) {
      },
      complete(res){
        wx.onBluetoothAdapterStateChange(function(res) {
          if(res.available){
            setTimeout(function(){
              _this.connect();
            },2000);
          }
        })
        _this.getBlueState();        
      }
    })
  },
  //發(fā)送消息
  sendMsg(msg,toArrayBuf = true) {
    let _this = this;
    let buf = toArrayBuf ? this.hexStringToArrayBuffer(msg) : msg;
    wx.writeBLECharacteristicValue({
      deviceId: _this.blue_data.device_id,
      serviceId: _this.blue_data.service_id,
      characteristicId:_this.blue_data.write_id,
      value: buf,
      success: function (res) {
        console.log(res);
      }
    })
  },
  //監(jiān)聽消息
  onNotifyChange(callback){
    var _this = this;
    wx.onBLECharacteristicValueChange(function (res) {
      let msg = _this.arrayBufferToHexString(res.value);
      callback && callback(msg);
      console.log(msg);
    })
  },
  disconnect(){
    var _this = this;
    wx.closeBLEConnection({
      deviceId: _this.blue_data.device_id,
      success(res) {
      }
    })
  },
  /*事件通信模塊*/

  /*連接設(shè)備模塊*/
  getBlueState() {
    var _this = this;
    if(_this.blue_data.device_id != ""){
      _this.connectDevice();
      return;
    }

    wx.getBluetoothAdapterState({
      success: function (res) {
        if (!!res && res.available) {//藍(lán)牙可用    
          _this.startSearch();
        }
      }
    })
  },
  startSearch(){
    var _this = this;
    wx.startBluetoothDevicesDiscovery({
      services:[],
      success(res) {
        wx.onBluetoothDeviceFound(function(res){
          var device = _this.filterDevice(res.devices);
          if(device){
            _this.blue_data.device_id = device.deviceId;
            _this.stopSearch();
            _this.connectDevice();
          }
        });
      }
    })
  },
  //連接到設(shè)備
  connectDevice(){
    var _this = this;
    wx.createBLEConnection({
      deviceId: _this.blue_data.device_id,
      success(res) {
        _this.getDeviceService();
      }
    })
  }, 
  //搜索設(shè)備服務(wù)
  getDeviceService(){
    var _this = this;
    wx.getBLEDeviceServices({
      deviceId: _this.blue_data.device_id,
      success: function (res) {
        var service_id = _this.filterService(res.services);
        if(service_id != ""){
          _this.blue_data.service_id = service_id;
          _this.getDeviceCharacter();
        }
      }
    })
  },
  //獲取連接設(shè)備的所有特征值  
  getDeviceCharacter() {
    let _this = this;
    wx.getBLEDeviceCharacteristics({
      deviceId: _this.blue_data.device_id,
      serviceId: _this.blue_data.service_id,
      success: function (res) {
        let notify_id,write_id,read_id;
        for (let i = 0; i < res.characteristics.length; i++) {
          let charc = res.characteristics[i];
          if (charc.properties.notify) {
            notify_id = charc.uuid;           
          }
          if(charc.properties.write){
            write_id = charc.uuid;
          }
          if(charc.properties.write){
            read_id = charc.uuid;
          }
        }          
        if(notify_id != null && write_id != null){
          _this.blue_data.notify_id = notify_id;
          _this.blue_data.write_id = write_id;
          _this.blue_data.read_id = read_id;

          _this.openNotify();
        }
      }
    })
  },
  openNotify(){
    var _this = this;
    wx.notifyBLECharacteristicValueChange({
        state: true,
        deviceId: _this.blue_data.device_id,
        serviceId: _this.blue_data.service_id,
        characteristicId: _this.blue_data.notify_id,
        complete(res) {
          setTimeout(function(){
            _this.onOpenNotify && _this.onOpenNotify();
          },1000);
          _this.onNotifyChange();//接受消息
        }
    })
  },
  /*連接設(shè)備模塊*/


  /*其他輔助模塊*/
  //停止搜索周邊設(shè)備  
  stopSearch() {
    var _this = this;
    wx.stopBluetoothDevicesDiscovery({
      success: function (res) {
      }
    })
  },  
  arrayBufferToHexString(buffer) {
    let bufferType = Object.prototype.toString.call(buffer)
    if (buffer != '[object ArrayBuffer]') {
      return
    }
    let dataView = new DataView(buffer)

    var hexStr = '';
    for (var i = 0; i < dataView.byteLength; i++) {
      var str = dataView.getUint8(i);
      var hex = (str & 0xff).toString(16);
      hex = (hex.length === 1) ? '0' + hex : hex;
      hexStr += hex;
    }

    return hexStr.toUpperCase();
  },
  hexStringToArrayBuffer(str) {
    if (!str) {
      return new ArrayBuffer(0);
    }

    var buffer = new ArrayBuffer(str.length);
    let dataView = new DataView(buffer)

    let ind = 0;
    for (var i = 0, len = str.length; i < len; i += 2) {
      let code = parseInt(str.substr(i, 2), 16)
      dataView.setUint8(ind, code)
      ind++
    }

    return buffer;
  }
  //過濾目標(biāo)設(shè)備
  filterDevice(device){
    var data = blueApi.arrayBufferToHexString(device.advertisData);
    if (data && data.indexOf(this.device_info.substr(4).toUpperCase()) > -1) {
        var obj = { name: device.name, deviceId: device.deviceId }
        return obj
    }
    else{
      return null;
    }
  },
  //過濾主服務(wù)
  filterService(services){
    let service_id = "";
    for(let i = 0;i<services.length;i++){
      if(services[i].uuid.toUpperCase().indexOf(this.server_info) != -1){
        service_id = services[i].uuid;
        break;
      }
    }

    return service_id;
  }
  /*其他輔助模塊*/
}

blueApi.setCfg({  
    device_info:"AAA",
    server_info:"BBB",
    onOpenNotify:function(){
      blueApi.sendMsg("test");
    }
})
blueApi.connect();
blueApi.onNotifyChange(function(msg){
  console.log(msg);
})
復(fù)制代碼

 

6. 跳坑總結(jié)

(1) 等待響應(yīng):很多情況下需要等待設(shè)備響應(yīng),尤其在IOS環(huán)境下,比如

監(jiān)聽到藍(lán)牙開啟后,不能馬上開始搜索,需要等待2秒

開啟notify以后,不能馬上發(fā)送消息,需要等待1秒

(2) Mac和UUID:安卓的mac地址是可以獲取到的所以設(shè)備的ID是固定的,但是IOS是獲取不到MAC地址的,只能獲取設(shè)備的UUID,而且是動態(tài)的,所以需要使用其他方法來查詢。

(3) IOS下只有搜索可以省略,如果你知道了設(shè)備的ID,服務(wù)ID和各種特征值ID,在安卓下可以直接連接,然后發(fā)送消息,省去搜索設(shè)備,搜索服務(wù)和搜索特征值的過程,但是在IOS下,只能指定設(shè)備ID連接,后面的過程是不能省略的。

(4) 監(jiān)聽到的消息要進(jìn)行過濾處理,有些設(shè)備會抽風(fēng)一樣的發(fā)送同樣的消息,需要在處理邏輯里面去重。

(5) 操作完成后要及時(shí)關(guān)閉連接,同時(shí)也要關(guān)閉藍(lán)牙設(shè)備,否則安卓下再次進(jìn)入會搜索不到設(shè)備除非關(guān)閉小程序進(jìn)程再進(jìn)才可以,IOS不受影響。

復(fù)制代碼
wx.closeBLEConnection({
      deviceId: _this.blue_data.device_id,
      success(res) {
      },
      fail(res) {
      }
    })
  wx.closeBluetoothAdapter({
      success(res){
      },
      fail(res){
      }
    })
復(fù)制代碼

除了以上的常見問題,你還需要處理很多異常情況,比如藍(lán)牙中途關(guān)閉,網(wǎng)絡(luò)斷開,GPS未開啟等等場景,總之和硬件設(shè)備打交道跟純UI交互還是有很大的差別的。


易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉庫:starfork
本文地址:http://m.szcjxy.com/wxmini/doc/course/18354.html 復(fù)制鏈接 如需定制請聯(lián)系易優(yōu)客服咨詢: 點(diǎn)擊咨詢
在線客服