公司项目用到蓝牙和硬件通讯,APP正在开发,弄一个微信小程序蓝牙通讯的demo,可能后期会有微信蓝牙的项目,第一次搞,遇到2个坑:

1.安卓和苹果获取的硬件服务UUID顺序不同

2.目前用的这一版 “启用低功耗蓝牙设备特征值变化时的 notify 功能”在安卓和苹果的测试机上都返回启动失败,其实是已经启动成功,在我同事安卓手机上返回的正常。

index.wxml

<!--index.wxml-->
<view class="content">
  <text class="status">适配器状态:{{ status }}</text>
  <text class="sousuo">是否搜索:{{ sousuo }}</text>
  <text class="msg">消息:{{ msg }} </text>
  <text class="msg1">消息:{{ msg1 }}</text>
  <button type="primary" class="button" bindtap="lanya1">1初始化蓝牙适配器</button>
  <button type="primary" class="button" bindtap="lanya2">2本机蓝牙适配状态</button>
  <button type="primary" class="button" bindtap="lanya3">3搜索周边设备</button>
  <button type="primary" class="button" bindtap="lanya4">4获取设备后在列表中连接</button>
  <button type="primary" class="button" bindtap="lanya5">5停止搜索周边设备</button>
  <button type="primary" class="button" bindtap="lanya6">6获取连接设备所有service</button>
  <button type="primary" class="button" bindtap="lanya7">7获取连接设备所有特征值</button>
  <button type="primary" class="button" bindtap="lanya8">8发送指定消息</button>
  <button type="primary" class="button" bindtap="lanya9">9启用设备特征值变化时的notify</button>
  <button type="primary" class="button" bindtap="lanya10">10接收消息</button>
  <view class="section">
    <text class="status">接收到消息:{{ jieshou }}</text>

  </view>

  <button type="primary" class="button" bindtap="lanya0">0断开蓝牙连接</button>
</view>
<view class="venues_list">
  <block wx:for="{{devices}}" wx:key="{{test}}">
    <view class="venues_item">
      <text class="status">设备名称:{{item.name}}</text>
      <text class="status">设备ID:{{item.deviceId}}</text>
      <text class="status">连接状态:{{connectedDeviceId == item.deviceId?"已连接":"未连接"}}</text>
      <view class="section">
      </view>
      <view class="section">
        <button type="warn" class="button" id="{{item.deviceId}}" bindtap="connectTO">连接</button>
      </view>
    </view>
  </block>
</view>

index.js

//index.js
//获取应用实例
var app = getApp();
Page({
    data: {
        status: "",
        sousuo: "",
        connectedDeviceId: "", //已连接设备uuid
        services: "", // 连接设备的服务
        characteristics: "",   // 连接设备的状态值
        writeServicweId: "", // 可写服务uuid
        writeCharacteristicsId: "",//可写特征值uuid
        readServicweId: "", // 可读服务uuid
        readCharacteristicsId: "",//可读特征值uuid
        notifyServicweId: "", //通知服务UUid
        notifyCharacteristicsId: "", //通知特征值UUID
        inputValue: "",
        characteristics1: "", // 连接设备的状态值
    },
    onLoad: function () {
        if (wx.openBluetoothAdapter) {
            wx.openBluetoothAdapter()
        } else {
            // 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
            wx.showModal({
                title: '提示',
                content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
            })
        }

    },
    // 初始化蓝牙适配器
    lanya1: function () {
        var that = this;
        wx.openBluetoothAdapter({
            success: function (res) {
                that.setData({
                    msg: "初始化蓝牙适配器成功!" + JSON.stringify(res),
                })
                //监听蓝牙适配器状态
                wx.onBluetoothAdapterStateChange(function (res) {
                    that.setData({
                        sousuo: res.discovering ? "在搜索。" : "未搜索。",
                        status: res.available ? "可用。" : "不可用。",
                    })
                })
            }
        })
    },
    // 本机蓝牙适配器状态
    lanya2: function () {
        var that = this;
        wx.getBluetoothAdapterState({
            success: function (res) {
                that.setData({
                    msg: "本机蓝牙适配器状态" + "/" + JSON.stringify(res.errMsg),
                    sousuo: res.discovering ? "在搜索。" : "未搜索。",
                    status: res.available ? "可用。" : "不可用。",
                })
                //监听蓝牙适配器状态
                wx.onBluetoothAdapterStateChange(function (res) {
                    that.setData({
                        sousuo: res.discovering ? "在搜索。" : "未搜索。",
                        status: res.available ? "可用。" : "不可用。",
                    })
                })
            }
        })
    },
    //搜索设备
    lanya3: function () {
        var that = this;
        wx.startBluetoothDevicesDiscovery({
            success: function (res) {
                that.setData({
                    msg: "搜索设备" + JSON.stringify(res),
                })
                //监听蓝牙适配器状态
                wx.onBluetoothAdapterStateChange(function (res) {
                    that.setData({
                        sousuo: res.discovering ? "在搜索。" : "未搜索。",
                        status: res.available ? "可用。" : "不可用。",
                    })
                })
            }
        })
    },
    // 获取所有已发现的设备
    lanya4: function () {
        var that = this;
        wx.getBluetoothDevices({
            success: function (res) {
                //是否有已连接设备
                wx.getConnectedBluetoothDevices({
                    success: function (res) {
                        console.log(JSON.stringify(res.devices));
                        that.setData({
                            connectedDeviceId: res.deviceId
                        })
                    }
                })

                that.setData({
                    msg: "搜索设备" + JSON.stringify(res.devices),
                    devices: res.devices,
                })
                //监听蓝牙适配器状态
                wx.onBluetoothAdapterStateChange(function (res) {
                    that.setData({
                        sousuo: res.discovering ? "在搜索。" : "未搜索。",
                        status: res.available ? "可用。" : "不可用。",
                    })
                })
            }
        })
    },
    //停止搜索周边设备
    lanya5: function () {
        var that = this;
        wx.stopBluetoothDevicesDiscovery({
            success: function (res) {
                that.setData({
                    msg: "停止搜索周边设备" + "/" + JSON.stringify(res.errMsg),
                    sousuo: res.discovering ? "在搜索。" : "未搜索。",
                    status: res.available ? "可用。" : "不可用。",
                })
            }
        })
    },
    //连接设备
    connectTO: function (e) {
        var that = this;
        wx.createBLEConnection({
            deviceId: e.currentTarget.id,
            success: function (res) {
                console.log(res.errMsg);
                that.setData({
                    connectedDeviceId: e.currentTarget.id,
                    msg: "已连接" + e.currentTarget.id,
                    msg1: "",
                })
            },
            fail: function () {
                console.log("调用失败");
            },
            complete: function () {
                console.log("调用结束");
            }

        })
        console.log(that.data.connectedDeviceId);
    },
    // 获取连接设备的service服务
    lanya6: function () {
        var that = this;
        wx.getBLEDeviceServices({
            // 这里的 deviceId 需要在上面的 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
            deviceId: that.data.connectedDeviceId,
            success: function (res) {
                console.log('device services:', JSON.stringify(res.services));
                that.setData({
                    services: res.services,
                    msg: JSON.stringify(res.services),
                })
            }
        })
    },
    //获取连接设备的所有特征值  for循环获取不到值
    lanya7: function () {
        var that = this;
        wx.getBLEDeviceCharacteristics({
            // 这里的 deviceId 需要在上面的 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
            deviceId: that.data.connectedDeviceId,
            // 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
            serviceId: that.data.services[0].uuid,
            success: function (res) {
                for (var i = 0; i < res.characteristics.length; i++) {
                    if (res.characteristics[i].properties.notify) {
                        console.log("11111111", that.data.services[0].uuid);
                        console.log("22222222222222222", res.characteristics[i].uuid);
                        that.setData({
                            notifyServicweId: that.data.services[0].uuid,
                            notifyCharacteristicsId: res.characteristics[i].uuid,
                        })
                    }
                    if (res.characteristics[i].properties.write) {
                        that.setData({
                            writeServicweId: that.data.services[0].uuid,
                            writeCharacteristicsId: res.characteristics[i].uuid,
                        })

                    } else if (res.characteristics[i].properties.read) {
                        that.setData({
                            readServicweId: that.data.services[0].uuid,
                            readCharacteristicsId: res.characteristics[i].uuid,
                        })

                    }
                }
                console.log('device getBLEDeviceCharacteristics:', res.characteristics);

                that.setData({
                    msg: JSON.stringify(res.characteristics),
                })
            },
            fail: function () {
                console.log("fail");
            },
            complete: function () {
                console.log("complete");
            }
        })

        wx.getBLEDeviceCharacteristics({
            // 这里的 deviceId 需要在上面的 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
            deviceId: that.data.connectedDeviceId,
            // 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
            serviceId: that.data.services[1].uuid,
            success: function (res) {
                for (var i = 0; i < res.characteristics.length; i++) {
                    if (res.characteristics[i].properties.notify) {
                        that.setData({
                            notifyServicweId: that.data.services[1].uuid,
                            notifyCharacteristicsId: res.characteristics[i].uuid,
                        })
                    }
                    if (res.characteristics[i].properties.write) {
                        that.setData({
                            writeServicweId: that.data.services[1].uuid,
                            writeCharacteristicsId: res.characteristics[i].uuid,
                        })

                    } else if (res.characteristics[i].properties.read) {
                        that.setData({
                            readServicweId: that.data.services[1].uuid,
                            readCharacteristicsId: res.characteristics[i].uuid,
                        })

                    }
                }
                console.log('device getBLEDeviceCharacteristics1:', res.characteristics);

                that.setData({
                    msg1: JSON.stringify(res.characteristics),
                })
            },
            fail: function () {
                console.log("fail1");
            },
            complete: function () {
                console.log("complete1");
            }
        })
    },
    //断开设备连接
    lanya0: function () {
        var that = this;
        wx.closeBLEConnection({
            deviceId: that.data.connectedDeviceId,
            success: function (res) {
                that.setData({
                    connectedDeviceId: "",
                })
            }
        })
    },
    //监听input表单
    inputTextchange: function (e) {
        this.setData({
            inputValue: e.detail.value
        })
    },
    //发送
    lanya8: function () {
        var that = this;
        // 这里的回调可以获取到 write 导致的特征值改变
        wx.onBLECharacteristicValueChange(function (characteristic) {
            console.log('characteristic value changed:1', characteristic)
        })
        var buf = new ArrayBuffer(16)
        var dataView = new DataView(buf)
        wx.request({
            url: **/getEncrypt',
            success: function (data) {
                var arr = data.data.data.split(",");
                console.log(arr);
                for (var i = 0; i < arr.length; i++) {
                    dataView.setInt8(i, arr[i]);
                }
                console.log('str', buf);
                console.log("writeServicweId", that.data.writeServicweId);
                console.log("writeCharacteristicsId", that.data.writeCharacteristicsId);
                wx.writeBLECharacteristicValue({
                    // 这里的 deviceId 需要在上面的 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
                    deviceId: that.data.connectedDeviceId,
                    // 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
                    serviceId: that.data.writeServicweId,
                    // 这里的 characteristicId 需要在上面的 getBLEDeviceCharacteristics 接口中获取
                    characteristicId: that.data.writeCharacteristicsId,
                    // 这里的value是ArrayBuffer类型
                    value: buf,
                    success: function (res) {
                        console.log('writeBLECharacteristicValue success', res.errMsg)
                    }
                })
            }
        })

    },
    //启用低功耗蓝牙设备特征值变化时的 notify 功能
    lanya9: function () {
        var that = this;
        //var notifyServicweId = that.data.notifyServicweId.toUpperCase();
        //var notifyCharacteristicsId = that.data.notifyCharacteristicsId.toUpperCase();
        //console.log("11111111", notifyServicweId);
        //console.log("22222222222222222", notifyCharacteristicsId);
        wx.notifyBLECharacteristicValueChange({
            state: true, // 启用 notify 功能
            // 这里的 deviceId 需要在上面的 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
            deviceId: that.data.connectedDeviceId,
            // 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
            serviceId: that.data.notifyServicweId,
            // 这里的 characteristicId 需要在上面的 getBLEDeviceCharacteristics 接口中获取
            characteristicId: that.data.notifyCharacteristicsId,
            success: function (res) {
                console.log('notifyBLECharacteristicValueChange success', res.errMsg)
            },
            fail: function () {
                console.log('shibai');
                console.log(that.data.notifyServicweId);
                console.log(that.data.notifyCharacteristicsId);
            },
        })
    },
    //接收消息
    lanya10: function () {
        var that = this;
        // 必须在这里的回调才能获取
        wx.onBLECharacteristicValueChange(function (characteristic) {
            let hex = Array.prototype.map.call(new Uint8Array(characteristic.value), x => ('00' + x.toString(16)).slice(-2)).join('');
            console.log(hex)
            wx.request({
                url: '***/getDecrypt',
                data: {hexString:hex},
                method:"POST",
                header: {
                    'content-type': 'application/x-www-form-urlencoded'
                },
                success:function(data){
                    //console.log(data)
                    var res = data.data.data;
                    that.setData({
                        jieshou: res,
                    })
                }
            })
        })
        console.log(that.data.readServicweId);
        console.log(that.data.readCharacteristicsId);
        wx.readBLECharacteristicValue({
            // 这里的 deviceId 需要在上面的 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
            deviceId: that.data.connectedDeviceId,
            // 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
            serviceId: that.data.readServicweId,
            // 这里的 characteristicId 需要在上面的 getBLEDeviceCharacteristics 接口中获取
            characteristicId: that.data.readCharacteristicsId,
            success: function (res) {
                console.log('readBLECharacteristicValue:', res.errMsg);
            }
        })
    },



})

index.wxss

.content {
  margin: 0 10px;
}

.status, .sousuo, .msg, .msg1 {
  display: block;
  line-height: 35px;
  margin: 0 10px;
}

.button {
  margin: 10px;
}

.sendto {
  line-height: 30px;
  display: block;
  margin: 10px;
}

在服务器端做的蓝牙加解密

/**
	 * 微信蓝牙加密接口
	 */
	public void getEncrypt(){
		byte[] bs = new byte[]{25,1,49};
		String string = null;
		try {
			string = AesEntryDetry.encrypt(bs);
		} catch (Exception e) {
			logger.info("加密错误");
		}
		if(string != null){
			setAttr("msg", "加密成功!");
			setAttr("code", "200");
			setAttr("data", string);
		}else{
			setAttr("msg", "加密失败!");
			setAttr("code", "400");
		}
		renderJson();
	}
	
	/**
	 * 微信蓝牙解密接口
	 */
	public void getDecrypt(){
		String hexString = getPara("hexString");
		byte[] bs = AesEntryDetry.hex2Bytes(hexString);
		String resString = null;
		try {
			resString = AesEntryDetry.decrypt(bs);
		} catch (Exception e) {
			logger.info("解密错误");
		}
		if(resString != null){
			setAttr("msg", "加密成功!");
			setAttr("code", "200");
			setAttr("data", resString);
		}else{
			setAttr("msg", "加密失败!");
			setAttr("code", "400");
		}
		renderJson();
	}
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AesEntryDetry {
	// 加密秘钥 ,16个字节也就是128 bit
	private static final byte[] AES_KEY = { 需要和硬件统一 };
	// 加密方法
	public static String encrypt(byte[] bs) throws Exception {
		SecretKeySpec skeySpec = new SecretKeySpec(AES_KEY, "AES");
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
		if(bs.length < 16){
			bs = Arrays.copyOf(bs, 16);
		}
		byte[] encrypted = cipher.doFinal(bs);
		return BytetohexString(encrypted);
	}
	// 解密方法
	public static String decrypt(byte[] bs)throws Exception {
		SecretKeySpec skeySpec = new SecretKeySpec(AES_KEY, "AES");
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
		byte[] decrypted = cipher.doFinal(bs);
		return BytetohexString(decrypted);
	}
	// 字节数组按照一定格式转换拼装成字符串
	private static String BytetohexString(byte[] b) {
		int len = b.length;
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < len; i++) {
			if (i < len - 1){
				sb.append(b[i]);
				sb.append(",");
			}else{
				sb.append(b[i]);
			}
		}
		return sb.toString();
	}
	
	public static byte[] hex2Bytes(String src){  
        byte[] res = new byte[src.length()/2];  
        char[] chs = src.toCharArray();  
        int[] b = new int[2];  
  
        for(int i=0,c=0; i<chs.length; i+=2,c++){              
            for(int j=0; j<2; j++){  
                if(chs[i+j]>='0' && chs[i+j]<='9'){  
                    b[j] = (chs[i+j]-'0');  
                }else if(chs[i+j]>='A' && chs[i+j]<='F'){  
                    b[j] = (chs[i+j]-'A'+10);  
                }else if(chs[i+j]>='a' && chs[i+j]<='f'){  
                    b[j] = (chs[i+j]-'a'+10);  
                }  
            }
            b[0] = (b[0]&0x0f)<<4;  
            b[1] = (b[1]&0x0f);  
            res[c] = (byte) (b[0] | b[1]);  
        }  
        return res;  
    } 
}