Exploring Custom Firmware on Xiaomi Thermometers

If we’ve learned anything over the years, it’s that hackers love to know what temperature it is. Seriously. A stroll through the archives here at Hackaday uncovers an overwhelming number of bespoke gadgets for recording, displaying, and transmitting the current conditions. From outdoor weather stations to an ESP8266 with a DHT11 soldered on, there’s no shortage of prior art should you want to start collecting your own environmental data.

Now obviously we’re big fans of DIY it here, that’s sort of the point of the whole website. But there’s no denying that it can be hard to compete with the economies of scale, especially when dealing with imported goods. Even the most experienced hardware hacker would have trouble building something like the Xiaomi LYWSD03MMC. For as little as $4 USD each, you’ve got a slick energy efficient sensor with an integrated LCD that broadcasts the current temperature and humidity over Bluetooth Low Energy.

You could probably build your own…but why?

It’s pretty much the ideal platform for setting up a whole-house environmental monitoring system except for one detail: it’s designed to work as part of Xiaomi’s home automation system, and not necessarily the hacked-together setups that folks like us have going on at home. But that was before Aaron Christophel got on the case.

We first brought news of his ambitious project to create an open source firmware for these low-cost sensors last month, and unsurprisingly it generated quite a bit of interest. After all, folks taking existing pieces of hardware, making them better, and sharing how they did it with the world is a core tenet of this community.

Believing that such a well crafted projected deserved a second look, and frankly because I wanted to start monitoring the conditions in my own home on the cheap, I decided to order a pack of Xiaomi thermometers and dive in.

Firmware Installation

Certainly one of the most appealing aspects of Aaron’s “ATC” firmware is how easy it is to install. You’d expect something like this would require cracking the case and attaching a USB to UART adapter, and while you actually can go that route if you need to, 99% of users will be using the extremely clever “Web Bluetooth” flashing tool.

In theory you should be able to install the ATC firmware from any computer running a modern enough web browser, but your mileage may vary with such a cutting-edge feature. My Linux desktop couldn’t connect to the thermometers at all, and trying it on a Chromebook only worked occasionally. Your best bet is probably going to be a smartphone or tablet, and I had no problem flashing several of the thermometers using my Pixel 4a.

All told, the flashing process takes a little under a minute to complete. After you pull the tab on the back of the thermometer, you tap “Connect” on the flasher and wait for it to pop up on the list of local Bluetooth devices. After connecting you need to hit the “Activate” button, which apparently establishes the secure connection required by the original firmware before it will allow for an over the air (OTA) upgrade.

Once the activation process is complete, you select a firmware binary, tap “Start Flashing”, and wait as the progress indicator ticks up towards 100%. This takes around 30 seconds to finish, and afterwards the thermometer should immediately reboot into the new firmware. If it doesn’t wake up after the flashing process, Aaron says you can just pull the battery and it should sort itself out. I’ve gone through this process several times now without ever needing to pull a battery though, so presumably it’s a fairly uncommon problem.

Should you ever need to, the same process can be used to put the stock firmware back on the device. Aaron doesn’t have a stock firmware image in the GitHub repository (likely to avoid a copyright claim) but in the documentation he does tell you how you can extract a copy of it from an official update file.

Now, the more security minded reader may be wondering it this means some militant nerd can go around bricking people’s Xiaomi thermometers. Unfortunately, it does. This tool will let anyone within Bluetooth range flash whatever they want to the Xiaomi LYWSD03MMC, regardless of whether or not it has a custom firmware installed. That said, there’s already been mention of adding some kind of authentication mechanism to the ATC firmware to defend against such attacks; making it notably more secure than the stock firmware.

Getting Comfortable

The web flashing tool doesn’t just install the ATC firmware, it also provides an easy to use interface for configuring it. After connecting to the device, you just scroll down a bit and just tap on the various options to enable or disable them.

Turning off the weird ASCII-art smiley face was the first thing I did, and being an American heathen, I also changed the display to Fahrenheit. The fact that you can enter an offset values for the temperature and humidity is handy, though it might become unnecessary in the future as Aaron says a more robust calibration routine is on the TODO list.

Showing the battery level on the screen is an interesting feature that’s enabled by default, but personally I turned it off. The problem is that the battery percentage and relative humidity have to share the smaller set of digits on the bottom right side of the LCD. They switch back and forth every five seconds or so, which in practice seems just fast enough that it’s sometimes confusing as to which one you’re looking at. Plus when the CR2032 battery inside is supposed to last up to a year, do I really need to see the battery level so often?

