crankyoldgit / IRremoteESP8266

Infrared remote library for ESP8266/ESP32: send and receive infrared signals with multiple protocols. Based on: https://github.com/shirriff/Arduino-IRremote/
GNU Lesser General Public License v2.1
2.85k stars 813 forks source link

AC IR Code Addition? #354

Closed bwze closed 6 years ago

bwze commented 6 years ago

(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the FAQ & Troubleshooting Guide. Your problem may already have an answer there.)

Version/revison of the library used

v2.1.1

Expected behavior

Not really a problem with the expected behavior as much as a request for a new AC decoder...

Actual behavior

As far as I can tell, it worked exactly as it should have...

Output of raw data from IRrecvDumpV2.ino (if applicable)

Represents, Power: ON, Mode: AUTO, Fan: AUTO, Setpoint: 65°F

Encoding : UNKNOWN Code : E656F582 (100 bits) Timing[199]:

Steps to reproduce the behavior

Not sure you can if you don't have my particular AC, but I'm willing to work with you however possible...

Example code used

No code for this yet.....

Circuit diagram and hardware used (if applicable)

I'm using the scenario as decribed in IRrecvDumpV2.ino which is working perfectly...

I have followed the steps in the [Troubleshooting Guide]

Yes

Other useful information

So, here goes.....I have two Midea mini splits ACs that I'm attempting to control with the help of your library and an open source home automation software (Home Assistant).

I have successfully used the Coolix decode/send commands included in your library with a slightly altered version of your MQTT server example to successfully emulate total control of one of my mini split ACs from the software with no issues whatsoever. This, I guess confirms that the Coolix protocol is the same as the one used by Midea.

My problem, however, is that the other AC's remote uses a different protocol and I don't have enough experience to change ir_Coolix..cpp to allow it work.

The included Coolix (A.K.A. Midea) protocol normally sends six bytes of data as such....

Header Spacer Byte 0 Byte 0 (Inverse) Byte 1 Byte 1 (Inverse) Byte 2 Byte 2 (Inverse) Long Gap Header Spacer Byte 0 Byte 0 (Inverse) Byte 1 Byte 1 (Inverse) Byte 2 Byte 2 (Inverse)

This other remote sends six bytes of data too, but slightly differently...

Header Spacer Byte 0 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 (Checksum) Long Gap Header Spacer Inverse - Byte 0 Inverse - Byte 1 Inverse - Byte 2 Inverse - Byte 3 Inverse - Byte 4 Inverse - Byte 5 (Checksum)

All of the timing seems to be the same as the included Coolix protocol (i.e. the headers, spacers, ones, zeros, bit mark, gap spacing, and carrier frequency look the same).

I have successfully, with research via the Internet and using Analysis IR software, decoded the other remotes byte structure....and it's somewhat similar to the Coolix protocol.

So, all of this to ask....would someone be willing to take a look at my IR captures and see if there is a way to add the ability to decode and send this other protocol via HEX?

IR Captures

bwze commented 6 years ago

OK, as a follow up, I've been successful in sending the new protocol type by altering the sendCOOLIX function in ir_Coolix.cpp.

I still haven't been able to figure out the decode portion yet.

This is what I'm using...

`#if SEND_COOLIX // Send a Coolix message // // Args: // data: Contents of the message to be sent. // nbits: Nr. of bits of data to be sent. Typically COOLIX_BITS >>>in this case it's 48, not 24... // repeat: Nr. of additional times the message is to be sent >>> no repeats due to checksum?? // // Status: Works like a charm.... :) // // Ref: // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_COOLIX.cpp // TODO(anyone): Verify repeat functionality against a real unit. // Verified...this works with my Midea AC unit.... (bwze) void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8.

// Set IR carrier frequency enableIROut(38);

for (uint16_t r = 0; r <= repeat; r++) { // Header mark(COOLIX_HDR_MARK); space(COOLIX_HDR_SPACE);

// Data
//   Break data into byte segments, starting at the Most Significant
//   Byte. Each byte then being sent normal and later being sent again 
//   as a second payload inverted.
for (uint16_t i = 8; i <= nbits; i += 8) {
  // Grab a bytes worth of data.
  uint8_t segment = (data >> (nbits - i)) & 0xFF;
  // Send Normal.
  sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE,
           COOLIX_BIT_MARK, COOLIX_ZERO_SPACE,
           segment, 8, true);
  }

// Footer
mark(COOLIX_BIT_MARK);
spaceace(COOLIX_MIN_GAP);  // Pause before repeating
mark(COOLIX_HDR_MARK);  //Resend Header
space(COOLIX_HDR_SPACE); //Resend Space

 for (uint16_t i = 8; i <= nbits; i += 8) {
  // Grab a bytes worth of data.
  uint8_t segment = (data >> (nbits - i)) & 0xFF; 
  // Now send Inverted.   
  sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE,
           COOLIX_BIT_MARK, COOLIX_ZERO_SPACE,
           segment ^ 0xFF, 8, true);
 }
 mark(COOLIX_BIT_MARK); //Need to send this to finish the overall payload

} }

endif

`

crankyoldgit commented 6 years ago

Thanks. That's an awesome start. I'll clean it up and try to write a decoder for it in the next few days.

Can you please indicate what exact make/models work for each protocols. e.g. sendCOOLIX() & decode COOLIX works for blah brand & blah model A/C unit, & blah model remote control unit. sendNewOne() works for blah brand & blah model A/C unit, & blah model remote control unit.

You mentioned a checksum. Have you worked out how its value is calculated? If so, I can add that in as well.

bwze commented 6 years ago

OK, so here's the thing...both of these protocols control both of the mini splits I own. I can interchange the wall panels and remotes and the systems both respond to each. The problem is that I only have one of each.

Just for clarity, the difference between these two protocols is the temperature measurement used.....C° vs F°.

The Coolix protocol uses C° as its temperature measurement control of choice with the remote and wall panel converting that to F° on the display. When increasing or decreasing the setpoint, if jumps by two F° which means that the setpoint displayed is not exactly the setpoint you're gonna get since the conversion isn't quite the same....but it's close enough.

The new protocol uses F° and when changing the setpoint, it will increase by one F° up or down meaning increased accuracy for Farenheit, but the downside is there is a larger possible code count.

The sendCOOLIX() and decode COOLIX works for these models for sure...I know I mentioned Midea in my original post, but my research indicates that these are simply rebranded Mideas being sold here in the US.

Pioneer System Model RYBO12GMFILCAD (12K BTU) Pioneer System Model RUBO18GMFILCAD (18K BTU)

Sorry, I don't have model numbers for the remotes....I can't find anything on them anywhere.

I have two of these wall controllers and one uses the old protocol and the other uses the new one.

This remote uses the new protocol.

I'm gonna go out on a limb though and say that both of these protocols would work with any Pioneer (rebranded Midea) that are manufactured. The 2017 catalog for these units is located here.

The checksum is calculated by reversing the bits (e.g. 10010110 -> 01101001) of the first five bytes, summing them, subtracting them from 2^8, then modulo 2^8, then reversing the bits of the resultant byte.

crankyoldgit commented 6 years ago

On a brief reading of your reply, I gotta say I'm confused. Mostly by the first statement:

both of these protocols control both of the mini splits I own

This sounds like, to me at least, that you're saying that the existing sendCOOLIX() works for both (all) of your units, AND the new protocol code you've provided works for both (all) of you units.

Are you saying your units accept both commands?

bwze commented 6 years ago

That is correct. Both units I own respond to each protocol.

I guess the next logical question is if that's the case why do I need to send or decode the new protocol.

The reason is that one wall panel controller I own controls using the Coolix protocol and the other uses this new one. My ultimate goal is the have the ability to control either system using its dedicated controller AND the home automation software I'm using AND having it update appropriately whether a control was initiated via one or the other.

I've successfully accomplished this with one of my units who's controller uses the Coolix protocol....and now I'd like to do the same for the other one using this new protocol.

All told...it's so that I can maintain a high wife acceptance factor for my home automation endeavors.

crankyoldgit commented 6 years ago

Okay. That seems weird, but hey, I don't care. I'll just name it sendMidea() and decodeMidea() and give it a unique protocol number.

crankyoldgit commented 6 years ago

@bwze I've made an attempt to add a sendMidea() and decodeMidea() to the library in this branch.

Any chance you can test it out? I've only got the single capture you provided in the initial report. If you have more captures, I'd be interested in adding them to the test data. The one you supplied is kind-of poor. It has a lot of variance in it's values. I had to turn up the tolerance (error margins) from 25% to 50% to get it to match.

