Marty's Blog.

ThermoPro reverse engineering 4: Trying to take control

Martin Hughes
Martin Hughes

Summary

  • The TP25 requires at least one 0x01 command before it'll send temperatures
    • Just sending 0x300030 leads to what I believe is an error code.
  • After that it'll happily send updates seemingly forever. I'm not sure what's different to the app-driven data flow.

More observations

Since my last post, I've noticed a couple more things about responses being sent by the device

  • They seem to always be 20 bytes long
  • The last two bytes are often 0x0200 (most commands) or 0x0140 (0x30 command). The exceptions are responses 0x01 and 0x23.

A basic Rust controller program

I left the last post feeling like I ought to try and control the thermometer myself. To that end, I wrote a basic controller program in Rust. You can see it in my thermopro-tools repo.

As it stands, it's not very much - it simply connects to the thermometer, sends a command, and then prints out any notifications it receives.

Why Rust? Basically I thought I ought to try and learn at least the basics. I like that it's memory-safe without needing a garbage collector, that it's fully typed, and that it compiles to machine code. It seems like it's easy enough to compile to WebAssembly as well, which might be useful for my purposes...

Sending a 0x30 command

Take a look at commit 94243b9 to see the program in this state.

My hypothesis was that you could send a 0x30 command and receive temperature responses. What actually happened was this:

Write / notify? Data
Write 300030
Notify E00230041600917D0000E9190020480000200200

And that was it! Not exactly what I was expecting... I assume the 0xe0 response is some kind of error code? At least it meets all the other usual constants - it's 20 bytes long, and finishes 0200.

Sending a 0x01 command

Commit 66b4f05 does this.

My original theory was that the 0x01 command is some kind of preamble / setup command. What happens if we send that?

Write / notify? Data
Write 01097032e2c1799db4d1c7b1
Notify 01010a0ce2c1799db4d1c7b10020c1799db4d1c7
Notify 300f5a0c00ffffffffffff0222ffffffffbf0140
Notify 300f5a0c00ffffffffffff0222ffffffffbf0140
Notify 300f5a0c00ffffffffffff0222ffffffffbf0140
Notify 300f5a0c00ffffffffffff0222ffffffffbf0140
Notify 300f5a0c00ffffffffffff0222ffffffffbf0140
Notify 300f5a0c00ffffffffffff0263ffffffff000140
Notify 300f5a0c00ffffffffffff0291ffffffff2e0140
Notify 300f5a0c00ffffffffffff0305ffffffffa30140
Notify 300f5a0c00ffffffffffff0305ffffffffa30140
Notify 300f5a0c00ffffffffffff0318ffffffffb60140
Notify 300f5a0c00ffffffffffff0318ffffffffb60140
Notify 300f5a0c00ffffffffffff0318ffffffffb60140
Notify 300f5a0c00ffffffffffff0318ffffffffb60140
Notify 300f5a0c00ffffffffffff0328ffffffffc60140
Notify 300f5a0c00ffffffffffff0328ffffffffc60140
Notify 300f5a0c00ffffffffffff0328ffffffffc60140
Notify 300f5a0c00ffffffffffff0328ffffffffc60140
Notify 300f5a0c00ffffffffffff0331ffffffffcf0140
Notify 300f5a0c00ffffffffffff0331ffffffffcf0140
... seemingly forever

The most interesting things are:

  1. We get a continuous set of responses without needing to send a continuous set of commands - so it's not clear why the app keeps sending 0x30 commands
  2. The thermometer does not seem to need anything other than 0x01 to get started.

Deliberately sending a wrong checksum

Notice how the last nybble has changed from one to zero:

Write / notify? Data
Write 01097032e2c1799db4d1c7b0
Notify e0020102e5c1799db4d1c7b00020480000200200

I wondered if this would be the same response as sending a 0x30 command straight away, but it is not. It is still a 0x0e response though, which solidifies my belief that 0x0e is an 'error' response of some kind.

Sending a slightly different 0x01 command

This time I changed the second-to-last byte, but made sure the checksum was still correct:

Write / notify? Data
Write 01097032e2c1799db4d1c6b0
Notify 01010f11e2c1799db4d1c6b00020c1799db4d1c7

Unlike the original 0x01 command snooped from the app, this one did not automatically trigger a sequence of 0x30 responses.

There is still a lot of repetition in the middle of the response. The beginning is subtly different, and the last few bytes exactly match the response from the snooped 0x01 command. I'm not sure what to make of this - perhaps:

  • The beginning few bytes are some flags indicating the current state
  • The last few bytes are the magic code that needs to be sent to kick things to life?

Conclusions

This hasn't been hugely enlightening. The main oddity is that the thermometer will send 0x30 notifications without needing to wait for an 0x30 command, and I'm not sure why. I assume that when the app is driving the thermometer, one of the commands must turn off the automatic 0x30 notifications. To check this, I need to expand my controller app.

Next steps:

  • Expand the rust controller to send more commands.
  • After that, record some more 'real' data flows to look for patterns.