neilpa / circulate

Reverse engineered iOS library for controlling the Anova 2 via Bluetooth
MIT License
59 stars 13 forks source link

Anova Nano Support #7

Open mehlkelm opened 5 years ago

mehlkelm commented 5 years ago

Anova Nano seems to have a new protocol. Any chance you will get to address it? Thanks! Stefan

neilpa commented 5 years ago

I don't have an Anova Nano so it's unlikely. I wasn't even aware of the product until seeing this issue.

If you're looking to dabble in reverse engineering, the way I figured this out before was a combination of a bluetooth software sniffer app and minimal probing of the app libraries. IIRC, I found enough information via a simple strings over the binaries that I didn't even have to load up a disassembler. That's not always the case though but it was enough for me to start experimenting.

It also looks like there are cheap hardware Bluetooth LE sniffers now. These didn't exist when I did this work 5 years ago. I suspect this may be the simplest path to figuring out the protocol.

If you do look further into this I'm more than happy to add support here. Also, if you do hit specific issues/roadblocks feel free to ask questions here. I may be able to help in that regard.

mehlkelm commented 5 years ago

Thanks for the quick answer! I am indeed already trying to reverse engineer.

Capturing the communication is actually easy with the Anova app, an Android phone and the BLE log enabled (at least I think I am getting the right thing).

So far I can't get my head around it though. Maybe we need this guy https://www.youtube.com/watch?v=xDDPFHhY7ec ;-)

neilpa commented 5 years ago

If you can post the BLE log here (or in a gist) and the steps you performed in the app I’d be happy to take a look and see if I notice anything.

mehlkelm commented 5 years ago

btsnoop_hci.log

This is the log of an Android device, pairing with the Nano and then turning the heating on and off three times. The set temperature was at 75°C during the whole time and the water temperature around 20°C.

The Nano part of the log starts around 22:24 with the first appearance of "TexasIns_12:f3:7a (Nano)", I think.

paul-brenner commented 5 years ago

Hey @neilpa I'm also going to take a crack at getting the nano working. I'll use BLE Scanner and pull some logs, but are you willing to share the logs you got from your tests and any notes you might have? I'll share any progress as I go.

neilpa commented 5 years ago

I finally spent some time digging into Stefan's log from above in Wireshark. I've made a bit of progress but haven't cracked the protocol yet.

My working theory is that frames 5029/5031, 5072/5074, and 5116/5118 are the "on" command request/response pairs. Similarly 5065/5067, 5094/5095, and 5138/5139 are the "off" command pairs. These are the only packets that have 0x0a and 0x0b respectively as the third byte in the btatt.value field. I suspect that byte is the "command" byte in the Nano's wire protocol.

Furthermore, I have a weaker suspicion that btatt.value[2] == 0x03 is the set temperature command. These requests are in frames 5024, 5027, 5070, 5114. The first probably from just pairing and opening the app. The latter three would correspond to the three "on" commands above.

If the above is true, then the "temperature parameter" bytes are 08:ee:05 (assuming the final 00 is just a null terminator for all packets). It's not obvious to me without extra examples how this encodes 75-degrees. It does have embedded bit sequences though that map to both seven (111) and five (101). One curious fact is that the number of 1 bits in those three bytes matches "75" in ASCII.

@mehlkelm would you be able to take another trace? In particular, can you set the target temperature to a few different values that increase in 1/10th degree increments. Maybe 53.8, 53.9, 54.0, 54.1 and 69.8, 69.9, 70.0, 70.1. Might also be useful to see 1 degree increments in some range too.

I've got more detailed notes walking through my process. I'm cleaning those up and will post them as a walk-through of the steps I did to get this far. For now though, here's some tshark commands that extract the raw payloads.

# on/off commands
$ tshark -r btsnoop_hci.log -T fields \
    -e frame.number -e frame.p2p_dir -e frame.time_relative -e btatt.value \
    -Y "hci_h4.type == 2 && btatt.opcode in { 0x52 0x1b } && (btatt.value[2] == 0x0a || btatt.value[2] == 0x0b)"
5029    0   30358.442725000 01020a00
5031    1   30358.521773000 01020a00
5065    0   30367.504277000 01020b00
5067    1   30367.589556000 01020b00
5072    0   30371.009801000 01020a00
5074    1   30371.098829000 01020a00
5094    0   30377.796039000 01020b00
5095    1   30377.875767000 01020b00
5116    0   30380.448411000 01020a00
5118    1   30380.508211000 01020a00
5138    0   30387.683550000 01020b00
5139    1   30387.771845000 01020b00

#  set temperature
$ tshark -r btsnoop_hci.log -T fields \
    -e frame.number -e frame.p2p_dir -e frame.time_relative -e btatt.value \
    -Y 'hci_h4.type == 2 && btatt.opcode in { 0x52 0x1b } && btatt.value[2] == 0x03'
5024    0   30356.371959000 01050308ee0500
5025    1   30356.425288000 01020300
5027    0   30358.302027000 01050308ee0500
5028    1   30358.375456000 01020300
5070    0   30370.929342000 01050308ee0500
5071    1   30371.001720000 01020300
5114    0   30380.302995000 01050308ee0500
5115    1   30380.361782000 01020300

# Or if you want a full JSON extract of above 
$ tshark -r btsnoop_hci.log -T json \
    -Y 'hci_h4.type == 2 && btatt.opcode in { 0x52 0x1b } && (btatt.value[2] == 0x0a || btatt.value[2] == 0x0b || btatt.value[2] == 0x03)'
...
mehlkelm commented 5 years ago

I recorded another session. Please excuse the large file, I don't know how to crop the log. The relevant part starts at 2019-06-12, 22:53. The protocol of the action:

22:53 Current temp. 21.0°C, target temp. is 75.0°C 22:59 Target temp. to 30.0 °C 22:59 Start cooking 23:02 Current temp. reaches 30.0 °C (alert in Anova app) 23:03 Set target temp. to 30.1 °C 23:05 Set target temp. to 30.2 °C 23:07 Set target temp. to 30.3 °C 23:09 Set target temp. to 40.0 °C 23:11 Set target temp. to 41.0 °C 23:13 Set target temp. to 42.0 °C 23:13 Current temp. reaches 42 °C 23:14 Stop cooking 23:17 Set target temp. to 50.0 °C 23:19 Set target temp. to 50.1 °C 23:21 Set target temp. to 50.2 °C (maybe it was 23:22) 23:23 Start cooking 23:25 Set target temp. to 64.0 °C 23:26 Set target temp. to 69.8 °C 23:27 Set target temp. to 69.9 °C 23:28 Set target temp. to 70.0 °C 23:29 Set target temp. to 70.1 °C 23:30 Stop cooking

2019-06-12_btsnoop_hci.log

neilpa commented 5 years ago

Awesome! I'll take a look and see what I can figure out.

neilpa commented 5 years ago

I extracted the raw payloads from what I think are the set temperature commands and correlated them to the temperature you were setting. The table below has the three bytes in both hex and binary. You can see the middle byte increase mostly corresponding to 1/10th degree changes. (I also included the 75.0 setting from the prior trace)

temp hex binary degree-delta mid-byte-delta
30.0 08 ac 02 00001000 10101100 00000010
30.1 08 ad 02 00001000 10101101 00000010 0.1 +1
30.2 08 ae 02 00001000 10101110 00000010 0.1 +1
30.3 08 af 02 00001000 10101111 00000010 0.1 +1
40.0 08 90 03 00001000 10010000 00000011 9.7 ??
41.0 08 9a 03 00001000 10011010 00000011 1.0 +10
42.0 08 a4 03 00001000 10100100 00000011 1.0 +10
50.0 08 f4 03 00001000 11110100 00000011 8.0 +80
50.1 08 f5 03 00001000 11110101 00000011 0.1 +1
50.2 08 f6 03 00001000 11110110 00000011 0.1 +1
64.0 08 80 05 00001000 10000000 00000101 13.8 ??
69.8 08 ba 05 00001000 10111010 00000101 5.8 +58
69.9 08 bb 05 00001000 10111011 00000101 0.1 +1
70.0 08 bc 05 00001000 10111100 00000101 0.1 +1
70.1 08 bd 05 00001000 10111101 00000101 0.1 +1
75.0 08 ee 05 00001000 11101110 00000101 4.9 +49

