首页 论坛 嵌入式软件专区 蓝牙BLE 深入浅出解读BBC Micro:bit-web Bluetooth上手实践(2)

发帖 回复

[原创] 深入浅出解读BBC Micro:bit-web Bluetooth上手实践(2)
748 查看
5 回复
 楼主 | 发布于 2019-08-20 | 只看楼主
分享到:


今天我们来看看怎样建立web Bluetooth页面并通过该页面建立蓝牙连接和发现服务器端的服务和属性 


如上回提到的,要在自己的PC侧(WINDOWS)建立服务器,大家需要在设备管理器-管理工具中把Internet Information Service(IIS)服务器打开,在网站目录下创建一个网站名,并通过编辑网站选项卡设定指向该站点的网页的物理路径。通常这个物理路径是你放置主页html文件(比方说index.html)的目录,比方你配置了8080端口,那么在Chrome浏览器中键入https://localhost:8080/index.html你就应当能在本机访问这个设在本机的服务器页面了。当然,如果你觉得在本机设置有可能太烦,也可以将你的网页文件上传到公众网络服务器上,这样通过internet,你在任何配置合适的电脑的Chrome浏览器都可以打开该服务页面。(我这里将示例页面放在 https://alberthinku.github.io/大家可以点击测试)。 





页面分为四个部分 

第一部分是状态显示,分为连接状态(connected)、服务发现(services discovery completed)以及通知(Notifications)。页面默认的状态都是False也就是说三个状态都为否定,而在后面三部分的人机交互状态改变后,相关的状态值就会随之改变 

 

第二部分是设备发现和连接(Device Discovery and Connection),这个部分提供了一个按钮(discoverDevices),当鼠标点击该按钮后,网页会执行相关的蓝牙发现和连接的操作 

 

第三部分是读写操作(Reading and Writing),在与BBC MicroBit建立蓝牙连接后,点击Read Model Number按钮,web将获取蓝牙服务对应的180A名称服务的特性值并显示在网页中,而如果你点击randomise LEDs,web将随机产生一组5个32以内的随机数(microbit的LED显示有5x5阵列组成),然后通过蓝牙发送给microbit并在LED阵列中对应显示出来 

 

第四部分是通知操作(Notifications),我通过点击toggle Notificaitons按钮注册了microbit的加速度传感器的X/Y/Z三轴数据通知服务并在收到新的数据后将所收到的数据显示在图表中 

 

为了实现上面的四个部分,我们需要依次来书写Javascript和html代码。下面我们就正式开始吧 

 

第一部分是状态显示的部分。这部分仅仅是消息提供,html脚本就可以实现了,代码如下 

    <h1>Web Bluetooth try</h1>
    <h2>Status</h2>
    <table border="1">
        <tr>
            <td>
                <b>Connected</b>
            </td>
            <td>
                <b>Services Discovery Completed</b>
            </td>
            <td>
                <b>Notifications</b>
            </td>
        </tr>
        <tr>
            <td id="status_connected">false</td>
            <td id="status_discovered">false</td>
            <td id="status_notifications">false</td>
        </tr>
    </table>

注意到,这个status表格中间的三个对应状态我们分别将它命名为“status_connected” “status_discovered” “status_notifications”,这三个变量的值在初始时为false,但随着代码的演进和互动发生,三个变量的值将被相关操作改变。 

 

第二部分,也是web bluetooth的核心之一:发现和建立连接。发现操作由onclick鼠标事件触发discoverDeviceOrDisconnect()函数,该函数将引导Chrome执行蓝牙发现和连接的全过程 



    <h2>Device Discovery and Connection</h2>
    <button id="btn_scan" onclick="discoverDevicesOrDisconnect()">discoverDevices</button>
    <hr>
    <h2>Reading and Writing</h2>

