关于微信小程序低功耗蓝牙ECharts实时刷新

news2024/11/14 16:32:40

最近搞了这方面的东西,是刚刚开始接触微信小程序,因为是刚刚开始接触蓝牙设备,所以这篇文章适合既不熟悉小程序,又不熟悉蓝牙的新手看。

项目要求是获取到蓝牙传输过来的数据,并显示成图表实时显示;

我看了很多网上的文章,也鼓捣了很长时间ChatGPT,最后弄出来了,其中出现了很多坑,在这里分享一下;

我想了一下这个文章还是写在CSDN,我知道这个平台有点拉,广告多,但毕竟这个平台普及度高,我希望这篇文章能帮到更多的人,至于什么关注看文章,收费!收NMLGB的费!

首先,微信开发者工具一些简单的配置我就不多说了,先说一些坑的地方;

当我刚刚准备在微信小程序搞蓝牙的是时候,当然是先去翻微信的官方文档,官方文档还是很不错的,给了一个蓝牙项目例子,运行起来很丝滑;

蓝牙 (Bluetooth) | 微信开放文档

在这个文档的最后会有一个代码示例:

我下载下来直接用,可以获取到数据,这个示例里提供了一个16进制显示的函数;

其中获取数据的地方是这里:

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
            })
            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)  //转16进制
          value: toASCII(characteristic.value)
        }
      } else {
        data[`chs[${idx}]`] = {
          uuid: characteristic.characteristicId,
          value: toASCII(characteristic.value)
        }
      }
      this.setData(data);
      // 图表刷新
      this.Refresh2(dataGenerator(this.data.bleDataList01, this.data.chs[0].value, 50));
    })
  },
})

有点长,监听数据变化并获取数据的仅仅是 wx.onBLECharacteristicValueChange((characteristic)这部分;

我发现一个问题,这个示例代码跑真机测试经常经常经常连不上,连的我怀疑人生,这样我Log大法输出的变量和任何报错都看不到!

最后还是新建了一个项目,使用这个示例程序的代码重新写才稳定的连接到手机,诸位如果和我一样,那我们真是难兄难弟……

OK,现在回到重构好的之后的程序。

硬件的蓝牙芯片是沁恒,型号是CH9141,这个是他们官网的下载资源:

搜索 CH9141 - 南京沁恒微电子股份有限公司 (wch.cn)

他们提供了Android的串口助手,在电脑端我用的Windows商店的串口工具:

感觉确实比网上的野鸡串口工具好用。

我的的硬件是低功耗蓝牙,这里连接后会有很多uuid,甚至这些uuid还分组,这是用Android串口工具看到的数据:

左边的uuid才是我要的。

我也不是搞硬件的,只能摸索代码,最后发现是这里:

  getBLEDeviceServices(deviceId) {
    wx.getBLEDeviceServices({
      deviceId,
      success: (res) => {
        console.log(res);
        //这里的services[1]就是定位分组的位置
        this.getBLEDeviceCharacteristics(deviceId, res.services[1].uuid)
        return
      }
    })
  },

这里我的低功耗蓝牙硬件是有多个服务,然后对应的,我要的服务在这里数组中的位置是1:

可以看一下和上面对的上,然后在这个服务里又有两个uuid,我只要第一个(具体服务具体对应),所以我在第一个代码块那里才会有 this.data.chs[0].value这种写法;

所以对接蓝牙数据的时候各位先打印一下这里的services,然后改成自己需要的uuid。

Refresh2(dataLists) {
    const chart = this.chart;
    if (chart) {
      chart.setOption({
        series: [{
          data: dataLists
        }]
      });
      console.log('完成刷新');
    }
  },

最开始我按照ECharts官网的小程序代码,把ECharts的初始化代码写在了Page对象外面,这样出现了一个问题,我在Page外部不能使用this来吧我声明好的chart对象保存到Page中;

所以改造了一下写法:

Page({
  data: {
    motto: 'Hello World',
    devices: [],
    connected: false,
    chs: [],
    bleDataList01: [],
    bleDataList02: [],
    ec: {
      onInit: null,
    },
    option: option,
  },

  onLoad() {
    this.chart = null; // 保存图表实例
    this.setData({
      ec: {
        onInit: this.initChart
      }
    });
  },
  initChart(canvas, width, height, dpr) {
    this.chart = echarts.init(canvas, null, {
      width: width,
      height: height,
      devicePixelRatio: dpr // 像素比
    });
    canvas.setChart(this.chart);
    this.chart.setOption(this.data.option);
    return this.chart;
  },
})

option是定义在Page外部的ECharts样式变量,所有代码都是写在index.js文件里的,

这样就能保证我在Page内部写ECharts初始化函数,又不用ECharts懒加载了;

最后写一下数据刷新函数,其调用是这样的:

// 图表数据填充
const dataGenerator = (dataList, data, xLength) => {
  if (data != "") {
    dataList.push(Number(data));
  }
  if (dataList.length === xLength) {
    dataList.shift()
  }
  return dataList;
};



//这里的数据刷新是写在Page内部的
Refresh(dataLists) {
    const chart = this.chart;
    if (chart) {
      chart.setOption({
        series: [{
          data: dataLists
       }]
     });
      console.log('完成刷新');
   }
 },


// 图表刷新,在    wx.onBLECharacteristicValueChange中调用,因为是写在Page内部的,所以前面带过this
this.Refresh(dataGenerator(this.data.bleDataList01, this.data.chs[0].value, 50));

整体代码是这样的:

import * as echarts from '../components/ec-canvas/echarts';

var option = {
  title: {
    text: '蓝牙对接数据图表',
    left: 'center'
  },
  legend: {
    data: ['测试数据'],
    top: 50,
    left: 'center',
    z: 100
  },
  grid: {
    containLabel: true
  },
  tooltip: {
    show: true,
    trigger: 'axis'
  },
  xAxis: {
    type: 'category',
    boundaryGap: true,
  },
  yAxis: {
    x: 'center',
    type: 'value',
  },
  series: [{
    name: '测试数据',
    type: 'line',
    smooth: true,
    data: []
  }, ]
};

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('');
}

