Hacking an LG Ducted Split for Home Automation

3,483

28

16

First of all - This is not another Infrared remote control emulation hack. My particular AC has no usable interface designed for any sort of control other than the included wall mounted smart controls.

I have an LG Ducted reverse split system in my house. Unfortunately it was made at a time where IoT was not high on any manufacturers list. I discovered it had some options for 'master' control but even though the unit was only 2 years old at the time of me first attempting this, the expansion boards were unobtanium and prices were astronomical anyway. As was the 'Wireless RF Remote' addon which would have made things a lot easier but impossible to buy.

Had it been my choice, it would not be an LG but since it was installed in the house when I purchased it (and it's replacement cost would likely be in excess of $10k) it's what I had to deal with.

Aim - To be able to control the AC via MQTT for the purposes of automation via OpenHAB and IFTTT/Google Assistant

Step 1: Decoding the Data Format

I started this process 4 years ago but didn't get very far and didn't want to risk damaging the unit - Especially since parts for it seem almost impossible to find.

Ripping the controller off the wall I found 3 wires which I determined to be Ground, 12v and 'signal'

The signaling voltage on the data line was at 12v, but i did notice that it seemed to fluctuate on the multimeter (some sort of pulses on the line).

I bread boarded a a basic circuit to drive an opto isolator via the data pin and connected the other side of the opto isolator as an input on my PC's sound card and got a poor version of a scope output (Pic 1).

This is about as far as I got at the time - I could see there was something there but didn't really know how to 'decode' it.

Since getting my Coffee Machine IoT enabled, I had a refreshed interest in trying this again with a little more determination this time.

I posted my findings over at the EEVBlog forums to see if someone might be able to shed some light and a great guy named Ian came to my rescue - He laid it out in a way it completely made sense (Pic 2)

Basically, the data stream is 13 bytes of 'standard serial' - 8 data bits, one start bit and one stop bit (no parity) but at a VERY low baud rate of 104bps.

Step 2: Looking Deeper

So now that I had an idea of how the data was formatted, I needed a way to be able to read the data in a more dynamic way.

I pulled one of my controllers off the wall and hooked it up via a logic level shifter to an Arduino with a simple sketch to read 13 bytes of data via software serial port configured at 104bps and print it out:

168,18,0,8,0,192,6,22,0,0,0,0,
168,18,0,8,0,192,6,22,0,0,0,0,
40,19,0,8,0,200,6,31,0,0,0,0,
40,19,0,8,0,200,6,31,0,0,0,0,
200,18,0,8,64,0,6,25,0,0,0,0,
200,18,0,8,64,0,6,25,0,0,0,0,
168,18,0,8,0,200,6,22,0,0,0,0,
168,18,0,8,0,200,6,22,0,0,0,0,
168,18,0,8,0,200,6,22,0,0,0,0,

**Actually 12 bytes here

We had action!

By then changing the various settings on the controller, I was able to work out the bytes that change:

168,3,0,0,0,192,3,31,0,0,0,0,248,Fan LOW
168,35,0,0,0,192,3,31,0,0,0,0,248,Fan MED
168,67,0,0,0,192,3,31,0,0,0,0,152,Fan HIGH

168,67,0,0,0,248,3,33,0,0,0,0,82,Z1234
168,67,0,0,0,192,3,34,0,0,0,0,133,Z1
168,67,0,0,0,160,3,34,0,0,0,0,229,Z2
168,67,0,0,0,144,3,34,0,0,0,0,245,Z3
168,67,0,0,0,136,3,35,0,0,0,0,204,Z4

168,75,0,0,0,136,3,35,0,0,0,0,244,Mode FAN
168,79,0,0,0,136,10,35,0,0,0,0,249,Mode AUTO
168,67,0,0,0,136,3,35,0,0,0,0,204,Mode COOL
168,83,0,0,0,136,15,34,0,0,0,0,225,Mode HEAT
168,7,0,0,0,136,15,34,0,0,0,0,61,Mode DH

168,15,0,0,0,136,3,34,0,0,0,0,49,Temp 18
168,15,0,0,0,136,4,34,0,0,0,0,48,Temp 19
168,15,0,0,0,136,5,34,0,0,0,0,51,Temp 20
168,15,0,0,0,136,15,34,0,0,0,0,37,Temp 30

The numbers make much more sense when you look at them in binary but what's with the 13th byte?? It's all over the place...

Step 3: Mapping It Out

Through trial and error, I was able to determine the relevant bits in the 13 bytes of data that I would need to be able to transmit.

Step 4: Brick Wall Ahead!

This is where it got complicated. I had two hurdles to overcome

a) The 13th byte appeared to be a checksum of the data that I needed to somehow work out.
b) How do I transmit the data then? It's only one wire.

