手摸手教你使用 Ai-WB2(BL602)做一个自动刷抖音工具 #30
bzhou830
started this conversation in
📚Tutorials(教程)
Replies: 3 comments
0 replies
|
不愧是你~ |
0 replies
|
不如autojs |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
目录
〇、一个关于腱鞘炎的严肃技术问题
事情是这样的。
某天你躺在床上刷抖音,刷了三个小时,大拇指从"灵活"刷到了"半身不遂"。你盯着天花板,产生了一个朴素的想法:
你打开某宝,搜"刷视频神器",出来一堆几十块钱的小玩意儿:一个小夹子,夹在手机上,伸出一根"机械手指",啪嗒啪嗒地在屏幕上点。或者一个蓝牙小遥控器,按一下切一个视频。
前一种是物理外挂(笨重、吵、还挑手机壳),后一种是蓝牙 HID 设备——也就是我们今天要自己焊(其实不用焊)的那个。
而且自制有个巨大优势:淘宝货你不知道它在干嘛,自己写的你知道每一个字节。
好,那问题来了:一个蓝牙小盒子,凭什么能让抖音翻页?
一、原理篇:刷抖音这个动作,到底发生了什么
这一章是全文最重要的部分。搞懂了它,后面写代码就是体力活;跳过了它,你会写出一个"能连上但就是不动"的玩意儿,然后怀疑人生。
1.1 你的手指,其实一直在"骗"手机
我们来做个思想实验:你的手指在屏幕上从下往上一抹,抖音就换了个视频。
这一抹,手机内部经历了什么?
InputReader收到一堆原始事件,认出这是"一个手指先按下、再移动、再抬起";MotionEvent送给前台 App:ACTION_DOWN—— 手指按下ACTION_MOVE× N —— 手指移动(N 通常十几个到几十个)ACTION_UP—— 手指抬起关键点:抖音认的不是"某个按键",而是一串有速度、有方向的连续触摸事件。
这就决定了我们的技术路线 —— 我们得想办法伪造出这么一串事件。
1.2 Android 的输入流水线
简单画一下:
这张图里藏着一个天大的好消息:
换句话说,只要我们的蓝牙设备能上报"标准的触摸屏数据",Android 根本分不清这到底是你那根肉手指,还是一块 WB2 开发板。它俩在系统眼里是平等的。
这就是整件事的理论基础。
1.3 蓝牙 HID 的三张脸
BLE HID(准确叫 HOGP,HID over GATT)允许一个设备把自己伪装成好几种输入设备。常见的有三张脸:
1.4 一个 bit 的差距:Rel 和 Abs
很多人(包括我第一次做的时候)会想:"鼠标也能移动光标,我让光标从屏幕底下移到上面不就完了?"
不行。 原因在一个 bit 上。
在 HID 报告描述符里,
Input这一项的最后一个字节决定了坐标是"相对"还是"绝对":区别在哪?
X=+20, Y=-30意思是"往右下挪 20 和 30",不是"移动到 (20, 30)"。Android 收到后,就在屏幕上画一个小箭头,然后平移一小段。它没有"按住-拖动"的概念,所以刷不动信息流。X=500, Y=780就是"手指在 (500, 780) 这个位置"。Android 收到一串坐标 + 按下标志,就能拼出完整的滑动手势。就这一个 bit,决定了你的设备是"能刷抖音"还是"只能画个光标"。
1.5 灵魂拷问:我哪知道你手机分辨率多少?
既然要发绝对坐标,那坐标范围怎么定?
手机分辨率千奇百怪:1080×2400、1080×2340、1440×3200、还有一加那种异形屏……难道我要在固件里内置一张分辨率表?
不用。 这里有个省事到有点狡猾的技巧:
在报告描述符里,我们自己声明坐标范围:
Android 读到
Logical Maximum = 1000之后,会把 0~1000 线性映射到屏幕真实像素。于是:
Y = 780→ 在任何手机上都是"屏幕高度 78% 的位置"Y = 260→ 在任何手机上都是"屏幕高度 26% 的位置"固件完全不需要知道手机分辨率。 1080×2400 也好、4K 折叠屏也好,通用。
这个技巧叫"归一化坐标",是整个方案能落地的关键之一。
1.6 野路子:音量键翻页
在查资料的过程中,我发现抖音其实留了个后门:设置 → 通用设置 → 音量键翻页,打开之后,按手机音量键就能切视频。
那思路就简单多了:把 WB2 伪装成多媒体键盘(Consumer Control),发一个"音量+"就完事。
结论:音量键方案简单,但要看 App 脸色,还会顺手改你音量。
所以本文以触摸屏方案为主(默认配置),同时把音量键方案也做进固件里(一个宏切换),你可以两个都试试,体会一下区别。
1.7 最终方案
二、开工前的准备
2.1 硬件清单
bl602dk2.1.1 接线详解(其实超简单)
如果你懒得焊,拿一根杜邦线一头怼 GPIO12、另一头碰一下 GND,就等于"按了一次键"。想正经一点,焊个轻触开关:
要点:GPIO12 用内部上拉,按键把引脚拉到 GND 即触发(下降沿检测,见
main.c:599)。main.c:694的GPIO_PULLUP)2.2 软件环境
D:/bouffalo_sdkD:/toolchain_gcc_t-head_windows/binriscv64-unknown-elf-gccD:/bouffalo_sdk/tools/make/make2.3 先验证环境能用
能看到版本号就说明工具链没问题。
三、搭工程
3.1 目录结构
3.2 创建工程
如果你已经有一个能编译的 BL602 BLE 工程(比如上一节的
ble_keyboard_down),直接把它的配置骨架复制过来最快:然后改两个地方,把工程名换掉:
CMakeLists.txt:flash_prog_cfg.ini:3.3 defconfig 关键点
这几个开关决定了 BLE 栈能不能起来:
四、核心关卡:HID Report Map 怎么写
4.1 描述符是个啥(一句话版)
报告描述符(Report Map)就是一份"自我说明书":设备告诉主机"我接下来发的这串字节,第几个字节到第几个字节是什么意思"。
它不是给人看的,是给主机的 HID 解析器看的。你写错了,主机就理解错 —— 或者干脆不理你。
一个描述符由很多"条目(Item)"组成,每个条目 1~3 字节:
常用的前缀:
0x050x05, 0x0D= Digitizer(数字化仪/触摸)0x090x09, 0x04= Touch Screen0xA10xA1, 0x01= Application0xC00x850x85, 0x01= 这是 1 号报告0x150x25/0x260x750x75, 0x10= 16 bit0x950x95, 0x02= 2 个0x810x81, 0x02= 绝对坐标4.2 触摸屏描述符逐行解剖
这是本工程的灵魂(
main.c:121)。我们一段一段来看:Tip Switch= 1 表示"手指按在屏幕上"In Range= 1 表示"手指悬在能感应到的范围内"HID 是按字节对齐的,前面只用了 2 bit,得补齐 8 bit 才能开始下一个字段。
0x26, 0xE8, 0x03=0x03E8= 1000(小端:低字节在前)。最后的
0x81, 0x02里的0x02就是Absolute。改成0x06你就得到一个鼠标了 —— 然后发现刷不动抖音。 别问我怎么知道的。4.3 所以 5 字节报文长这样
举例:手指按在 (500, 780):
(
500 = 0x01F4,小端就是F4 01;780 = 0x030C,小端是0C 03。)4.4 Consumer 描述符:为什么必须是"位图"
第二路报告(Report ID 2)是多媒体键,用于音量键方案。它有个坑,值得单独讲:
每个功能占 1 个 bit,8 个功能正好 1 字节。全 0 = 全部松开。
为什么不能用
Report Size=16 / Count=1配 6 个 usage?因为那样是畸形描述符 —— 字段数(1 个 16 位字段)和 usage 数(6 个)对不上。后果很阴间:主机解析时会认为"全 0 的帧不合法",直接丢弃。
于是你发"按下音量+"能生效,但发"松开"(全 0)被丢了 —— 键就永远卡在按下状态。在 Windows 上的表现是:音量一路飙到最大,停不下来。
4.5 完整描述符
main.c:121的完整内容(已验证可用):五、把描述符挂到 GATT 上
5.1 HIDS 属性表
描述符写好了,得通过 GATT 暴露给手机。HIDS(0x1812)服务的属性表长这样(
main.c:322起):0x18120x2A4E0x2A4B0x2A4A0x2A4C0x2A4D0x29020x2908id=1,type=Input)0x2A4D0x29020x2908id=2,type=Input)对应代码:
5.2 Report Reference:主机怎么知道"这条特征 = 那个 Report ID"
注意上面那个
BT_UUID_HIDS_REPORT_REF(0x2908)描述符。它的值是:这就是告诉主机:"这条 GATT 特征承载的是 1 号报告,类型是 Input。"
有它之后,空中报文里就不需要带 Report ID 前缀了 —— 主机靠这个描述符就能把特征和 Report ID 对上。
本文用的是"Report Reference + 不带前缀"的接法。
5.3 索引怎么数:记住 +4 规律
bt_gatt_notify()需要你告诉它"往哪个特征发",用的是属性数组下标:下标怎么算?每路 Report 占 4 个属性(声明 + 值 + CCC + Report Reference)。
5.4 READ|WRITE|NOTIFY 一个都不能少
这一条值得单独加粗:
千万别为了"简化"把它改成 NOTIFY only。
Windows 的 HID 驱动在枚举设备时会先 Read 一次 Input Report 取初始状态。不可读 → 初始化失败 → 后面收多少 Notification 都不理你。
现象极具迷惑性:设备能连上、能配对、串口日志显示报文发出去了,但按键死活没反应。
(这条是踩坑换来的,详见项目里的另一篇教程。)
5.5 电量服务(BAS)
顺手把标准电量服务也挂上,手机设置里能显示电量,显得很专业:
电量值来自一个弱函数,默认返回 100:
想接真实电量?在你自己的
.c里重写它,用 ADC 读 VBAT 分压,映射到 0~100 就行。六、滑动手势:从"点一下"到"划一下"
6.1 上报一帧触摸(
main.c:494)那个
printf建议保留 —— 调试时看着一串DOWN/MOVE ... UP滚过去,非常有安全感。6.2 一次完整上滑(
main.c:522)三步,对应
ACTION_DOWN→ACTION_MOVE × N→ACTION_UP。6.3 参数怎么调(
main.c:67)调参指南:
TOUCH_XTOUCH_Y_STARTTOUCH_Y_ENDSWIPE_STEPSSWIPE_STEP_MS当前配置的总时长 = 16 × 10 = 160 ms,划过屏幕高度的 52%。这个速度接近真人快速上滑,抖音识别率很高。
6.4 音量键路径(
main.c:561)如果你想试试备胎方案:
那个 120 ms 的延时不能省:按下去立刻松开,主机可能来不及处理,或者把它当成一次无效抖动。
七、按键与自动刷
7.1 按键触发(
main.c:599)标准套路:下降沿检测 + 消抖延时 + 二次确认。40 ms 消抖对机械按键够用。
7.2 切换刷法
改
main.c:55一行:7.3 彻底解放双手:自动刷
改
main.c:58:打开后固件会多起一个任务,每隔
AUTO_SWIPE_MS毫秒自己划一次。连按键都省了。八、编译
成功的话,最后几行长这样:
产物在
build/build_out/ble_douyin_swiper_bl602.bin。8.1 常见编译错误
find_package(bouffalo_sdk)失败BL_SDK_BASE没设或写成/d/...BL_SDK_BASE=D:/bouffalo_sdkriscv64-unknown-elf-gccexport PATH="/d/toolchain_gcc_t-head_windows/bin:$PATH"ble_controller_initble_lib_api.h+ble_controller_init()GPIO_FUNC_GPIO未定义bflb_gpio_init(..., GPIO_INPUT|GPIO_PULLUP...)cli.h: No such fileCONFIG_BT_STACK_CLInsend_consumer defined but not usedif分支,别用#if硬裁最后那个告警:因为
SWIPE_METHOD=1时send_consumer()没被调用。本工程用运行期if (SWIPE_METHOD == 1)而不是#if,让两个函数都保持被引用,GCC 会自动折叠常量裁掉另一支 —— 两个配置都是 0 error / 0 warning。九、烧录
COMX=COM6换成你实际的串口号(设备管理器里看)BAUDRATE叫这个名,不是b=make flash会先编译再烧录9.1 烧录失败怎么办
PermissionError。Start handshake或报LOAD HELP BIN FAIL:Handshake succeeded/The ack data is b'4f4b'就成了成功标志:
十、效果展示与验证
10.1 串口日志
烧录后复位,串口(115200)应该看到:
手机连上后:
按一下 GPIO12,你会看到一串触摸帧滚过去:
Y 从 780 一路减小到 260,最后一帧
00开头表示抬手 —— 一次标准的上滑手势。10.2 🔥 调试神技:开发者选项的"指针位置"
这一招能让你"看见"手势,强烈推荐先用它验证,再打开抖音。
打开它之后,按一下板子的键,你会看到:
如果轨迹出来了,说明 HID 部分已经完全成功 —— 剩下就是抖音认不认的问题了。
10.3 实测效果
10.4 如果抖音不翻页
按这个顺序排查:
TOUCH_Y_START调大到 850、TOUCH_Y_END调小到 200(划得更长)SWIPE_STEP_MS调小到 8(划得更快)TOUCH_Y_START别超过 850十一、踩坑大全
Q1:能连上,但几秒就断(HCI 0x22 / 0x13)?
A:99% 是 32.768 kHz 时钟源不对。确认
main()里有HBN_32K_Sel(HBN_32K_RC);。如果板子上有外部晶振,改成
HBN_32K_XTAL。Q2:手机搜不到设备?
A:确认广播数据里的 UUID16 列表包含 HIDS:
BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x12, 0x18, 0x0F, 0x18)。(
0x1812小端是12 18。)Q3:连上了,按按键串口只有
host not subscribed?A:手机没对 Input Report 的 CCC 写
0x0001。正常配对时系统会自动做;如果没做,检查一下
BT_GATT_CCC的权限是不是READ|WRITE。Q4:Windows 上鼠标不动?
A:这是正常的。 触摸屏设备本来就不会移动鼠标光标。想验证请用安卓手机 + "指针位置"。
Q5:划一次翻了两三个视频?
A:手速太快、距离太长,抖音判断为"快速连续滑动"。
把
SWIPE_STEPS调大到 20、SWIPE_STEP_MS调大到 15(划得慢一点)。Q6:电量一直显示 100%?
A:默认实现就是返回 100。重写
read_battery_level()接真实 ADC。Q7:想刷快手 / B站 / 小红书?
A:直接就能用,不用改代码。因为我们的方案是在系统层模拟真实触摸,
跟 App 无关 —— 这正是它比"音量键方案"高明的地方。
Q8:能不能加个"双击点赞"?
A:能。发两次快速的"按下-抬起"(间隔 < 100 ms)就是双击。见第十二章。
十二、进阶玩法
搞定基础款之后,可以玩点花的:
12.1 双击点赞
抖音里在屏幕中间双击就是点赞。把它绑到另一个 GPIO,你就有了"刷 + 赞"二合一遥控器。
12.2 随机间隔(更像真人)
12.3 手势参数做成可配置
把
TOUCH_Y_START/TOUCH_Y_END/SWIPE_STEPS存到 Flash,再加一个厂商自定义 GATT 服务(参考
ble_hid_selfie的 0xFF10),就能用手机 App 实时调手势 —— 不用每次改代码重新烧录。
12.4 外壳
拿个 3D 打印的小盒子,或者直接用热熔胶把板子粘在手机壳背面。
接个小锂电池,贴个轻触开关。恭喜你做出了淘宝上卖 39 块的东西 —— 但你的能改代码。
十三、小结
回过头看,我们其实只做了三件事:
Logical Maximum = 1000,让 Android 自己去做映射然后代码就水到渠成了:写描述符 → 挂 GATT → 循环发 18 帧报文。
最关键的那个认知是:真触摸屏和蓝牙 HID 设备,在 Android 内核的 input 子系统那一层就汇合了。
往上走的路完全一样。所以只要你的报文格式标准,系统根本分不清那是手指还是一块 WB2。
参考
All reactions