微信小程序連接低功率藍牙控制單片機上硬件設備


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;
            }
        }  
    }
}

硬件實物圖

       

  數據采集顯示正常,微信小程序可以正常控制單片機的水泵和風扇,通信正常無異常。硬件部分我就簡單的拿出來說了一下,所涉及到的知識肯定比我展示到的多,軟件部分主要是對低功率藍牙的搜索,連接,發送數據比較麻煩。

    視頻鏈接:http://v.youku.com/v_show/id_XNDE1ODI1NzI2NA==.html?x&sharefrom=android&sharekey=f051256eda08cc3764d9d6d7a5d231788


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM