Embedded systems and stuff

Reverse engineering the Tempur-Pedic adjustable base remote control

Updated 6/6/2023.

Check out the code here: https://github.com/mostthingsweb/temperbridge-esphome


This is the first in a (probably) three part series.

About a year ago, I decided to upgrade my old spring mattress to a TEMPUR-Contour Elite Breeze and I cannot say enough good things about it. I opted to also get an adjustable base for it, which has also been great. It comes with a remote like the one pictured here:

from tempurpedic.com

Another advertised feature of the base is control via Android and iPhone apps. Unfortunately, the Android app hasn’t been updated since 2014 (update 6/6/2023: at the time in 2019 this was true, but the app was updated in 2022). I could not get it to work with my Google Pixel, and based on the reviews, I’m not the only one. Clearly, this is DIY territory.

Project goals

  1. Build a gateway that communicates with the base using its own RF protocol
  2. Develop an Android app that can control the base through the gateway (next time)

Previous work

Other projects that achieve something similar fall into two categories:

  1. Talking to the onboard WiFi module (e.g. https://github.com/docwho2/java-alexa-tempurpedic-skill)
  2. Integrating relays into an existing remote control to simulate button presses (e.g. http://www.quadomated.com/technology/automation/diy-tempurpedic-ergo-adjustable-bed-automation/, https://github.com/tomchapin/tempurpedic-remote-relay)

The benefits of reversing the actual RF protocol are (a) no dependence on integrated WiFi and (b) non-destructivity.

Researching the FCC filing

Since the remote is a wireless device, it is regulated by the FCC. We can lookup the FCC ID (UNQTPTAES) to learn some useful information about the device. Confidentially requests by the manufacturer prevent us from accessing the BOM, schematic, and block diagram :/. In this case the most helpful document is the Test Report.

Back of the remote

Some interesting excerpts from the test report follow.

One page 7, we learn there is some kind of mapping between the channel number and the frequency. The remote supports lots of channels, which could be useful for people with more than one adjustable base.

Page 7

Page 12 gives us a glimpse at the signals we will soon be hunting:

Page 12

Page 13-14 detail that the remote transmits in pulses, e.g. on each key press. This implies that there is no persistent connection between the base and the remote, and that communication is probably unidirectional (remote => base). Makes sense because the base shouldn’t need to talk back to the remote.

Page 14

Initial exploration with software defined radio (SDR)

In addition to being practical(-ish), I intended to use this project as an opportunity to play with SDRs for the first time. That being said, I will not try to attempt to teach any of the concepts here.

My weapon of choice was the HackRF One (Amazon link). Using the test report as a guide, it was relatively simple to find a signal after playing with the sliders in SDR#:

SDR# screenshot, with test report signal plot overlaid on right

The test report signal plot is overlaid on the right. We can see that the two signals are very close, so we’re on the right track.

It turns out that the type of modulation in use here is Gaussian frequency shift keying. A big thanks to my friend Tim for identifying it as FSK.

Listening in on the SPI bus

I got relatively far using GNU Radio to demodulate the signal (something I won’t go into here, mainly due to my lack of ability to speak on the subject). Eventually I got bored of it, and sought a faster way to reverse engineer the signal.

For this, I broke out my trusty Saleae logic analyzer (mine is an older model; here’s the newer kind on Amazon). Cracking open the remote, one finds a Si4431 RF transceiver:

My own macro photograph of the remote PCB

And also a Renesas microcontroller:

Macro photograph of the Renesas uC

It is tempting to try and dump the uC’s ROM and reverse the firmware, but the FCC test report from earlier reveals that ROM protection is active. Maybe next time we’ll try anyway, since PIN bypasses like this exist: https://github.com/q3k/m16c-interface.

But for now, the plan is to simply listen in on the SPI bus connecting the uC and RF transceiver. This bus is used for configuring the RF chip and for transmitting the data. The Si443x’s datasheet reveals an incredible number of configurable settings: https://www.silabs.com/documents/public/data-sheets/Si4430-31-32.pdf.

We will capture the traffic on the bus during initialization (i.e. right after installing the batteries) to learn how the uC configures the transceiver. This will reveal the modulation scheme (spoiler: GFSK) and any other parameters we need to impersonate a remote control. To make this easier, I wrote a small Python script (available here) that interprets the SPI traffic (exported from Saleae logic as CSV) and displays it as register reads and writes:

Transceiver initialization traffic

This traffic reveals the following details of the modulation scheme. Note that in this case, my remote was set to channel 9345, which is how it came delivered.

  • GFSK
  • Nominal carrier frequency: 434.5856250 MHz (again, this is dependent on the channel)
  • Data rate: 12.8 kbps
  • Frequency deviation: 25 kHz (corresponds to 50 kHz bandwidth)

Nominal carrier frequency is another term for the center frequency. Therefore, channel 9345 occupies ~[434.585 MHz – 25 kHz, 434.585 MHz + 25 kHz].

Figuring out the channel mapping

So how does the remote get 434.5856250 MHz from “channel 9345”? This is a question I wrestled with for about 2 days. There is no easy linear relationship. I collected a few dozen initialization sequences, each time with a different (random) channel number and looked for a relationship.

Without further ado, the final answer is the following function:

f_{TX} = 10 MHz * \left(19 + 24 + \left\{\begin{array}{lr} 2 * channel + 10658, & \text{if } channel>8862 \\ channel + 19520, & \text{if } channel\leq 8862 \end{array}\right\}\right)

It’s not nearly as bad as it looks. In fact, the only part I had to figure out was the piecewise portion which corresponds to the fc (frequency center) register in the Si443x. So, when the user changes the RF channel, all the uC has to do to tune the transceiver is change the fc register.

The full formula I have presented above was derived from this equation in the datasheet:

Aside: Si443x frequency hopping

The Si443x has a neat feature that lets you do channel-tuning from a single channel select register. You first set the nominal carrier frequency, then the channel step size (increments of >= 10 kHz). It’s generally used for timing critical applications like frequency hopping. So why didn’t the Tempurpedic remote use it? Since the system supports 9999 channels, the 10 kHz channel step is way too big. The remote’s channel scheme uses ~156.2 Hz channel step.

Si443x packet structure

Recall that we’re dealing with burst communications. This means that the receiver will always need to be ready and listening for data. How can a device like this remote control do so in a power-efficient manner? Answer: Combination of preamble (to “wake up” the receiver) and a sync word.

Generalized packet structure diagram from Si443x datasheet


The preamble is designed to be both “easy” to detect and something that is unlikely to be received randomly. A common choice is an alternating series of 1 and 0. The Tempur-Pedic remote uses this sequence, 40 digits long.


Sync word

The preamble detector may wake up the transceiver in the middle of the preamble. How can it know where the data actually begins? This is what the sync word is for. After detecting the preamble, the receiver searches for sync word for a (configurable) period of time.

Sync word

Packet length

The Si443x can operate in fixed or variable length mode. In our case, the remote uses variable length mode, even though all of the data I’ve seen come from it is the same length. So, we’ll need to handle the packet length header.

Data and CRC

The CRC is a sanity check to detect transceiver errors. It’s handled transparently by the Si443x.

Into the data

It’s finally time to investigate what data is actually sent by the remote. Here’s an excerpt of the table I used to decode the protocol. I tuned my remote to various channels and pressed each button.

Immediately, we can see that, independent of channel and command, each transmissions begins with 0x96:

The next three bytes are independent of channel, dependent on command.

The next two bytes are the channel number. E.g. 0x03F2 == 101010

But what about the last part? There is no obvious correlation between neither the command nor channel.

Reverse engineering CRCs

Turns out, the last byte in each transmission is another CRC. But which one? Well, none of the common ones. Thankfully, there are tools for reversing CRCs. I chose this one: http://reveng.sourceforge.net/

All you need to do is feed it a map of inputs (data) => outputs (CRC) and it will brute force the CRC parameters. The output I got is pictured below (the actual command got truncated):

Parameters: width=8, poly=0x8D, init=0xFF, refin=false, refout=false, xorout=0x00, check=0xFD, residue=0x00, name=(none)

The “name=(none)” part means that this set of parameters doesn’t correspond to any well-known CRC.

Recall that the Si443x already has its own CRC. Why does the remote use another one? My best guess is that the engineers chose to add this application-level CRC to guard against data corruption between uC and transceiver. For example, suppose some noise on the SPI bus caused the transceiver to “see” different data that what the uC intended to transmit. The transceiver has no way of knowing this, and so it will dutifully transmit it. The receiver in the base may or may not recognize the corrupted command. An application-level CRC ensures transmission errors on the SPI bus can be detected.

Command “bursting”

Most of the buttons on the remote don’t seem to be debounced. For example, when pressing a command like STOP or FLAT, the remote will actually transmit it between one to three times (in my experience). This is a good thing, since it increases the probability that the receiver actually receives the command. Those types of commands are idempotent, so even if the base does receive multiple of the same, it doesn’t matter.

Buttons that are meant to raise/lower the legs and head are intended to be held down, which causes the respective command to be repeatably transmitted until the button is released.

Massage buttons

The massage buttons are an interesting case. These six buttons increase/decrease the massage intensity of the legs, lumbar, and head region. Initially I had expected that they would be implemented using one command per button, just as the leg/head raise/lower buttons. But in fact, it is more complicated.

There are eleven massage levels, between 0 and 10, with 0 being off. The remote control keeps track of the current massage level for each region. When the user hits the “+” or “-” button, the remote transmits a massage command that encodes the region and the new level. In other words, it transmits the absolute massage level. This means it actually 33 discrete commands to implement the remote’s massage buttons (11 levels * 3 regions). Contrast this to the leg/head raise/lower buttons, which merely transmit relative positioning commands (either “+1” or “-1”).

So why does the massage feature have this complication? Here’s my theory. From the previous section “Command bursting”, we understand that the remote transmits commands multiple times to improve the probability that they are received correctly. This is fine for idempotent commands like STOP, FLAT, etc. It’s also fine for buttons that raise/lower the heads/legs since their positioning is so granular that the user probably won’t notice the difference between a button press that raises their legs 1 step or 3 steps. But since the massage only has 11 levels, the user would definitely notice if one press of “Lumbar message increase” added +1 to the level whereas the next added +3.

Channel auto-learn

A mildly interesting feature of the system is the ability to automatically choose a channel. To do this, you press and hold the FLAT and STOP buttons for ten seconds, then press the STOP button. The remote’s LCD will blink while displaying the new RF channel. While it is blinking, you unplug and then replug the base. If done properly, a relay in the base will click to confirm the new channel has been learned.

While the remote is blinking the new channel, what it’s actually doing internally is transmitting the channel number on a special broadcast channel 5568 (fc = 25088, frequency = 433.9200000 MHz). Presumably, the base listens on this channel immediately after being plugged in.

Side note: A limitation of the system is that the RF channel is chosen purely randomly, and although there are 9999 possible channels, collisions are possible.

Prototyping a receiver

With the protocol understood, I sought to build a receiver that could decode commands from my physical Tempur-Pedic remote. I chose the following Si443x transceiver breakout for prototyping: https://www.tindie.com/products/modtronicsaustralia/rfm22b-wireless-breakout-board-800m-range/ ($22.95). Eventually I am actually going to switch to the Si446x (example breakout), since the Si443x is end-of-life.

The microcontroller I am using is the ESP32 ($21.95): https://www.sparkfun.com/products/13907. It is the perfect choice because it supports Bluetooth Low Energy and WiFi, one/both of which I’m planning to use for the next part of this project wherein I build my own Android app for controlling my base.

A product based on this work is now available to buy! https://www.temperbridge.com/ 

Or clone the code on GitHub and build your own: https://github.com/mostthingsweb/temperbridge-esphome

41 responses to “Reverse engineering the Tempur-Pedic adjustable base remote control”

  1. Keith Monahan Avatar
    Keith Monahan

    Fantastic job here. I love to see detailed reverse engineering like this. Despite being a ham, I’ve only messed with the RTLSDR, but seeing a waveform is completely different from decoding that waveform, especially in light of all the different modulation schemes. Newer devices are even harder, smaller, have newer chipsets, more integrated designs, and so on. I was glad to see you go the logic analyzer route, because it seems to make it a lot easier. I enjoy doing that, but there can be challenges there too, so I appreciate the hard work and the sharing with us!!

    I’ve been working on-again/off-again on reversing a sega genesis Z80 communication to the Yamaha sound chips.

    See here if you’re interested. https://www.techtravels.org/2017/12/watching-ym2612-communication-on-sega-genesis/

    Thanks again for sharing and great job, very interesting!

    1. Chris Laplante Avatar
      Chris Laplante

      Hi Keith, not sure why I never replied to this. Thanks so much for your kind words 🙂

      1. Keith Monahan Avatar
        Keith Monahan

        No worries! It happens sometimes to me my blog — but I try not to! Thanks again!

  2. Corey Vidal Avatar
    Corey Vidal

    Wow, incredible! I just found this while looking up the Tempur-pedic Wi-Fi module on Google.
    I deeeeeeply want to encourage you to keep going. Having an app or any additional ways to control my bed would be incredible.

    1. Chris Laplante Avatar
      Chris Laplante

      Thanks! I assure you it will be finished, eventually :). I just started back up looking at it recently. Out of curiosity, when it is finished, do you think you’d build the interface board yourself, or would you be interested in a pre-assembled board? No promises just want to gauge interest.

      1. Corey Vidal Avatar
        Corey Vidal

        I can’t build anything. Nor can I code anything. Sooooo. 😁

      2. Chris Laplante Avatar
        Chris Laplante

        I’ll tentatively mark that down as one vote for pre-assembled kit 🙂

      3. Ron Avatar

        I have the skills but little time so mark one more vote for interest in a pre-assembled kit 😉

      4. Chris Laplante Avatar
        Chris Laplante

        Roger that. Now let’s just see if I can find the time 🙂

      5. Chris Laplante Avatar
        Chris Laplante

        Hi Corey, it’s been a while but I’ve now got a product for sale based on this :). https://www.temperbridge.com/product/temperbridge-assembled. It targets Home Assistant.

  3. Ron Avatar

    Found this in my hunt for something to control my bed via Alexa and Home Assistant. This would be a great start. Interested in any progress you made.

    1. Chris Laplante Avatar
      Chris Laplante

      No noteworthy progress recently, but I’m back actively working on it. Stay tuned!

      1. Ron Avatar

        I’ll be watching. I tried to download and build the code but haven’t built a c project in years and don’t know cmake so having a bit of trouble getting it to compile.

      2. Chris Laplante Avatar
        Chris Laplante

        Make sure you have completed the ESP32 set up guide: https://docs.espressif.com/projects/esp-idf/en/latest/get-started/. The code right now is a bit of a hodgepodge. As covered in the blog post, the remote uses the Si4431 wireless transceiver, but I have actually decided to utilize the Si446x family – the repository right now represents this state of transition. I am making the switch because the Si443x family is not recommended for new designs (by the manufacturer), and is getting a bit hard to find on Mouser and DigiKey. (The Si4431 is now ~$6 a chip on Mouser, whereas Si4463 is ~$3).

        If you want to tinker with the code yourself, I’d recommend sticking with the Si4431 for now. I have not figured out how to receive packets with the Si446x, let alone transmit any. Checkout commit 86f7c5b (https://bitbucket.org/MostThingsWeb/temper-bridge/commits/86f7c5b16aab0e791fced58fbe756795f41267ce), since it is just before I started diverting my efforts towards Si446x.

        Feel free to email me if you have any questions and I’ll try to help as time allows :). Email is chris @ my domain name (excluding the “blog.” part).

      3. Chris Laplante Avatar
        Chris Laplante

        Hi Ron, I finally have a Home Assistant product out. Please check it out here: https://www.temperbridge.com/product/temperbridge-assembled

      4. Ron Avatar

        That’s great. I am interested in getting one. I do have a split bed but I think I can manage the setup in home assistant to create some virtual switches and automations to change channel. Can you provide some details on how the channel is changed, is modifying or configuring the code required or can I do it all in Home Assistant?

        Also I see the home assistant screen shots on your product page but what I would really like to see is what entities get created so I can figure out how I would write some automations.

        With information on how channel is changed and how entities are triggered I think I would be all set if figuring this out.

      5. Chris Laplante Avatar
        Chris Laplante

        Hi Ron,

        Sorry for the delay – for some reason I wasn’t getting emails about new comments on my blog. The channel is configured directly in Home Assistant as an entity (specifically, it is just a ‘number’ entity). No code changes are required for usage of TemperBridge :).

        I am working on writing up documentation here about how to configure things in Home Assistant. It will go into detail about the entities. I hope to have the docs completed soon, but if you want, you can shoot me an email (chris @ this domain name without the blog.) and we can chat directly :). (Same goes for anyone reading this that has questions about TemperBridge).


  4. David Avatar

    Any idea if this protocol is the same as the one used by the rc-wm-101 remote? (fcc id VFKRC-WM-101)

    We got a tempur bed in probably 2013 or 2014 and no wifi is built in at all, just RF.

    I have an old raspberry pi sitting around. I maybe could run a web server on it and then could get some kind of RF module for it that would adjust the bed (at least the 4 memories and the flat button) in response to clicking buttons on a web page. Should be relatively easy if I could send the right RF signals to the bed from the Pi.

    I’m really resistant to pay tempur $100+ for a new one of their crappy remotes and while our remote still works, it’s flaky if you don’t hold it just the right way and the batteries shift in the battery compartment. Plus I’d rather just control basic functions with my phone.

    1. Chris Laplante Avatar
      Chris Laplante

      It’s most likely a different protocol.

  5. Nathan Fogg Avatar
    Nathan Fogg

    Is this still a thing? If be in for a kit as well.

    1. Chris Laplante Avatar
      Chris Laplante

      Maybe eventually, but I haven’t actively worked on it for a couple months :/

    2. Chris Laplante Avatar
      Chris Laplante

      Hi Nathan, you may be interested in my latest blog post and new Patreon: https://www.patreon.com/chrislaplante. Thanks, Chris

    3. Chris Laplante Avatar
      Chris Laplante

      Hi Nathan, I’ve now got a product for sale based on this :). https://www.temperbridge.com/product/temperbridge-assembled. It targets Home Assistant.

  6. Bob Ryan Avatar
    Bob Ryan

    Chris, Once you finish your Android App, how difficult would it be for you to develop a Control4 plug-in for the Tempur-ERGO? It would be very cool if bed states could be incorporated into Control4 Scenes.

    1. Chris Laplante Avatar
      Chris Laplante

      Hi Bob,

      It wasn’t something I was initially planning, but eventually maybe. I haven’t done much with this project since I last posted, but you’re the second person this week to express interest in it, so I’m going to try to make a concerted effort to push it forward.


  7. Steve Avatar

    I’m interested as well! 🙂

    1. Chris Laplante Avatar
      Chris Laplante

      Hi Steve, thanks for the interest! You may be interest in my latest blog post and new Patreon: https://www.patreon.com/chrislaplante. Thanks again, Chris

  8. Warren cohn Avatar
    Warren cohn

    Me too! Thank you!

    1. Chris Laplante Avatar
      Chris Laplante

      Hi Warren, glad to hear! You may be interested in following my Patreon for additional updates: https://www.patreon.com/chrislaplante . Thanks, Chris

    2. Chris Laplante Avatar
      Chris Laplante

      Hi Warren, I’ve now got a product for sale based on this :). https://www.temperbridge.com/product/temperbridge-assembled. It targets Home Assistant.

  9. Dee Avatar

    Great information.

    There are products like the BroadLink RM4 pro https://www.BroadLink.com and the towide transmitter https://www.amazon.com/dp/B09FKZLJY6?ref_=cm_sw_r_cp_ud_dp_3N5ASGGPRGJM6CZ7CMTD

    Have you worked with either product or others that already have some features? What are the difficulties you see in implementation of a generic solution?

    1. Chris Laplante Avatar
      Chris Laplante

      Hi Dee,

      It’s a good question, but I have no experience with the RM4 pro or similar devices. It may be possible but it’s not something I have investigated. At the very least, I think you’d need to manually ‘teach’ the RM4 each individual button. TemperBridge (https://www.temperbridge.com/) already has the codes programmed in. All you need to do is configure the channel.


  10. Bob Ryan Avatar
    Bob Ryan

    Hi Chris, If you ever get a successful Control4 driver for the Ergo Extend, I’d gladly pay you a few hundred dollars for a license of it. Bob

    1. Chris Laplante Avatar
      Chris Laplante

      Hi Bob,

      Thanks for your comment! I don’t currently have any plans to write a Control4 driver myself, but I think it could be accomplished relatively easily by someone using a TemperBridge and the integrated ESPHome API (https://esphome.io/components/api.html). Alternatively, maybe it would be possible for Control4 to interact with Home Assistant.


  11. J Avatar

    This project is inspiring! Have you taken a look at making this work with the “Temper Ease” base, their current budget model? I’m assuming the same hardware would work?

    I have one, happy to help but this would be my first RF project.

    1. MostThingsWeb Avatar

      Thanks! I have started looking into it (e.g. https://www.tempurpedic.com/other-products/wireless-remote-control/v/3915/), but I’m not sure how far I will get just from a free time standpoint. Hopefully I’ll have something to share before the end of the year.

  12. Jan Avatar

    Hi, thnx for all the effort and you’r product
    Will this also work with this unit https://nl.tempur.com/afstandsbediening-NL68597M.html

    1. MostThingsWeb Avatar

      Hello! Unfortunately I can’t say whether or not it would work. I would guess it would not.

  13. Chris Thompson Avatar
    Chris Thompson

    Hi Chris,

    Just stumbled across your post. This is most excellent! I’ve been working on a very similar issue with a roller shade controller. It’s running a similar Si3348 chip, and I’ve purchased and hooked up a logic analyzer. So far, I can make some heads/tails about what’s happening but I’m wondering if you could help a brother out. Any chance I could borrow that python script you wrote to assist with my decoding?

    Great work! Thank you for your post.

    Chris [t]

  14. Chris Thompson Avatar
    Chris Thompson

    Hi Chris,

    I apologize if this gets posted twice, but WP was doing something weird.

    I’m doing a very similar thing with some roller shades. It uses an Si4438 which is similar enough to take a very similar approach to breaking it open.

    I purchased a logic analyzer and hooked it up (lots of wire and solder) and pulled a dump of some of the logic. I am getting some useful information but it’d be helpful if I could borrow your python script. Any chance you could help a brother out?

    Great work here! Thanks for posting your process!

    1. MostThingsWeb Avatar

      Thanks Chris! I have updated the blog post to include a link to the Python script.

Leave a Reply

%d bloggers like this: