大疆特洛TELLO手柄盖世小鸡Gamesir-T1D蓝牙连接破解DIY

来自https://www.hsli.top

手头有个大疆TELLO并买了个盖世小鸡Gamesir-T1D遥控器,想着玩够了小飞机还可以用手柄来做DIY小车。。。万万没想到这个遥控器居然是定制的,只能遥控TELLO小飞机。。。电脑和手机都无法通过蓝牙直接连接。遂破解之。

先拆为敬

正面,做工还真是可以,本来手柄的手感就挺好的。可以看到单片机控制板和蓝牙板是单独的两个小板,可能不同遥控用的是同一套底板和不同的蓝牙以及单片机吧。

蓝牙板,芯片名称被磨掉了打上GS-T1d他们自家的标签。看起来像是CC2541方案。单片机和蓝牙之间预留有IIC的测试点。

单片机控制板,同样磨掉了芯片丝印。。

背后貌似预留了串口测试点?

硬件上看不出怎么破,手头没有设备,算了,拆开看一眼装回去。

用BLE调试软件连接,发现并不是用的设想的蓝牙串口方案来遥控,而是真的蓝牙协议。一共有3个Services,其中第三个Service接收的字节流一直不变,第二个变化没有什么规律。第一个UUID开头是00008651的Servie接收的字节流,变化比较有规律。

通过调试发现,在遥控器无动作的时候,第一个Service接收值一直都是C9-C6-86-A1-00-DB-B9-03-01-01-01-0B-01-E1-07-07-06-10-1E-56

有动作的时候,前两个Bytes一直是A1-C5。因此,前两个字节可以用来判断遥控状态。

以下的讨论都是在A1-C5状态下。

最后一个字节是一直自增的,按键按下,放开会使得该字节自增2,一直到0xFF又变回0x00,这个是用来记录按键按下次数的。

10-13字节是按键按下的状态,手柄上每个按键对应一位。实际上只要把这几个字节读取出来,就能和按键一一对应上了。

接下来是摇杆。3-7字节就是摇杆状态,一开始花了挺大劲去猜哪个摇杆对应哪些字节,后来发现其实很好猜的。一共5个字节,2个摇杆共4个方向,所以每个方向就是10bit。一般来说10bit的AD也是相当常见的。

所以只要把这些字节读取出来,然后每10位凑成整数,就可以还原出摇杆的读数了。

在linux上连接读取BLE可以用pyblue模块,可以在树莓派上用了。

下面给出读取的测试代码:

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
from __future__ import print_function
import struct
import time
from bluepy.btle import Peripheral

# 来自(https://www.hsli.top)

# 手柄的MAC地址可以通过蓝牙调试软件获得
my_gamesir = Peripheral('c6:xx:xx:xx:xx:b9', 'random')

services = my_gamesir.getServices()
for service in services:
print(service)

control_service = services[3]
charac_dics = control_service.getCharacteristics()
for charac in charac_dics:
print(charac.uuid)


while True:
time.sleep(0.01)

charac1, charac2, charac3 = [item.read() for item in charac_dics]

status_code = struct.unpack('H', charac1[:2])[0]

if status_code == 50593:
on_press_key = struct.unpack('I', charac1[9:13])[0]
press_counter = struct.unpack('B', charac1[-1])[0]

bar_status = struct.unpack('5B', charac1[2:7])
bar_status_bin = ''.join([bin(item).split('b')[1].rjust(8).replace(' ', '0') for item in bar_status])

left_drag = int(bar_status_bin[0:10], 2)
left_push = int(bar_status_bin[10:20], 2)
right_drag = int(bar_status_bin[20:30], 2)
right_push = int(bar_status_bin[30:40], 2)

print("status %s" % status_code, end=' ')
print("on_press %s" % on_press_key, end=' ')
print("press_counter %s" % press_counter, end=' ')
print("left_drag %s" % left_drag, end=' ')
print("right_drag %s" % right_drag, end=' ')
print("left_push %s" % left_push, end=' ')
print("right_push %s" % right_push, end='\r')

# break
如果文章有用,请随意打赏