返回资讯中心
外部精选
软件工程
#开发者实时流

用户误将智能吊扇变砖后逆向工程将其修复

来源标题:I bricked my smart ceiling fan, then reverse-engineered it back to life

一位用户因不满智能吊扇无法设置通电即启动,决定自行逆向工程,最终在保留原厂应用正常使用的前提下实现了完全本地控制。起因是他不想额外购买智能开关,希望墙上原有开关直接生效,但风扇固件缺少「始终开启」选项。他先用Home Assistant自动化临时解决,但因Wi-Fi连接太慢而转向逆向工程。

Hacker News 最新ahmedwalid052026/11/15 · 今天26 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

彻底本地控制一台云端风扇——同时不破坏家里其他人仍在使用的 app。

事情的起因,和我做的很多事情一样,是因为懒。

最近,我买了一台智能吊扇,这样我就能在躺到床上之后不用起身就能开关灯和风扇。

用了几个星期后,我开始对它总是记住上一次状态这一点感到恼火:如果我之前把它关了,它回来时就是关着的,我得伸手去拿手机或者叫语音助手来打开,而这些并不总在手边。自然的解决方案是买一个智能开关来搭配风扇,但我不想这么做,墙上已经有一个完全好用的开关,它本该直接就能用。我的其他一些智能设备有一个设置,可以让你决定它们是应该记住上一次的状态,还是始终开启。我的风扇上没有这个设置。

几周后,我妥协于一个偷懒的方案:一个 Home Assistant 自动化,在风扇通电的瞬间把灯打开。它确实起作用了,但风扇完全连接到 Wi-Fi 并接入我的 Home Assistant 要花很长时间。我想要比这更快的东西,所以我决定不再等厂商,自己动手修复启动行为。

我的第一反应是捣鼓固件,试着找一个现成的 cloudcutter 项目来复用。我找到了 dreo-cloudcutter——只不过它是为另一款 Dreo 产品(塔扇)构建的,其内部结构跟我的不匹配。所以它并不适用,但它是有用的参考。两款产品的固件刷写方法非常相似:在配对模式下,Wi-Fi 模块会暴露一个无需认证的 /ota 上传页面。

我犯了一个错误,让我的 agent 去研究现有的 dreo-cloudcutter 项目如何能适配我的吊扇,结果它决定往风扇上刷一个空的 ESPHome 镜像。

固件刷写成功了,ESPHome 落在了 Wi-Fi 模块上——一颗 Beken BK7231N,这颗小芯片负责处理网络,而真正运行风扇的是另一个独立的电机控制器。而恰恰是这次“成功”,毁掉了一切。现在 ESPHome 跑在我的网络上,完全可达、已登录,却毫无用处。我打不开风扇。我开不了灯。Dreo app 显示它已离线,挂在 Dreo 账号下的 Google Home 控制也随之失效。我把一台能用的智能风扇,变成了一台带网页的“哑”风扇。

我唯一的救命稻草是物理遥控器还能用。实际上,我等于买了一台非常昂贵的、带遥控器的风扇。

我当时还不知道、而这一点后来拯救了整个项目的是:刷 ESPHome 只会重写应用固件。出厂数据存放在芯片自己的独立区域中,那一块完全没有被动过。

在对我 agent 发了几次全大写的催促、并意识到我无法回滚之后,我决定向前推进,开始在网上搜索我的风扇的固件。没有找到任何可用的东西。仅有的公开固件镜像是 2021 年的,而且还是错误的型号——又是那款塔扇。

于是我开始转而研究 Dreo 手机 app。这个 app 显然有一个更新固件的功能,这意味着它知道如何获取固件,所以我让我的 agent 拆开 APK(com.hesung.dreo)并解码它的 React-Native bundle。这一下挖出了很多东西:云端 API 端点、整套配对和注册流程、app 拉取所用的 S3 存储桶,甚至证书配置。它没有挖出的,是固件本身。只找到了一种让风扇自行更新的方法。我有了云端的地图,却没有回去的路。

坐在一间没有风扇的黑暗房间里(只是为了戏剧效果,遥控器还是能用的),我决定已经没什么可失去的了。我让 agent 把它从 2021 年塔扇固件和反编译的 Android app 中学到的一切都用上,尝试直接与风扇的电机控制器——也就是第二颗芯片——对话,它用一种很小的 55AA 串行协议与 Wi-Fi 模块通信。

agent 开始试验,很长一段时间里毫无反应。然后风扇响了一声,正是你在遥控器上更改设置时它发出的那声小小的提示音。风扇的设置实际上并没有任何改变,但它是在确认收到了一个信号。芯片在监听;我只需要找到正确的话——以及,正如我后来会了解到的,正确的时机。该模块会执行一套启动流程(几秒钟的静默,然后是一连串心跳和状态帧),之后才会应用任何一次写入;在那之前,命令都会被无声吞掉。在一台风扇罢工、一个 app 罢工之后,那声提示音仍然是我几天来听到的最令人鼓舞的声音。

带着“这个思路可能行得通”的动力,我搭了一个小工具,让我能发送许多不同的命令,并通过读回数据来监控它们的结果。

协议本身——55 AA——相当简单:一个命令字节、一条 data-point 记录,以及一个校验和。下面这个构建器标注了每个 data point 最终被确认是什么;而此时我只知道帧的形状,并不知道哪个 DP 起什么作用。试试看:

首先,我按下遥控器上的按钮,记录 MCU 状态如何变化。这帮我读出了当前状态,但我无法把它转译成真正能改变风扇任何东西的命令。

我的下一次尝试是把一台摄像头朝上正对着风扇安装。agent 逐帧对比原始像素,弄清了“开”和“关”看起来是什么样。有了反馈,我让 agent 基于它所知的历史数据以及它对协议本身的理解,暴力尝试各种命令。

agent 不止一次自信地宣布电机“锁死”,而每次都错了——其中一个“修复”其实是我在背景里开关了一盏房间的灯,污染了画面。它还总是把灯和风扇的命令弄混(dp2 与 dp3)。它没有理解控制器只在值发生变化时才动作。一旦理解了这些注意事项,命令就开始一个接一个地被攻克。

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →