去年十月,我买了一块米家智能温湿度计3。到手之后却发现,时间竟会倒着走。拍视频给售后确认了之后,直接退了款,让我自己再下单一个。

新的很好,功能也正常。旧的售后也没要回去。那旧的怎么办?撇了?核心功能温湿度又没坏,所以我把它放在了我的桌前。时间嘛,电脑、手机、手表,哪个不能看。

不过看温湿度的时候,总是会注意到时间。看久了居然也看出了规律来。十分钟一循环。设定时间后,过了十分钟,就会又回到设定的时间。如果设定的时间刚好要在十分钟内跨小时,那么它将会在跨小时的时候重置,连十分钟都不到。我想办法让自己忽略时间,并关掉了星期显示,希望能让它不那么惹眼。

但看多了总觉得膈应。温湿度计的生命只有十分钟,我要给它完整的一生。起初,我以为是出厂固件的问题,但是升级、重刷都没有用。于是我想试试第三方的固件,看看能不能解决。

ATC_MiThermometer是一个BLE温度计的自定义固件,适配基于Telink芯片组的小米温湿度计和其他的一些设备,并且还有个Telink Flasher,基于Web Bluetooth,可以在线配置和刷机。

我没想到这么个小玩意还有人在折腾,于是满怀期待地刷了进去。

天啊!它活过了第一个十分钟,时间在十分钟之后依然在走!

看来问题就这么轻易地解决了,官方固件确实有问题。在第三方固件的加持下,甚至还更省电。这位温湿度计能安心地当它的摆件了。

好景不长,事与愿违。温湿度计还是难逃循环的命运。过了半个小时,我发现时钟又重置回了我设定的时刻。是不是这台机器的内存有问题,让它记录不了走过的这么长时间?

我把记录温湿度的间隔调长,最多记录的条数减到最少,希冀着它能走过更久的时间。上天似乎回应了我的愿望,温湿度计的时钟走过了半个小时,走过了四十五分钟,最终,倒在了整点之前。

整点似乎是个坚固的魔障,每当温湿度计的时钟想要跨越它,都会被无情地弹回上次同步的时刻。我取回温湿度计的记录,一次又一次跨越整点的尝试织成了一张紧密的网,似乎揭示着这温湿度计在一次次循环中沉沦的命运。

由于时间循环而重叠在一起的折线图

算了,我尽力了。我想。以后个隔三差五同步一下时间吧。但是,我亲爱的朋友,你知道的,隔三差五最终总是意味着永远。

直到昨天,我的温湿度计还困在2025年的十二月,没能走出那个冬天。

阴雨连绵,这两天空气潮湿得让我以为回到了南方。我的注意力又频繁回到了温湿度计上,为我的烦躁寻求一份解释。

我自然注意到了时间, 2025年十二月,那是我最后一次想起来给温湿度计同步的时间,距今已有将近半年。

好奇心又一次被勾起。我想知道这到底是怎么一回事,为什么时间会循环走不出整点,以及能不能修复。

米家温湿度计3 / MJWSD05MMC的主控是Telink TLSR8250,可以软件计时。除此之外,它还有一个单独的RTC时钟芯片AT85163T (PCF85163) 来提供更准确的时间。

平时,主控会用自己的软件时间驱动LCD显示,在屏幕上展示具体的时间。每隔一段时间,主控就会从RTC读取更准确的时间来矫正自身,从而让显示的时间更加精准,同时降低功耗。而这个从RTC同步准确时间的间隔,在官方固件上是十分钟,在pvvx的固件上是每整小时。

对上了,都对上了。看起来是这块RTC芯片出了问题。

在ChatGPT的协助下,我打开了nRF Connect。这是之前用AppleJuice整蛊使用iPhone的朋友时下载的,没想到还能派上正经用场。

利用pvvx固件暴露的BLE调试命令扫I²C总线。按照文档,我在私有服务0x1F10中的特征0x1F1F下发送了命令0x03,并且得到了返回通知(0x) 03-7C-88-A2。其中03是命令号,后边的就是I²C设备地址了。

0xA2就是RTC的地址,说明他还能在总线上ACK确认。问题出在RTC的内部。

进一步,发送04-01-10-A2-00-00读RTC的16个字节,得到04-01-A2-08-1F-FF-7F-3F-3F-07-9F-FF-FF-BF-BF-87-83-87-FF

其中第四个08,也就是从RTC读出的第一个 (0x00) 字节,是Control_1,代表着RTC的状态,按照PCF85163的定义,似乎是正常的。紧跟着下一位的Control_2则是1FAFTFAIETIE全被点亮,意味着告警和定时器中断。

再往后,FF这一位就是秒了,最高位的VL被置位,表示芯片经历过电压跌落,所记的时间已经不可信。后边的分、时、日之类的也全是非法BCD。

如果运气好的话,把它覆写成正常的值就能让RTC恢复正常。可惜我的运气并不好。覆写成正常值之后半天没有动静,还是原来写入的时间。后来试了很多次,扣电池、短接、重置。除了Control变回了报错值之外,时种一秒也没有走。

排查到这里整件事就已经很明朗了:可能由于时钟源晶振坏了或者缺焊,导致RTC的计时震荡或者计数坏掉了,但自检却没发现,让时钟一直停在最后写入的值。而主控会按照设定好的时间间隔,从RTC读取这个固定的值,所以才会时间循环,一直重置到上次同步的时间。

知道问题了就好对症下药,把主控从RTC读精确时间这一行为改掉了就行。遗憾的是,似乎不能直接从软件上把RTC直接断电,不然还能更省电。

在Claude的协助下,我Clone了pvvx固件的源码。起初,我们以为把RTC从I²C上屏蔽掉就可以了:

void init_rtc(void) {
    (void) rtc_init_b1;
    (void) rtc_init_b2;
    rtc_i2c_addr = 0;
    utime_to_rtc(wrk.utc_time_sec, &rtc);
}
...
u32 rtc_get_utime(void) {
    utime_to_rtc(wrk.utc_time_sec, &rtc);
    return wrk.utc_time_sec;
}

但神奇的是,由于主控会在每跨小时的时候从RTC读取时间,所以主控计算时间时压根就没考虑过小时进位,还是会每跨小时就循环。所以还得把小时进位给加上:

#if (DEV_SERVICES & SERVICE_HARD_CLOCK)
    u8 prev_min = rtc.minutes;
    utime_to_rtc(wrk.utc_time_sec, &rtc);
    if(rtc.minutes != prev_min)
        SET_LCD_UPDATE();
#endif

找个Linux环境编译好,用Telink Flasher给刷进去。

正常走时的温湿度计

现在,这台温湿度计终于走出了循环的魔沼,向着未来一去不复返。而我也拥有了一台虽然走时不太准,但是也能看的温湿度计。可喜可贺。