学校洗衣机初探


2024.04.05
科普 技术 教学 电子
9 分钟阅读

  学校每个寝室的阳台上都有一台洗衣机,通过专门的APP付款之后便可以启动洗衣机进行洗衣。这种有意思的东西自然引起了我的注意。在和同学进行交流的时候发现对方也对这个洗衣机有了一些兴趣,并且已经拆解并了解过了内部结构,于是便合计着打算应该好好研究研究。

  本文中提到的大多数软件部分例如电脑端串口读取脚本、Android软件主要由我完成,大多数硬件部分例如PCB绘制、硬件程序编写由好友ESsanae完成。

第一次尝试

  学校里的洗衣机型号为TCL的TB-V6001G,但是洗衣机的按钮全部被拆除,无法通过原有的按钮进行操控。通过同学的拆解得知,洗衣机的控制部分主要有两块板子组成,一块板子集成了网卡,用于接收指令并上传一些数据(例如洗衣机的当前状态和洗衣剩余时间等等),另一块板子用于对洗衣机进行控制,执行洗衣操作。

  知道了内部的一些信息之后,就可以尝试去破解一下了。原本以为洗衣机与云端的通信使用的是学校的校园网。于是做了一个钓鱼WiFi想要尝试看看洗衣机发送和接收的数据,但是一直没有设备连上我的钓鱼WiFi,最后只能拆开一探究竟了。拆解之后发现自己还是too young,这玩意和云端通信用的是自己的物联网卡,根本不走WiFi。抓手机发送给云端的数据包也比较麻烦且大概率有较强的加密,只能尝试使用其他办法进行破解了。

尝试使用WiFi钓鱼抓包

  经过进一步的拆机研究发现,洗衣机接受云端信号的模块与洗衣机的控制模块可以分离,两个模块之间的连线处标有VCC、GND、TX和RX字样。看到这思路变得清晰了起来,可以通过读取并抓取两者之间互相发送的数据,再重发捕捉到的联网模块发给洗衣机控制模块的数据,这样也许就可以实现绕过正常的支付让洗衣机正常工作了。于是我们便拆下了带有物联网卡的云端数据接收模块连接至电脑,想要分析一下信号来一探究竟。

洗衣机内部结构

  根据目前的拆解了解到,数据的流向大概如下所示:

Layer 1 用户 云端 洗衣机网卡 发送数据 发送数据 洗衣机控制板 发送数据

  当我们把模块连接至电脑并用串口助手读取数据时,映入眼帘的只是一些毫无意义的乱码文字,可能是接收文本的编码方式不对?于是我们又更改了接收文本的编码方式,但是仍然不能接收到任何有意义的信息。当我们把接收数据转为十六进制显示时,貌似也不能显示出什么正确的结果,改变了奇偶校验位、数据位、波特率等相关配置也不能显示出什么有用的信息,这条路貌似也不太能走得通,这也为之后绕了一大个圈子埋下了伏笔,于是第一次的探索只能到此为止了。

第一次尝试

第二次尝试

  第一次尝试并不是很顺利,我们猜测可能是厂商使用了私有的协议进行编码,导致我们无法正常读取数据。如果再往底层考虑大概只能用示波器抓取最原始的高低电平数据,奈何财力不够。在询问淘宝卖洗衣机控制板的卖家也无果之后,买了一块国产Raspberry Pi Zero小板子并使用Github上的项目将其作为一台逻辑分析仪来使用,最终抓到了一些高低电平数据。但由于性能限制,由树莓派做成的逻辑分析仪在高采样率下只能保存极短一段时间的数据,所以只能对一段玄乎其玄的数据进行各种猜测和分析,但是在试过各种神奇的协议之后发现都不是很能对的上,于是又无功而返。

Raspberry Pi Zero
分析抓取到的数据

  第二天周六在寝室里闲着也是闲着,于是便打开抓到的数据再次细细研究,发现抓到的数据正好能对上9600波特率、8位数据位、无校验位、停止位为1的TTL信号(是的,不知道为什么第一次读取出来不对,可能是之前测试的波特率不对或者执着于想要读出文本数据吧)。到此为止,工作大概完成六七成了。

接收到的十六进制信号

  于是再将洗衣机的云端数据接收模块连接至电脑,调至正确的波特率,其他设置均为默认,果然读出了正常的数据。再在手机上发送一个付费洗衣的信号,果然也成功抓到了控制洗衣机板子的信号。和我们想象的一样,厂商应该没有在这一层做额外的加密,将这个信号重发,也成功使洗衣机正常运行。

手动抓取数据并重发

