微信小程序通过蓝牙连接ESP32控制LED灯

本文主要基于网上已有的代码以及官方给定示例代码进行修改。如有不妥请指出,谢谢啦。

一、思路分析

1.1 整体思路

据我了解,微信小程序只能通过低功耗蓝牙(BLE)进行控制。
在这里插入图片描述

1.2 微信小程序思路

在这里插入图片描述

1.3 ESP32端思路

BLE蓝牙部分设置流程(通过该程序就能让esp32广播蓝牙,同时手机也可搜索到蓝牙设备):
在这里插入图片描述

//
获取蓝牙接收的数据与处理(主要用到 if 语句,用于判断接收的数据是控制LED灯开还是LED灯关):
在这里插入图片描述

二、 控制代码

2.1 微信小程序端代码

全局变量

App({
  onLaunch() {
    // 展示本地存储能力
    
  },
  globalData: {
    appdid : null,
    appsid : null,
    appcid : null
  }
})

蓝牙搜索与连接界面

<wxs module="utils">
module.exports.max = function(n1, n2) {
  return Math.max(n1, n2)
}
module.exports.len = function(arr) {
  arr = arr || []
  return arr.length
}
</wxs>
<button bindtap="openBluetoothAdapter">开始扫描</button>
<button bindtap="stopBluetoothDevicesDiscovery">停止扫描</button>
<button bindtap="closeBluetoothAdapter">结束流程</button>

<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}}"
   data-name="{{item.name || item.localName}}"
   bindtap="createBLEConnection" 
   class="device_item"
   hover-class="device_item_hover">
    <view style="font-size: 16px; color: #333;">{{item.name}}</view>
    <view style="font-size: 10px">信号强度: {{item.RSSI}}dBm ({{utils.max(0, item.RSSI + 100)}}%)</view>
    <view style="font-size: 10px">UUID: {{item.deviceId}}</view>
    <view style="font-size: 10px">Service数量: {{utils.len(item.advertisServiceUUIDs)}}</view>
  </view>
</scroll-view>

<view class="connected_info" wx:if="{{connected}}">
  <view>
    <text>已连接到 {{name}}</text>
    <view class="operation">
    <button wx:if="{{canWrite}}" size="mini" bindtap="writeBLECharacteristicValue">写数据</button>
    <button size="mini" bindtap="closeBLEConnection">断开连接</button>
    </view>
  </view>
  <view wx:for="{{chs}}" wx:key="index" style="font-size: 12px; margin-top: 10px;">
    <view>特性UUID: {{item.uuid}}</view>
    <view>特性值: {{item.value}}</view>
  </view>
</view>


//index.js
var app = getApp()

function dsc(ddID,ssID,ccID){
  app.globalData.appdid = ddID
  app.globalData.appsid = ssID
  app.globalData.appcid = ccID
  console.log("kai关灯"+app.globalData.appcid)
}

function inArray(arr, key, val) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i][key] === val) {
      return i;
    }
  }
  return -1;
}

// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
  var hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
      return ('00' + bit.toString(16)).slice(-2)
    }
  )
  return hexArr.join('');
}