那么discoverDeviceOrDisconnect()函数究竟是什么样儿的呢? 回忆一下我们在使用app来和蓝牙设备建立连接的时候是怎样的?首先当然是让系统扫描37/38/39三个低功耗蓝牙的广播信道,看看有没有在信号覆盖区域中有相关的设备在广播,并通过分析广播数据包来确认该设备的身份;第二,在获得目标设备的身份后,系统将要发起连接建立的请求--也就是以Master身份来与目标设备商定建立连接的参数(配对,时间,频道等等)以及安全规约(密钥交换),当这个连接请求被Slave设备接受并完成彼此的眼神确认后,连接建立;第三,一旦连接建立,Master(Client)将要逐一发现Slave(Server)端的所有服务以及属性,然后对于系统感兴趣的服务、属性做出标记,从而完成服务发现的流程。怎样,是不是很简单?如果你希望了解更多与蓝牙发现和建立连接相关的细节,下面提供的蓝牙BLE讨论区链接的其他文章可以帮到你,比方  

http://club.digiic.com/Forum/PostDetail/p-10926.html (Ellisys低功耗蓝牙培训视频4-连接) 

http://club.digiic.com/Forum/PostDetail/p-14513.html (Nordic低功耗蓝牙培训视频 



书归正传,下面我们就逐行看看web bluetooth的discoverDeviceOrDisconnect()以及发起和建立连接发现服务等相关函数的 JS脚本 

function discoverDevicesOrDisconnect() {
    console.log("discoverDevicesOrDisconnect");
    if (!connected) {
        discoverDevices();
    } else {
        selected_device.gatt.disconnect();
        resetUI();
    }
}

function discoverDevices() {
    console.log("discoverDevices");

    var options = {
        //acceptAllDevices: true
//Chrome蓝牙服务可以将全部蓝牙设备扫描出来,但考虑到现在家里的电器设备蓝牙功能的增多,强烈建议给出扫描选项在扫描阶段就先过滤一下 
//下面的参数从设备名称和服务UUID两方面做了过滤,提高了扫描的效率 
        filters: [{ namePrefix: 'BBC' }],
        optionalServices: [DEVICE_INFORMATION_SERVICE, ACCELEROMETER_SERVICE, LED_SERVICE]
    }

	

//下面的requestDevice API将弹出对话框,并将扫描到的符合options规则的广播中的器件 

//列出来,然后通过promise机制(navigator.bluetooth.requestDevice事实上是一 

//个promise)在用户通过鼠标点选确认或取消操作后返回操作内容device 

//然后一个带有所选器件描述属性的JSON参数对象device被传送到“then”的语句体中 

//关于Promise,大家可以自行在google或度娘搜索。我看到一些有意思的解释,在这里 

//分享一点:在处理异步逻辑时,操作系统往往需要对时间和事件做效率和优先安排,异 

//步进程函数返回后主程序告知Promise执行相关操作--与之对应的Callback机制也是处理 

//异步逻辑,但Callback是在异步进程结束后告知主程序接下来该做什么了 

//有读过Microbit Runtime的朋友一定会发现,在 

//http://club.digiic.com/Forum/PostDetail/p-11632.html 的concurrency一节中,我们也见到异 

//步程序的处理,通过不同的路径实现并发和异步,大家可以交叉验证,融汇贯通 

navigator.bluetooth.requestDevice(options) .then(device => { console.log('> Name: ' + device.name); console.log('> Id: ' + device.id); console.log('> Connected: ' + device.gatt.connected); selected_device = device; console.log(selected_device); connect();// this function will connect to the device }) .catch(error => { alert('ERROR: ' + error); console.log('ERROR: ' + error); }); } function connect() { if (connected == false) { console.log('connecting'); selected_device.gatt.connect().then( function (server) { console.log('Connected to ' + server.device.id); console.log('connected = ' + server.connected); setConnectedStatus(true); connected_server = server; selected_device.addEventListener( 'gattserverdisconnected', onDisconnected); discoverSvcsAndChars(); }, function (error) { console.log('ERROR: could not connect -' + error); alert('ERROR: could not connect -' + error); setConnectedStatus(false); } ); } } function onDisconnected() { console.log("onDisconnected"); resetUI(); } function resetUI() { setConnectedStatus(false); setDiscoveryStatus(false); setNotificationStatus(false); document.getElementById('model_number').innerHTML = ""; document.getElementById('accelerometer_data').innerHTML = ""; }
	

  //在服务发现的进程中,也需要异步处理--事件和时间并不能由单方面决定同时在service 

//发现完成后才会进入到属性的发现。所以这里看到connected.server.getPrimaryService()函 

//数也是一个promise,返回services后,在每一个services都有一个services.forEach的 

//promise来执行该service的标记(比较主函数定义的service uuid,如果有相符的service  

//uuid就把相关服务的布尔变量置为真,为后续的处理做准备),同时,在每个service下 

//都会进行characteristics的发现(过程与service类似,也是先获取该service下的char的数 

//量,然后依次发现全部的characteristic同时做好标记)。 

function discoverSvcsAndChars() { console.log("discoverSvcsAndChars server=" + connected_server); connected_server.getPrimaryServices() .then(services => { has_accelerometer_service = false; has_led_service = false; has_device_information_service = false; services_discovered = 0; service_count = services.length; console.log("Got" + service_count + " services"); services.forEach(service => { if (service.uuid == ACCELEROMETER_SERVICE) { has_accelerometer_service = true; } if (service.uuid == LED_SERVICE) { has_led_service = true; } if (service.uuid == DEVICE_INFORMATION_SERVICE) { has_device_information_service = true; } console.log('getting Characteristics for service' + service.uuid); service.getCharacteristics().then(characteriscs => { console.log('> Service: ' + service.uuid); services_discovered++; characteriscs_discovered = 0; characterisc_count = characteriscs.length; characteriscs.forEach(characterisc => { characteriscs_discovered++; console.log('>> Characteristic: ' + characterisc.uuid); if (characterisc.uuid == ACCELEROMETER_DATA) { accelerometer_data = characterisc; has_accelerometer_data = true; } if (characterisc.uuid == LED_MATRIX_STATE) { led_matrix_state = characterisc; has_led_matrix_state = true; } if (characterisc.uuid == MODEL_NUMBER_STRING) { model_number_string = characterisc; has_model_name_string = true; } if (services_discovered = service_count && characteriscs_discovered == characterisc_count) { console.log("FINISHED DISCOVERY"); setDiscoveryStatus(true); } }); }); }); }); }

由于论坛的篇幅和格式所限,这里就不再作进一步的代码展开了。但关于web Bluetooth的蓝牙发现建立连接的过程,你也可以在https://github.com/alberthinku/alberthinku.github.io/tree/master/libraries页面上获得源文件,文件名为Webblue-phaseOne.js 

 

今天的内容你是否都在自己的电脑上理解并实现了?如果是的话,恭喜你,你已经能够通过Chrome和WINDOWS发现蓝牙设备并建立连接了。下一回,我们就来看看连接建立以后我们如何来通过web使用所获得的蓝牙服务吧。 

 

<未完待续> 



(0 ) (0 )
回复 举报

回复于 2019-08-21 沙发

谢谢分享
(0 )
评论 (0) 举报

回复于 2019-08-22 2#

谢谢分享!!!!!!!!!!!
(0 )
评论 (0) 举报

回复于 2019-08-24 3#

支持下,谢谢分享!
(0 )
评论 (0) 举报

回复于 2019-08-26 4#

感谢分享!!
(0 )
评论 (0) 举报

回复于 2019-09-17 5#

谢谢分享。。。
(0 )
评论 (0) 举报
  • 发表回复
    0/3000





    举报

    请选择举报类别

    • 广告垃圾
    • 违规内容
    • 恶意灌水
    • 重复发帖

    全部板块

    返回顶部