终于结束了……吗?

  看到这里,接下来的操作基本上大家都能想到,无非就是替代洗衣机网卡向洗衣机的控制板发送信号即可。实现的方案也很简单,断开洗衣机网卡与控制板的连接,用一个蓝牙串口模块与洗衣机控制板相连,给蓝牙串口模块发送捕获到的信号就行了。但是这样的方法有一个问题,就是云端会显示洗衣机离线,想要解决这个问题也很简单,就是不拦截控制板发回网卡的数据,只模拟洗衣机网卡发向控制板的数据。

Layer 1 用户 云端 洗衣机网卡 发送数据 发送数据 洗衣机控制板 发送数据 STM32 发送数据 第三方软件 蓝牙模块 发送数据 发送数据

  正当我们以为完工而欣喜时,在测试的时候又遇到了新的问题。在本地而非通过云端发送洗衣机运行信号时,云端将在几秒内发送一个停止信号使洗衣机中断当前的工作。因此必须将洗衣机控制板和洗衣机网卡的数据完全隔离,这也诞生了最终版本的控制系统。

Layer 1 洗衣机正常通信 分析信号源 云端 蓝牙 直至接收到信号 启动定时器 在定时器内屏蔽云端信号 向洗衣机发送指令 结束后恢复正常通信 正常洗衣 流程结束

  为了更加方便的使用洗衣机,我在一个GitHub项目的基础上修修改改,最终完成了使用手机蓝牙连接HC05蓝牙模块的 Android软件。至于洗衣机内部嘛…看不到的地方就随便将就一下吧(这个密密麻麻的走线令人看着就头大

更优雅的方法

  上图中的面包板走线实在是令人头疼,大大降低了安装和维护的便捷性,不是长久之计,于是便诞生了第二版的蓝牙控制模块。这一版蓝牙控制模块可以不用面包版与杜邦线与洗衣机的控制模块连接,大大降低了安装的复杂度。同时大幅缩小了整个模块的体积,使得其能够更加灵活的放置进洗衣机中。(不过由于有一点小缺陷懒得修,所以到现在为止洗衣机内还使用着第一版的杂乱走线方案💦

(使用AI辅助)深入理解

  在完成硬件搭接与信号重发验证后,洗衣机虽然已经能够正常受控运转,但串口中频繁交互的大量十六进制报文依然未被系统性解析。借助 AI 工具对多组完整档位的串口抓包日志进行数据挖掘、差分比对与时序对齐分析,成功还原出了这套物联网洗衣机私有串口通信协议的完整定义与状态机模型。

1. 通信协议骨架与校验算法

  物理层为标准的串口 TTL 电平(波特率 9600 8-N-1)。AI 对各类报文的尾部校验位进行特征检索与多项式拟合,确认其采用的校验算法为经典的 8 位累加和低 8 位 (Checksum Sum8):

$$ \text{Checksum} = \left( \sum_{i=0}^{N-1} \text{Byte}[i] \right) \mathbin{\text{&}} \text{0xFF} $$

  即报文前 $N-1$ 个字节十六进制数值相加,取低 8 位作为校验码。下位机接收时一旦校验不匹配则直接丢弃。


2. 核心报文协议与示例辅助分析

【示例一】心跳维持交互(A2 / A3)

  上位机以固定 1 秒(1000ms) 的频率持续向下位机下发 3 字节心跳包,下位机周期性上报状态以维持长连接。

  • 真实抓包日志片断:
    1
    2
    
    2024-04-21 21:55:51 [云端发送] A2 01 A3
    2024-04-21 21:55:51 [机器响应] A3 01 02 00 01 00 00 00 00 A7
    
  • 协议分析:
    • 云端发送 A2 01 A3:A2 为心跳帧头,01 为设备地址,校验和为 0xA2 + 0x01 = 0xA3;
    • 机器待机回复 A3 01 02 00 01 00 00 00 00 A7:表示当前处于待机空闲状态,无档位运行(后文详述状态帧格式)。

【示例二】档位参数配置与差分计算(BA 帧)

  在正式触发启动前,上位机会连续发送 3 次 BA 开头的 11 字节参数帧,下位机收到后将时长与洗涤参数存入主板并回复 BB 00 BB 确认。

  • 真实抓包日志片断:
    1
    2
    3
    4
    
    2024-04-21 11:39:49 [云端发送] BA 05 17 21 2B 00 03 05 08 00 32
    2024-04-21 11:39:49 [机器响应] BB 00 BB
    2024-04-21 11:39:50 [云端发送] BA 05 17 21 2B 00 03 05 08 00 32
    2024-04-21 11:39:50 [机器响应] BB 00 BB
    
  • 字段结构解析:
    • Byte 00 (BA):参数配置帧头;
    • Byte 01 ~ 04 (05 17 21 2B):分别定义 4 个基础档位的预设总倒计时长(十六进制分钟数):
      • 0x05 = 5 分钟(档位 1:单脱水)
      • 0x17 = 23 分钟(档位 2:快洗)
      • 0x21 = 33 分钟(档位 3:标准洗)
      • 0x2B = 43 分钟(档位 4:大件洗)
    • Byte 06 ~ 08:分段阶段参数(洗涤强度/时间、漂洗等级、脱水转速控制)。
  • 各档位校验和差分演算示例: 各档位根据衣物属性,洗涤细节参数不同,累加后导致校验和呈现 32、34、36:
档位分类 报文十六进制内容 Byte 06 (洗涤) Byte 07 (漂洗) Byte 08 (脱水) 累加和计算示例 校验码
快洗 (档位2) BA 05 17 21 2B 00 03 05 08 00 32 03 (轻度洗涤) 05 (快漂洗) 08 0xBA+0x05+0x17+0x21+0x2B+0+3+5+8+0 = 0x132 32
标准洗 (档位3) BA 05 17 21 2B 00 05 05 08 00 34 05 (洗涤增强+2) 05 08 0xBA+0x05+0x17+0x21+0x2B+0+5+5+8+0 = 0x134 34
大件洗 (档位4) BA 05 17 21 2B 00 05 08 07 00 36 05 08 (深度漂洗+3) 07 (平稳脱水-1) 净增2,0x134 + 2 = 0x136 36

【示例三】启动控制时序与极速 ACK 验证(A0 / A1 帧)

  上位机通过 41 字节主体 + 1 字节帧尾 FF 的 A0 报文控制洗衣机上锁启动。

  • 真实抓包日志时序(带毫秒时间戳):

    1
    2
    3
    4
    5
    6
    7
    8
    
    1713707757228 [云端发出 A0 启动指令]:
    A0 01 02 05 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 AA
    
    1713707757256 [机器响应 A1 确认帧]: (+28ms)
    A1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A1
    
    1713707757297 [云端紧跟发出冲刷符]: (+69ms)
    FF
    
  • 时序特征分析:

    1. 下位机极速确认:下位机在收到 A0 指令后仅 28ms,就已回传完整的 41 字节 A1 启动确认帧,说明单片机在第 41 字节校验通过后便立即开始执行;
    2. 末尾 FF 的机制:云端发出的 FF 出现在机器响应之后(或紧贴校验码后发出)。单片机在 41 字节解析完毕状态机复位后,遇到 FF 会安全丢弃。模拟上位机时带上 FF(42 字节)可以最严谨地还原物理模块的通信行为。
  • 各档位 A0 控制报文速查表:

档位名称 模式代码 预计实际耗时 完整十六进制发送指令
筒自洁 05 ~5.5 分钟 A0 01 02 05 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 AA FF
单脱水 01 ~6 分钟 A0 01 02 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A6 FF
快洗 02 ~26 分钟 A0 01 02 02 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A7 FF
标准洗 03 ~40 分钟 A0 01 02 03 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A8 FF
大件洗 04 ~55 分钟 A0 01 02 04 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A9 FF

【示例四】下位机实时状态帧(A3 帧)生命周期演变

  下位机在运行期间每秒主动向外广播一帧 10 字节的状态数据:

1
A3  01  02  [MODE]  [STAGE]  01  [REMAIN]  00  00  [SUM]
  • Byte 03 (MODE):当前档位(01脱水、02快洗、03标准洗、04大件洗、05筒自洁);

  • Byte 04 (STAGE):洗涤阶段代码

    • 01:待机空闲 (Standby)
    • 03:洗涤注水与正反转 (Wash)
    • 04:排水与漂洗冲洗 (Rinse)
    • 05:高速离心脱水 (Spin)
    • 06:洗涤程序完成 (Finished,持续鸣叫提示取衣)
  • Byte 06 (REMAIN):十六进制表示的剩余倒计时分钟数。

  • 以快洗(档位 2)全程真实日志为例:

    1
    2
    3
    4
    5
    6
    7
    
    11:39:51 [洗涤阶段] A3 01 02 02 03 01 17 00 00 C3  -> 档位02, 阶段03(洗涤), 剩余0x17=23分钟
    11:45:00 [洗涤持续] A3 01 02 02 03 01 13 00 00 BF  -> 倒计时递减至 0x13=19分钟
    11:50:01 [转入漂洗] A3 01 02 02 04 01 0E 00 00 BB  -> 阶段切为04(漂洗), 剩余0x0E=14分钟
    11:59:21 [转入脱水] A3 01 02 02 05 01 06 00 00 B4  -> 阶段切为05(脱水), 剩余0x06=6分钟
    12:04:35 [脱水末尾] A3 01 02 02 05 01 01 00 00 AF  -> 阶段05, 剩余1分钟
    12:05:34 [洗涤完成] A3 01 02 00 06 00 00 00 00 AC  -> 阶段切为06(完成!), 蜂鸣器长鸣
    12:05:38 [恢复待机] A3 01 02 00 01 00 00 00 00 A7  -> 档位归零, 阶段01待机, 等待下一次控制