0%

HitMan Tube拟辉光钟制作记录

前言

之前看到B站大佬做了一个拟辉光钟,并且恰好我的同学Sadudu也在做辉光钟,于是就正好一起做了一下这个用LED模拟的辉光钟,不为啥,就是使用的是LED,便宜并且寿命长,下面是效果图:
2.1

相信大家看到这个肯定也是比较有能力的,因此我下面就简单介绍硬件和微信小程序的设计,重点还是就核心的代码的分享,分别是STM32单片机的WS2812B驱动程序和微信小程序的BLE通讯程序,保证可用!!!

原理分析

自从我看到了以后我就一直在思考他是一个怎么样的东西。由于我看了辉光管的实物,知道是一层一层的电热丝结构,于是就想是LED隔开每一层,然后单独点亮即可,当时想到这个一晚上都没有睡着呢!但是单独控制每个LED有比较麻烦,如果是单片机IO用PWM调光的话可能会使用非常多的锁存器,电路设计较为麻烦,成本也会一定程度增加,经过一番调研(其实就是问的我的几个同学),最终确定了WS2812B这个方案,这个LED是单总线的,我们看到的圣诞节灯带或者是家庭装修的大部分就是用的这个,他可以一直向后链接,只需要在程序里面设计即可。选定了光源的方案,我觉得了解的人一般也都有一定的开发基础,而且这个链接起来非常简单,随便找就可以看到电路图,就不展示了,下面决定开源一下驱动,我是使用的STM32来设计的,并且是原子的库,没有使用HAL库。驱动的思路是定时器加上DMA,因为这个单总线对时间要求比较严格,很多都是微秒级别的操作,因此用DMA会好很多,具体的操作参考如下

微信小程序设计

在PC段直接通信即可,但是每次使用电脑的确很不方便,于是想到了APP,但是安卓和IOS需要开发两版APP的话我觉得工作量还是比较大的,经过一番调研(还是和我的几个同学讨论),最终确定了使用微信小程序的方案。于是通信的方式又需要重新选定,由于IOS的微信小程序调用蓝牙的API的时候有一定的限制,具体请看官方文档,解决方案就是固定蓝牙的名称,通过名称确定设备,我设计了一个简单的界面,如下所示.做一个声明,由于微信小程序不能本地上传图片,只能使用网络地址的图片,我这里使用的是B站的壁纸娘的相簿
2.2
2.3

下面开始微信小程序的学习,于是到B站找到相应的课程学习。由于以前学过网页设计,并且也管理了实验室的网站,通过一段时间学习很快就上手了。微信小程序的连接只能连接BLE,就是低功耗蓝牙,我们以前电赛经常用到的HC 05,HC-06这样的设备是蓝牙3.0或以下的,BLE是蓝牙4.0协议,虽然他也向下兼容,但是其优秀的低功耗设计是搭建集体、家庭、个人网络的最佳选择。

这里贴一下蓝牙链接和通信的代码。我想吐槽一下,前期在网上找了很多的测试代码都不能用,最后还是使用官方的示例一步一步吃透才搞定的,以后看文档一定还是看官方的或者GitHub,CSDN上面的感觉有点鱼龙混杂的感觉,下面是BLE连接的代码:

首先是 ble.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
const app = getApp()

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: [],
},

//首先是IOS指定设备的
connectBLE() {
//先初始化(在打开蓝牙之后也执行了搜寻的任务)
this.openBluetoothAdapter()

//然后列出所有的设备ID,安卓是Mac地址,IOS是设备的UUID
//console.log()

},

//1.初始化蓝牙模块
openBluetoothAdapter() {
console.log("初始化蓝牙")
wx.openBluetoothAdapter({
success: (res) => { //初始化成功
console.log('openBluetoothAdapter success', res)
this.startBluetoothDevicesDiscovery() //开始搜寻周围的设备
},
fail: (res) => {
wx.showModal({
title: '温馨提示',
content: '请检查手机蓝牙是否打开',
showCancel: false,
});
}
})
},



//2.开始搜寻周围的设备(一般在连接后使用wx.stopBluetoothDevicesDiscovery停止搜索)
startBluetoothDevicesDiscovery() {
console.log("开始搜寻周围的设备")
if (this._discoveryStarted) {
return //一直搜索
}
this._discoveryStarted = true
console.log('***************************************')

wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true, //允许重复上报同意设备(RSSI值可能会不同)
success: (res) => {
console.log('startBluetoothDevicesDiscovery success', res)
this.onBluetoothDeviceFound() //监听寻找到新设备的事件
},
})

},

//3.监听寻找新设备的事件
onBluetoothDeviceFound() {
console.log("监听寻找新设备的事件")
wx.onBluetoothDeviceFound(function (devices) {
console.log(devices)

if (devices.name == 'HitManTube') {
this.stopBluetoothDevicesDiscovery();
console.log("device found")

}
})
wx.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
console.log("Name:" + device.name)

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)

if (device.name == "HitManTube") {
console.log("找到相应的ID")

this.createBLEConnection(device.name)
}

})
})
},

//4.连接BLE设备(可直接传入之前小程序连接的蓝牙设备)
createBLEConnection(e) {
console.log("开始连接设备!!" + 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() //连接之后停止蓝牙搜索
},


createBLEConnection(founddeviceId) {
console.log("开始连接设备!!" + e)
deviceId = founddeviceId
wx.createBLEConnection({
deviceId,
success: (res) => {
this.setData({
connected: true,
name,
deviceId,
})
this.getBLEDeviceServices(deviceId) //获取蓝牙设备所有服务
}
})
this.stopBluetoothDevicesDiscovery() //连接之后停止蓝牙搜索
},
//5.停止寻找蓝牙设备(在连接之后调用,因为搜寻蓝牙设备比较耗费系统资源)
stopBluetoothDevicesDiscovery() {
console.log("停止搜寻蓝牙设备")
wx.stopBluetoothDevicesDiscovery()
},

//6.获取蓝牙设备所有服务(serviceID)
getBLEDeviceServices(deviceId) {
console.log("获取蓝牙设备所有服务(service) ")
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
}
}
}
})
},

//7.获取蓝牙设备某个服务中所有特征值(characteristicID)
getBLEDeviceCharacteristics(deviceId, serviceId) {
console.log("获取蓝牙设备某个服务中所有特征值(characteristic) ")
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId, //蓝牙服务 uuid,需要使用 getBLEDeviceServices 获取
success: (res) => {
console.log('getBLEDeviceCharacteristics success', res.characteristics)

for (let i = 0; i < res.characteristics.length - 1; i++) {
let item = res.characteristics[i]
//显示所有的特征值
console.log(item)

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)
}
} else {
data[`chs[${idx}]`] = {
uuid: characteristic.characteristicId,
value: ab2hex(characteristic.value)
}
}
this.setData(data)
})
},
})

其次是 ble.wxml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<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>
<button bindtap="connectBLE">一键连接</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>

有了这两个文件应该就可以成功连接并且发送一定的数据了,想要自己发送相应的东西还是需要自定义相应的通讯协议,后面需要根据自己的需求来设计了。

结语

关于这个开发就到这里了,很多文件可以到QQ群:1321653,我也是经过了群主和管理员的帮助才能顺利完成,而且里面的成员都是非常友好的,有问题大家也可以一起思考。最后呢感觉做这些还是非常有成就感的,非常开心,2333

-------------本文结束感谢您的阅读-------------