当前位置: 首页 > news >正文

uniapp快速回顾,新学websocket连接和BLE连接

Uni APP的学习

官方文档

uni-app官网 (dcloud.net.cn)

任何的博客都不如官方文档

一、快速复习

文件结构

main.js

  • 功能:项目的入口文件,初始化 Vue 实例。
    App.vue
  • 功能:根组件,包含应用的基本结构和全局样式。
    manifest.json
  • 功能:应用的全局配置文件,包含应用名称、图标、平台特定配置等信息。
    pages.json
  • 功能:配置应用的页面路由、导航栏、选项卡等信息。
    uni.scss
  • 功能:全局的 SCSS 样式文件,可以在整个项目中使用。
    components/
  • 功能:用于存放项目中的可复用组件。
    pages/
  • 功能:用于存放项目的页面,每个页面通常有一个独立的目录和对应的 .vue 文件。
    static/
  • 功能:用于存放项目中的静态资源,如图片、字体等。
    uni_modules/
  • 功能:用于存放 UniApp 插件模块。
    store/
  • 功能:用于存放 Vuex 状态管理的相关文件。
    utils/
  • 功能:用于存放工具函数和公共方法。
    unpackage/
  • 功能:用于存放编译后的文件和生成的包。包含生成的各个平台的编译文件,是项目发布和运行的产物目录

为什么每个页面都使用template包围

每个页面都是一个组件:

  • 在 UniApp 中,一个页面本质上是一个独立的 Vue 组件。它包含了自己的 <template><script><style>,因此可以独立运行。
  • 由于 Vue 组件的特性,这些页面也可以在其他地方被当作子组件使用。
    组件复用:
  • 你可以将一个页面或组件嵌套在另一个页面或组件中,复用其内容和逻辑。例如,你可以在一个主页面中引入一个通用的头部或底部组件。
  • 通过在不同页面之间复用组件,你可以保持代码的简洁,并减少重复劳动。
    组件之间的通信:
  • 当你将一个页面作为组件嵌套在另一个页面中时,你可以通过 props 传递数据,或者通过事件 $emit$on 来实现父子组件之间的通信。
  • 这使得页面之间可以互相传递数据,形成一个有机的整体。

view标签

<view> 标签在 UniApp 中是一个非常重要且常用的标签。它在 UniApp 中起到了类似于 HTML 中 <div> 标签的作用,用于构建页面的基本布局和结构。下面是对 <view> 标签的详细讲解:

1. 基本功能

  • <view> 标签是 UniApp 中的一个容器元素,用来包含其他的视图元素或者组件。
  • 你可以将 <view> 标签理解为一个通用的布局容器,类似于 HTML 中的 <div> 标签。

2. 属性

<view> 标签支持很多属性,下面是一些常见的属性:

  • class: 用于为 <view> 添加样式类,通过 CSS 进行样式控制。
  • style: 直接内联样式设置,例如 style="margin:10px; padding:5px;"
  • id: 为 <view> 指定唯一标识符,用于在 JS 代码中进行操作。
  • hover-class: 当用户触摸时会给 <view> 添加的样式类。
  • hover-start-time: 按住时长达到多少毫秒才显示 hover 状态,默认 50 毫秒。
  • hover-stay-time: 手指松开后,保留 hover 状态的时长,默认 400 毫秒。

3. 使用示例

下面是一个简单的示例,展示如何使用 <view> 标签来构建一个页面的布局:

<template><!-- 页面根容器,使用 Flex 布局来组织页面结构 --><view class="container"><!-- 头部区域 --><view class="header"      <!-- 应用样式类 "header" -->hover-class="hover-effect"  <!-- 用户触摸时,应用 "hover-effect" 样式 -->><text>这是头部(带有hover效果)</text> <!-- 显示在头部的文本内容 --></view><!-- 主要内容区域,带有点击事件 --><view class="content"      <!-- 应用样式类 "content" -->@click="handleClick" <!-- 点击时触发 handleClick 方法 -->hover-class="hover-effect" <!-- 用户触摸时,应用 "hover-effect" 样式 -->hover-start-time="100"     <!-- 用户触摸 100 毫秒后,开始显示 hover 效果 -->hover-stay-time="300"      <!-- 松开触摸后,hover 效果持续 300 毫秒 -->id="main-content"    <!-- 设置唯一标识符为 "main-content" -->><text>点击这里触发事件</text> <!-- 显示在内容区域的文本内容 --></view><!-- 底部区域,使用动态内联样式 --><view class="footer"       <!-- 应用样式类 "footer" -->style="background-color: #00aaff;"  <!-- 内联样式,设置背景颜色为蓝色 -->:style="{ height: dynamicHeight + 'px' }" <!-- 动态设置高度,使用 Vue 的绑定语法 -->><text>这是底部(背景颜色通过style设置)</text> <!-- 显示在底部的文本内容 --></view></view>
</template><script>
export default {data() {return {dynamicHeight: 50 // 动态设置底部区域的高度为 50 像素};},methods: {handleClick() {// 点击事件的处理逻辑console.log('内容区域被点击');}}
};
</script><style>
/* 容器的样式:设置为 Flex 布局,子元素按垂直方向排列,容器高度占满页面 */
.container {display: flex;          /* 启用弹性布局 */flex-direction: column; /* 子元素垂直排列(从上到下) */height: 100%;           /* 容器高度占满父容器或视口 */
}/* 头部的样式 */
.header {background-color: #f8f8f8; /* 设置背景颜色为浅灰色 */padding: 10px;             /* 内边距为 10 像素 */text-align: center;        /* 文本居中对齐 */
}/* 内容区域的样式 */
.content {flex: 1;                    /* 占据容器中剩余的所有空间 */background-color: #ffffff;  /* 设置背景颜色为白色 */padding: 20px;              /* 内边距为 20 像素 */
}/* 底部的样式 */
.footer {padding: 10px;              /* 内边距为 10 像素 */text-align: center;         /* 文本居中对齐 */
}/* 用户触摸时的 hover 效果样式 */
.hover-effect {background-color: #e0e0e0;  /* 设置触摸时的背景颜色为浅灰色 */
}
</style>

在这个例子中:

  • hover-class:
    • 作用:为 <view> 组件添加一个在用户触摸时显示的样式类。通常用于按钮或可点击的区域,提供视觉反馈。
    • 示例:hover-class="hover-effect",当用户点击或触摸时,hover-effect 样式会被应用。
      hover-start-time:
    • 作用:设置用户触摸多少毫秒后,开始显示 hover-class
    • 示例:hover-start-time="100",用户触摸 100 毫秒后,开始显示 hover-class 的效果。
      hover-stay-time:
    • 作用:设置用户松开触摸后,hover-class 的效果持续多久(毫秒)。
    • 示例:hover-stay-time="300",在用户松开触摸后,hover-class 的效果会持续 300 毫秒。
      id:
    • 作用:为 <view> 组件设置唯一的标识符,可以用于在 JS 中进行 DOM 操作。
    • 示例:id="main-content",你可以通过 this.$refs['main-content'] 来引用这个元素。
      style:
    • 作用:内联样式直接应用于 <view> 元素,用于设置动态样式。
    • 示例:style="background-color: #00aaff;"<view> 设置了蓝色背景。 :style="{ height: dynamicHeight + 'px' }" 使用 Vue 的动态绑定来设置高度。
      @click:
    • 作用:绑定点击事件,当 <view> 被点击时触发对应的事件处理函数。
    • 示例:@click="handleClick",点击 content 区域时,将调用 handleClick 方法。
      class:
    • 作用:应用定义在 <style> 中的样式类,用于控制 <view> 的外观和布局。
    • 示例:class="header" 应用了 .header 中定义的样式。

4. 为什么使用 <view> 而不是 <div>

  • 跨平台支持: <view> 标签在所有 UniApp 支持的平台上都可以正常使用,而 <div> 是 HTML 元素,不适用于小程序、App 等其他平台。
  • 性能优化: UniApp 对 <view> 标签做了许多平台优化,可以更好地适应移动端设备,提供更流畅的用户体验。
  • 样式控制: <view> 标签内的样式和布局在不同平台之间更一致,使用 <view> 可以减少跨平台开发时的样式兼容性问题。

5. 常见的使用场景

  • 布局容器: 创建页面的基础结构和布局。
  • 点击区域: 配合 hover-class 等属性,实现点击效果。
  • 数据展示: 包裹和组织其他元素,如文本、图片、列表等.

flex布局

1. 基础概念

  • Flex 容器(Flex Container): 包含子元素(Flex 项目)的父容器,通常通过设置 display: flex; 来定义。
  • Flex 项目(Flex Items): 容器内的所有直接子元素,它们将自动成为弹性布局的一部分,可以自由地根据容器的规则进行排列和对齐。

2. 基本属性

在 Flexbox 布局中,最重要的属性主要有以下几类:

2.1. 容器属性(应用于 Flex 容器)
  1. display: flex;
    • 将一个元素定义为 Flex 容器,容器内的子元素将变成 Flex 项目。
  2. flex-direction
    • 决定 Flex 项目的排列方向。
      • row: 默认值,从左到右水平排列。
      • row-reverse: 从右到左水平排列。
      • column: 从上到下垂直排列。
      • column-reverse: 从下到上垂直排列。
  3. justify-content
    • 控制 Flex 项目在主轴(flex-direction 方向)的对齐方式。
      • flex-start: 靠主轴的起点对齐。
      • flex-end: 靠主轴的终点对齐。
      • center: 居中对齐。
      • space-between: 项目之间的间距相等,首尾项目与容器边缘对齐。
      • space-around: 项目之间的间距相等,项目和容器边缘之间留有一半的间距。
  4. align-items
    • 控制 Flex 项目在交叉轴(与主轴垂直的方向)的对齐方式。
      • stretch: 默认值,项目在交叉轴方向上拉伸以填充容器。
      • flex-start: 靠交叉轴的起点对齐。
      • flex-end: 靠交叉轴的终点对齐。
      • center: 在交叉轴方向居中对齐。
      • baseline: 项目基线对齐。
  5. flex-wrap
    • 决定如果容器空间不足,Flex 项目是否换行。
      • nowrap: 默认值,不换行。
      • wrap: 换行,项目在多行中排列。
      • wrap-reverse: 反向换行。
2.2. 项目属性(应用于 Flex 项目)
  1. flex
    • 简写属性,用于指定 Flex 项目的可伸缩性。
      • flex: 1; 表示项目将占据容器的所有可用空间,多个项目之间将按比例分配空间。
  2. align-self
    • 允许单个项目对齐方式与容器的 align-items 属性不同。
  3. order
    • 控制项目的排列顺序,数值越小的项目会排在前面,默认值为 0

二、websocket的连接

函数介绍

1. connect

  • 功能:建立 WebSocket 连接,并设置各种事件回调来处理连接成功、收到消息、连接关闭和连接出错的情况。
  • 核心操作:通过 uni.connectSocket 方法创建 WebSocket 连接,成功后发送初始数据。

2. sendMessage

  • 功能:通过 WebSocket 连接向服务器发送文本消息。
  • 核心操作
    • 检查 WebSocket 连接是否处于打开状态。
    • 构造要发送的消息(包含目标客户端 ID 和消息内容)。
    • 通过 socketTask.send 方法发送消息。

3. handleFileChange

  • 功能:处理用户选择的音频文件,并将文件保存在组件的状态中,以便稍后发送。
  • 核心操作
    • 捕获用户选择的文件,并将其存储在 audioFile 变量中。

4. sendAudio

  • 功能:将选择的音频文件通过 WebSocket 连接发送给服务器。
  • 核心操作
    • 使用 FileReader 读取音频文件的内容,并转换为 ArrayBuffer 格式。
  • 通过 socketTask.send 方法逐块发送音频数据,并在发送结束后发送 "EOF" 标志表示文件传输结束。

详细功能介绍

1. connect
  • 功能:启动 WebSocket 连接,是整个应用程序的核心功能。它确保了客户端能够与服务器通信,并在连接成功后立即向服务器发送客户端的身份信息(即 uniqueIdmateUniqueId)。
  • 使用场景:当用户想要连接到服务器进行实时通信时,点击“Connect”按钮触发。
2. sendMessage
  • 功能:允许用户通过 WebSocket 连接向特定的客户端发送消息。这是聊天应用或任何需要发送点对点消息的应用程序的核心功能。
  • 使用场景:当用户输入目标客户端 ID 和消息内容后,点击“Send”按钮发送消息。
3. handleFileChange
  • 功能:捕获用户选择的音频文件,并为后续的音频传输做准备。这个函数确保了音频文件能够正确读取并保存在组件的状态中。
  • 使用场景:当用户选择音频文件时,这个函数被触发,准备好文件以便稍后发送。
4. sendAudio
  • 功能:将音频文件通过 WebSocket 连接发送到服务器。这个函数负责处理音频文件的分块传输,并确保传输的完整性。
  • 使用场景:当用户选择了音频文件并点击“Send Audio”按钮时,触发这个函数开始传输音频数据。

连接函数

connect() {// 创建一个 WebSocket 连接,保存到 socketTask 变量中this.socketTask = uni.connectSocket({url: "wss://erroright.cn/ws", // WebSocket 服务器的地址success: () => {// 如果 WebSocket 连接创建成功,输出成功日志console.log("WebSocket connection created.");},fail: (error) => {// 如果 WebSocket 连接创建失败,输出错误信息console.error("Failed to create WebSocket connection:", error);}});// 当 WebSocket 连接成功时触发的事件回调this.socketTask.onOpen(() => {// 输出调试信息,表示已经成功连接到 WebSocket 服务器console.log("Connected to WebSocket server.");// 构造发送给服务器的初始数据,包含客户端的唯一 ID 和配对客户端的唯一 IDlet data = {send_id: this.uniqueId,      // 客户端的唯一 IDreceive_id: this.mateUniqueId // 配对客户端的唯一 ID};// 输出调试信息,显示即将发送的初始数据console.log("Sending initial data:", data);// 通过 WebSocket 连接发送初始数据this.socketTask.send({data: JSON.stringify(data), // 将数据转换为 JSON 字符串格式发送fail: (error) => {// 如果发送初始数据失败,输出错误信息console.error("Failed to send initial data:", error);}});});// 当从服务器接收到消息时触发的事件回调this.socketTask.onMessage((event) => {// 输出调试信息,显示接收到的消息内容console.log("Received message from server:", event.data);// 将接收到的消息添加到 messages 数组中,以便在页面中显示this.messages.push(event.data);});// 当 WebSocket 连接关闭时触发的事件回调this.socketTask.onClose(() => {// 输出调试信息,表示 WebSocket 连接已关闭console.log("Disconnected from WebSocket server.");});// 当 WebSocket 连接出错时触发的事件回调this.socketTask.onError((error) => {// 输出调试信息,显示具体的错误内容console.error("WebSocket error occurred:", error);});
}

发送信息

sendMessage() {// 检查 WebSocket 连接是否存在并且处于打开状态if (this.socketTask && this.socketTask.readyState === WebSocket.OPEN) {// 构造要发送的消息对象,包含目标客户端 ID 和消息内容let message = JSON.stringify({content: this.messageText  // 要发送的消息内容});// 输出调试信息,显示即将发送的消息内容console.log("Sending message:", message);// 通过 WebSocket 连接发送消息this.socketTask.send({data: message,  // 发送的消息数据success: () => {// 发送成功后清空消息输入框this.messageText = '';},fail: (error) => {// 如果消息发送失败,输出错误信息console.error("Failed to send message:", error);}});} else {// 如果 WebSocket 未连接或状态不正确,输出错误信息console.error("WebSocket is not connected or is in an invalid state.");}},

捕获音频文件,为后续音频传输做准备

handleFileChange(event) {// 从事件对象 event 中获取用户选择的文件// event.target.files 是一个 FileList 对象,包含了用户选择的所有文件// [0] 代表获取第一个文件对象,因为通常用户只选择一个文件this.audioFile = event.target.files[0];// 输出调试信息,显示用户选择的文件的名称// 通过 this.audioFile.name 获取文件的名称,确认用户选择的文件正确无误console.log("Selected audio file:", this.audioFile.name);
}

发送音频文件给服务器

sendAudio() {// 检查是否选择了音频文件if (!this.audioFile) {// 如果没有选择音频文件,提示用户先选择文件uni.showToast({title: 'Please select an audio file first', // 显示的提示信息icon: 'none' // 不显示图标});return; // 结束函数执行}// 创建一个 FileReader 对象,用于读取文件内容let reader = new FileReader();// 定义 FileReader 的 onload 回调,当文件读取完成时触发reader.onload = (event) => {// 获取读取的文件内容,作为 ArrayBuffer 对象let arrayBuffer = event.target.result;// 将 ArrayBuffer 转换为 Uint8Array,这样可以逐字节处理数据let bytes = new Uint8Array(arrayBuffer);// 输出调试信息,显示即将发送的音频数据console.log("Sending audio data:", bytes);// 通过 WebSocket 发送音频数据this.socketTask.send({data: bytes.buffer, // 将 Uint8Array 的缓冲区发送success: () => {// 音频数据发送成功后,再发送一个 "EOF" 标志,表示传输结束this.socketTask.send({data: "EOF", // 发送 "EOF" 表示文件传输结束success: () => {console.log("EOF sent."); // 输出 "EOF" 发送成功的日志},fail: (error) => {console.error("Failed to send EOF:", error); // 输出 "EOF" 发送失败的错误信息}});},fail: (error) => {console.error("Failed to send audio data:", error); // 输出音频数据发送失败的错误信息}});};// 开始异步读取音频文件的内容,并将其转换为 ArrayBuffer 格式reader.readAsArrayBuffer(this.audioFile);
}

三、蓝牙

1. initBlue

  • 功能:初始化蓝牙模块。
  • 操作
    • 调用 uni.openBluetoothAdapter 打开蓝牙适配器。
  • 如果初始化成功,调用 startBluetoothDevicesDiscovery 开始搜索蓝牙设备。

2. startBluetoothDevicesDiscovery

  • 功能:启动蓝牙设备的搜索。
  • 操作
    • 调用 uni.startBluetoothDevicesDiscovery 开始搜索蓝牙设备。
  • 如果搜索成功,设置蓝牙设备发现的回调函数 onBluetoothDeviceFound

3. onBluetoothDeviceFound

  • 功能:处理发现的蓝牙设备。
  • 操作
    • 当发现蓝牙设备时,检查设备名称是否与 deviceName 匹配。
  • 如果匹配,保存设备的 deviceId 并调用 createBLEConnection 尝试连接设备。

4. createBLEConnection

  • 功能:创建蓝牙低功耗设备 (BLE) 的连接。
  • 操作
    • 调用 uni.createBLEConnection 连接到指定的蓝牙设备。
    • 如果连接成功,更新连接状态为已连接 (isConnected: true)。
    • 延迟 3 秒后调用 getBLEDeviceServices 获取设备的服务列表。

5. getBLEDeviceServices

  • 功能:获取连接设备的服务列表。
  • 操作
    • 调用 uni.getBLEDeviceServices 获取设备的服务。
    • 如果成功,遍历服务列表并检查是否包含指定的服务 UUID (SERVICE_UUID)。
    • 如果找到匹配的服务,调用 getBLEDeviceCharacteristics 获取该服务的特性。
    • 如果获取服务失败或服务列表为空,最多重试三次。

6. getBLEDeviceCharacteristics

  • 功能:获取指定服务的特性。
  • 操作
    • 调用 uni.getBLEDeviceCharacteristics 获取服务的特性列表。
    • 如果成功,检查特性是否支持写操作,并将其 UUID 保存到 CHARACTERISTIC_UUID
    • 如果没有找到支持写操作的特性,输出错误信息。

7. sendDataToBluetooth

  • 功能:发送数据到蓝牙设备。
  • 操作
    • 首先检查是否已连接到蓝牙设备。
  • 如果连接正常,调用 sendDataInChunks 将数据分块发送到蓝牙设备。

8. sendDataInChunks

  • 功能:将数据分块发送到蓝牙设备。
  • 操作
    • 根据设备的 MTU 大小(这里假设为 517 字节)将数据分块。
  • 对每个数据块,创建 ArrayBuffer 并转换为字节数据后,通过 uni.writeBLECharacteristicValue 发送给设备。

9. action

  • 功能:发送 “over” 命令到蓝牙设备,表示数据传输完成或其他特定操作。
  • 操作
    • 类似于 sendDataToBluetooth,但是这里发送的是 “over” 字符串。
<template><view class="container"><view class="intro">蓝牙消息发送示例。</view><button @click="initBlue">连接蓝牙</button><button @click="sendDataToBluetooth">发送数据</button><button @click="action">开始运行</button></view>
</template><script>export default {data() {return {deviceName: "MiniBay", // 要连接的设备名称deviceId: null, // 设备 IDSERVICE_UUID: "4FAFC201-1FB5-459E-8FCC-C5C9C331914B", // 服务的 UUID (大写)CHARACTERISTIC_UUID: "BEB5483E-36E1-4688-B7F5-EA07361B26A8", // 特性的 UUID (大写)dataToSend:"{10, 10, 200},{20, 10, 200},{10, 20, 200},{30, 10, 200},{25, 25, 100},{10, 30, 200},{20, 20, 100},{30, 30, 100},{35, 35, 100},{40, 40, 100},{45, 45, 100},{50, 50, 100},{55, 55, 100},{60, 60, 100},{65, 65, 100},{70, 70, 100},{75, 75, 100},{80, 80, 100},{85, 85, 200},{90, 90, 200},{95, 95, 200},{90, 90, 200},{95, 95, 200},{90, 90, 200},{95, 95, 200},{90, 90, 200},{95, 95, 200},{90, 90, 200},{95, 95, 200},{90, 90, 200},{95, 95, 200},{90, 90, 200},{45, 45, 200},{0, 0, 0}",isConnected: false // 用于跟踪蓝牙是否已连接}},methods: {initBlue() {uni.openBluetoothAdapter({success: res => {console.log('初始化蓝牙成功');this.startBluetoothDevicesDiscovery();},fail: err => {console.log('初始化蓝牙失败');console.error(err);}});},startBluetoothDevicesDiscovery() {uni.startBluetoothDevicesDiscovery({success: res => {console.log('开始搜索设备...');this.onBluetoothDeviceFound();},fail: err => {console.log('搜索设备失败');console.error(err);}});},onBluetoothDeviceFound() {uni.onBluetoothDeviceFound(devices => {devices.devices.forEach(device => {if (device.name === this.deviceName) {console.log('找到设备:', device);this.deviceId = device.deviceId;this.createBLEConnection();}});});},createBLEConnection() {if (this.deviceId) {uni.createBLEConnection({deviceId: this.deviceId,success: res => {console.log('连接设备成功');this.isConnected = true; // 更新连接状态// 延迟以确保设备服务准备就绪setTimeout(() => {this.getBLEDeviceServices();}, 3000);},fail: err => {console.log('连接设备失败');this.isConnected = false; // 确保连接失败时状态更新console.error(err);}});}},getBLEDeviceServices(retryCount = 0) {uni.getBLEDeviceServices({deviceId: this.deviceId,success: res => {if (res.services && res.services.length > 0) {console.log('获取服务成功:', res.services);res.services.forEach((service, index) => {console.log(`服务 ${index + 1}: UUID = ${service.uuid}`);});// 检查是否找到特定的服务const service = res.services.find(s => s.uuid === this.SERVICE_UUID);if (service) {this.getBLEDeviceCharacteristics(service.uuid);} else {console.error('未找到指定的服务');}} else {console.error('服务列表为空或未找到服务');if (retryCount < 3) { // 尝试重试三次console.log(`重试获取服务 (${retryCount + 1}/3)`);setTimeout(() => {this.getBLEDeviceServices(retryCount + 1);}, 3000); // 每次延迟3秒再重试}}},fail: err => {console.log('获取服务失败');console.error(err);}});},getBLEDeviceCharacteristics(serviceId) {uni.getBLEDeviceCharacteristics({deviceId: this.deviceId,serviceId: serviceId,success: res => {console.log('获取特性成功:', res.characteristics);// 检查特性是否支持写操作const writableCharacteristic = res.characteristics.find(char => char.properties.write);if (writableCharacteristic) {this.CHARACTERISTIC_UUID = writableCharacteristic.uuid;} else {console.error('未找到支持写操作的特性');}},fail: err => {console.log('获取特性失败');console.error(err);}});},sendDataToBluetooth() {if (!this.isConnected) {console.error('未连接到蓝牙设备');return;}const data = this.dataToSend;this.sendDataInChunks(data);},sendDataInChunks(data) {const maxChunkSize = 517; // 依据设备的 MTU,20 是默认蓝牙包大小for (let i = 0; i < data.length; i += maxChunkSize) {const chunk = data.slice(i, i + maxChunkSize);const buffer = new ArrayBuffer(chunk.length);const dataView = new DataView(buffer);for (let j = 0; j < chunk.length; j++) {dataView.setUint8(j, chunk.charCodeAt(j));}uni.writeBLECharacteristicValue({deviceId: this.deviceId,serviceId: this.SERVICE_UUID,characteristicId: this.CHARACTERISTIC_UUID,value: buffer,success: res => {console.log('数据发送成功:', chunk);},fail: err => {console.log('数据发送失败');console.error(err);}});}},action() {if (!this.isConnected) {console.error('未连接到蓝牙设备');return;}const formattedData = "over";const buffer = new ArrayBuffer(formattedData.length);const dataView = new DataView(buffer);for (let i = 0; i < formattedData.length; i++) {dataView.setUint8(i, formattedData.charCodeAt(i));}uni.writeBLECharacteristicValue({deviceId: this.deviceId,serviceId: this.SERVICE_UUID,characteristicId: this.CHARACTERISTIC_UUID,value: buffer,success: res => {console.log('数据发送成功:', formattedData);},fail: err => {console.log('数据发送失败');console.error(err);}});}}}
</script><style>.container {padding: 20px;font-size: 14px;line-height: 24px;}
</style>

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • TinyEngine是什么?
  • 大疆秋招笔试
  • 【数学建模】MATLAB快速入门
  • 深度学习的数学之魂:传统机器学习的超越者
  • 深度学习基础—学习率衰减与局部最优问题
  • Java-Web面试题汇总
  • XML Schema 复合元素 - 仅含文本
  • Python实现水果忍者(开源)
  • Openstack 所需要的共享服务组件及核心组件
  • 代码随想录:动态规划6-10
  • TCP/IP 协议及其协议号
  • Webrtc之SDP协议
  • 20221元组
  • 在阿里云上部署 Docker并通过 Docker 安装 Dify
  • Linux (Ubuntu) conda:未找到命令报错处理
  • [数据结构]链表的实现在PHP中
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 77. Combinations
  • Apache的80端口被占用以及访问时报错403
  • CentOS7 安装JDK
  • download使用浅析
  • Gradle 5.0 正式版发布
  • Javascript弹出层-初探
  • JDK 6和JDK 7中的substring()方法
  • PHP CLI应用的调试原理
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • SpringCloud集成分布式事务LCN (一)
  • sublime配置文件
  • 聚类分析——Kmeans
  • 每天一个设计模式之命令模式
  • 驱动程序原理
  • 如何使用 JavaScript 解析 URL
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 微信小程序填坑清单
  • 为视图添加丝滑的水波纹
  • 走向全栈之MongoDB的使用
  • ​ssh免密码登录设置及问题总结
  • ​如何在iOS手机上查看应用日志
  • # 数论-逆元
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #100天计划# 2013年9月29日
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (一)模式识别——基于SVM的道路分割实验(附资源)
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (自适应手机端)响应式服装服饰外贸企业网站模板
  • *p++,*(p++),*++p,(*p)++区别?
  • .a文件和.so文件
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .NET Core中的去虚
  • .NET/C#⾯试题汇总系列:⾯向对象