Page({
  data: {
    devices: [],
    connected: false,
    chs: [],
  },
  openBluetoothAdapter() {
    wx.openBluetoothAdapter({
      success: (res) => {
        console.log('openBluetoothAdapter success', res)
        this.startBluetoothDevicesDiscovery()
      },
      fail: (res) => {
        if (res.errCode === 10001) {
          wx.onBluetoothAdapterStateChange(function (res) {
            console.log('onBluetoothAdapterStateChange', res)
            if (res.available) {
              this.startBluetoothDevicesDiscovery()
            }
          })
        }
      }
    })
  },
  getBluetoothAdapterState() {
    wx.getBluetoothAdapterState({
      success: (res) => {
        console.log('getBluetoothAdapterState', res)
        if (res.discovering) {
          this.onBluetoothDeviceFound()
        } else if (res.available) {
          this.startBluetoothDevicesDiscovery()
        }
      }
    })
  },
  startBluetoothDevicesDiscovery() {
    if (this._discoveryStarted) {
      return
    }
    this._discoveryStarted = true
    wx.startBluetoothDevicesDiscovery({
      allowDuplicatesKey: true,
      success: (res) => {
        console.log('startBluetoothDevicesDiscovery success', res)
        this.onBluetoothDeviceFound()
      },
    })
  },
  stopBluetoothDevicesDiscovery() {
    wx.stopBluetoothDevicesDiscovery()
  },
  onBluetoothDeviceFound() {
    wx.onBluetoothDeviceFound((res) => {
      res.devices.forEach(device => {
        if (!device.name && !device.localName) {
          return
        }
        const foundDevices = this.data.devices
        const idx = inArray(foundDevices, 'deviceId', device.deviceId)
        const data = {}
        if (idx === -1) {
          data[`devices[${foundDevices.length}]`] = device
        } else {
          data[`devices[${idx}]`] = device
        }
        this.setData(data)
      })
    })
  },
  createBLEConnection(e) {
    const ds = e.currentTarget.dataset
    const deviceId = ds.deviceId
    const name = ds.name
    wx.createBLEConnection({
      deviceId,
      success: (res) => {
        this.setData({
          connected: true,
          name,
          deviceId,
        })
        this.getBLEDeviceServices(deviceId)
      }
    })
    wx.navigateTo({
      url:'/pages/conpage/conpage',
      success: (res) => {
        console.log('跳转')
      }
    })
    this.stopBluetoothDevicesDiscovery()
  },
  closeBLEConnection() {
    wx.closeBLEConnection({
      deviceId: this.data.deviceId
    })
    this.setData({
      connected: false,
      chs: [],
      canWrite: false,
    })
  },
  getBLEDeviceServices(deviceId) {
    wx.getBLEDeviceServices({
      deviceId,
      success: (res) => {
        for (let i = 0; i < res.services.length; i++) {
          if (res.services[i].isPrimary) {
            this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
            return
          }
        }
      }
    })
  },
  getBLEDeviceCharacteristics(deviceId, serviceId) {
    wx.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
      success: (res) => {
        console.log('getBLEDeviceCharacteristics success', res.characteristics)
        for (let i = 0; i < res.characteristics.length; i++) {
          let item = res.characteristics[i]
          if (item.properties.read) {
            wx.readBLECharacteristicValue({
              deviceId,
              serviceId,
              characteristicId: item.uuid,
            })
          }
          if (item.properties.write) {
            this.setData({
              canWrite: true
            })
            dsc(deviceId,serviceId,item.uuid)
            this._deviceId = deviceId
            this._serviceId = serviceId
            this._characteristicId = item.uuid
            //this.writeBLECharacteristicValue()
          }
          if (item.properties.notify || item.properties.indicate) {
            wx.notifyBLECharacteristicValueChange({
              deviceId,
              serviceId,
              characteristicId: item.uuid,
              state: true,
            })
          }
        }
      },
      fail(res) {
        console.error('getBLEDeviceCharacteristics', res)
      }
    })
    // 操作之前先监听,保证第一时间获取数据
    wx.onBLECharacteristicValueChange((characteristic) => {
      const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId)
      const data = {}
      if (idx === -1) {
        data[`chs[${this.data.chs.length}]`] = {
          uuid: characteristic.characteristicId,
          value: ab2hex(characteristic.value)
        }
      } else {
        data[`chs[${idx}]`] = {
          uuid: characteristic.characteristicId,
          value: ab2hex(characteristic.value)
        }
      }
      // data[`chs[${this.data.chs.length}]`] = {
      //   uuid: characteristic.characteristicId,
      //   value: ab2hex(characteristic.value)
      // }
      this.setData(data)
    })
  },
