微信小程序蓝牙温度数据采集
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
微信小程序蓝牙温度数据采集
陈拓2019/6/2-2019/6/15
0. 概述
本文是《树莓派蓝牙温度传感器》的后续文章,请按先后顺序看,该文档的网址是:https:///view/423628b3905f804d2b160b4e767f5acfa0c783c1。
本文不讲述小程序的基本知识和编程语法,需要事先熟悉。
下面本文的最后效果:
1. 开发工具安装
https:///miniprogram/dev/devtools/devtools.html
点击下载:
在稳定版下面,在Windows 64 、 Windows 32 、 macOS中选择一项下载。
2. 蓝牙通信小程序起步
●开始 > 微信web开发者工具
●用手机微信扫描登录
在电脑屏幕上打开小程序开发工具:
●点击大“+”号
项目名称:ble-data-acquisition
●填写AppID
⏹打开网页https:///
⏹用小程序账号(不是公众号账号)登录微信公众平台,进入小程序后台
如果没有账号就去申请注册。
⏹点击开发 > 开发设置就可以看到你自己的AppID(小程序ID)
在“新建项目”界面填写AppID,其他保持默认。
注意:小程序名称和图标也在这里设置。
●点击“新建”按钮
注意:微信只支持低功耗蓝牙BLE,不支持经典的蓝牙。
●熟悉开发工具的目录和文件
打开开发工具index目录:
我们可以看到最上面有一个pages(页面)目录,这个目录里面包括了小程序所有的界面,index目录是起始页面。
每个页面包括4个文件
3个必须文件:
⏹index.wxml对应index.html,主要负责本页面的界面展示以及事件的绑定等等。
⏹index.wxss对应index.css,主要负责页面的样式,与wxml文件一起使用,优
化wxml页面。
⏹index.js就是js文件,主要负责本页面的业务逻辑,包括生命周期,事件的绑
定处理,数据的初始化等等。
1个可选文件:
index.json:主要是负责本界面的基本配置,设置页面标题等功能。
json文件不是必须的一般情况下我们只需要用全局配置的 app.json文件配置就可以了。
在图中还有一个utils的目录:可以看出utils和pages文件是并列的,所以他就不代表页面的信息了,里面只有一个js文件。
这个js文件其实就是把代码模块化了,已经有的代码是将日期的转换封装好了,直接引入使用就可以了。
最后剩下一系列app的文件,app.js app.wxss app.json代表的信息和上面的pages里面的页面文件夹中的其实差不多,只是pages里面的代表的仅仅是某个界面的配置,而app 的代表的是这个项目的配置。
●找一个官方DEMO
测试wx.getBLEDeviceServices(Object object)
https:///miniprogram/dev/api/device/bluetooth/w x.getBLEDeviceServices.html
滚动到最后面:
点击“在开发工具中预览效果”,导入代码片段:
因为涉及到蓝牙硬件,所以不能用模拟器调试,下面我们把这个代码片段添加到前面创建的项目中。
图片位置
在pages下面建一个文件夹images,把图片集中放在这里。
添加页面
下面我们添加一个目录scanble和一组4个页面文件scan,我们不用去创建scan页面相关的文件scan.js、scan.wxss和scan.wxml,点击打开app.json文件就可以自动创建一个小程序空白页面。
app.json是一个数组,由pages和window组成。
找到pages数组,它里面所存放的就是一个个页面的名称了,如图:
我们只需要在Pages中加上“pages/scanble/scan”,并在前面用一个逗号隔开数组元素:
注意:pages数组里面,哪个路径在第一个,就先显示那个路径对应的界面。
保存(Ctrl+s)之后开发工具会自动帮我们创建文件夹和其中的4个scan页面文件:
app.json文件中的windows用于设置小程序的状态栏、导航条、标题、窗口背景色。
修改导航栏标题将“WeChat”改为“传感器数据采集”,保存(trl+s),模拟器界面自动刷新。
●修改scan页面代码
把导入的代码片段index页面的4个文件内容复制到相应的scan页面代码中。
●页面跳转
前面我们说过了,pages数组里面,哪个路径在第一个就先显示那个路径对应的界面,index 在第一个,所以小程序先显示页面index.wxml,我们再转到scan.wxml。
为了实现点击跳转,我们把“Hello World”改造成“开始”按钮。
修改index.wxml:
修改index.wxss:
效果:
添加事件bindtap,它网站开发中的click事件一样,都是点击时触发的事件,我们把它写在矩形框的那个view标签里面:
bindtap将事件绑定到组件上面,绑定了之后点击组件可以触发这个函数。
bindtap=“go”的意思就是,当点击绑定的view时触发一个事件,这个事件名称叫做go,我们需要去index.js文件中去编写go事件。
进入index.js文件中,找到page,在“//事件处理函数”的最后添加go函数:
与前后的函数用逗号分隔。
真机调试(iPhone、安卓手机都可以)
因为涉及到蓝牙硬件,所以不能用模拟器调试。
点击“真机调试”,或者“预览”。
如果不需要调试,只是测试,用“预览”要快一些。
用微信扫码,验证通过后还需要登录“小程序账号”补充一些信息,按提示做就行。
在“小程序账号”中可以设置和修改小程序图标和名称等信息:
我已经设置好了,点击“查看详情”:
开始真机调试后小程序会下载到你的微信中,可以反复测试:
点击我们的小程序:
点击“开始”,显示scan.wxml页面,点击“开始扫描”:
左边是苹果手机,右边是安卓手机。
扫描到3个蓝牙外围设备,第一个设备ble-temp就是我们的树莓派蓝牙设备。
当然要看到这个设备先要按照我的文章《树莓派蓝牙温度传感器》,将树莓派温度传感器运行起来:https:///view/423628b3905f804d2b160b4e767f5acfa0c783c1。
3. 扫描控制,获取数据
为避免占用过多篇幅,下面只说明要修改的代码,源代码请看官方DEMO。
3.1 在scan.wxss中添加
.scan_btn{
width:100%;
position:fixed;
bottom:30rpx;
}
.btn{
margin-top:30rpx;
width:450rpx;
background:#14a1fd;
color:#fff;
border-radius:70rpx;
}
3.2 修改scan.wxml
<!--pages/scanble/scan.wxml-->
<view class="devices_summary">已发现 {{devices.length}} 个外围设备:</view>
<scroll-view class="device_list"scroll-y scroll-with-animation>
<view wx:for="{{devices}}"wx:key="index"data-device-id="{{item.deviceId}}"
bindtap="displayData"
class="device_item"
hover-class="device_item_hover">
<view
style="display:flex;flex-direction:row;justify-content:space-between;align-items:center;font-s ize:12px;color:#333;">
<text>{{}}</text>
<text style="font-size:10px;color:#14a1fd;">{{item.deviceId}}</text>
<text>{{item.RSSI}}dBm</text>
</view>
<view style='font-size:14px'>
<text>{{item.date}}</text>
<text>{{item.temp}}C</text>
</view>
</view>
</scroll-view>
<view class='scan_btn'>
<button class='btn' bindtap='scanCtrl'>{{scaningStr}}</button>
</view>
单引号,双引号
双引号会搜索引号内的内容是不是有变量,有则输出其值,没有则输出原有内容。
所以输出纯字符串的时候用单引号比双引号效率高,因为省去检索的过程。
3.2 扫描控制
●在scan.wxml中
bindtap='scanCtrl'
●在scan.js中添加点击事件响应函数
// 开始/停止扫描
scanCtrl(e) {
if (this.data.scaningStr === '停止扫描') {
this.stopBluetoothDevicesDiscovery() // 停止扫描
} else {
this.openBluetoothAdapter()
}
},
●停止扫描
stopBluetoothDevicesDiscovery() {
this._discoveryStarted = false
wx.stopBluetoothDevicesDiscovery()
this.data.scaningStr = '开始扫描'
this.setData({
scaningStr: this.data.scaningStr
})
},
●开始蓝牙设备扫描
startBluetoothDevicesDiscovery() {
this.data.scaningStr = '停止扫描'
this.setData({
scaningStr: this.data.scaningStr
})
if (this._discoveryStarted) {
return
}
this._discoveryStarted = true
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: (res) => {
//console.log('startBluetoothDevicesDiscovery success', res)
this.onBluetoothDeviceFound()
},
})
发现了蓝牙设备
onBluetoothDeviceFound() {
wx.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
if (! && !device.localName) {
return
}
if ( != 'ble-temp' && device.localName != 'ble-temp') {
return// 过滤名字不是 ble-temp 的设备
}
const objDevice = {} // 重新构造一个device,添加温度
objDevice.deviceId = device.deviceId
objDevice.localName = device.localName
=
objDevice.RSSI = device.RSSI
let dateSec = Date.now() // 取手机系统时间秒
let date = new Date(dateSec) // 取手机系统时间
objDevice.date = util.formatTime(date)
objDevice.advertisData = device.advertisData
let adDataStr = ab2hex(device.advertisData)
console.log('广播包数据:' + adDataStr)
objDevice.temp = getTemp(adDataStr)
console.log('温度:' + objDevice.temp)
let foundDevices = this.data.devices
let idx = inArray(foundDevices, 'deviceId', device.deviceId) // 找到当前deviceId对应的i
if (idx === -1) {
tempData = tempData + objDevice.deviceId + dateSec + objDevice.temp this.data.devices.push(objDevice) // 如果this.data.devices没有找当前的deviceId,就添加一个新的
} else {
if (this.data.devices[idx].temp != objDevice.temp) { // 不记录重复的数据 tempData = tempData + objDevice.deviceId + dateSec + objDevice.temp }
this.data.devices[idx] = objDevice // 如果在inArray函数中找到了当前的deviceId,就更新
}
console.log(温度数据:' + tempData)
this.setData({
devices: this.data.devices
})
})
})
添加函数和变量
注意,根据Page({来判断下面的函数和变量应该写在什么位置。
// 从广播包中取温度数据
function getTemp(adDataStr) {
var temp = adDataStr.substring(4, adDataStr.length)
var temperature = temp[1] + temp[3] + '.' + temp[5] + temp[7] // 去掉前导0,加小数点return temperature
}
// 声明变量
var tempData = ''
const util = require('../../utils/util.js')
Page({
data: {
devices: [],
connected: false,
chs: [],
scaningStr: '开始扫描',
},
// 开始/停止扫描
scanCtrl(e) {
……
请对照官网DEMO源代码看。
3.3 真机调试
为了看到程序运行时的中间结果,我们用真机调试一下:
对于简单的修改,如果不需要看中间结果,可以用预览进行测试,比真机调试节省时间。
“真机调试”窗口:
console.log('广播包数据:' + adDataStr)
console.log('温度:' + objDevice.temp)
console.log('tempData: ' + tempData)
这3条调试语句将程序运行的中间结果显示出来。
手机显示:
左边是苹果手机,右边是安卓手机。
注意,苹果手机显示的是设备ID,安卓手机显示MAC。
4. 历史数据展示
我们已经将数据缓存到了变量tempData中,为了便于观察数据的变化,下面我们将tempData中的数据用列表和曲线显示。
4.1 创建一个新的页面display
这个操作我们已经很熟悉了,在app.json中添加一行"pages/displays/display"
4.2 写页面代码
display.wxss
.back-img{
width:10%;
background:none;
margin-left:5px;
}
.canvas {
width:100%;
height:250px;
}
.device_list {
margin:5rpx5rpx;
margin-top:0;
border:1rpx solid#EEE;
border-radius:5rpx;
width:100%;
height:500rpx;
}
/*定义滚动条高宽及背景高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar
{
width:10px;
height:10px;
background-color:#ffffff;
}
/*定义滚动条轨道内阴影+圆角*/
::-webkit-scrollbar-track
{
-webkit-box-shadow:inset0010px rgba(0,0,0,0.3);
border-radius:10px;
background-color:yellow;
}
/*定义滑块内阴影+圆角*/
::-webkit-scrollbar-thumb
{
border-radius:10px;
-webkit-box-shadow:inset0010px rgba(0,0,0,.3);
background-color:#ff5500;
}
display.wxml
<view
style="height:100rpx;display:flex;flex-direction:row;justify-content:space-between;align-items :center;color:#eee;background-color:#38B0DE;">
<image class='back-img'mode='widthFix'src='../images/backs.jpg'bindtap="back"></image>
<text style="font-weight:bold;font-size:12px;margin-right:10px;">{{deviceId}}</text>
</view>
<view>
<canvas canvas-id="lineCanvas"disable-scroll="true"class="canvas"
bindtouchstart="touchstart"bindtouchmove="touchmove"bindtouchend="touchend"></canvas>
</view>
<view
style="height:50rpx;display:flex;flex-direction:row;justify-content:space-between;align-items: center;color:#eee;background-color:#38B0DE;">
<text></text>
<text style="font-weight:bold;font-size:12px;margin-right:10px;">共 {{amount}} 条记录</text> </view>
<scroll-view class="device_list"scroll-y='true'scroll-into-view='100'>
<view wx:for="{{idata}}"wx:key="index"data-device-id="{{item.deviceId}}">
<view class="content">
<view style="font-size:14px; color:#333;">
<text>{{item.samplingTime}}</text>
<text>{{item.temp}}C</text>
</view>
</view>
</view>
</scroll-view>
●微信小程序wx-charts图表插件
显示曲线需要下载wx-charts,网址https:///xiaolin3303/wx-charts,解压后,把dist里面的wxcharts.js或者wxcharts-min.js放在小程序的文件夹里,在当前页面引用文件:
const wxCharts = require('../../utils/wxcharts-min.js')
在网址上看README.md,有详细的使用说明。
●带参数可返回页面跳转
点击扫描到的一个设备,scan.wxml发送点击事件:
bindtap="displayData"
在scan.js中写响应代码:
前面我们用:
wx.redirectTo({
url: '../scanble/scan',
})
进行页面跳转。
跳转还有一个方法叫做wx.navigateTo,与wx.redirectTo的区别是:wx.navigateTo跳转到一个应用内的某个页面,还保留着当前页面,可以用wx.navigateBack 返回,wx.navigateTo还可以带参数。
wx.redirectTo关闭当前页面,跳转到另外一个页面。
在scan.js中我们用wx.navigateTo:
// 数据展示
displayData(e) {
this.stopBluetoothDevicesDiscovery() // 停止扫描
const deviceId = e.currentTarget.dataset.deviceId
// 传递的参数可返回跳转
wx.navigateTo({
url: '../displays/display?ideviceId=' + deviceId + '&tempData=' + tempData
})
},
用wx.redirectTo跳转会关闭当前页面,跳转页面后不能返回上一页,这样我们就看不到其他设备的数据了。
所以我们改用wx.navigateTo函数,它在跳转时保留当前页面,使用wx.navigateBack可以返回原页面。
下面的语句传递了2个参数deviceId和tempData。
url: '../displays/display?ideviceId=' + deviceId + '&tempData=' + tempData
●display.js
const util = require('../../utils/util.js')
const wxCharts = require('../../utils/wxcharts-min.js')
var lineChart = null
const recordLeni = 54// 苹果手机记录长度
const recordLena = 35// 安卓手机记录长度
const deviceIdLeni = 36// 苹果手机的deviceId长度
const deviceIdLena = 17// 安卓手机的deviceId长度
var recordCount = 0// 记录计数
var tempData = ''// 原始数据
var temp_min = 100
var temp_max = -60
Page({
/**
* 页面的初始数据
*/
data: {
deviceId: '',
idata: [],
amount: 0,
},
touchstart: function (e) {
//console.log(lineChart.getCurrentDataIndex(e))
lineChart.showToolTip(e, {
background: '#1874CD',
format: function (item, category) {
return category + ' ' + + ':' + item.data }
})
//(e)
lineChart.scrollStart(e);//开始滚动
},
touchmove: function (e) {
//(e)
lineChart.scroll(e)
},
touchend: function (e) {
//(e)
lineChart.scrollEnd(e)
},
// 创建图表数据
createChartData: function () {
var categories = []
var data1 = []
let idataLength = this.data.idata.length
//console.log('idataLength: ' + idataLength)
for (var i = 0; i < idataLength; i++) {
categories.push(this.data.idata[i].samplingTime)
data1.push(this.data.idata[i].temp)
}
return {
categories: categories,
temp_data: data1,
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 页面初始化 options为页面跳转所带来的参数
const ideviceId = options.ideviceId
tempData = options.tempData
let recordLen = 0
let deviceIdLen = 0
let brand = wx.getSystemInfoSync().brand
if (brand.indexOf('iPhone') >= 0) { // 苹果手机
recordLen = recordLeni
deviceIdLen = deviceIdLeni
} else {
recordLen = recordLena
deviceIdLen = deviceIdLena
}
let recordNum = tempData.length / recordLen
recordCount = 0
this.data.idata = []
// 将该deviceId的数据过滤出来
for (let i = 0; i < recordNum; i++ ) {
let begin = i * recordLen
let end = begin + recordLen
let record = tempData.substring(begin, end)
let deviceId = record.substring(0, deviceIdLen)
if (deviceId == ideviceId) {
const obj = {} // 构造一个对象,添加采样时间,温度
let dateSec = parseInt(record.substring(deviceIdLen, recordLen-5)) let date = new Date(dateSec) // 取手机系统时间
obj.samplingTime = util.formatTime(date)
obj.temp = record.substring(recordLen - 5, recordLen)
this.data.idata.push(obj)
if (temp_min > obj.temp) {
temp_min = obj.temp
}
if (temp_max < obj.temp) {
temp_max = obj.temp
}
recordCount++
}
}
this.setData({
deviceId: ideviceId,
amount: recordCount,
idata: this.data.idata
})
// 画曲线
let windowWidth = 320; // 用于设置 wxCharts 宽度try {
var res = wx.getSystemInfoSync();
windowWidth = res.windowWidth;
} catch (e) {
console.error('getSystemInfoSync failed!'); }
let chartData = this.createChartData()
lineChart = new wxCharts({
canvasId: 'lineCanvas',
type: 'line',
categories: chartData.categories,
animation: false,
// background: '#f5f5f5',
series: [{
name: '温度',
data: chartData.temp_data,
format: function (val, name) {
return val + '℃';
}
}],
xAxis: {
disableGrid: false
},
yAxis: {
title: '数值',
format: function (val) { // 返回数值
return val.toFixed(2);
},
min: temp_min, // 最小值
max: temp_max, //最大值
//gridColor: '#D8D8D8',
},
enableScroll: true, //配置该折线图可滑动
width: windowWidth,
height: 250,
dataLabel: false,
dataPointShape: true,
extra: {
lineStyle: 'curve'//曲线
}
});
},
// 返回上一层
back(e) {
wx.navigateBack({
delta: 2
})
},
})
预览
左边是苹果手机手机的效果,右边是安卓手机的效果。
数据列表可以上线滑动,曲线可以左右滑动。
参考文献
1.树莓派介绍https:///p/1bac80afc502
2.电脑连接树莓派3B+ https:///p/cbfb28d3a0fb
3.树莓派ZeroW+温度传感器DS18B20
https:///chentuo_1/p/9267528.html
4.《树莓派蓝牙温度传感器》
https:///view/423628b3905f804d2b160b4e767f5acfa0c783c1 5.树莓派官方网站
https:///products/raspberry-pi-zero-w/
6.蓝牙规范V4.0(BLUETOOTH SPECIFICATION Version 4.0)Core_V4.0.zip:
https:///docman/handlers/downloaddoc.ashx?doc_id=229 737。