1.軟件部分介紹
微信小程序是
一種新的應用,用戶不需要下載應用只用通過掃二維碼或者打開鏈接就能使用,使用完后不需要卸載,直接關閉就行了。微信在2017年初推出微信小程序開發環境。任何企業,媒體,個人都可以注冊開發。是一種全新的 開發模式。微信也因此受到許多程序員的一致好評,尤其是微信小程序的雲開發,提供大量數據處理接口,讓初學者也可以很快入手。不需要后端數據庫的支持,自己一個人就可以開發前端和后台。
微信小程序為藍牙模塊提供了18個API。其中低功率藍牙9個,傳統藍牙9個。本次設計使用了其中的9個接口:
(1) openBluetoothAdapter,這個API用來初始化藍牙適配器;
(2) startBluetoothDevicesDiscovery,開始搜索藍牙設備;
(3) onBluetoothDeviceFound,判斷搜索到的藍牙設備的信號強度;
(4) createBLEConnection,連接搜索到的藍牙設備;
(5) stopBluetoothDevicesDiscovery,關閉搜索藍牙設備;
(6) getBLEDeviceServices,獲取藍牙的deviceId;
(7) getBLEDeviceCharacteristics,獲取藍牙設備服務的所有特征值;
(8) notycharacteristicsId,啟用低功耗藍牙特征值的notify功能;
(9) writeBLECharacteristicValue,通過微信小程序向藍牙模塊發送命令。
js源代碼
Page({ /** * 頁面的初始數據 */ data: { connect: false, send_hex: false, send_string: true, send_string_val: 'Hex', recv_string: true, recv_string_val: 'Hex', recv_value: '', send_number: 0, recv_number: 0, recv_hex: true, wendu: 30, yanwu: 60 }, /*** 生命周期函數--監聽頁面加載 */ onLoad: function (options) { wx.stopBluetoothDevicesDiscovery({ success: function (res) { console.log('停止搜索設備', res) } }) console.log(options); this.setData({ deviceId: options.id, deviceName: options.name }); console.log('設備的ID', this.data.deviceId); }, /*** 生命周期函數--監聽頁面顯示 */ onShow: function () { wx.stopBluetoothDevicesDiscovery({ success: function (res) { console.log('停止搜索設備', res) } }) var that = this; /* 連接中動畫 */ wx.showLoading({ title: '連接中...', }); /* 開始連接藍牙設備 */ wx.createBLEConnection({ deviceId: that.data.deviceId, success: function (res) { console.log('連接成功', res); wx.hideLoading(); /* 獲取設備的服務UUID */ wx.getBLEDeviceServices({ deviceId: that.data.deviceId, success: function (service) { that.setData({ serviceId: "0000FFE0-0000-1000-8000-00805F9B34FB" //確定需要的服務UUID }); console.log('需要的服務UUID', that.data.serviceId) that.Characteristics(); //調用獲取特征值函數 }, }); that.setData({ connect: true }) }, }) }, Characteristics: function () { var that = this; var device_characteristics = []; var characteristics_uuid = {}; wx.getBLEDeviceCharacteristics({ deviceId: that.data.deviceId, serviceId: that.data.serviceId, success: function (res) { var characteristics = res.characteristics; //獲取到所有特征值 var characteristics_length = characteristics.length; //獲取到特征值數組的長度 console.log('獲取到特征值', characteristics); console.log('獲取到特征值數組長度', characteristics_length); that.setData({ notycharacteristicsId: "0000FFE1-0000-1000-8000-00805F9B34FB", //需確定要的使能UUID characteristicsId: "0000FFE1-0000-1000-8000-00805F9B34FB" //暫時確定的寫入UUID }); console.log('使能characteristicsId', that.data.notycharacteristicsId); console.log('寫入characteristicsId', that.data.characteristicsId); that.notycharacteristicsId(); //使能事件 }, }) }, /* 使能函數 */ notycharacteristicsId: function () { var that = this; var recv_value_ascii = ""; var string_value = ""; var recve_value = ""; wx.notifyBLECharacteristicValueChange({ deviceId: that.data.deviceId, serviceId: that.data.serviceId, characteristicId: that.data.notycharacteristicsId, state: true, success: function (res) { console.log('使能成功', res); /* 設備返回值 */ wx.onBLECharacteristicValueChange(function (res) { var length_hex = []; var turn_back = ""; var result = res.value; var hex = that.buf2hex(result); console.log('返回的值', hex); if (that.data.recv_string == true) { /* 成功接收到的值的展示 */ that.setData({ recv_value: that.data.recv_value + hex }); /* 接收成功的值的字節 */ var recv_number_1 = that.data.recv_number + hex.length / 2; var recv_number = Math.round(recv_number_1); that.setData({ recv_number: recv_number }); } else { console.log('設備返回來的值', hex); var f_hex = hex; var length_soy = f_hex.length / 2; var length = Math.round(length_soy); for (var i = 0; i < length; i++) { var hex_spalit = f_hex.slice(0, 2); length_hex.push(hex_spalit); f_hex = f_hex.substring(2); } console.log('length_hex', length_hex); for (var j = 0; j < length_hex.length; j++) { var integar = length_hex[j]; //十六進制 recve_value = parseInt(integar, 16); //十進制 console.log('recve_value', recve_value); turn_back = turn_back + String.fromCharCode(recve_value); console.log('turn_back', turn_back); } console.log('最終轉回來的值', turn_back) var recv_number_1 = that.data.recv_number + turn_back.length; var recv_number = Math.round(recv_number_1); that.setData({ recv_number: recv_number, recv_value: that.data.recv_value + turn_back }) } }); }, fail: function (res) { console.log('使能失敗', res); } }) }, /* 斷開連接 */ DisConnectTap: function () { var that = this; wx.closeBLEConnection({ deviceId: that.data.deviceId, success: function (res) { console.log('斷開設備連接', res); wx.reLaunch({ url: '../index/index', }) } }); }, /*** 生命周期函數--監聽頁面卸載 */ onUnload: function () { var that = this; wx.closeBLEConnection({ deviceId: that.data.deviceId, success: function (res) { console.log('斷開設備連接', res); } }); }, /* 清除Recv Bytes */ CleanNumberRecv: function () { this.setData({ recv_number: 0 }) }, /* ArrayBuffer類型數據轉為16進制字符串 */ buf2hex: function (buffer) { // buffer is an ArrayBuffer var hexArr = Array.prototype.map.call( new Uint8Array(buffer), function (bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join(''); }, switch1Change: function (e) { var that = this; let buffer = new ArrayBuffer(1) let dataView = new DataView(buffer) if (e.detail.value) { dataView.setUint8(0, 0) } else { dataView.setUint8(0, 1) } wx.writeBLECharacteristicValue({ deviceId: that.data.deviceId, serviceId: that.data.serviceId, characteristicId: that.data.characteristicsId, value: buffer, success: function (res) { console.log('數據發送成功', res); console.log(buffer); }, fail: function (res) { console.log('調用失敗', res); /* 調用失敗時,再次調用 */ wx.writeBLECharacteristicValue({ deviceId: that.data.deviceId, serviceId: that.data.serviceId, characteristicId: that.data.characteristicsId, value: buffer, success: function (res) { console.log('第2次數據發送成功', res); } }) } }) }, switch1Change1: function (e) { var that = this; let buffer = new ArrayBuffer(1) let dataView = new DataView(buffer) if (e.detail.value) { dataView.setUint8(0, 2) } else { dataView.setUint8(0, 3) } wx.writeBLECharacteristicValue({ deviceId: that.data.deviceId, serviceId: that.data.serviceId, characteristicId: that.data.characteristicsId, value: buffer, success: function (res) { console.log('數據發送成功', res); console.log(buffer); }, fail: function (res) { console.log('調用失敗', res); /* 調用失敗時,再次調用 */ wx.writeBLECharacteristicValue({ deviceId: that.data.deviceId, serviceId: that.data.serviceId, characteristicId: that.data.characteristicsId, value: buffer, success: function (res) { console.log('第2次數據發送成功', res); } }) } }) }, add: function (e) { var id = e.target.id; if (this.data[id] > 98) { wx.showToast({ title: '已超過最大數值', icon: 'loading', duration: 2000 }) return; } this.setData({ [id]: +this.data[id] + 1 }); this.numbers(id) }, lessen: function (e) { var id = e.target.id; if (this.data[id] < 1) { wx.showToast({ title: '已小於最小數值', icon: 'loading', duration: 2000 }) return; } this.setData({ [id]: +this.data[id] - 1 }); this.numbers(id) }, changeVal: function (e) { var id = e.target.id; if (e.detail.value < 1 || e.detail.value > 100) { wx.showToast({ title: '請輸入有效數值', icon: 'loading', duration: 2000 }) return; } this.setData({ [id]: e.detail.value }); this.numbers(id) }, numbers: function (id) { var that = this; var number = '9'; let buffer = new ArrayBuffer(1) let dataView = new DataView(buffer) console.log(id) if (id == 'wendu') { number = '8' + that.data[id]; dataView.setUint8(0, 8) } else { number = number + that.data[id]; dataView.setUint8(0, number) } wx.writeBLECharacteristicValue({ deviceId: that.data.deviceId, serviceId: that.data.serviceId, characteristicId: that.data.characteristicsId, value: buffer, success: function (res) { console.log('數據發送成功', res); console.log(buffer); } }) } })
wxss源代碼
.connect_box { width: 100%; height: 30px; line-height: 30px; font-size: 32rpx; color: #666; font-family: "Microsoft YaHei"; } .connect_device_name{ float: left; padding-left: 10px; color: #39beff; } .connect_state { float: right; padding-right: 10px; text-decoration: underline; color: #39beff; } .fan{ width: 2rem; height: 2rem; vertical-align: middle; margin-left: 2.25rem; margin-right: 0.25rem; } .water{ width: 2.5rem; height: 2rem; vertical-align: middle; margin-left: 2rem; } .name{ display: inline-block; width: 22%; margin-left: 1.5rem; font-size: 0.9rem; } .key{ float: right; margin-right: 2rem; margin-top: 0.2rem; } .detail_box{ padding: 1.5rem 0; border-bottom: 1px solid #ccc; } .num { display: inline-block; width: 45%; text-align: right; vertical-align: middle; } .num input { display: inline-block; width: 2rem; text-align: center; border: 1px solid #f2f2f2; border-left: none; border-right: none; color: #a2a2a2; } .num text { display: inline-block; width: 1.4rem; height: 1.4rem; line-height: 1.4rem; text-align: center; border: 1px solid #f2f2f2; vertical-align: top; color: #dcdcdc; } .wendu{ width:1.9rem; height:2rem; vertical-align:middle; margin-left:2.3rem; margin-right:.3rem; }
wxml源代碼
<view class="connect_box"> <text class='connect_device_name'>{{deviceName}}</text> <text wx:if="{{connect}}" class="connect_state" catchtap="DisConnectTap">已連接</text> <text wx:else class="connect_state">未連接</text> </view> <view > <view class="detail_box"> <image src='../../images/airFan.png' class="fan"></image> <view class='name'>風扇</view> <switch bindchange="switch1Change" class='key' /> </view> <view class="detail_box"> <image src='../../images/waterPump.png' class="water"></image> <view class='name'>水泵</view> <switch bindchange="switch1Change1" class='key' /> </view> <view class="detail_box"> <image src='../../images/temperature.png' class="wendu"></image> <view class='name'>溫度閥值</view> <view class='num'> <text style='border-radius: 3px 0 0 3px;' id='wendu' bindtap='lessen'>-</text> <input type='number' value='{{wendu}}' name='piece' id='wendu' bindblur="changeVal" /> <text style='border-radius: 0 3px 3px 0;' id='wendu' bindtap='add'>+</text> </view> </view> <view class="detail_box"> <image src='../../images/smog.png' class="water"></image> <view class='name'>煙霧閥值</view> <view class='num'> <text style='border-radius: 3px 0 0 3px;' id='yanwu' bindtap='lessen'>-</text> <input type='number' value='{{yanwu}}' name='piece' id='yanwu' bindblur="changeVal" /> <text style='border-radius: 0 3px 3px 0;' id='yanwu' bindtap='add'>+</text> </view> </view> </view>
微信小程序展示頁面
微信小程序不能在電腦上模擬,智能用手機操作,我們需要用手機打開我們的微信小程序。首先如果手機藍牙沒有打開回提醒打開藍牙重新加載。如果手機藍牙打開了就會去搜索附近的藍牙模塊,搜索到自己的低功率藍牙,點擊就可以連接到自己的藍牙。我們就到了控制頁面。
我們可以通過微信小程序風扇和水泵。點擊開關時會調用writeBLECharacteristicValue接口通過藍牙模塊給單片機發送指令,控制單片機上的風扇和水泵等硬件設備。
2.硬件設備介紹
硬件部分主要介紹單片機、低功率藍牙、風扇和水泵。單片機用什么型號的都行,都能與藍牙模塊正常通信,收發數據。低功率藍牙主要優點是功率低,壽命長,價格便宜。多用於硬件連接上位機軟件。風扇和水泵是外接設備,由單片機控制。
1.單片機
單片機的型號是stc89c52rc,STC89C52RC是STC公司生產的一種低功耗、高性能CMOS8位微控制器,具有8K字節系統可編程Flash存儲器。STC89C52使用經典的MCS-51內核,但是做了很多的改進使得芯片具有傳統的方法51單片機不具備的功能。在單芯片上,擁有靈巧的8 位CPU 和在系統可編程Flash,使得STC89C52為眾多嵌入式控制應用系統提供高靈活、超有效的解決方案。
2.藍牙模塊
我用的是藍牙型號是HC-06,給HC-06上電之后,HC-06的指示燈會不停地閃爍,這個時候就標志着進入AT模式了,配置的時候,HC-06的Rx和Tx 接到 51單片機的 Rx和 Tx,一般是P3.0,和P3.1,正常工作時,HC-06的Rx和Tx 接到 51單片機的 Tx和 Rx,8位數據位,1位結束位,無奇偶校驗。一般HC-06模塊的默認名稱就是hc-06,默認配對密碼是1234或0000。我們如果連接微信小程序,我們要把密碼取消,這樣微信小程序才能直接來連接。
3.風扇和水泵
風扇和水泵是直接供電就可以使用,我們只需要一個繼電器就可以控制這兩個設備,我選擇了P3.5,P3.4這兩個引腳來控制,高電平驅動,低電平關閉,這兩個外接設備主要是測試數據有沒有接受成功。
單片機程序主程序
void ctrl(unsigned char a) //單字節數據接收 { //注意:若單片機TXD(P3.1)無上拉能力,必須在P3.1端接上拉電阻。本次測試需要接上拉電阻 TI=0; SBUF=a; while(TI==0); TI=0; Mode=1; if(SBUF==0){ LED_yanwu=0; baojing=0; fs=0; led1=0; led2=0; }else if(SBUF==1){ LED_yanwu=1; baojing=1; fs=1; Mode=0; }else if(SBUF==2){ baojing=0; LED_wendu=0; fs1=0; }else if(SBUF==3){ baojing=1; LED_wendu=1; fs1=1; led1=0; led2=0; Mode=0; } } void main() { check_wendu(); check_wendu(); Init1602(); ES=0; //關中斷 SCON = 0x50; // REN=1允許串行接受狀態,串口工作模式1, //10位UART(1位起始位,8位數據位,1位停止位,無奇偶校驗),波特率可變 TMOD = 0x20; // 定時器1工作於方式2,8位自動重載模式, 用於產生波特率 TH1=TL1=0xFD; // 波特率9600 (本次測試采用晶振為11.0592) PCON &= 0x7f; // 波特率不倍增 TR1 = 1; //定時器1開始工作,產生波特率 TI=0; //接收標志位置0 RI=0; ES=1; while(1) { temp=ADC0809(); check_wendu(); Key(); if(RI==1) // 是否有數據到來 { RI = 0; ctrl(SBUF); } Display_1602(yushe_wendu,yushe_yanwu,c,temp); //c溫度值,temp煙霧值 if(Mode==0) { if(temp>=yushe_yanwu) { LED_yanwu=0; baojing=0; fs=0; } else { LED_yanwu=1; } if(c>800){ c = 0; } if(c>=(yushe_wendu*10)) { baojing=0; LED_wendu=0; fs1=0; } else { LED_wendu=1; } if((temp<yushe_yanwu)&&(c<(yushe_wendu*10))) { baojing=1; fs=1; fs1=1; } } } }
硬件實物圖
數據采集顯示正常,微信小程序可以正常控制單片機的水泵和風扇,通信正常無異常。硬件部分我就簡單的拿出來說了一下,所涉及到的知識肯定比我展示到的多,軟件部分主要是對低功率藍牙的搜索,連接,發送數據比較麻煩。