Close

Some time later...

A project log for Reverse Engineering Grant ASHP Remote Protocol

Reverse engineering the protocol used by our Grant ASHP remote.

mjc506mjc506 02/28/2022 at 19:150 Comments

It's been a while...

Since the last post, I went ahead and had my gadget control the CH and DHW demands, replacing the room thermostat. This consisted of using the 'spare' optocouplers to drive a pair of 5V relays, and also using some DS18B20's to measure room temperature, and the temperature on the CH buffer and DHW cylinder. There were some integration issues (it seems that switching the relays sometimes upset the DS18B20s) but the system worked well. Well enough that I stopped fiddling with it, which is quite some achievement :-)

However, "working well" isn't "working perfectly". My main 'issue' was that I was only turning the heatpump 'on' and 'off' in response to room temperature. If it was cold out, the weather compensation would ramp the compressor right up, even if the room temperature was only a little under the set point, resulting in short cycle times and a noticeable room temp fluctuation. Alternatively, if I dialled back the flow temperatures in the weather compensation settings, it would trickle for hours slllooowwwwly warming the building up. I needed some control over flow temperatures.

Trying to do this by 'pressing' the buttons was not feasible. Requesting the basic 'user' data was tedious enough, but to set 'installer' variables required one additional button, and many button presses (too each to 'miss' a press and get out of sync). Time to try talking directly to the UART again...

My first attempt was to cut the connection between the MCU in the remote and the transceiver chip, and insert a microcontroller under my own control and essentially perform a MITM attack. I would need to forward bytes between the MCU and heat pump as quickly as possible, and then 'insert' my own messages in the gaps between transmissions. It's only 1200baud, how hard could it be...

This needed a micro with at least two UARTs (software UARTs should have worked, but the low speed meant long delays and slow interrupts. A third hardware UART would be handy as a 'console' connection over USB so I could see what was flowing back and forth). An Arduino Mega would have done the job, and is 5V, but it rather large. An ESP32 is faster, has three hardware UARTs (with user-definable pins), and also has wifi built in. Ideal, although the 3.3V logic needed a logic level converter - not a problem, still got a pack from last time...

One (of many) quirk is that anything transmitting to the transceiver chip would immediately receive an echo due to the way it works. Handy in one sense (easy to check for collisions or other corruption as the message is being sent), but also means I have to keep the MCU happy by echoing its transmitted bytes back to it. I quickly found that just Serial.write(Serial.read()); wasn't quick enough. Connecting TX to RX should have solved that, but introduced other problems (have to clear the RX buffer after transmitting, without trampling genuine RXs, two TXs and two RX lines connected together causes pull up problems)... eventually it became clear that I wasn't going to be able to get this working quickly (also considering that while I was fiddling, the heating to the house was off).

Attempt two restored the connection between the MCU and transceiver across a couple of resistors, with the ESP32 now connected strategically each side of the 'RX' resistor. The plan was I could 'blind' the MCU by pulling it's RX line high while I was transmitting (preventing it seeing bytes that it didn't send), while still being able to read the RX line before the resistor...

It (sort of) worked! I also had to perform another dirty hack to move the Serial TX pin around (the TX pin was of course holding the TX line high, preventing the MCU from talking to the heat pump), and I was still not entirely satisfying the MCU (the remote was still erroring out, but was either unable or not trying to disable the heatpump as before) but I could send messages and receive responses! I had previously decoded much of the serial protocol, and a quick datalog (before pulling the remote apart again) had unveiled how to set 'installer variables' - the only authentication was on the remote, the serial side was relatively easy):

So, to set register A to B, we send (0xAl is least significant byte of A, 0xAm is most significant byte)
0x0C 01 00 99 0E 0xAl 0xAm 0xBl 0xBm CRC CRC Checksum

The 5th byte would be 0x02 to just request the current setting/data

and the heatpump confirms with
0x13 00 01 9A 0E 0xAl 0xAm 0xBl 0xBm 00 00 01 00 01 00 00 CRC CRC Checksum

The 'user data' I was requesting previously with the buttons is accessible at registers 0x64 to 0x6D (100 to 109). The setting to enable/disable the internal weather compensation curve is 2100 (0x32 0x08, lsb first) with the 'fixed flow temperature' setting at 2101 (in 0.5°C steps)

When powering up the system from cold, the heat pump and remote go through some kind of auth/setup sequence. I captured, but could not decode this. Once booted, the user needs to press the 'power' button on the remote to move the heat pump from 'standby' to 'on'. This doesn't work is the remote has faulted (for example, if someone's kludged something in to the serial lines that's sending a load of data) so in the interests of keep in the heating working if there's a power cut, I'll need to send this message to the heatpump too. Again, thankfully, I've already decoded this packet - 

0x97

This one contains some data.

5th byte - minute
6th byte - hour
7th byte - second
8th byte - day
9th and 10th bytes - 00 00 means the ASHP is on stand-by, 01 00 is on, but no DHW or CH demand, 01 01 is on with DHW demand, and 05 00 is on with CH demand.
The remote sends day/time and stand-by/on data to the ASHP, and the ASHP sends the demand data.

And some captures:

19:34:55.788 -> RX from line: 0x0E 00 01 98 2D 12 28 06 05 00 50 78 F3 2B POWER status: 1
19:35:03.157 -> RX from line: 0x0E 01 00 97 2D 12 2F 06 05 00 50 1F 27 4A 
19:35:04.318 -> RX from line: 0x0E 01 00 97 2D 12 30 06 04 00 50 49 B4 93 
19:35:04.683 -> RX from line: 0x0E 00 01 98 2D 12 30 06 00 00 50 95 74 8A POWER status: 0
19:35:05.913 -> RX from line: 0x0E 01 00 97 2D 12 32 06 00 00 50 D1 F7 CA 
19:35:13.124 -> RX from line: 0x0E 01 00 97 2D 12 39 06 01 00 50 0A 38 48 
19:35:14.021 -> RX from line: 0x0E 00 01 98 2D 12 3A 06 05 00 50 38 2A 22 POWER status: 1

Not too difficult.

Now, I have a system that reads indoor/cylinder/buffer/outdoor temperatures, and can command a target flow temperature for the heatpump to follow. I'm using a PI controller plus a feed-forward term (based on the standard weather compensation curve), which seems to be working very well - the flow temperature ramps up quite aggressively to heat the house up from cold quickly, but then throttles back as the room temperature approaches the set point, resulting in long (in theory, continuous) cycles at a lower flow temp and higher CoP.

Of course, it's not that simple. If the heat pump runs at low power continuously, this does result in a high CoP, but does also mean that the circulation pumps also run continuously. There will be some level where the savings in power from improving CoP are completely eroded by the power used by the circulation pumps. As I have complete (perhaps indirect) control over cycle times and flow temperatures, however, this is something that can be monitored and adjusted. It may not even make a significant difference (the circ pumps use less than 150W combined, the heat pump compressor can peak at 7kW...)

Discussions