However, the jumps from 42.0 to 50.0 and 50.2 to 64.0 don't make sense yet. There's probably some bit packing/shifting going in here and I'm just not seeing it.

Another possibility is that these are always encoded as Fahrenheit and one of the zero bits is actually meaningful. We just aren't "seeing it" because Celsius is about half as granular as Fahrenheit (e.g. F = C * 9/5 + 32).

@mehlkelm Can you do one more trace that goes from 51.0 to 51.8 in 1/10th degree increments? That would probably give the insight as to what's happening with that third byte.

Also the tshark command and a bit extra that grabs the relavent hex bytes above

tshark -r 2019-06-12_btsnoop_hci.log -T fields -e btatt.value -Y 'hci_h4.type == 2 && btatt.opcode in { 0x52 } && btatt.value[2] == 0x03' | uniq | cut -b 7-12
mehlkelm commented 5 years ago

Finally got around to more tracing: 2019-07-07 13:30 Current temp: 26.6 °C, Target temp: 70.1 13:31 Set target temp: 51.0 °C, start cooking -> low water error… 13:33 Start cooking 13:34 Set target Temp: 51.1 °C 13:35 Set target temp: 51.2 °C 13:36 Set target temp: 51.3 °C 13:37 Set target temp: 51.4 °C 13:38 Set target temp: 51.5 °C 13:39 Set target temp: 51.6 °C 13:40 Set target temp: 51.7 °C 13:41 Set target temp: 51.8 °C 13:42 Stop cooking

btsnoop_hci.log

neilpa commented 5 years ago

Thanks for the new log @mehlkelm.

That trace gave the last clue for the temperature encoding. The upper bit in the "mid-byte" in my above table is always 1. Looking at frames 2288 and 2594 in the new log those correspond to the setting 51.1 and 51.2 respectively. You can see the key bytes go from 08:ff:03 to 08:80:04. That aligns with the other large gaps in the table which I couldn't figure out before.

Given that, I can naively replicate the numeric encoding. However, I still don't understand the logic behind this encoding. Another interesting thing to test would be trying the same numeric temperatures but in Fahrenheit to see if units are also encoded in that data.

I'll start hacking on a branch to test out some of these ideas. But without a device it's going to be hard to test since it also appears that the initial communication setup is different as well.

mehlkelm commented 4 years ago

So, since iOS 13 you can easily trace Bluetooth packets in realtime: https://www.bluetooth.com/blog/a-new-way-to-debug-iosbluetooth-applications/

Will be posting some new findings

mehlkelm commented 4 years ago

Start device From PacketLogger.app

ATT Send         0x0042  C4:64:E3:12:F3:7A  Write Request 
- Handle:0x000B - 0E140001-0AF1-4582-A242-773E63054C68 
- Value: 0103 1008 0100

Stop device From PacketLogger.app

ATT Send         0x0042  C4:64:E3:12:F3:7A  Write Request 
- Handle:0x000B - 0E140001-0AF1-4582-A242-773E63054C68 
- Value: 0102 0B00

Set temp 55 From PacketLogger.app

ATT Send         0x0042  C4:64:E3:12:F3:7A  Write Request 
- Handle:0x000B - 0E140001-0AF1-4582-A242-773E63054C68 
- Value: 0105 0308 A604 00  

Set temp 66 From PacketLogger.app

ATT Send         0x0042  C4:64:E3:12:F3:7A  Write Request 
- Handle:0x000B - 0E140001-0AF1-4582-A242-773E63054C68 
- Value: 0105 0308 9405 00
garret commented 3 years ago

May I ask if there are any news on Anova Nano support?

neilpa commented 3 years ago

@garret No. I don't have a device so any further progress is going to be difficult. It'll likely require someone with a device to pick up where this thread left-off and provide specifics one what the protocol looks like.