IR-controlled A/C (2/many) - Don’t mean a thing

2 minute read

… if you ain’t got that swing… (it says so here)

At the end of the previous episode, I was fairly confident I had the IR part figured out.

And then I tested a little bit more. And the vertical swing wasn’t working, which isn’t a deal breaker at all, but I got curious and decided to go down that rabbit hole.

Remember, I was using the daikin-pi project. The code is pretty readable, and the horizontal and vertical swing settings are set as follows.

In the code extracts below, I’m removing the lines that aren’t relevant to the point I’m trying to make, and these are indicated by [...]

# relevant bit indicies
# [...]
SWING_HORIZONTAL = 8
# [...]
SWING_VERTICAL = 9
# [...]

# Set initial fixed frame bits
frame[SWING_HORIZONTAL] = 0xb0
frame[MODE_POWER_TIMERS] = 0x08
# [...]

# Fan Swing
self._set_second_nybble(frame, SWING_HORIZONTAL,
                        self.state.swing_horizontal)
self._set_second_nybble(frame, SWING_VERTICAL,
                        self.state.swing_vertical)

Where the self.state.swing_vertical and self.state.swing_horizontal variables are boolean, set by setters.

As I mentioned earlier, the Daikin-IR-Reverse project on GitHub has a great explanation of the IR codes. That documentation mentions two things

  • The offset for swing is 8
  • The value is 0 or 0x0F (on the 4 least significant bits)

From the former, we can’t really learn anything, it seems that this doc was written for a unit that only has one type of swing.
From the latter however, I started wondering if the call to _set_second_nybble would actually work. What integer does a boolean convert to?

At this point, I’m trying my best to remember, but the exact sequence may be a little out of order.

One thing we can do is force the 0xF. There’s probably a more pythonic way to do this, but I like that it’s really readable.

# Fan Swing
temp = 0
if self.state.swing_horizontal:
    temp = 0xf
self._set_second_nybble(frame, SWING_HORIZONTAL,
                        temp)
temp = 0
if self.state.swing_vertical:
    temp = 0xf
self._set_second_nybble(frame, SWING_VERTICAL,
                        temp)

At that point (and this is where I don’t remember precisely), one of them started to do something, but would trigger the “other” one. Something around these lines.

I looked into other projects, and at least one had the same frame setup.
However, this analysis seemed to show that Vertical was earlier than Horizontal in the frame. At this point I started to look into the lircd.conf file that I was producing, and starting adding comments so that I would be able to actually see easily the hexadecimal value of the bytes I was sending out.

And so instead of

  430 1320
  430 430
  430 430
  430 430
  # [...]

I can now read

  # 11 da 27 00 c5 00 00 d7
  430 1320
  430 430
  430 430
  430 430

It’s the small things … And now, I just had to count my bits and bytes. In the daikin-pi project, the offset for TEMPERATURE is 6, while in the article the bit offset is 187, they are just not counting from the same origin, we have a 187-6*8 (=139) bit offset. With that in mind, Vertical Swing is said to be bits 203-206, which is byte (203-139)/8 = 8 and the Horizontal Swing is at byte 9. That made sense, it was a simple swap!!

With that I tested the following change, and if worked!

SWING_VERTICAL = 8
SWING_HORIZONTAL = 9

Now that I’ve explained my changes, I’m happy providing a diff file as well as the generate-codes.py. I should also reach out to the author, which I’ll probably do soon (I’m not familiar with GitHub, I’m not sure what the etiquette is).