I was a bit confused by “Advertising Interval” at first. I assumed this would be how often the thermometer sends out a BLE packet, but in practice, it sends them out every few seconds regardless of what you change this setting to. What this setting actually changes is how often the temperature data that’s being broadcast gets updated. The default is one minute, so you should expect to get 20 or so packets that will contain the same temperature and humidity values before they get refreshed. Setting the update interval to 10 seconds would give you more granular data, but presumably at the cost of battery life.

Custom Builds

While Aaron doesn’t go into too much detail on the project page, it’s fairly easy to compile your own firmware for the thermometer that can then be flashed with the web tool. On Linux, all I had to do was extract the Xiaomi Telink TLSR825X tarball and add the directory to my $PATH environment variable. From there I could build the ATC firmware and start poking around at the code.

Custom names are just a few lines of code away.

If you’re into low-level C programming, there are a number of Issues in the GitHub repository that you could probably help out with. It sounds like the major one right now is establishing persistent flash storage, which would pave the way for being able to retain configuration across battery changes and more advanced features like local temperature logging.

For my own purposes, it was easy enough to find the part of the code that changes the Bluetooth device name. If you’re going to be dotting these all over the house, seeing something like “ATC_BEDROOM” will certainly be more helpful than the default naming scheme which uses the last characters of the MAC address.

Pulling in the Data

Now to be clear, you don’t need to install Aaron’s firmware to use the data from these thermometers in your own projects. There’s at least one tool that allows you to pull the temperature, humidity, and battery level from the sensors out of the box, and they’re also supported in ESPHome. But the problem with the stock firmware is that you need to actually connect to each sensor to read its data, which is a slow and energy intensive process. One of the main improvements in Aaron’s firmware is that the data is constantly being pushed out in the clear as a BLE advertisement; meaning any nearby device can sniff the values right out of the air without establishing a connection or pairing.

To that end, there are a few packages worth taking a look at. At the time of this writing there’s a pull request to add support for ATC firmware to aioblescan, a simple Python 3 tool that will dump data from the BLE packets to the terminal. A little scripting would allow you to easily parse the output from aioblescan and shuffle the information around however you see fit.

I’ve also had good luck with py-bluetooth-utils, a library designed for working with BLE advertisements. Based on some examples included with the project, I’ve come up with this minimal script which should get you started:

#!/usr/bin/env python3
import sys
from datetime import datetime
import bluetooth._bluetooth as bluez from bluetooth_utils import (toggle_device, enable_le_scan, parse_le_advertising_events, disable_le_scan, raw_packet_to_str) # Use 0 for hci0
dev_id = 0 toggle_device(dev_id, True) try: sock = bluez.hci_open_dev(dev_id)
except: print("Cannot open bluetooth device %i" % dev_id) raise # Set filter to "True" to see only one packet per device
enable_le_scan(sock, filter_duplicates=False) try: def le_advertise_packet_handler(mac, adv_type, data, rssi): data_str = raw_packet_to_str(data) # Check for ATC preamble if data_str[6:10] == '1a18': temp = int(data_str[22:26], 16) / 10 hum = int(data_str[26:28], 16) batt = int(data_str[28:30], 16) print("%s - Device: %s Temp: %sc Humidity: %s%% Batt: %s%%" % \ (datetime.now().strftime("%Y-%m-%d %H:%M:%S"), mac, temp, hum, batt)) # Called on new LE packet parse_le_advertising_events(sock, handler=le_advertise_packet_handler, debug=False)
# Scan until Ctrl-C
except KeyboardInterrupt: disable_le_scan(sock)

Just Do It

If you can’t tell, I really like Aaron Christophel’s custom firmware for the Xiaomi LYWSD03MMC. As far as I can tell, there’s absolutely no reason not to install it if you’ve already got some of these thermometers laying around. In its current state it gives you greater control over the hardware and what you can do with it, and the features currently in the planning stages promise to take things even farther. Being able to locally log temperature data on the thermometer and download it over BLE at a later date is definitely something to keep an eye out for.

For as cheap as these little thermometers are, I’d highly recommend adding a few to your next parts order. Even if you don’t have an idea in mind for them yet, I wouldn’t be surprised if something comes to you after spending an afternoon hacking their firmware.