Closed soosp closed 5 years ago
Yes, it can sort-of be added but the code you linked to doesn't/won't actually work. I could see some bugs with the linked code within a minute or two of looking at it. It also appears it was never merged into the IRremote library. Possibly for those reasons.
I can implement what I think it is trying to do. Do you have a Raw Data capture from IRrecvDumpV2 that you can share? That would help immensely.
I have a TCL manufactured AC unit, and assumed that the TCL112AC protocol works with it. I can dump the IR codes of my remote if it helps.
Yes, it will help, and ultimately, it's what you want to emulate I'm guessing.
Here are the captured data files. I tried to change only one thing in a file (e.g. change the temperature only). Most functions captured in cooling mode, and at 24 C, and with automatic fan settings.
OnOff.txt Temperatures, Cooling, Auto fan.txt Modes.txt Fan levels, Cooling, 24 C.txt VSwing, Cooling, 24 C, Auto fan.txt Display, Cooling, 24 C, Auto fan.txt Eco, Cooling, 24 C, Auto Fan.txt Health, Cooling, 24 C, Auto fan.txt HSwing, Cooling, 24 C, Auto fan.txt Turbo, Cooling, 24 C, Auto fan.txt
Great. Thanks for that data. It's very useful.
Here is a breakdown of the "on" message from the first file.
IRremoteESP8266/tools$ ./auto_analyse_raw_data.py --code -f /tmp/on.txt
Found 227 timing entries.
Potential Mark Candidates:
[3030, 524]
Potential Space Candidates:
[1658, 1074, 336]
Guessing encoding type:
Looks like it uses space encoding. Yay!
Guessing key value:
kHdrMark = 3030
kHdrSpace = 1658
kBitMark = 495
kOneSpace = 1065
kZeroSpace = 324
Decoding protocol based on analysis so far:
kHdrMark+kHdrSpace+1100010011010011011001001000000000000000001001001100000011100000000000100000000000000000000000000000000111000000
Bits: 112
Hex: 0xC4D364800024C0E00200000001C0 (MSB first)
0x038000000040070324000126CB23 (LSB first)
Dec: 3992100527850402204402608498540992 (MSB first)
70988433613961943391698424351523 (LSB first)
Bin: 0b1100010011010011011001001000000000000000001001001100000011100000000000100000000000000000000000000000000111000000 (MSB first)
0b0000001110000000000000000000000000000000010000000000011100000011001001000000000000000001001001101100101100100011 (LSB first)
Total Nr. of suspected bits: 112
Generating a VERY rough code outline:
// WARNING: This probably isn't directly usable. It's a guide only.
const uint16_t kHdrMark = 3030;
const uint16_t kBitMark = 495;
const uint16_t kHdrSpace = 1658;
const uint16_t kOneSpace = 1065;
const uint16_t kZeroSpace = 324;
const uint16_t kXyzBits = 112;
const uint16_t kXyzStateLength = 14;
// DANGER: More than 64 bits detected. A uint64_t for 'data' won't work!
// Function should be safe up to 64 bits.
void IRsend::sendXyz(const uint64_t data, const uint16_t nbits, const uint16_t repeat) {
enableIROut(38); // A guess. Most common frequency.
for (uint16_t r = 0; r <= repeat; r++) {
// Header
mark(kHdrMark);
space(kHdrSpace);
// Data
// e.g. data = 0xC4D364800024C0E00200000001C0, nbits = 112
sendData(kBitMark, kOneSpace, kBitMark, kZeroSpace, data, nbits, true);
// Footer
mark(kBitMark);
space(100000); // A 100% made up guess of the gap between messages.
}
}
// Alternative >64 bit Function
void IRsend::sendXyz(uint8_t data[], uint16_t nbytes, uint16_t repeat) {
// nbytes should typically be kXyzStateLength
// data should typically be:
// uint8_t data[kXyzStateLength] = {0xC4, 0xD3, 0x64, 0x80, 0x00, 0x24, 0xC0, 0xE0, 0x02, 0x00, 0x00, 0x00, 0x01, 0xC0};
// data[] is assumed to be in MSB order for this code.
for (uint16_t r = 0; r <= repeat; r++) {
sendGeneric(kHdrMark, kHdrSpace,
kBitMark, kOneSpace,
kBitMark, kZeroSpace,
kBitMark,
100000, // 100% made-up guess at the message gap.
data, nbytes,
38000, // Complete guess of the modulation frequency.
true, 0, 50);
}
}
It looks like it should be easy enough to get the basic protocol supported. i.e. sending/capturing the 112 bit/14 byte message, but not decoding what they mean.
The values collected seem to match up with what was in the PR you linked to. I'll try to knock up some experimental code for you to try out soon.
@soosp Can you please test out the code in the tcl
branch of the library? e.g. https://github.com/markszabo/IRremoteESP8266/tree/tcl
Let me know how it goes.
@soosp I've now added more decoding/support for the TCL protocol (power, mode, & checksum).
Per FAQ/similar issues, you'll have to do the analysis work to work out which bits in each state byte control which functions for the remaining functions. i.e. When you change something on the remote, you need to be able to account for every bit/byte that changes. When you've worked that out, let me know.
Otherwise I'll merge what we have and leave it at that. i.e. I won't be doing more message breakdown, it's up to you/someone else to work it out.
You should only need to report/record the state[]
lines for each mode etc in your analysis if you use the code in the tcl branch. Let me know if you have any issues with that code.
Thanks for your efforts! Currently I'm very busy, but I'll try to test your code in the next days.
I made a quick test with code below, and it works well. I'll try to figure out remaining functions soon.
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Tcl.h>
const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRTcl112Ac ac(kIrLed); // Set the GPIO to be used for sending messages.
void setup() {
ac.begin();
Serial.begin(115200);
}
void loop() {
Serial.println("Sending...");
// Set up what we want to send. See ir_Tcl.cpp for all the options.
ac.setPower(true);
ac.setMode(kTcl112AcCool);
ac.setTemp(24);
// Now send the IR signal.
#if SEND_TCL112AC
ac.send();
#else // SEND_TCL112AC
Serial.println("Can't send because SEND_TCL112AC has been disabled.");
#endif // SEND_TCL112AC
delay(5000);
}
Thanks for the confirmation!
You should only need to report/record the state[] lines for each mode etc in your analysis if you use the code in the tcl branch.
Do you mean values in these files? I copied only uint8_t state[14]
lines from the output of IRrecvDumpV2.
Display.Cooling.24.C.Auto.fan.txt Eco.Cooling.24.C.Auto.Fan.txt Fan.levels.Cooling.24.C.txt Health.Cooling.24.C.Auto.fan.txt HSwing.Cooling.24.C.Auto.fan.txt Modes.txt OnOff.txt Temperatures.Cooling.Auto.fan.txt Turbo.Cooling.24.C.Auto.fan.txt VSwing.Cooling.24.C.Auto.fan.txt
Meanwhile the temperature values was tested succesfully with the code below.
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Tcl.h>
const uint16_t kIrLed = D6; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRTcl112Ac ac(kIrLed); // Set the GPIO to be used for sending messages.
void setup() {
ac.begin();
Serial.begin(115200);
}
void loop() {
float temp;
Serial.println("Sending...");
// Walk over all supported temperatures
for(temp = 16; temp <= 31; temp +=0.5) {
ac.setPower(true);
ac.setMode(kTcl112AcCool);
ac.setTemp(temp);
Serial.print("Temperature: ");
Serial.print(temp, 1);
Serial.println(" C");
// Now send the IR signal.
#if SEND_TCL112AC
ac.send();
#else // SEND_TCL112AC
Serial.println("Can't send because SEND_TCL112AC has been disabled.");
#endif // SEND_TCL112AC
delay(3000);
}
// Tur off and wait for 15s
ac.setPower(false);
ac.send();
delay(15000);
}
Do you mean values in these files? I copied only
uint8_t state[14]
lines from the output of IRrecvDumpV2.
Yes.
To work out what a function/button does, you basically need to explain every bit-flip that happens between messages.
e.g.
in Display.Cooling.24.C.Auto.fan.txt
The byte in state[5] changes from 0x24
(on) to 0x64
(off).
If you look at that in binary 0x24 = 0b00100100 and 0x64 = 0b01100100
Hence, that single bit is what controls what is Display On and Display Off.
The last byte of the state array (i.e. state[13]
) is the checksum byte, that will change when some other part of the message changes, so you can ignore the last byte of the message when you are working out what changed etc.
Let us know what you work out from your data.
FYI, v2.5.6 has just been release which includes the changes/improvements mentioned.
Thanks for the apprise! Meanwhile I identified some bits in the protocol:
Display state:
state[5]
bits: 0b01000000
mask: 0b10111111 = 0xBF
on: 0b00000000 = 0x00
off: 0b01000000 = 0x40
Eco mode:
state[5]
bits: 0b10000000
mask: 0b01111111 = 0x7F
on: 0b10000000 = 0x80
off: 0b00000000 = 0x00
Fan mode:
state[8]
bits: 0b00000111
mask: 0b11111000 = 0xF8
auto: 0b00000000 = 0x00
min: 0b00000010 = 0x02
middle: 0b000000011 = 0x03
max: 0b000000101 = 0x05
Heath mode:
state[6]
bits: 0b00010000
mask: 0b11101111 = 0xEF
on: 0b00010000 = 0x10
off: 0b00000000 = 0x00
Horizontal swing:
state[12]
bits: 0b00001000
mask: 0b11110111 = 0xF7
on: 0b00001000 = 0x08
off: 0b00000000 = 0x00
Modes:
state[6]
bits: 0b00001111
mask: 0b11110000 = 0xF0
auto: 0b00001000 = 0x08
cooling: 0b00000011 = 0x03
drying: 0b00000010 = 0x02
ventillating: 0b00000111 = 0x07
heating: 0b00000001 = 0x01
Note: on ventillating mode the remote controller turns the fan to max state too: state[8]: 0x05 with mask 0xF8
On/off:
state[5]
bits: 0b00001000
mask: 0b11110111 = 0xF7
on: 0b00001000 = 0x04
off: 0b00000000 = 0x00
Turbo mode:
state[6]
bits: 0b01000000
mask: 0b10111111 = 0xBF
on: 0b01000000 = 0x40
off: 0b00000000 = 0x00
Note: It seems that the remote controller sets the temperature to the end value (state[7] and state[12]) and turns fan to max value and turns on the vertical swing (state[8] for both). It has been tested in cooling mode only, but needs testing in other modes.
Vertical swing:
state[8]
bits: 0b00111000
mask: 0b11000111 = 0xC7
on: 0b00111000 = 0x38
off: 0b00000000 = 0x00
Temperature: state[7] and state[12] as you investigated yet.
That's excellent work. Thanks. I'll try to add support for most of that in the next few days.
On/off:
state[5] bits: 0b00001000 mask: 0b11110111 = 0xF7 on: 0b00001000 = 0x04 off: 0b00000000 = 0x00
Are you sure on that one? It differs from what I originally had. Just double checking before I change it.
Strange, because your code worked for me. It turned on/off my AC unit.
I tested the remote controller in some different modes, and seems that the only difference between the on/off command sequences the bit 3 of the state[5]:
ON
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0x03};
OFF
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x03, 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0xFF};
ON
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x02, 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0x02};
OFF
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x02, 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0xFE};
ON
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x07, 0x07, 0x45, 0x00, 0x00, 0x00, 0x80, 0x0C};
OFF
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x07, 0x07, 0x45, 0x00, 0x00, 0x00, 0x80, 0x08};
It seems that IRecvDumpV2 correctly identify the on/off state:
Timestamp : 000694.257
Encoding : TCL112AC
Code : 23CB260100240107400000000081 (112 bits)
Mesg Desc.: Power: On, Mode: 1 (HEAT), Temp: 24C
[...]
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x01, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x81};
Timestamp : 000745.771
Encoding : TCL112AC
Code : 23CB26010020010740000000007D (112 bits)
Mesg Desc.: Power: Off, Mode: 1 (HEAT), Temp: 24C
[...]
uint8_t state[14] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x01, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x7D};
On/off:
state[5] bits: 0b00001000 mask: 0b11110111 = 0xF7 on: 0b00001000 = 0x04 off: 0b00000000 = 0x00
Okay, I think that it's just your hex/bit maths that is wrong then for this one case.
The diff in state[5]
is as you & I indicated. ie. 0x04 ... which is 0b00000100,
not your above 0b00001000
which is 0x08
. Thus this threw out your bits/mask values.
If you want to try out the code in this branch: https://github.com/markszabo/IRremoteESP8266/tree/tcl_update it should support what you've listed/analysed thus far.
Oh, it's my mistake. Thank you for correction. I'll test your code soon.
@soops I've now merged those changes into the master
branch. I think that covers everything you reverse-engineered. I'm going to close this issue for now. If there is something wrong with the new code, please update this issue and I'll re-open if it's within a week or so. Anything longer, please open a new issue.
Thanks for you effort.
FYI, this has been included in the newly released v2.6.0 of the library.
There is an protocol driver for original IRremote library: https://github.com/z3t0/Arduino-IRremote/pull/259/files Is this possible to implement it?