Issue 'a' turned out to be REALLY easy, but it was by pure coincidence that I managed to get past it.

In my tests, I was looking at data like:
A802000000040F61000000004B
A81200004004169A00000000FB
A81200004004159A00000000F8
A81200004004149A00000000E5
A81200084000149C00000000E7
A83200084000149C0000000087
A85200084000149C00000000A7

This is the 13bytes of data including the checksum (here in HEX instead of DEC).

When I was searching the oracle that is google on 'how to reverse engineer a checksum' I came across this page on stack exchange with someone else going by the name of Nick asking pretty much the same thing as me but not only that, they talked about an air conditioner and their data was almost identical format to mine - Could it be??? In all my searching (in 4 or so years), not one person had posted any information on how to hack the protocol on these air conditioners and I just happen to stumble upon someone doing the same thing by searching for something almost completely unrelated? It was a blessing - He even posted that he worked it out and the solution was: Add up all of the Bytes of data and then XOR with "U".

With that in hand I added it to my code to calculate what I thought the checksum should be vs what it actually was but it was all WRONG!!

As it turns out, it was sort of wrong. When I started looking at the numbers in binary, it made complete sense.

The response from the 'XOR with U' always returned 9 bits of data (the 9th bit always one) but the other bits were right. I simply removed the 9th bit by taking 256 from the resulting number and then it matched!!

Had it not have been for this individual, I might still be scratching my head. Hats off to him too but I can't contact him - That was basically his only post on the stackexchange forum. Well, thank you stranger :)

The next challenge was making a circuit that would allow me to simulate the existing controller. I mapped out the schematic for the drive circuit (Pic1 and Pic 2) but it seemed way too complicated for me to need to reproduce it to get what I wanted. I was already reading the signal after all. I opted for a much simpler method - Using the arduino to drive an opto isolator to pull the 12v signal line low as required.

I also designed a simpler circuit for the Rx but this is untested, I ended up sticking with the level converter for simplicity.

Step 5: Making It Work..

Once I had the transmit circuit breadboarded, and with a racing heart, I mangled up a (static) string of 12 bytes, calculated the checksum and had the arduino send the command - Amazingly, the display updated!!! Win!

The final actual test was to add my arduino to the BUS with the 2 other controllers for a real live test and sure enough, it worked.

So now I could Read and Write to the bus but just lacked the ability to be able to do it simply.

Since I use MQTT almost exclusively for all my home automation, it was natural that this would be the same.
I wrote out the code over several days to control the 4 main elements of the AC, also reading back the existing status (from the other modules on the BUS)

The intention was to have the code running on an ESP8266 module however it would seem that the ESP8266 is not able to produce a baud rate as low as 104bps. I had to revert to a generic Arduino Uno with Wiznet ethernet but that was not hard as my comms rack was literally on the other side of the wall from one of the AC controllers.

The code is a bit all over the place but should be legible. I had a lot of problems with preventing the controller from reading it's own output but also repeating the code it it's own published topics received from MQTT back to the aircon. Basically, it would create an infinite loop. In the end, some buffer clearing and delays in the processing of code after publishing to MQTT got it sorted.

Rx,Tx pins to the AC are coded as 3,4 but change if you like

The code is configured to publish and accept commands as such:

ha/mod/5557/P 0/1 - Power
ha/mod/5557/M 0/1/2/3/4 - Mode Cool,Dehumidify, Fan, Auto, Heat
ha/mod/5557/F 0/1/2 - Fan low, med, high
ha/mod/5557/Z i.e 1111 for all zones on 1000 for just zone 1 on.

**From the controller, zones can not be set to '0000' however it would seem that if you issue the value, it will revert to '1000'.

The latest version of the code is available from my GitHub Repo: https://github.com/ahuxtable/LG_Aircon_MQTT_interface

Step 6: Something More Permanent

I gathered up an arduino prototype board and installed all of the parts as I had them bread boarded.

Step 7: OpenHAB Config

See attached file for OpenHAB Items, sitemap and rules

Combine this with the IFTTT OpenHab binding and Google Assistant/Home and you have a very powerful voice controlled and/or 'Smart' aircon that surpasses almost every commercially available product!

Step 8: Summary

In Conclusion - If you are one of the poor souls with a slightly older LG ducted split air conditioner, you are not alone. There is still hope for us!

I hope this instructable finds someone that needs it as much as I did. There is basically NO information that I could find (other than the checksum from 'Nick'). I had to start from scratch but I am ecstatic with the result.

The information is a tad vague I know but if you are in the same situation as I was, I'll be more than willing to help out.

--- Caution / Update ---
Although it is possible to change the settings on the AC with the unit Off, I have found that when it comes to the Zone control it seems to mess with it. I done a lot of testing with the unit off and I found that the zones would show as inactive but when the unit is operating, it seems that the dampers are not fully closed (but not fully open either). I reset the unit at the main breaker and this resolved the issue. Since only changing zones when the unit is on, this has not been a problem.

I have also updated the code to only publish (to MQTT) changes that come from the master controller and not the main unit. Once again, this could cause problems because the main unit will send '0000' for the zones (which could also have been the problem).

Updated code also introduces some timing constraints to try to prevent the arduino from transmitting at the same time of the master and main unit. I'm sure there is probably a method that the controller uses to initiate a data send like pulling the line low for Xms before sending but I have not discovered it yet if there is.

I discovered that the main unit will send data every 60 seconds and the master controller sends every 20 seconds. The code attempts to stall sending data within 2 seconds of receiving data packet. However, sometimes the master and main unit transmit very close to each other. This will probably be refined more soon.
----------------------------

**May work on newer units

*** Some information found in my research travels indicated Panasonic ducted split might use the same protocol. YMMV.

Share

    Recommendations

    • Plastics Contest

      Plastics Contest
    • Make it Glow Contest 2018

      Make it Glow Contest 2018
    • Optics Contest

      Optics Contest

    16 Discussions

    0
    None
    andracolsyn

    8 weeks ago

    Hi Andrew,

    This is great! I've managed to start with your extensive work and have done a fair bit of experimentation with this over the last few weeks and thought I should share where I'm at for everyone's benefit.

    I have a current gen LG ducted AC system, which appears to use the same / similar protocol as yours. The serial comms is ~100bps on the yellow wire with 12v signalling. The bus on my unit periodically receives a bunch of different messages, and I'll go through each below.

    A8 messages, sent by the wall controller, are sent every 20s and approximately take the form:

    A813 8000 00AC 1717 0000 0000 40

    Roughly, I've worked out each byte to be carrying the following information:

    uint8_t message_type; // A8
    uint8_t power_command_fan; // 0000 00X0 is set when the unit is on, 0000 000X is set when a command is being sent, 0XX0 0000 carries the current fan speed (00, 01, or 10), 000X XX00 carries the current mode (i.e. heating, cooling etc)
    uint8_t unk0; // always seems to be 0x80
    uint8_t fan; // 0000 X000 is set when the fan is not spinning and power is on, 0000 0X00 is set when the outdoor unit is defrosting (this field originally comes from a C8 message and the A8 echoes them back)
    uint8_t unk1; // never seen data
    uint8_t zones; // 0XXX X000 carries the current state of zones 1-4, read left to right
    uint8_t set_temp; // 0000 XXXX carries the set temp in the form X + 15 celsius
    uint8_t current_temp; // 00XX XXXX carries the current room temp in the form (10 + (X/2)) celsius
    uint8_t unk2; // never seen data
    uint8_t unk3; // never seen data
    uint8_t zones2; // 000X XXX0 carries the current state of zones 5-8, read left to right
    uint8_t unk5; // never seen data
    uint8_t crc; // same algorithm as documented by you (sum XOR 0x55)

    I expect the zones2 field is unique to the newer units. Do the previous generation ones support 4 or 8 zones?

    There are also C8 messages sent every 60s with roughly the same format as the A8 messages, although I haven't worked out what the current_temp byte is carrying. I suspect this message type is being sent by the indoor unit, so I thought the current temp might be the intake temperature or something but it seems to volatile to be that. Example:

    C852 0000 40E4 1783 0000 0000 8D

    The wall controller also sends an AB message every 10 minutes, which contains the number of zones configured:

    AB00 0000 0000 0000 0000 8000 7E where 8 can be 1-8

    There are also CC messages approximately every 60s. The only field I have worked out is that the first byte after the message type (2D) counts down by 1 every hour the unit is on. It is presumably some sort of run time counter. The other fields change occasionally but I don't know why.

    CC2D 4400 0000 1716 0017 0000 D4

    I can also force the wall controller to send AA messages, which set the low/medium/high fan speeds from 0-255:

    AA00 0046 6478 0000 0000 0000 99 // 46 is low speed (70 dec), 64 is med speed (100 dec), and 78 is high speed (120 dec).

    If I send this message on the bus it changes the fan speed immediately even when the unit is operating. I have no idea why it's restricted to 3 pre-configured fan speeds when the unit is obviously capable of much more.

    There are CB messages sent every 10 minutes. Below is an example sequence. No idea what they carry:

    CB80 0058 2E00 0000 1209 3000 49
    CB00 0058 2E00 0000 1209 3000 C9
    CB80 0060 3400 0000 1209 3000 7F
    CB00 0060 3400 0000 1209 3000 FF
    CB80 00ED E700 0000 1209 3000 3F
    CB00 00ED E700 0000 1209 3000 BF

    If anyone out there has any suggestions as to what any other fields carry I'd love to hear it. One thing I was really hoping was to be able to limit how hard the compressor is working to try to minimise the chance of the outdoor unit frosting over when humidity is high with low outdoor temps in winter, but I suspect that might not be possible.

    Oh, I've also got an ESP8266 able to read/write the bus using the software serial library, and can send/receive MQTT messages over wifi for control. The code is still messy so I want to clean it up a bit, but plan on uploading to github at some stage. I'm hoping that I will be able to power it from the 12v line using a buck converter. I'll share how it goes.

    3 replies
    0
    None
    andrew_handracolsyn

    Reply 5 weeks ago

    Awesome work - Great to see that I have helped others and that you have been able to push it further. I am happy with the amount of control I have at the moment. Mine is only 4 Zones ( I wish it was more, as I have manual switched dampers for 3 other zones). Glad you got the ESP module to work - That was my initial intention but I ran into issues with the Baud rate and just stuck with the Uno - It works and it was no hassle for me to cable it. Please do publish your code.

    0
    None
    andracolsynandracolsyn

    Reply 6 weeks ago

    I can confirm that the 12v line is sufficient to power an ESP8266 using a buck converter. Has been working for about a week or so now. It's rebooting occasionally but that might just be my dodgy code.

    Can also confirm that the current_temp octet described above contains the return air temperature measured by the indoor unit when sent as a "C8" message. It is calculated using the same formula as an "A8" message. The value tracks the reported value by the controller if I change the temperature probe on it to 'Indoor' rather than 'Remote' or '2th'.

    Have also worked out that bits 00XX 0000 in the set_temp byte contain which temperature probe is being used by the unit to tell when the set temperature has been reached. 00 is "Indoor", 01 is "Remote", and 10 is "2th".

    0
    None
    andracolsynandracolsyn

    Reply 8 weeks ago

    I've worked out a little more of the protocol. The first byte is always the sender of the message in the 4 msb (A for the main wall controller, C for the indoor/outdoor unit, or 2 for a slave wall controller). The second byte is the message type.

    I've also since discovered that the CB message indicates whether DRED (demand response enabling device - utility power management) is enabled. The 4 lsb of the second byte will be 0 when DRED is disabled, 1 when DRM1 is enabled, 2 when DRM2 is enabled, and 3 when DRM3 is enabled. Additionally, as this is a 'B' type message, the highlighted 3 in the 11th byte is an echo of the AB command sent by the wall controller - that is, the number of zones configured. If I send an AB command with the DRED nibble set it unfortunately doesn't put the unit in DRED mode. I still haven't figured out the other bytes in this message.

    CB0200646500000012093000B4

    0
    None
    GuidoT8

    4 months ago

    Hi Andrew,

    this is amazing, thank you so much! I actually got it to work with an ESP8266, using the alternative software serial from https://github.com/plerup/espsoftwareserial

    I've only got one wall controller (the master) so I'm not sure if the slave uses a slightly different protocol. I noticed that the master sets bit 0 of byte 1 when it changes any setting, and then back to 0 for the regular status updates. Does the slave do the same? I implemented this and it seems to be okay. Another thing I think I found out is that bit 3 of byte 3 is 1 if the AC is in "idle" (ie not running due to low temperature difference).

    I've also been trying to decode the current room temperature (seems to be in byte 7). However I can't figure out yet how to know whether the sensor is set to "indoor" or "remote", the master doesn't seem to send anything when I switch between the sensors, but starts sending the correct temperature value afterwards. Anyway, great stuff. I'll post my code on GitHub when it's finished (I'm connecting the ESP8266 to homebridge / Apple HomeKit with a raspberry pi).

    1 reply
    0
    None
    andrew_hGuidoT8

    Reply 4 months ago

    Hi Guido, you are correct however it's actually bit 8 of byte 1 (msb first). 0 for slave, 1 for master. This is also changed when you flip the switch on the back of the controller. I never really bothered with the room temp but looking at the data I logged, I would think that it is likely in the 8th byte (assuming first byte is 1, not 0), the set temp is in bits 0-4 of byte 7 . I never saw anything in the 9-12th bytes. If you do find these, i'd love to add them into the code for everyone else.

    You'll see that the main unit transmits every minute, and the master controller every 20 seconds. You may have to play with timings - I modified the code a couple of weeks ago as I was having issues with colissions but to be honest, I think I made it worse.

    Great to see people using this code!

    0
    None
    RanM5

    Question 7 months ago on Step 8

    Hi Andrew,

    AMAZING WORK!!! I was searching for such solution for so long...

    I tried to come up with such solution myself using esp8266 module but didn't get so far. I really appreciate your effort, it's not trivial to do such reverse engineering... Kudos!

    I have to give it a try as soon as i can.

    few questions:

    1. Following your comment on the ESP modules and the baud rate, any idea if it will work with the Arduino MKR1000 (wifi enabled)?

    https://store.arduino.cc/arduino-mkr1000-with-head...

    2. Can you share the wiring for connecting the Arduino to the signal wire?

    3. My home automation setup is based on MQTT and home assistant. If you ever made such sample config file for home assistant, please share.

    It would be great to publish your code on GitHub, to allow others to contribute and collaborate.

    6 more answers
    0
    None
    RanM5andrew_h

    Answer 7 months ago

    This is great.

    I order the parts i'm missing and I will give it a try once they arrive.

    I will connect an esp8266 module to the arduino using a bi-directional logic level converter and refactor the code to communicate with it properly.

    As for the issue of modifying the settings when the unit is off, what do you think about ignoring these mqtt commands when the unit is off?

    0
    None
    andrew_hRanM5

    Answer 7 months ago

    Contrary to popular belief, there is no need to shift the logic of the ESP<->Arduino, the ESP8266 is definitely, 100% 5v logic safe. This was a common misinterpretation from the original chinese to english translation of the datasheet. Now that there is a expressif published English datasheet, the section on I/O has been reworded and states it is 5v logic safe. I have also interfaced several with 5v logic and it works 100% fine since some of the first chips were released.

    I actually revised my sitemap so that it hides all of the features unless the power is on:

    Group item=HVAC label="HVAC" icon="climate-on" {

    Switch item=AC_Power label="Power"

    Selection item=AC_Mode label="Mode" mappings=[0="Cool", 1="DH", 2="Fan", 3="Auto", 4="Heat"] visibility=[AC_Power==ON]

    Selection item=AC_Fan label="Fan" mappings=[0="Low", 1="Med", 3="High"] visibility=[AC_Power==ON]

    Setpoint item=AC_Temp label="Temp [%d]" minValue=18 maxValue=30 step=1 visibility=[AC_Power==ON]

    Group item=Zones label="Zones" visibility=[AC_Power==ON] {

    Switch item=AC_Zone0 label="Zone 1"

    Switch item=AC_Zone1 label="Zone 2"

    Switch item=AC_Zone2 label="Zone 3"

    Switch item=AC_Zone3 label="Zone 4"

    }

    }

    0
    None
    RanM5andrew_h

    Answer 7 months ago

    Cool! thanks for the tip.

    I think that adding this 'is on' check on the Arduino level would be safer. I will share my code once it's done.

    1
    None
    RanM5RanM5

    Answer 7 months ago

    Thanks!
    I'm looking forward to see these photos.

    1
    None
    andrew_hRanM5

    Answer 7 months ago

    I do not have any experience with the MKR1000, the only way you would know is if you tried it really. The ESP8266 will compile at that baud rate but it will not work, so I am assuming that the MKR1000 would operate the same making a test compile irrelevant too. You may be able to do some trickery by joining a Pro Mini and an ESP8266 with i2C or something though if you specifically need wireless control. Just be careful though, I don't know what the current delivery capability of the main unit is - It might overload the circuit if you try to use the 12v from the controller to drive the hack setup.

    I will take some more photos and specifically map out my exact setup for you and add them here

    I have never worked with home assistant, sorry.

    Happy to publish on GitHub too with the updated details.

    Give me a little time :)

    0
    None
    RanM5

    Question 7 months ago

    another question, is the .ino file attached above is the latest version?

    0
    None
    the_moesiah

    1 year ago

    Just out of curiosity, why would you prefer it if this wasn't an LG system?

    1 reply
    0
    None
    andrew_hthe_moesiah

    Reply 1 year ago

    Parts are almost impossible to find or excruciatingly expensive. There is only one company within 100km of me that services them and it takes weeks just to get someone out to look at it.

    The first time I had a problem with it (unit 2 years old with a 5 year warranty) they refused to even schedule someone to look at it unless I had a proof of purchase even though the build date showed it was well in warranty. Try finding a proof of purchase for a unit that was installed when the house was built (and you didn't build it!).

    So, it's not so much the unit itself as far as quality, more so the after sales service.