We can try to add setting temp/fan/modes etc in a later commit.

bwze commented 6 years ago

Yep...that worked.

I'll grab some more captures here this morning and post them a little later.

crankyoldgit commented 6 years ago

Yep...that worked.

The send routine? The decode (i.e. dumpV2 etc)? or both? Either way \o/

crankyoldgit commented 6 years ago

I also note that your spreadsheet doesn't have any data for when the power is Off. i.e. We should fine which bits/byte to toggle to turn the power on and off etc.

bwze commented 6 years ago

Sorry....too early.

Yeah, the send routine (MQTT Serever) and decode routine (dumpV2) worked.

Funny you should ask....the below output is for off. I also know what bits/bytes do the toggling...

`⸮Encoding : MIDEA Code : A11878FFFFE2 (48 bits) Timing[199]:

crankyoldgit commented 6 years ago

I'm also noting that the 1st, 4th, & 5th byte also seemed fixed. e.g. 0xA1, 0xFF, & 0xFF respectively.

Are their any other functions on the remote/unit etc? Or can we assume those bits are unused effectively, thus we could eliminate them from the bit-size, and bring it down from 48 to 24 bits of 'real' data.

I also note that the capture in your initial report (which we match as 0xA10278FFFFF8) doesn't agree with your spreadsheet (ie. mode auto, fan auto, 65F = 0xA18263FFFF6E in the spreadsheet)

bwze commented 6 years ago

I've updated the Google Sheet with notes above each of the Bytes to represent what the bits reference.

Not sure on the original capture. This is what I get now when using the latest branch with the same settings of mode auto, fan auto, 65F...

Encoding : MIDEA Code : A18263FFFF6E (48 bits) Timing[199]:

crankyoldgit commented 6 years ago

Oh cool. It had me worried for a second. That last capture is much cleaner (ie. the values are much more consistent. I may update the timings based on that sample, and reduce the tolerances accordingly. That first (initial) capture in this issue is just all over the shop.

crankyoldgit commented 6 years ago

Oddball question. Does the remote/panel/whatever have a Celsius mode? and if so, does it change the transmit data, and how?

bwze commented 6 years ago

The two wall controllers I have look exactly the same...but one uses the Coolix protocol and transmits in Celsius and the other uses this newer protocol and transmits in Farenheit. I see no way to switch them from one to the other....no dip switches or jumpers whatsoever.

Another odd thing about these controllers is that they're really just wall mounted IR transmitters that are powered by the unit. If you follow the cable from the wall panel back to the unit, it terminates in a plastic case that when opened reveals a IR LED transmitter and a receiver. So when you press a button on the wall controller, it's the exact same thing as if you'd use the hand held remote....which seems weird to me, but hey....it works.

Additionally, I've updated the Google Sheet with off payloads for 62-68....

Would additional dumpV2 captures be helpful?

crankyoldgit commented 6 years ago

I think we have enough captures now. You've already done the bulk of the work decoding what the bits mean etc. I'll try to code up something that allows someone to encode & decode temp, fan speed, power state, and mode tomorrow or the next few days. When I'm done, you can add in additional modes etc. if you want. e.g. power saving etc etc which you list in the table headers.

I'm done for the night. :-)

bwze commented 6 years ago

Too late :-) ....I just went ahead a did captures for 62 - 86, mode-auto, fan-auto. They're here in case anyone is interested. Starting from the top of the document....first capture is 62 on, then 62 off, and so forth to the end of the document with 86 on, 86 off.

Also, just curious, what does it mean when I get the snippets of unknown captures after the main decoding sometimes. I'm assuming it's overflow? Timing slightly off?

@crankyoldgit, thanks so much for your help....if there's anything else you need, please let me know.

crankyoldgit commented 6 years ago

Those small 'unknown' messages are most likely IR noise that is being picked up. You can ignore them. If they really bother you, you can tune them out. (see PR #347 which will become available when 2.3.0 gets release)

FYI, support for your device will be standard when 2.3.0 is released too.

bwze commented 6 years ago

So I've got the automation software sending everything over in HEX ....minus the checksum.

I felt like it would be way too difficult using the scheme I have in place in the automation software to calculate it there so I just focused on doing it in the ESP8266.

Well......my eyes feel like they're bleeding after all the research I've done today on that topic.

I'm calling it quits for a few days as my head is killing me.

Apparently I have a looooong way to go before I understand bit manipulation in C++.... :)

crankyoldgit commented 6 years ago

I know how you feel. I've got most of the basics working for setting the common options (power, temp, mode, fan) and checksum calcs etc. When I'm finished with the unit tests, I'll upload it.

With some simple changes you could update/fix the checksum for the hex value on the ESP end.

crankyoldgit commented 6 years ago

@bwze BTW, are you sure of the fan values? As it seems odd that the MED setting is larger than the HI setting. Just curious. (I'm reading from the byte notes)

bwze commented 6 years ago

@crankyoldgit Mistake on my part.....MED is 010, not 101....sorry, I've corrected the comments on the Google Sheet.

It's 2 am here....I think maybe I should go to bed.

crankyoldgit commented 6 years ago

@bwze I've just uploaded my effort for the day in https://github.com/markszabo/IRremoteESP8266/commit/6cf32e2e653d27e21343284f1068b58fe6bed7c5 to the same branch

Care to test it/try it out?

bwze commented 6 years ago

@crankyoldgit I haven't forgot about testing this...it's just that I'm having to teach myself how to work with libraries in ways that are completely new to me.

I've just been hacking code together for the last year or so and never properly learned all foundational stuff that I should have. That usually means I try this and it doesn't work....so I try something else and it doesn't work. Fast forward to a day later and I get lucky and it finally works...sort of. :)

You'd probably laugh your ass off at some of the stuff I've got stitched together.

Anyway, I can say for sure that the send and decode are working flawlessly....but I really want to try the set/get functions you put together....still wrapping my head around how to get those tested.

Stay tuned....and thanks again...

crankyoldgit commented 6 years ago

@bwze haha. I'm just like a duck, it all appears smooth on the surface, but underneath it's all chaos. ;-)

So, that reminds me I didn't do example code for this one. Basically, grab one of the other AC examples and use that. That'll show you how to use it.

bwze commented 6 years ago

Works like a charm....you sir are my hero. :-)

crankyoldgit commented 6 years ago

\o/ You might need to check the fan mode, as it doesn't do the trickery you mentioned in your notes for the temp. Let me know how that behaves badly etc.

bwze commented 6 years ago

Let me rephrase that....the set functions are working. I'm not sure if I'm using the get function correctly.

I assume that the get function is looking for a command from the remote and captures and displays the state, fan speed, temp, and mode? Is that correct?

crankyoldgit commented 6 years ago

FYI, this code is now merged in to the 2.3.0-dev branch. I'm going to mark this issue closed. If you find anything wrong, please report it in a new issue.

crankyoldgit commented 6 years ago

@bwze The getBlah() functions interrogate the internal state of the object only. It does NOT do any capture from an actual remote.

In order to use them on a (incoming) capture etc, you'd need to put the decode_result->value into the object via setRaw(), or by calling the various individual setBlah() methods. Once that is done, the getBlah() routines will do what you are suggesting.

e.g.

  IRMideaAC midea(0);
  ...
  // Code that actually captures the IR messages goes here.
  ...
  if (results->decode_type == MIDEA) {
    midea.setRaw(results->value);  // Copy the captured value and populate the IRMideaAC object with it
    text_description = midea.toString();  // Human readable description of the Midea message.
    power_state = midea.getPower();  // Get the power state from the message
    temp = midea.getTemp();  // Get the temp (F) from the message
    // etc etc etc
  }

Or take a look at some of the unit tests. e.g. https://github.com/markszabo/IRremoteESP8266/blob/v2.3.0-dev/test/ir_Midea_test.cpp#L336

or different example in the toshiba unit tests: https://github.com/markszabo/IRremoteESP8266/blob/v2.3.0-dev/test/ir_Toshiba_test.cpp#L648

However, it uses the state part of the decode_result_t object, where as the Midea fits inside a uint64_t, so Midea uses the value part. Just a note so you don't just cut and paste code.

bwze commented 6 years ago

@crankyoldgit , just checking back in. I've successfully used the getBlah() functions to retrieve IR commands sent to the AC and have them publising via MQTT perfectly.

What I have been unable to do is figure out how to use your checksum calculator for the hex values I'm sending over from my home automation software. I've can successfully send over the first five bytes (e.g. for power-on, mode-heat, fan-high, temp-72 --> a19b6affff)... but I'm not seeing how to use those values with calcChecksum to get the desired end result of a19b6affff72.

I can't seem to find a function in irMidea.cpp that will allow me to accomplish this....I'm sure I'm just overlooking it....but still....

Could you point me in the direction of an example that might pull the wool from over my eyes?

crankyoldgit commented 6 years ago

Excellent, thanks for the confirmation.

As for your checksum issue, TL;DR answer: You need to shift left 8 bits (<< 8) before calling the function. i.e. add '00' to the end.

Longer answer: The value you are created (a19b6affff) is short by 8 bits. i.e. it's not the correct size so to speak, you need to shift it (by 8 bits) to accommodate the checksum bits. The "correct" positioning of the bits for the state is as you point out, the a19b6affff72. i.e. a call of calcChecksum(0xa19b6affff); would return you the checksum of a (your short version) value 0xa19b6aff. i.e. A call of calcChecksum(0xa19b6affff72); will return 0x72, so would a call of calcChecksum(0xa19b6affff00); or calcChecksum(0xa19b6affffff); calcChecksum() basically ignores the last 8 bits, because that is where the checksum is supposed to go.

Basically you want to use any of the following code to learn what to do:

// Long example, but easier to understand.
uint64_t bwze_value = 0xa19b6affffULL;
// Your above value is missing the space for the checksum bits. i.e. misaligned by a byte. 
uint64_t corrected_bwze_value = bwze_value << 8;  // i.e. 0xa19b6affff00, Correct size but with wrong checksum.
uint8_t checksum = midea_obj.calcChecksum(corrected_bwze_value);
uint64_t corrected_with_checksum = corrected_bwze_value | checksum;

or

// A more terse version of above.
uint64_t code_to_send = 0xa19b6affffULL << 8;
code_to_send |= midea_obj.calcChecksum(code_to_send);

or

// Probably the best & safest solution, but technically slower/more expensive.
uint64_t code_to_send = midea_obj.getRaw(midea_obj.setRaw(0xa19b6affffULL << 8));

ObNote: calcChecksum() is a private method of the IRMideaAC object, so direct calls to it probably won't work unless you change that or copy the code etc. The last example should work as it uses only public methods/functions etc.

bwze commented 6 years ago

Thanks @crankyoldgit for the point in the right direction...

I got where I wanted to be with the following...

uint64_t new_code = (code_from_software << 8); midea.setRaw(new_code); new_code = midea.getRaw(); midea.send();

Also, I noticed that the timing was slightly off such that AnalysisIR wasn't picking up the protocol type like it would with the actual remote. I made the following changes to the timing and it seems to work flawlessly....it's the same timing as the Coolix protocol...

define MIDEA_TICK 560U

define MIDEA_BIT_MARK_TICKS 1U

define MIDEA_BIT_MARK (MIDEA_BIT_MARK_TICKS * MIDEA_TICK)

define MIDEA_ONE_SPACE_TICKS 3U

define MIDEA_ONE_SPACE (MIDEA_ONE_SPACE_TICKS * MIDEA_TICK)

define MIDEA_ZERO_SPACE_TICKS 1U

define MIDEA_ZERO_SPACE (MIDEA_ZERO_SPACE_TICKS * MIDEA_TICK)

define MIDEA_HDR_MARK_TICKS 8U

define MIDEA_HDR_MARK (MIDEA_HDR_MARK_TICKS * MIDEA_TICK)

define MIDEA_HDR_SPACE_TICKS 8U

define MIDEA_HDR_SPACE (MIDEA_HDR_SPACE_TICKS * MIDEA_TICK)

define MIDEA_MIN_GAP_TICKS (MIDEA_HDR_MARK_TICKS + \

                            MIDEA_ZERO_SPACE_TICKS)

define MIDEA_MIN_GAP (MIDEA_MIN_GAP_TICKS * MIDEA_TICK)

define MIDEA_TOLERANCE 30U // Percent

crankyoldgit commented 6 years ago

Unless you use the value of new_code later, the line new_code = midea.getRaw(); should be redundant. The midea.send(); call will calculate the checksum prior to sending.

I've adjusted the final timings accordingly, just using a smaller common tick factor.

Let me know how it goes.