小程序蓝牙连接ESP32通信(可直接拿来用)

news2024/11/23 13:21:50

小程序中的蓝牙能力

在小程序中,要使用蓝牙能力(Beacon 除外)必须首先调用 wx.openBluetoothAdapter 初始化蓝牙适配器模块,其生效周期为调用 wx.openBluetoothAdapter 至调用 wx.closeBluetoothAdapter 或小程序被销毁为止。只有在小程序蓝牙适配器模块生效期间,开发者才能够正常调用蓝牙相关的小程序 API,并收到蓝牙模块相关的事件回调(绑定监听不受此限制)。

小程序对蓝牙支持情况如下:

经典蓝牙:iOS 因系统限制暂无法提供,安卓目前已在规划中。

蓝牙低功耗 (BLE):

  • 主机模式:基础库 1.1.0(微信客户端 iOS 6.5.6,Android 6.5.7)开始支持。
  • 从机模式:基础库 2.10.3 开始支持。
  • 蓝牙信标 (Beacon):基础库 1.2.0 开始支持。

前置条件

  • 蓝牙模块需要是 低功耗蓝牙(BLE) ,不然小程序搜索不到(将导致无法继续进行开发),可购买我们的设备 来进行开发。
  • 开发者工具上只能模拟部分蓝牙接口能力,完整功能请使用真机调试。

官方文档

微信小程序蓝牙开发文档

微信小程序提供的操作蓝牙的 API

微信小程序目前有蓝牙 API 共 18 个

操作蓝牙适配器的 API
wx.openBluetoothAdapter 初始化蓝牙适配器
wx.closeBluetoothAdapter 关闭蓝牙模块
wx.getBluetoothAdapterState 获取本机蓝牙适配器状态
wx.onBluetoothAdapterStateChange 监听蓝牙适配器状态变化事件
连接前使用的 API
wx.startBluetoothDevicesDiscovery 开始搜寻附近的蓝牙外围设备
wx.stopBluetoothDevicesDiscovery 停止搜寻附近的蓝牙外围设备
wx.getBluetoothDevices 获取所有已发现的蓝牙设备
wx.onBluetoothDeviceFound 监听寻找到新设备的事件
连接和断开时使用的 API
wx.createBLEConnection 连接低功耗蓝牙设备
wx.closeBLEConnection 断开与低功耗蓝牙设备的连接
连接成功后使用的 API
wx.getConnectedBluetoothDevices 根据 uuid 获取处于已连接状态的设备
wx.getBLEDeviceServices 获取蓝牙设备所有 service(服务)
wx.getBLEDeviceCharacteristics  获取蓝牙设备所有 characteristic(特征值)
wx.readBLECharacteristicValue  读取低功耗蓝牙设备的特征值的二进制数据值
wx.writeBLECharacteristicValue 向低功耗蓝牙设备特征值中写入二进制数据
wx.notifyBLECharacteristicValueChange  启用低功耗蓝牙设备特征值变化时的 notify 功能
wx.onBLECharacteristicValueChange 监听低功耗蓝牙设备的特征值变化
wx.onBLEConnectionStateChange 监听低功耗蓝牙连接的错误事件

基本操作流程

最基本的操作流程是:初始化蓝牙适配器 → 开始搜寻附近的蓝牙外围设备 → 监听寻找到新设备的事件 → 连接低功耗蓝牙设备 → 获取蓝牙设备所有 service 和 characteristic → 读取或写入低功耗蓝牙设备的特征值的二进制数据值。

蓝牙组件代码

由于我们 index 中代码量比较多,不利于维护,所以我们将蓝牙功能单独抽离成一个 component(组件),方便管理和复用。

新建组件

新建一个 components 目录,在该目录下新建一个目录为 bluetooth-comp,然后在 bluetooth-comp 点击"新建 Component"按钮,输入 index 并点击确定。

页面结构部分
<!-- bluetooth-comp/index.wxml -->
<view style="margin: 26rpx">
  <button wx:if="{{!connected}}" bindtap="openBluetoothAdapter">开始扫描</button>
  <button wx:else bindtap="closeBLEConnection">断开连接 - {{name}}</button>

  <view class="devices_summary">已发现 {{devices.length}} 个外围设备:</view>
  <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</view>
    <view style="font-size: 10px">UUID: {{item.deviceId}}</view>
  </view>
</view>
样式部分
/* bluetooth-comp/index.wxss */
.devices_summary {
  margin-top: 30px;
  padding: 10px;
  font-size: 16px;
}

.device_item {
  border-bottom: 1px solid #eee;
  padding: 10px;
  color: #666;
}
.device_item_hover {
  background-color: rgba(0, 0, 0, 0.1);
}
逻辑部分