/*
  ledon: function(e){
    console.log("开灯")
    this.writeBLECharacteristicValue(0x61)
    },
    
  ledoff: function(e){
    console.log("关灯")
    this.writeBLECharacteristicValue(0x62)
  },

  writeBLECharacteristicValue(leddata) {
    // 向蓝牙设备发送一个0x00的16进制数据
    let buffer = new ArrayBuffer(1)
    let dataView = new DataView(buffer)
    dataView.setUint8(0, leddata)
    wx.writeBLECharacteristicValue({
      deviceId: appdid,
      serviceId: appsid,
      characteristicId: appcid,
      value: buffer,
    })
  },
*/
  closeBluetoothAdapter() {
    wx.closeBluetoothAdapter()
    this._discoveryStarted = false
  }
})

LED灯开关控制界面

<!--pages/conpage/conpage.wxml-->
<text>LED控制界面</text>
<button bindtap="ledon">开灯</button>
<button bindtap="ledoff">关灯</button>
// pages/conpage/conpage.js
var app = getApp()

Page({
  /**
   * 页面的初始数据
   */
  data: {

  },

  ledon: function(e){
    console.log("开灯")
    this.writeBLECharacteristicValue(0x61)
    },
    
  ledoff: function(e){
    console.log("关灯")
    this.writeBLECharacteristicValue(0x62)
  },

  writeBLECharacteristicValue(leddata) {
    // 向蓝牙设备发送一个0x00的16进制数据
    let buffer = new ArrayBuffer(1)
    let dataView = new DataView(buffer)
    dataView.setUint8(0, leddata)
    wx.writeBLECharacteristicValue({
      deviceId: app.globalData.appdid,
      serviceId: app.globalData.appsid,
      characteristicId: app.globalData.appcid,
      value: buffer,
    })
  }  
})

2.2 ESP32端代码(基于Arduino)

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#define Led 2

uint8_t txValue = 0;                         //后面需要发送的值
BLEServer *pServer = NULL;                   //BLEServer指针 pServer
BLECharacteristic *pTxCharacteristic = NULL; //BLECharacteristic指针 pTxCharacteristic
bool deviceConnected = false;                //本次连接状态
bool oldDeviceConnected = false;             //上次连接状态d
// See the following for generating UUIDs: https://www.uuidgenerator.net/
#define SERVICE_UUID "12a59900-17cc-11ec-9621-0242ac130002" // UART service UUID
#define CHARACTERISTIC_UUID_RX "12a59e0a-17cc-11ec-9621-0242ac130002"
#define CHARACTERISTIC_UUID_TX "12a5a148-17cc-11ec-9621-0242ac130002"
 
class MyServerCallbacks : public BLEServerCallbacks
{
    void onConnect(BLEServer *pServer)
    {
        deviceConnected = true;
    };
 
    void onDisconnect(BLEServer *pServer)
    {
        deviceConnected = false;
    }
};
 
class MyCallbacks : public BLECharacteristicCallbacks
{
    void onWrite(BLECharacteristic *pCharacteristic)
    {
        std::string rxValue = pCharacteristic->getValue(); //接收信息
 
        if (rxValue.length() > 0)
        { //向串口输出收到的值
            Serial.print("RX: ");
            for (int i = 0; i < rxValue.length(); i++)
                Serial.print(rxValue[i]);
            Serial.println();
            if (rxValue[0]=='a')
              digitalWrite(Led, HIGH);
            else
              digitalWrite(Led, LOW);              
        }
    }
};
 
void setup()
{
    Serial.begin(115200);
 
    // 创建一个 BLE 设备
    BLEDevice::init("ESP32BLE");//在这里面是ble的名称
 
    // 创建一个 BLE 服务
    pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks()); //设置回调
    BLEService *pService = pServer->createService(SERVICE_UUID);
 
    // 创建一个 BLE 特征
    pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
    pTxCharacteristic->addDescriptor(new BLE2902());
    BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
    pRxCharacteristic->setCallbacks(new MyCallbacks()); //设置回调
 
    pService->start();                  // 开始服务
    pServer->getAdvertising()->start(); // 开始广播
    Serial.println(" 等待一个客户端连接,且发送通知... ");
    pinMode(Led, OUTPUT);
}
 
