微信小程序之蓝牙开发&虚拟摇杆

文章用于学习记录


前言

尝试 App Inventor、uni-app 以及微信小程序三种方式进行蓝牙开发,最后选择微信小程序进行蓝牙开发。


一、App Inventor

  • App Inventor 是一款 Android 小工具制作软件,使用积木编程,非常简单易上手。

  • App Inventor

    在这里插入图片描述

  • 制作 APP 的操作主要分组件设计和逻辑设计二部分

    在这里插入图片描述

  • 组件设计主要是对用户界面的控件(如按钮等) 的布局设计,
    在这里插入图片描述

  • 逻辑设计是对控件添加上逻辑功能,然后通过客户端(如蓝牙) 进行传送控制信息数据,

    在这里插入图片描述

  • 组件设计和逻辑设计完成后,再点击打包 APK 在手机上直接安装,安装好后,就可以进入控制界面,系统用户登陆界面如下图所示:
    在这里插入图片描述
    在这里插入图片描述
    但这块的逻辑部分功能并未实现。

二、uni-app

  • 开发工具:HBuilder X

  • 以安卓 App 的方式运行(需要连接数据线)

  • 参考 uniapp 通过蓝牙与硬件通讯

  • 可实现连接蓝牙发送控制指令

    在这里插入图片描述

     使用蓝牙进行数据传输的大概思路如下:
    
     初始化:打开蓝牙模块;
     搜索:检测附近存在的设备,找到目标设备;
     通讯:获取目标设备的服务值和特征值,看是否能监听、读写数据;
     发送指令:发送前后左右等控制指令;
    

三、微信小程序

3.1 示例&应用

  • 采用 forEach() 的 return 语句实现 continue 关键字效果(跳过当前循环迭代并继续执行下一次迭代)
  • 解决蓝牙(未知设备)过滤问题
    在这里插入图片描述
  • forEach() 应用
    在这里插入图片描述
  • 通过判断设备的名称,当设备名称为"JDY-24M"时,将其设备 ID 保存在变量 deviceId 中,并进行打印输出。

3.2 服务值与特征值

  • 在蓝牙中,服务值(Service UUIDs)和特征值(Characteristic UUIDs)是用于标识蓝牙设备和其功能的唯一标识符。
  • 服务值:服务值用于标识蓝牙设备提供的一组相关功能或服务。一个蓝牙设备可以包含一个或多个服务。
  • 特征值:特征值用于标识服务中的某个具体特征或数据。一个服务可以包含一个或多个特征值。
  • 每个服务都有一个唯一的服务值,通常以 UUID(Universally Unique Identifier)的形式表示。
  • 服务值可以用来描述设备的整体功能或者区分不同类型的服务。
  • 特征值也有唯一的 UUID 来进行标识,特征值可以表示传感器数据、配置选项、控制命令等。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.3 控制指令

  • 蓝牙控制指令是用于与蓝牙设备进行通信和控制的指令。
  • 这些指令通常以字节流或字符串的形式发送到蓝牙设备,以执行特定的操作或获取设备的信息。
    在这里插入图片描述

3.4 测试

  1. 串口调试助手用于与串口设备进行通信、调试和测试。

  2. 串口设置:设置串口的通信参数,如串口号、波特率(Baud Rate)、数据位(Data Bits)等。

  3. 数据发送和接收:输入要发送的数据并发送到串口,同时显示接收到的数据。

  4. 数据解析和显示:支持对接收到的串口数据进行解析和显示,例如十六进制显示等。

    在这里插入图片描述

3.5 十六进制转换

function ab2hex(buffer) {
      var hexArr = Array.prototype.map.call(
      new Uint8Array(buffer),
      function(bit) {
      	return ('00' + bit.toString(16)).slice(-2)
  	 }
  )
   return hexArr.join('');
}
代码具体含义:
	这段代码定义了一个函数ab2hex,用于将数组缓冲区(buffer)转换为十六进制字符串。
	'ab2hex' 函数接受一个参数 buffer,表示要进行转换的数组缓冲区;
	在函数内部,使用 Array.prototype.map 方法对 buffer 进行遍历,创建一个新的数组 hexArr;
	在遍历过程中,对于 buffer 中的每个元素(bit),使用匿名函数进行处理;
	匿名函数将每个元素转换为两位的十六进制字符串,并使用 ('00' + bit.toString(16)).slice(-2) 的方式实现补零操作;
	bit.toString(16) 将当前元素转换为十六进制字符串;
	('00' + bit.toString(16)) 在转换的字符串前面添加两个零,确保字符串的长度为两位;
	.slice(-2) 从字符串的末尾截取最后两位,以确保字符串长度不超过两位;
	Array.prototype.map.call 将匿名函数应用于 buffer 中的每个元素,并返回一个新的数组 hexArr;
	最后,通过函数返回 hexArr.join('') 将转换后的十六进制字符串数组连接成一个字符串,并将其作为函数的返回值;

3.6 问题解决

  • Keil 编译报错
  • Missing Complier Version 5
  • 将 v5 版本的编译器 ARMCC 复制到 keil 安装目录下的 ARM 文件夹里即可;
    在这里插入图片描述

四、虚拟摇杆

4.1 界面布局

参考 微信小程序之虚拟摇杆练习,实现手指拖动图片任意移动并限定在一个圆形范围内,当松开手指时图片返回到中心点。在视图容器中添加一个背景图片、一个摇杆图片,分别设置图片样式的宽、高,实现图片叠加的效果。
在这里插入图片描述
根据变量 leftLooks 和 topLooks 动态调整图片左偏移和上偏移,同时,绑定了 bindtouchend、bindtouchmove、bindtap 事件分别对应触摸结束、触摸移动和触摸点击的事件处理函数,并在底部添加了三个文本框显示虚拟摇杆的中心位置、当前滑动位置和摇杆的偏转角度。

4.2 代码示例

<view class="relative">

  <!-- 背景图片 -->
  <image style="width: 100px; height: 100px;" class="pic_background" src="../../yaogan_di.png"></image>

  <!-- 摇杆图片 -->
  <image style="width: 80px; height: 80px;left:{{leftLooks}}px;top:{{topLooks}}px;" bindtouchend='ImageReturn' bindtouchmove='ImageTouchMove' class="pic_tou" src="../../yaogan_tou.png " bindtap="ImageTouch"></image>

  <div class="box">
    <text class="log-item">虚拟摇杆中心:(160,310)</text>
    <text class="log-item">当前滑动位置:({{leftLooks}},{{topLooks}})</text>
    <text class="log-item">摇杆偏转角度:{{angle}}°</text>
  </div>

</view>

4.3 摇杆角度

原先摇杆角度存在象限(正负)之分,若要根据角度发送控制指令,出现度数重复的情况下不易处理。
在这里插入图片描述

故采取把摇杆地图看成两个半圆,当摇杆移动到左边,角度为0度或360度(因为360度为一圈,已经绕回到起点);当摇杆移动到上方,角度为90度;当表示摇杆移动到右边,角度为180度;当表示摇杆移动到下方,角度为270度。
在这里插入图片描述

4.4 代码示例

