微信app和蓝牙设备接入通信,通信交互通道有两种,如下图通道路径结构图@V型知识库原创
黑色箭头 公众号html界面发送命令到设备,设备最终返回数据到厂商服务器,厂商服务器也就是咱们开发者自己工程项目的服务器后端(数据返回到服务器配置url的 servelet的post方法中了)。这也是导致初学者开发者在开发h5界面发送指令后,无论如何在h5前端界面也得不到蓝牙设备返回数据包的原因,这是个坑,大家注意。
红色箭头 也就是我们接下来介绍的通道路径,公众号html界面发送指令到设备,设备最终返回数据到发送html命令的界面,意思就是H5界面发送指令到设备,设备返回响应数据包到H5界面,而不是服务器端。
点击公众号菜单或链接跳转到H5界面的java方法(案例用的springMVC),在此方法中获取timestamp,nonceStr,signature三个参数。
1、spring的controller方法
//V型知识库 www.vxzsk.com
@RequestMapping(value="/goReadCardAnniu")
public ModelAndView goReadCardAnniu2(HttpServletRequest request,HttpServletResponse response){
String appId="";//应用id
String appsecret="";//应用秘钥
//1,获取access_token
AccessToken accessToken = WeixinUtil.getAccessToken(appId, appsecret);
String access_token=accessToken.getToken();
//2,获取调用微信jsapi的凭证
String ticket = WeixinUtil.getJsapiTicket(access_token);
Map<String,String> map = WeixinUtil.sign(ticket, AppConst.SITE_DOMAIN+"lanya/card/goReadCardAnniu.do");
request.setAttribute("timestamp", map.get("timestamp"));
request.setAttribute("nonceStr", map.get("nonceStr"));
request.setAttribute("signature", map.get("signature"));
request.setAttribute("appId", appId);
return new ModelAndView("/weixin/device/b_chat_s_anniu");
}
注意第12行的WeixinUtil.sign方法,第二个参数路径一定是跳转H5界面的路径,否则在H5界面初始化微信硬件jsapi会报错。
2、获取access_token请参考http://www.vxzsk.com/28.html
3、获取getJsapiTicket方法
public static String getJsapiTicket(String access_token){
String getticket_url="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=";//接口凭据
String jsonData=HttpUtil.sendGet(getticket_url+access_token+"&type=jsapi", "utf-8", 30000);
JSONObject jsonObj = JSONObject.fromObject(jsonData);
String errcode = jsonObj.getString("errcode");
String ticket = null;
if(errcode.equals("0")){
ticket = jsonObj.getString("ticket");
}
return ticket;
}
HttpUtil.sendGet方法 请参考http://www.vxzsk.com/doc/25.html
4、WeixinUtil.sign方法获取timestamp,nonceStr,signature三个参数方法
/***
* 获取界面调用jsapi的所需参数
* @param jsapi_ticket 凭据
* @param url 界面请求地址
* @return
V型知识库 www.vxzsk.com
*/
public static Map<String, String> sign(String jsapi_ticket, String url) {
Map<String, String> ret = new HashMap<String, String>();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
System.out.println(string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
以上便是跳转H5界面的controller方法,跳转到b_chat_s_anniu.jsp界面的代码如下,先来个分割线
1、jsp界面引入微信硬件jsapi的js库,jquery库
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.1.0.js"> </script>
2、
<body></body>之间的html代码
<!--标题行-->
<h2 style="color: white;background-color: green;text-align: center;background-position: center;">蓝牙设备</h2>
<div class="page">
<div class="bd spacing">
<div class="weui_cells weui_cells_form">
<div class="weui_cell">
<div class="weui_cell_hd"><label class="weui_label" style="width: auto;">当前设备: </label></div>
<div class="weui_cell_bd weui_cell_primary">
<label id="lbdeviceid" class="weui_label" style="width: auto;"></label>
</div>
</div>
<div class="weui_cell">
<div class="weui_cell_hd"><label class="weui_label" style="width: auto;">状态信息: </label></div>
<div class="weui_cell_bd weui_cell_primary">
<label id="lbInfo" class="weui_label" style="width: auto;"></label>
</div>
</div>
<div class="weui_cell" >
<div class="weui_cell_hd"><label class="weui_label">日志: </label></div>
<div class="weui_cell_bd weui_cell_primary">
<textarea id="logtext" class="weui_textarea" placeholder="日志" rows="5"></textarea>
</div>
</div>
</div>
<div class="weui_btn_area weui">
<button class="weui_btn weui_btn weui_btn_warn" id="CallGetWXrefresh">获取设备</button><br>
</div>
</div>
<div class="weui_dialog_alert" id="Mydialog" style="display: none;">
<div class="weui_mask"></div>
<div class="weui_dialog">
<div class="weui_dialog_hd" id="dialogTitle"><strong class="weui_dialog_title">着急啦</strong></div>
<div class="weui_dialog_bd" id="dialogContent">亲,使用本功能,请先打开手机蓝牙!</div>
<div class="weui_dialog_ft">
<a href="#" class="weui_btn_dialog primary">确定</a>
</div>
</div>
</div>
<!--BEGIN toast-->
<div id="toast" style="display: none;">
<div class="weui_mask_transparent"></div>
<div class="weui_toast">
<i class="weui_icon_toast"></i>
<p class="weui_toast_content" id="toast_msg">已完成</p>
</div>
</div>
<!--end toast-->
<!-- loading toast -->
<div id="loadingToast" class="weui_loading_toast" style="display:none;">
<div class="weui_mask_transparent"></div>
<div class="weui_toast">
<div class="weui_loading">
<div class="weui_loading_leaf weui_loading_leaf_0"></div>
<div class="weui_loading_leaf weui_loading_leaf_1"></div>
<div class="weui_loading_leaf weui_loading_leaf_2"></div>
<div class="weui_loading_leaf weui_loading_leaf_3"></div>
<div class="weui_loading_leaf weui_loading_leaf_4"></div>
<div class="weui_loading_leaf weui_loading_leaf_5"></div>
<div class="weui_loading_leaf weui_loading_leaf_6"></div>
<div class="weui_loading_leaf weui_loading_leaf_7"></div>
<div class="weui_loading_leaf weui_loading_leaf_8"></div>
<div class="weui_loading_leaf weui_loading_leaf_9"></div>
<div class="weui_loading_leaf weui_loading_leaf_10"></div>
<div class="weui_loading_leaf weui_loading_leaf_11"></div>
</div>
<p class="weui_toast_content" id="loading_toast_msg">数据加载中</p>
</div>
</div>
<!-- End loading toast -->
<!--BEGIN dialog1-->
<div class="weui_dialog_confirm" id="dialog1" style="display: none;">
<div class="weui_mask"></div>
<div class="weui_dialog">
<div class="weui_dialog_hd"><strong class="weui_dialog_title">弹窗标题</strong></div>
<div class="weui_dialog_bd">自定义弹窗内容,居左对齐显示,告知需要确认的信息等</div>
<div class="weui_dialog_ft">
<a href="javascript:;" class="weui_btn_dialog default" id="qxBtn">取消</a>
<a href="javascript:;" class="weui_btn_dialog primary" id="okBtn">确定</a>
</div>
</div>
</div>
<!--END dialog1-->
<!--BEGIN dialog2-->
<div class="weui_dialog_alert" id="dialog2" style="display: none;">
<div class="weui_mask"></div>
<div class="weui_dialog">
<div class="weui_dialog_hd"><strong class="weui_dialog_title">弹窗标题</strong></div>
<div class="weui_dialog_bd">弹窗内容,告知当前页面信息等</div>
<div class="weui_dialog_ft">
<a href="javascript:;" class="weui_btn_dialog primary">确定</a>
</div>
</div>
</div>
<!--END dialog2-->
</div>
<div id="myparams" style="display: none">
<span id="timestamp">${timestamp }</span>
<span id="nonceStr">${nonceStr }</span>
<span id="signature">${signature }</span>
<span id="appId">${appId }</span>
</div>
上述html最后四行代码就是我们在controller中存储在request对象中的四个参数
3、重点来了,初始化微信硬件jsapi库,代码如下
jQuery(document).ready(function(){
//初始化库
loadXMLDoc();
//初始化库结束
//点击获取设备按钮的函数 开始
$("#CallGetWXrefresh").on("click",function(e){
//1. 打开微信设备
my_openWXDeviceLib();
//2. 获取设备信息
my_getWXDeviceInfos();
});
//点击获取设备按钮的函数 结束
});
loadXMLDoc();方法是初始化微信硬件jsapi库,后面方法是给"获取设备"按钮绑定一个点击事件
1)、loadXMLDoc();方法代码
//初始化 微信硬件jsapi库 V型知识库 www.vxzsk.com
function loadXMLDoc()
{
var appId =jQuery("#appId").text();
var timestamp=jQuery("#timestamp").text();
var nonceStr =jQuery("#nonceStr").text();
var signature=jQuery("#signature").text();
wx.config({
beta: true,
debug: true,// 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: appId,
timestamp: timestamp,
nonceStr: nonceStr,
signature: signature,
jsApiList: [
'openWXDeviceLib',
'closeWXDeviceLib',
'getWXDeviceInfos',
'getWXDeviceBindTicket',
'getWXDeviceUnbindTicket',
'startScanWXDevice',
'stopScanWXDevice',
'connectWXDevice',
'disconnectWXDevice',
'sendDataToWXDevice',
'onWXDeviceBindStateChange',
'onWXDeviceStateChange',
'onScanWXDeviceResult',
'onReceiveDataFromWXDevice',
'onWXDeviceBluetoothStateChange',
]
});
alert("初始化库结束");
}
此方法需要四个参数,我们已经在controller中获取并存放到request对象中了。
2)、打开设备方法my_openWXDeviceLib()代码
function my_openWXDeviceLib(){
var x=0;
WeixinJSBridge.invoke('openWXDeviceLib', {},
function(res){
mlog("打开设备返回:"+res.err_msg);
if(res.err_msg=='openWXDeviceLib:ok')
{
if(res.bluetoothState=='off')
{
showdialog("太着急啦","亲,使用前请先打开手机蓝牙!");
$("#lbInfo").innerHTML="1.请打开手机蓝牙";
$("#lbInfo").css({color:"red"});
x=1;
isOver();
};
if(res.bluetoothState=='unauthorized')
{
showdialog("出错啦","亲,请授权微信蓝牙功能并打开蓝牙!");
$("#lbInfo").html("1.请授权蓝牙功能");
$("#lbInfo").css({color:"red"});
x=1;
isOver();
};
if(res.bluetoothState=='on')
{
//showdialog("太着急啦","亲,请查看您的设备是否打开!");
$("#lbInfo").html("1.蓝牙已打开,未找到设备");
$("#lbInfo").css({color:"red"});
//$("#lbInfo").attr(("style", "background-color:#000");
x=0;
//isOver();
};
}
else
{
$("#lbInfo").html("1.微信蓝牙打开失败");
x=1;
showdialog("微信蓝牙状态","亲,请授权微信蓝牙功能并打开蓝牙!");
}
});
return x; //0表示成功 1表示失败
}
3)、获取设备信息方法my_getWXDeviceInfos代码
function my_getWXDeviceInfos(){
WeixinJSBridge.invoke('getWXDeviceInfos', {}, function(res){
var len=res.deviceInfos.length; //绑定设备总数量
for(i=0; i<=len-1;i++)
{
//alert(i + ' ' + res.deviceInfos[i].deviceId + ' ' +res.deviceInfos[i].state);
if(res.deviceInfos[i].state==="connected")
{
$("#lbdeviceid").html(res.deviceInfos[i].deviceId);
C_DEVICEID = res.deviceInfos[i].deviceId;
$("#lbInfo").html("2.设备已成功连接");
$("#lbInfo").css({color:"green"});
break;
}
}
});
return;
}
4)、日志输出方法
//打印日志
function mlog(m){
var log=$('#logtext').val();
//log=log+m;
log = m;
$('#logtext').val(log);
}
好了,至此代码都已经写玩了,咱们来看看效果吧。
第一、打开手机蓝牙并登录微信,扫描设备的二维码,具体如何生成设备二维码和授权设备请参考左上角菜单中的其它章节。
第二,绑定设备-进入公众号,然后我们可以看到公众号头部已经有"已连接1个设备或未连接"
第三、点击我们上面所说的controller方法,进入H5界面
第四、点击获取设备按钮
如图所示,当前设备的deviceid已经被输出来了, 并且打开设备的日志也被打印输出。