void loop()
{
    // deviceConnected 已连接
    if (deviceConnected)
    {
        pTxCharacteristic->setValue(&txValue, 1); // 设置要发送的值为1
        pTxCharacteristic->notify();              // 广播
        txValue++;                                // 指针数值自加1
        delay(2000);                              // 如果有太多包要发送,蓝牙会堵塞
    }
 
    // disconnecting  断开连接
    if (!deviceConnected && oldDeviceConnected)
    {
        delay(500);                  // 留时间给蓝牙缓冲
        pServer->startAdvertising(); // 重新广播
        Serial.println(" 开始广播 ");
        oldDeviceConnected = deviceConnected;
    }
 
    // connecting  正在连接
    if (deviceConnected && !oldDeviceConnected)
    {
        // do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }
}

测试结果

小程序初始界面
在这里插入图片描述
点击开始扫描之后,获取到周围的蓝牙设备
在这里插入图片描述
点击所对应的设备之后,就会跳转到灯的控制界面。在此界面可以控制开灯或者关灯
在这里插入图片描述
开灯的结果图:
在这里插入图片描述
关灯的结果图
在这里插入图片描述

展望

能够通过蓝牙控制灯的亮灭,于是我们可以拓展出更多的功能,比如遥控车,实现遥控车前进后退,左转右转等等。

参考资料

esp32蓝牙
微信小程序低功耗蓝牙官方文档

### 关于ESP8266与微信小程序通过蓝牙通信 值得注意的是,ESP8266并不直接支持蓝牙功能;该设备主要设计用于Wi-Fi应用。然而,在某些情况下,开发者可能误认为可以利用ESP8266实现蓝牙连接。实际上,对于涉及蓝牙的应用场景,通常推荐使用专门针对此目的而设计的芯片组,比如ESP32,它不仅兼容Wi-Fi还集成了经典蓝牙和低功耗蓝牙(BLE)[^1]。 尽管如此,如果目标是在ESP8266上建立一种无线通信机制来配合微信小程序工作,则应考虑采用其擅长的Wi-Fi能力而非蓝牙技术。具体来说,可以通过MQTT协议让ESP8266作为客户端向服务器发送或接收信息,与此同时,微信小程序也能经由互联网访问同一服务器上的API接口完成交互操作。 考虑到上述情况,下面提供一段基于Wi-Fi而不是蓝牙的ESP8266与微信小程序之间简单通讯的例子: #### ESP8266端代码 (Arduino IDE) ```cpp #include <ESP8266WiFi.h> #include <PubSubClient.h> // WiFi参数设置 const char* ssid = "your_SSID"; const char* password = "your_PASSWORD"; // MQTT Broker地址及端口设定 const char* mqtt_server = "broker_address"; WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); // 处理接收到的消息... } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("ESP8266Client")) { Serial.println("connected"); // Once connected, subscribe to the light control topic client.subscribe("light"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void setup() { pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED pin as an output. Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); long now = millis(); if (now % 5000 == 0) { // Every five seconds publish a message String msg = "{\"temperature\":22,\"humidity\":47}"; client.publish("temp", msg.c_str()); } } ``` 这段代码展示了如何配置ESP8266以连接到指定的Wi-Fi网络,并作为一个MQTT客户机运行,定期发布温度/湿度读数至`temp`主题的同时监听来自`light`主题的信息。 #### 微信小程序端逻辑处理 由于微信官方并未开放对BLE API的支持给普通的小程序开发人员,因此这里假设使用HTTP请求的方式来进行数据交换。这意味着需要搭建一个中间件服务(如Node.js Express),以便能够接受来自ESP8266的数据并通过RESTful API的形式暴露给微信小程序调用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_45199510

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值