function toASCII(buffer) {
  return String.fromCharCode.apply(null, new Uint8Array(buffer));
};
// 图表数据填充
const dataGenerator = (dataList, data, xLength) => {
  if (data != "") {
    dataList.push(Number(data));
  }
  if (dataList.length === xLength) {
    dataList.shift()
  }
  return dataList;
};

Page({
  data: {
    motto: 'Hello World',
    devices: [],
    connected: false,
    chs: [],
    bleDataList01: [],
    bleDataList02: [],
    ec: {
      onInit: null,
    },
    option: option,
  },

  onLoad() {
    this.chart = null; // 保存图表实例
    this.setData({
      ec: {
        onInit: this.initChart
      }
    });
  },

  Refresh(dataLists) {
    const chart = this.chart;
    if (chart) {
      chart.setOption({
        series: [{
          data: dataLists
        }]
      });
      console.log('完成刷新');
    }
  },

  initChart(canvas, width, height, dpr) {
    this.chart = echarts.init(canvas, null, {
      width: width,
      height: height,
      devicePixelRatio: dpr // 像素比
    });
    canvas.setChart(this.chart);
    this.chart.setOption(this.data.option);
    return this.chart;
  },

  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)
      }
    })
    this.stopBluetoothDevicesDiscovery()
  },

  closeBLEConnection() {
    wx.closeBLEConnection({
      deviceId: this.data.deviceId
    })
    this.setData({
      connected: false,
      chs: [],
      canWrite: false,
      bleDataList01: [],
      bleDataList02: [],
    });
    //断开连接的时候清理图表数据
    if (this.chart) {
      this.chart.setOption({
        series: [{
          data: []
        }]
      });
    }
    console.log('Bluetooth connection closed and data cleared');
  },

  getBLEDeviceServices(deviceId) {
    wx.getBLEDeviceServices({
      deviceId,
      success: (res) => {
        console.log(res);
        //这里的services[1]就是定位分组的位置
        this.getBLEDeviceCharacteristics(deviceId, res.services[1].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
            })
            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)  //转16进制
          value: toASCII(characteristic.value)
        }
      } else {
        data[`chs[${idx}]`] = {
          uuid: characteristic.characteristicId,
          value: toASCII(characteristic.value)
        }
      }
      this.setData(data);
      // 图表刷新
      this.Refresh(dataGenerator(this.data.bleDataList01, this.data.chs[0].value, 50));
    })
  },

  writeBLECharacteristicValue() {
    // 向蓝牙设备发送一个0x00的16进制数据
    let buffer = new ArrayBuffer(1)
    let dataView = new DataView(buffer)
    dataView.setUint8(0, Math.random() * 255 | 0)
    wx.writeBLECharacteristicValue({
      deviceId: this._deviceId,
      serviceId: this._deviceId,
      characteristicId: this._characteristicId,
      value: buffer,
    })
  },

  closeBluetoothAdapter() {
    wx.closeBluetoothAdapter()
    this._discoveryStarted = false
  },
})