Page({
  data: {
    logs: [],
    StartX: '160',
    StartY: '310',
    leftLooks: '',
    topLooks: '',
    radius: '60',
    angle: '',
  },

  onLoad: function () {},
  
// 函数在摇杆图片被点击时触发,打印出"点击"的信息

  ImageTouch: function (e) {
    console.log("点击");
  },
  
//  函数在摇杆图片被拖动时触发,首先获取触摸点的坐标,
  ImageTouchMove: function (e) {
    var self = this;
    // 通过 e.touches[0].clientX 和 e.touches[0].clientY 获取相对于页面的触摸点的 X 和 Y 坐标,
    // 并减去40来调整坐标位置(可能是根据图片大小进行的调整)
    var touchX = e.touches[0].clientX - 40;
    var touchY = e.touches[0].clientY - 40;
    // 将触摸点的坐标传递给GetPosition函数,以获取移动后的摇杆位置和角度信息
    var movePos = self.GetPosition(touchX, touchY);
//   console.log("接触坐标:(" + touchX + "," + touchY + ")");
//   console.log("图片坐标:(" + movePos.posX + "," + movePos.posY + ")");
	// 将移动后的摇杆位置和角度信息更新到页面的数据中,从而实现实时显示
    self.setData({
      leftLooks: movePos.posX,
      topLooks: movePos.posY,
      angle: Math.round(movePos.angle)
    });
    console.log("角度:" + Math.round(movePos.angle));
  },
  
// 在触摸结束时触发,将摇杆图片的位置和角度重置为初始状态
  ImageReturn: function (e) {
  	// 将当前上下文的 this 引用保存到变量 self 中,以便在函数内部使用
    var self = this;
    // 更新页面的数据,将 leftLooks 和 topLooks 设置为初始位置 self.data.StartX 和 self.data.StartY,
    // 将角度 angle 设置为空字符串"",当触摸结束时,摇杆图片的位置将回到初始位置,角度信息将被清空
    self.setData({
      leftLooks: self.data.StartX,
      topLooks: self.data.StartY,
      angle: ""
    });
  },
  
// 用于计算摇杆图片的位置和角度信息,
// 函数接受touchX和touchY作为参数,表示当前触摸点的坐标
  GetPosition: function (touchX, touchY) {
    var self = this;
    // 计算触摸点与摇杆中心点的水平距离 DValue_X 和垂直距离 Dvalue_Y,以及两者的欧几里得距离 Dvalue_Z。
    var DValue_X = touchX - self.data.StartX;
    var Dvalue_Y = touchY - self.data.StartY;
    var Dvalue_Z = Math.sqrt(DValue_X * DValue_X + Dvalue_Y * Dvalue_Y);
    // 调用 self.GetAngle 方法计算摇杆的偏转角度,并将 DValue_X 和 Dvalue_Y 作为参数传递
    self.GetAngle(DValue_X, Dvalue_Y);

	// 计算触摸点与摇杆中心点的夹角,并将其转换为角度制
    var angle = Math.atan2(Dvalue_Y, DValue_X) * 180 / Math.PI;
    // 将负角度转换为正角度,确保角度在0到360之间
    angle = (angle + 360) % 360; // 将负角度转换为正角度

    var imageX, imageY;

	// 在范围内,将 imageX 和 imageY 设置为触摸点的坐标 touchX 和 touchY。
    if (Dvalue_Z <= self.data.radius) {
      imageX = touchX;
      imageY = touchY;
    } else {
      // 若超出范围,计算缩放比例 ratio,并根据比例将 DValue_X 和 Dvalue_Y 乘以比例后再加上摇杆中心点的坐标(160, 310),得到缩放后的坐标 imageX 和 imageY。
      var ratio = self.data.radius / Dvalue_Z;
      imageX = DValue_X * ratio + 160;
      imageY = Dvalue_Y * ratio + 310;
    }
	// 使用 Math.round 将 imageX 和 imageY 四舍五入为整数,并将计算得到的位置和角度信息以对象的形式返回。
    imageX = Math.round(imageX);
    imageY = Math.round(imageY);

    return { posX: imageX, posY: imageY, angle: angle };
  },

  // 用于计算摇杆的偏转角度
  // 函数接受 DValue_Y 和 Dvalue_X 作为参数,分别表示触摸点的垂直距离和水平距离
  GetAngle: function (DValue_Y, Dvalue_X) {
    var self = this;
    // 条件判断检查 DValue_Y 是否为非零值,避免出现除以零的情况。
    if (DValue_Y != 0) {
      // 计算 angleTan 的反正切值,并将结果保存在变量ang中。
      var angleTan = Dvalue_X / DValue_Y;
      var ang = Math.atan(angleTan);
      // 将 ang 乘以180 / Math.PI 进行角度制的转换,得到角度值 angs
      var angs = ang * 180 / Math.PI;
      // 将 angs 四舍五入为最接近的整数,得到最终的偏转角度 result
      var result = Math.round(angs);
      // 将偏转角度result设置为页面数据中的angle属性,实现数据的更新和绑定
      self.setData({
        angle: result
      });
      console.log(result);
    }
  }
});

4.5、效果展示

在这里插入图片描述


总结

以上就是整个蓝牙开发过程以及虚拟摇杆的实现与改进总结。
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

701044

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

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

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

打赏作者

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

抵扣说明:

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

余额充值