在 data 中定义变量

// bluetooth-comp/index.js
data:{
  devices: [],
  connected: false,
  chs: []
}

js 文件顶部定义工具方法

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

// 将字符串转为 ArrayBuffer
function str2ab(str) {
  let buf = new ArrayBuffer(str.length);
  let bufView = new Uint8Array(buf);
  for (var i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

在 methods 中定义方法

// bluetooth-comp/index.js
methods: {
  /* 初始化蓝牙模块 */
  openBluetoothAdapter() {
    // 先关闭蓝牙模块再开启 防止断开后点连接连接不上
    this.closeBluetoothAdapter();

    wx.openBluetoothAdapter({
      success: response => {
        console.log("初始化蓝牙模块成功:openBluetoothAdapter", response);
        this.startBluetoothDevicesDiscovery();
      },
      fail: err => {
        if (err.errCode === 10001) {
          /* 监听蓝牙适配器状态变化事件 */
          wx.onBluetoothAdapterStateChange(res => {
            console.log("监听蓝牙适配器状态变化事件:onBluetoothAdapterStateChange", res);
            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: response => {
        console.log("开始搜寻附近的蓝牙外围设备:startBluetoothDevicesDiscovery", response);
        this.onBluetoothDeviceFound();
      },
      fail: err => {
        console.log("搜索设备失败", err);
        wx.showToast({ title: "搜索设备失败", icon: "none" });
      },
    });
  },
  /* 停止搜寻附近的蓝牙外围设备。*/
  stopBluetoothDevicesDiscovery() {
    console.log("停止搜寻附近的蓝牙外围设备");
    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: () => {
        this.setData({ connected: true, name, deviceId });
        wx.showToast({ title: "连接蓝牙设备成功", icon: "none" });
        this.getBLEDeviceServices(deviceId);
      },
      fail: e => {
        console.log("连接失败", e.errMsg);
        wx.showToast({ title: "连接失败,错误信息: " + e.errMsg, icon: "none" });
      },
    });
    // 停止搜寻蓝牙设备
    this.stopBluetoothDevicesDiscovery();
  },
  /* 断开与蓝牙低功耗设备的连接。 */
  closeBLEConnection() {
    console.log("断开与蓝牙低功耗设备的连接");
    wx.showToast({ title: "已断开和蓝牙设备的连接", icon: "none" });
    wx.closeBLEConnection({ deviceId: this.data.deviceId });
    this.setData({ connected: false, chs: [], canWrite: false });
  },
  /* 获取蓝牙低功耗设备所有服务 (service) */
  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;
          }
        }
      },
    });
  },
  /* 获取蓝牙低功耗设备某个服务中所有特征 (characteristic)。 */
  getBLEDeviceCharacteristics(deviceId, serviceId) {
    wx.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
      success: res => {
        console.log("获取蓝牙低功耗设备某个服务中所有特征:getBLEDeviceCharacteristics");

        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 });
            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,
              success(res) {
                console.log("notifyBLECharacteristicValueChange success", res);
              },
            });
          }
        }
      },
      fail(res) {
        console.error("getBLEDeviceCharacteristics", res);
      },
    });

    // 操作之前先监听,保证第一时间获取数据
    wx.onBLECharacteristicValueChange(characteristic => {
      // TODO 收到的信息为ArrayBuffer类型,可根据自己的需要转换 可发送给父组件用来回显
      console.log("收到原始的数据", characteristic, characteristic.value);
      // 测试向设备发送数据
      // this.writeBLECharacteristicValue(JSON.stringify({"FAN":"OFF"}))
    });
  },
  /* 向蓝牙低功耗设备特征值中写入二进制数据 */
  writeBLECharacteristicValue(jsonStr) {
    let arrayBufferValue = str2ab(jsonStr);
    console.log("发送数据给蓝牙", "原始字符串", jsonStr, "转换arrayBuffer", arrayBufferValue);

    wx.writeBLECharacteristicValue({
      deviceId: this._deviceId,
      serviceId: this._serviceId, // 微信文档上是错误的
      characteristicId: this._characteristicId,
      value: arrayBufferValue, // 只能发送arrayBuffer类型数据
      success(res) {
        console.log("消息发送成功", res.errMsg);
        wx.showToast({ title: "消息发送成功", icon: "none" });
      },
      fail(e) {
        console.log("发送消息失败", e);
        wx.showToast({ title: "发送消息失败,错误信息: " + e.errMsg, icon: "none" });
      },
    });
  },
  closeBluetoothAdapter() {
    console.log("关闭蓝牙模块");
    wx.closeBluetoothAdapter();
    this._discoveryStarted = false;
  },
};