再说一下,这些代码都是写在index.js文件里的,

这个是项目代码:GitHub - DingAi/WeChatProject-EchartsBluetooth: 一个微信小程序,用Echarts实时显示蓝牙数据

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

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

相关文章

【全开源】招聘求职小程序系统源码(ThinkPHP+原生微信小程序)

基于ThinkPHP和原生微信小程序开发的招聘平台系统&#xff0c;包含微信小程序求职者端、微信小程序企业招聘端、PC企业招聘端、PC管理平台端 构建高效人才交流平台 一、引言&#xff1a;招聘求职市场的数字化趋势 在数字化时代&#xff0c;招聘求职市场也迎来了巨大的变革。…

软件杯 题目: 基于深度学习的疲劳驾驶检测 深度学习

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 &#x1f525; 优…

RT-DRET在实时目标检测上超越YOLO8

导读 目标检测作为计算机视觉的核心任务之一&#xff0c;其研究已经从基于CNN的架构发展到基于Transformer的架构&#xff0c;如DETR&#xff0c;后者通过简化流程实现端到端检测&#xff0c;消除了手工设计的组件。尽管如此&#xff0c;DETR的高计算成本限制了其在实时目标检测…

一文了解 - GPS/DR组合定位技术

GPS Global Position System 全球定位系统这个大家都很熟悉&#xff0c; 不做太多介绍。 DR Dead Reckoning 车辆推算定位法&#xff0c; 一种常用的辅助的车辆定位技术。 DR系统的优点&#xff1a; 不需要发射和接收信号&#xff1b; 不受电磁波干扰。 DR系统的缺点&#x…

Leetcode 剑指 Offer II 079.子集

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返…

Java——接口后续

1.Comparable 接口 在Java中&#xff0c;我们对一个元素是数字的数组可以使用sort方法进行排序&#xff0c;如果要对一个元素是对象的数组按某种规则排序&#xff0c;就会用到Comparable接口 当实现Comparable接口后&#xff0c;sort会自动调用Comparable接口里的compareTo 方法…

【Shell】sed编辑器实例

sed是用来解析和转换文本的工具&#xff0c;它使用简单&#xff0c;是简洁的程序设计语言。 sed编辑器 &#xff08;一&#xff09; sed编辑器基础1. 简介2. sed的模式空间 &#xff08;二&#xff09;基本的sed编辑命令&#xff08;三&#xff09;sed命令实例1. 向文件中添加或…

leetcode-189. 旋转数组 原地递归算法(非官方的三种方法)

Problem: 189. 轮转数组 思路 首先&#xff0c;很明显&#xff0c;题目要求的操作等同于将数组的后k%n个元素移动到前面来。 然后我们思考原地操作的方法&#xff1a; &#xff08;为了方便讲解&#xff0c;我们先假设k<n/2&#xff09; 1.我们将数组划分为 [A&#xff0c;B…

MCU最小系统电路设计

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 何为最小系统 最小系统板就是一个最精简的电路&#xff0c;精简到只能维持MCU最基本的正常工作 最小系统包括哪些模块 电源模块 MircoUSB接口 在这个图片当中&#xff0c;我…

ubuntu22.04 vsc命令行复制粘贴时下划线消失

vscode 在ubuntu的terminal中下划线不显示解决方案 CtrlShiftP&#xff0c;打开搜索&#xff0c;Perferences:Open User Settings 设置Editor:Font Family 为 ‘Ubuntu Mono’, monospace 保存&#xff0c;效果如图&#xff1a;

