Wednesday, 20 January 2021

Hacking Bluetooth to Save My Back

A few years ago, I got a new bed from a popular brand that supports user customizable mattress settings by inflating air bladders. The bed has been wonderful up until this point.

The Problem:

One side of the mattress has started to leak! Over the course of a few hours, the air bladder deflates and the user is left lying on the support platform in something that resembles a sad hammock.

The Fix:

After a short conversation with the nice support folks, the permanent fix is on its way. A new pump and new bladder will be shipped in 3-5 days, but what about all the nights I need to sleep until they get here?

The Band-Aid:

Since the leak is still slow (only going flat after several hours), I have been inflating the bed with the remote when it woke me up, about two to three times a night. Needles to say, this is an unworkable solution. My back hates me and the exhaustion is real. Any reasonable person would probably just sleep on the pull-out bed we have downstairs until the parts arrive, but my deep-seated desire to overcomplicate things won the day (as it does more often than I like to admit).

Enter the computer! I have a computer with Bluetooth, so I figured I should be able to automate re-inflating my mattress every hour or so while I sleep. What follows is a rundown of everything that I needed to do in order to make that happen.

Step one was determining what commands are being sent to the bed to trigger it to adjust to a new setting. There are several ways to do this, including sniffing the packets out of the air, but I chose to take advantage of the control app and dump the information directly from my phone.

I’m a fairly solid member of the Apple family, so the following walkthrough involves an iPhone and a Mac, but this should be adaptable for Windows and Android fairly easily.

Bed Control

Getting started with the iPhone, I needed a way to dump the Bluetooth traffic between the phone and the mattress. Using the information posted on a blog here, I downloaded an iOS profile that allows me to dump the Bluetooth traffic directly to my Mac. I was able to download and install the developer profile and the extra xCode tools without registering as a developer (so, for free), and use the PacketLogger application (part of the extra xCode tools) to record everything the iPhone app sent and received, to and from the bed. Keep in mind that PacketLogger actually dumps all Bluetooth traffic from the phone, so during the trace you will most likely see other communications taking place. After I recorded adjusting the mattress settings, I filtered the results of the capture to exclude any traffic from other devices.

Too much traffic

I find it easier to work with packet data by using WireShark, so I ended up exporting the PacketLogger data in the BTSnoop format. After opening it up in WireShark, it looks something like this.

PacketLogger Data

Now we can start to see which device sent what information, and what the responses were.

In the end, I didn’t need the responses from the bed to get everything working, but keep in mind this is a hacked together band-aid and not a fully-featured application. I also probably got lucky that the bed would take single commands at all. I found this article really helpful in getting through this part.

For me, I like sleeping with the mattress set around 55 (an arbitrary number), so I recorded Bluetooth dumps for setting the mattress to 60 and 55. After playing around with the packet information for several hours, I finally had a breakthrough after converting the numbers for the settings (55 and 60) to HEX (0x37 and 0x3c). With these new values in mind, I was able to spot them in a few different packets.

They look like this:

161602538c532c02538c1102003c12ae   3c is 60 in hex
161602538c532c02538c1102003712a9   37 is 55 in hex

I know there are a lot more commands my bed can use, but I didn’t dive any deeper here because I was focused on getting a good night’s sleep.

Testing It Out

Now that I had examples of the correct packets, it was time to see if I could get the mattress to inflate. I used a great app called nRF Connect to scan for available devices and connect to the bed. Once connected, the app gave me lots of good information about the device, including the reading and writing characteristics.

nRF Connect

Comparing the data from nRF Connect and WireShark, we can see that the app is using the “[UUID: ffffd1fd388d938b344a939d1f6efee2]” characteristic to write commands to the bed. Pressing the up arrow in nRF Connect for the characteristic lets the user send values. After a few tries, I found that when I resent the information from the “Value” field for the packet from WireShark, the pump sprang to life!

Writing a Script

Tl;dr: Here's the GitHub Repo!

With a working prototype, I needed a way to repeatedly trigger the action. I don’t know of an easy way using my phone, but I figured I could probably use the Bluetooth built into my laptop.

The script uses the Bleak Python package as a cross-platform way to manage the Bluetooth communication. You can check out the package here. I ended up using the example from the documentation to scan for devices and show the unique identifier for my mattress. Windows and Mac have a slightly different approach here, so if you’re following along on your own project, I’d suggest taking a quick look at the docs. In the end, I copied the identifier I found by running the example device scan into my final script.

With my device identifier tracked down, I began building out more functionality. I started using more of the package example code to connect to the bed and start querying services. I was trying to get a feel for how the Python package behaved compared to how nRF Connect displayed the same information from my phone.

It was about here that I came across this article. It’s a really good walk through of communicating over Bluetooth using Bleak. I started by using some of the pieces of code that create the Bleak client, send the data, and read the response. From there I started testing.

Testing and Iterations

Once I began testing, I quickly found out that I couldn’t send the same setting twice. It appears the bed is looking for changes in settings and can’t tell that the air bladder has leaked. I was able to get around this by changing between the settings for 55 and 60. The script pulls the command strings from a list and appends them back to the end once they’ve been used, giving me a different string every time.

Another issue that cropped up was my computer going to sleep. A simple way around this would be to adjust power settings, but I just didn’t like something about that approach. I’d probably end up forgetting to change that setting back after the replacement parts arrive, and my computer would forever live in the awake state. After a bit of searching, I found a Mac specific python package called “caffeine.” This package only needs to be imported, and for the life of your script will keep your computer from sleeping while still allowing your display to turn off.

The first night I ran the script when better than expected. I made it until about 3:00 a.m. before the initial connection to the pump failed, threw an exception, and exited the script. I woke up a few hours later to a flat mattress, but a quick addition of some exception handling allowed everything to be a bit more robust.

I’m still waiting on the shipping notification for the parts, but until they arrive, I think I’ve got this fairly nailed down. The script toggles the bed between two settings every 45 minutes, and I’m sleeping much better. It has also set the stage for a future project, building a single button controller to lay the adjustable bases flat when making the bed in the morning. That’ll mean no more fumbling around for an app or remote that’s fallen behind the nightstand.

If you’re interested, here’s a GitHub Repo with the full code.

comments | comment feed



No comments:

Post a Comment