最终效果

界面

界面

真机调试结果

真机调试结果


嵌入式物联网教学开发 传感器控制拓展板二合一

不同于其他卖家提供资料和技术支持,我们提供手把手教学,完整的开发流程如下:

实现完整的一个物联网嵌入式项目:将数据上传并通过小程序对硬件进行控制。具体有:

[1]服务器配置,阿里云搭建mqttx服务器,ssl证书配置;

[2]微信小程序设计,借助微信开发者工具开发,js代码和类html语言;

[3]硬件驱动,基于Arduino平台开发esp32,提供wifi和蓝牙版本,读取传感器:温度、湿度、烟雾;控制设备:小灯、继电器。

提供教学视频在b站,合集播放破6w!提供项目开发文档,网页链接。

我们提供搭建好的mqtt测试服务器,供大家免费使用。

适合如下人群:

1.喜欢diy的电子极客;

2.物联网专业的相关学生课程或毕业设计;

3.寻求物联网项目经验的求职者;

有硬件工程师和软件工程师答疑,欢迎咨询~

文档资料,可参考:基于ESP32+微信小程序的物联网应用开发文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1679350.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

红黑树底层封装map、set C++

目录 一、框架思考 三个问题 问题1的解决 问题2的解决&#xff1a; 问题3的解决&#xff1a; 二、泛型编程 1、仿函数的泛型编程 2、迭代器的泛型编程 3、typename&#xff1a; 4、/--重载 三、原码 红黑树 map set 一、框架思考 map和set都是使用红黑树底层&…

【STM32-MX_GPIO_Init分析】

MX_GPIO_Init分析源码如下&#xff1a; __HAL_RCC_GPIOE_CLK_ENABLE源码如下&#xff1a; #define RCC ((RCC_TypeDef *) RCC_BASE) #define RCC_BASE (AHB1PERIPH_BASE 0x3800UL) #define AHB1PERIPH_BASE (PERIPH_BASE 0x00020000U…

2024-简单点-ultralytics库解析-data模块

data模块 overview布局\_\_init__.pyfrom .base import BaseDataset\_\_all__ annotator.pyaugment.pyclass BaseTransformclass Composeclass BaseMixTransformclass Mosaic静态方法更新label class MixUpRandomPerspectiveclass RandomHSVclass RandomFlipclass LetterBoxcla…

搭载全新升级viaim AI,讯飞会议耳机Pro 2首销价1399元起

2024年5月15日&#xff0c;人工智能硬件公司未来智能发布了讯飞会议耳机Pro 2、iFLYBUDS 2以及Kit 2三款旗舰新品&#xff0c;为用户带来全新升级的viaim AI&#xff0c;也为AIGC智能耳机树立了新标杆。 在发布会上&#xff0c;未来智能CEO马啸表示&#xff1a;在AIGC领域&…

20232803 2023-2024-2 《网络攻防实践》实践九报告

目录 1.实践内容2.实践过程2.1 手工修改可执行文件&#xff0c;改变程序执行流程&#xff0c;直接跳转到getShell函数2.2 利用foo函数的Bof漏洞&#xff0c;构造一个攻击输入字符串&#xff0c;覆盖返回地址&#xff0c;触发getShell函数2.3 注入一个自己制作的shellcode并运行…

数论专题练习

质数专题 我的思路就是一个素数筛&#xff0c;然后双指针 class Solution { public:int maximumPrimeDifference(vector<int>& nums) {unordered_map<int, int> mp;for (int i 2; i < 100; i) {if (mp[i] 0) {for (int j 2 * i; j < 100; j i) {mp[…

将PDF转换成电子杂志,轻松打造畅销内容!

在数字化时代&#xff0c;将PDF转换成电子杂志是一种非常受欢迎的内容创作方式。这种方式不仅可以提高内容的传播效果&#xff0c;还可以为创作者带来更多的收益。那么&#xff0c;如何轻松地将PDF转换成电子杂志&#xff0c;打造畅销内容呢&#xff1f; 市面上有许多可以将PDF…

vivo X100s发布,搭载最新天玑9300+平台

在沉寂了半年后&#xff0c;vivo终于发布了新的旗舰产品。相较于前代的X100&#xff0c;X100s作为小迭代也有不少让人眼前一亮的地方&#xff0c;下面就让我们一同来了解下吧。 外观方面&#xff0c;虽然vivo X100s相较于X100没有大改&#xff0c;但却十分具有质感。以“青云”…

Android 逆向

一、apk 查壳工具 ApkScan-PKID 相关APK文件可以在 豌豆荚 官网下载 ApkScan-PKID查壳工具 下载 - 简书 (jianshu.com) 二、脱壳工具&#xff1a;frida 1、Android端配置 frida-server&#xff1a; 该步骤需要使用到 adb&#xff0c;操作Android文件 Releases frida/frid…

机器学习中10种损失函数大梳理!建议收藏,你一定用得到

今儿想和大家聊聊关于损失函数方面的问题。 损失函数&#xff08;Loss Function&#xff09;是在机器学习和深度学习中用来衡量模型预测值与真实标签之间差异的函数。不同的任务和模型可能需要不同的损失函数。 今天就聊聊下面常见的损失函数&#xff0c;关于原理、使用场景&…

高效调度新篇章:详解DolphinScheduler 3.2.0生产级集群搭建

转载自tuoluzhe8521 导读&#xff1a;通过简化复杂的任务依赖关系&#xff0c; DolphinScheduler为数据工程师提供了强大的工作流程管理和调度能力。在3.2.0版本中&#xff0c;DolphinScheduler带来了一系列新功能和改进&#xff0c;使其在生产环境中的稳定性和可用性得到了显著…

Apache2.4和PHP8的量子纠缠

Apache不建议你用&#xff0c;PHP建议使用

更新Windows 11 后遇到的一些问题(更新中...)

目录 插入U盘后读取不到 在磁盘中新建文件夹需要管理员权限 导致不能安装一些软件 插入U盘后读取不到 解决方法&#xff1a;点击我的电脑或者是此电脑、选择管理、找到设备管理器、选择通用串行总线控制器、右键、选择启动。 第一步&#xff1a;点击我的电脑或者是此电脑、选…

Java类和对象(二)—— 封装,static 关键字与代码块

前言 在面向对象的编程语言中&#xff0c;有三大特性&#xff1a;封装、继承和多态~~ 今天我们就来学习封装的知识 封装 什么是封装 在现实生活中&#xff0c;我们经常使用手机来进行沟通与交流&#xff0c;实际上我们拿到的手机是被封装好的&#xff0c;精美的屏幕&a…

MYSQL和JAVA中将中文汉字按照拼音首字母排序

一、MYSQL将中文汉字按照拼音首字母排序 数据库使用的字符编码是utf8_general_ci&#xff0c;如下 ORDER BY CONVERT(表名.字段名 USING gbk) COLLATE gbk_chinese_ci ASC;若是表查询&#xff0c;CONVERT中可以不添加表名。 查询结果如下&#xff1a; 二、JAVA中将中文汉字…

自定义 Gradle 插件进行统一的静态代码分析

静态代码分析是一项了不起的技术, 它能让代码库更易于维护. 但是, 如果你在不同的版本库中拥有多个服务(可能由不同的团队开发), 如何才能让每个人都遵循既定的代码风格呢? 一个好办法是将所有规则封装在一个插件中, 该插件会在每个项目构建时自动执行所需的验证. 因此, 在本…

【2024系统架构设计】回顾历史,查缺补漏篇 ③

前言 hello,大家好: 💡💡💡 我们一起来备考软考高级系统架构设计师吧,本专栏提供综合知识、案例科目、论文(论点和部分示例范文)等内容,包括知识点总结和记忆小妙招哦。 🚀🚀🚀 可以减少资料查找和收集的时间,提高效率,我们一起集中精力学习干货吧! 💡…

Milvus的存储/计算分离

前言 根据数据面与控制面相隔离的原则&#xff0c;从可扩展性和灾难恢复来看&#xff0c;Milvus由4个相互独立的层组成 访问层 由一系列无状态的代理组成&#xff0c;访问层是系统和用户之间的第一层&#xff0c;它主要是验证客户端请求和规整返回的结果 代理是无状态的&am…

GPU学习记一下线程分组相关

在compute的时候&#xff0c;是要dispatch一个数量的代表分了多少块任务集&#xff0c;dispatch的块内部也是有一个数量的&#xff0c;那么这些值怎么取的呢 内部&#xff0c;N卡32 外面dispatch的数量就是all/32 然后细说这个值 这有一个叫core的东西&#xff0c;就是相当于th…

【opencv】答题卡判分实验

实验环境&#xff1a; anaconda、jupyter notebook 实验用的包&#xff1a;numpy、matplotlib、opencv 实验的目的还是以熟悉图像的透视变换、轮廓特征提取为主要目的 关于如何判断答题卡被选项&#xff1a;通过几个覆盖备选项的掩膜与原二值图像想与&#xff0c;最终整个图像…