SpringBoot使用rsa-encrypt-body-spring-boot实现接口加解密

废话不多说&#xff0c;直接上代码 引入依赖 <dependency><groupId>cn.shuibo</groupId><artifactId>rsa-encrypt-body-spring-boot</artifactId><version>1.0.1.RELEASE</version> </dependency>配置文件 rsa:encrypt:# 是…

JAVA -- > 初识JAVA

初始JAVA 第一个JAVA程序详解 public class Main {public static void main(String[] args) {System.out.println("Hello world");} }1.public class Main: 类型,作为被public修饰的类,必须与文件名一致 2.public static 是JAVA中main函数准写法,记住该格式即可 …

【how2j java应用】

[Log4j] 演示如何使用log4j进行日志输出 1.导入jar包 2.使用Log4j 3.代码说明 LOG4J 配置讲解 在src目录下添加log4j.properties文件 说明 log4j.xml 除了使用log4j.properties&#xff0c;也可以使用xml格式进行配置。 [junit] 通过main方法来进行测试&#xff1a;如果…

5.20Git

版本控制工具Git&#xff0c;其他的工具还有SVN 共享代码&#xff0c;追溯记录&#xff0c;存储.c文件 Git实现的功能&#xff1a;回溯&#xff08;以前某个时间节点的数据情况&#xff09;共享&#xff08;大家共享修改&#xff09; Git&#xff1a;80% SVN&#xff…

MySQL——MySQL目录结构

MySQL安装完成后&#xff0c;会在磁盘上生成一个目录&#xff0c;该目录被称为MySQL的安装目录。在MySQL的安装目录中包含了启动文件、配置文件、数据库文件和命令文件等。 下面对 MySQL 的安装目录进行详细讲解 (1)bin 目录 : 用于放置一些可执行文件,如 mysql.exe、mysqld. …

数组-下一个排列

一、题目描述 二、解题思路 1.反向遍历当前排列&#xff0c;比如 排列A[a,b,c,d,e,f...] &#xff0c;当遍历到e时&#xff0c;说明以 a,b,c,d,e为前缀的排列中不存在A排列的下一个排列。 2.把e&#xff08;位置设为idx&#xff09;和后面的元素作比较&#xff1a; 2.1 如果有…

网络模型—BIO、NIO、IO多路复用、信号驱动IO、异步IO

一、用户空间和内核空间 以Linux系统为例&#xff0c;ubuntu和CentOS是Linux的两种比较常见的发行版&#xff0c;任何Linux发行版&#xff0c;其系统内核都是Linux。我们在发行版上操作应用&#xff0c;如Redis、Mysql等其实是无法直接执行访问计算机硬件(如cpu&#xff0c;内存…

LabVIEW步开发进电机的串口控制程序

LabVIEW步开发进电机的串口控制程序 为了提高电机控制的精确度和自动化程度&#xff0c;开发一种基于LabVIEW的实时、自动化电机串口控制程序。利用LabVIEW软件的图形化编程特性&#xff0c;通过串口实时控制电机的运行参数&#xff0c;实现电机性能的精准控制与评估。 系统组…

Spring MVC+mybatis 项目入门:旅游网(三)用户注册——控制反转以及Hibernate Validator数据验证

个人博客&#xff1a;Spring MVCmybatis 项目入门:旅游网&#xff08;三&#xff09;用户注册 | iwtss blog 先看这个&#xff01; 这是18年的文章&#xff0c;回收站里恢复的&#xff0c;现阶段看基本是没有参考意义的&#xff0c;技术老旧脱离时代&#xff08;2024年辣铁铁&…

《Ai学习笔记》自然语言处理 (Natural Language Processing):机器阅读理解-基础概念解析01

自然语言处理 (Natural Language Processing)&#xff1a; NLP四大基本任务 序列标注&#xff1a; 分词、词性标注 分类任务&#xff1a; 文本分类、情感分析 句子关系&#xff1a;问答系统、对话系统 生成任务&#xff1a;机器翻译、文章摘要 机器阅读理解的定义 Machi…