diff --git a/.gitignore b/.gitignore index ccc9fd9..b9f3806 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.DS_Store \ No newline at end of file +.pio +.vscode diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ccc4e4 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# IR decoder + +Decodes long IR codes, for example from air conditioner / heat pump devices. + +Shows the timings, the symbols, and also the decoded signal for certain air conditioners. + +Required hardware: +- Arduino (any compatible will do, but Arduino Uno or Nano is easiest for prototyping) +- Infrared receiver, for example VS1838 will do fine, see also https://arduino-info.wikispaces.com/IR-RemoteControl +- Breadboard, wiring +- IR remote control from the aircon/heatpump you plan to decode + +## Build instructions +Defines for the different brands were introduced to limit the memory footage on a Arduino UNO. +Uncomment, in the beginning of the sketch, the define for your remote brand. +You will get a compiler error if you forget to uncomment! + +* //#define MITSUBISHI_ELECTRIC +* //#define FUJITSU +* //#define MITSUBISHI_HEAVY +* //#define DAIKIN +* //#define SHARP_ +* //#define CARRIER +* //#define PANASONIC_CKP +* //#define PANASONIC_CS +* //#define HYUNDAI +* //#define GREE +* //#define GREE_YAC +* //#define FUEGO +* //#define TOSHIBA +* //#define NIBE +* //#define AIRWELL +* //#define HITACHI +* //#define SAMSUNG +* //#define BALLU +* //#define AUX +* //#define ZHLT01_REMOTE +* //#define PHILCO + +### Arduino IDE +* Open the sketch from subdirectory 'rawirdecode' in Arduino IDE and build + +### PlatformIO +* platformio.ini contains build definitions for Arduino Mega, ESP32 (both tested on M5STACK ATOM LITE) and ESP8266 (tested on nodemcu) +* On Mega, connect the receiver data pin to GPIO 2 +* On ESP32, connect the receiver data pin to GPIO 25 +* On ESP8266, connect the receiver data pin to GPIO 5 + +## Instructions + +* Connect an IR receiver into the Arduino +* Start the sketch, and enter 1, 2, 3, 4 or 5 into the 'Serial Monitor', to select which timings to use + * Try out the alternatives until you get sensible output + * The signal should always start with 'Hh', and within the signal there should only be a couple of 'Hh' pairs (if any) + * 'H' and 'h' should be there only in pairs 'Hh' + * 'H' stands for 'header mark' and 'h' for 'header space' +* Point your IR remote to the IR receiver and send the code + * If the symbols are known, then the decoder shows its meaning on the serial monitor + * If the symbols are unknown, then you can help by writing a decoder for the unknown remote + +-> Mode '9' can be used to decode known signals, in that case you can send the symbols from the terminal, like entering this: + + Hh001101011010111100000111001001010100000000000111000000001111111011010100000001000111001011 + +![Schema](arduino_irreceiver.png) diff --git a/arduino_irreceiver.png b/arduino_irreceiver.png new file mode 100644 index 0000000..6950b81 Binary files /dev/null and b/arduino_irreceiver.png differ diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..4406c02 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,20 @@ +[platformio] +src_dir = rawirdecode + +; Tested on M5STACK ATOM LITE +[env:esp32] +platform = espressif32 +framework = arduino +board = esp32dev +upload_speed = 115200 + +; Tested on Arduino Mega +[env:atmega2560] +platform = atmelavr +framework = arduino +board = megaatmega2560 + +[env:esp8266] +platform = espressif8266 +framework = arduino +board = nodemcuv2 \ No newline at end of file diff --git a/rawirdecode.pde b/rawirdecode.pde deleted file mode 100755 index 3175317..0000000 --- a/rawirdecode.pde +++ /dev/null @@ -1,103 +0,0 @@ -/* Raw IR decoder sketch! - - This sketch/program uses the Arduno and a PNA4602 to - decode IR received. This can be used to make a IR receiver - (by looking for a particular code) - or transmitter (by pulsing an IR LED at ~38KHz for the - durations detected - - Code is public domain, check out www.ladyada.net and adafruit.com - for more tutorials! - */ - -// We need to use the 'raw' pin reading methods -// because timing is very important here and the digitalRead() -// procedure is slower! -//uint8_t IRpin = 2; -// Digital pin #2 is the same as Pin D2 see -// http://arduino.cc/en/Hacking/PinMapping168 for the 'raw' pin mapping -#define IRpin_PIN PIND -#define IRpin 2 - -// the maximum pulse we'll listen for - 65 milliseconds is a long time -#define MAXPULSE 65000 - -// what our timing resolution should be, larger is better -// as its more 'precise' - but too large and you wont get -// accurate timing -#define RESOLUTION 20 - -// we will store up to 100 pulse pairs (this is -a lot-) -uint16_t pulses[100][2]; // pair is high and low pulse -uint8_t currentpulse = 0; // index for pulses we're storing - -void setup(void) { - Serial.begin(9600); - Serial.println("Ready to decode IR!"); -} - -void loop(void) { - uint16_t highpulse, lowpulse; // temporary storage timing - highpulse = lowpulse = 0; // start out with no pulse length - - -// while (digitalRead(IRpin)) { // this is too slow! - while (IRpin_PIN & (1 << IRpin)) { - // pin is still HIGH - - // count off another few microseconds - highpulse++; - delayMicroseconds(RESOLUTION); - - // If the pulse is too long, we 'timed out' - either nothing - // was received or the code is finished, so print what - // we've grabbed so far, and then reset - if ((highpulse >= MAXPULSE) && (currentpulse != 0)) { - printpulses(); - currentpulse=0; - return; - } - } - // we didn't time out so lets stash the reading - pulses[currentpulse][0] = highpulse; - - // same as above - while (! (IRpin_PIN & _BV(IRpin))) { - // pin is still LOW - lowpulse++; - delayMicroseconds(RESOLUTION); - if ((lowpulse >= MAXPULSE) && (currentpulse != 0)) { - printpulses(); - currentpulse=0; - return; - } - } - pulses[currentpulse][1] = lowpulse; - - // we read one high-low pulse successfully, continue! - currentpulse++; -} - -void printpulses(void) { - Serial.println("\n\r\n\rReceived: \n\rOFF \tON"); - for (uint8_t i = 0; i < currentpulse; i++) { - Serial.print(pulses[i][0] * RESOLUTION, DEC); - Serial.print(" usec, "); - Serial.print(pulses[i][1] * RESOLUTION, DEC); - Serial.println(" usec"); - } - - // print it in a 'array' format - Serial.println("int IRsignal[] = {"); - Serial.println("// ON, OFF (in 10's of microseconds)"); - for (uint8_t i = 0; i < currentpulse-1; i++) { - Serial.print("\t"); // tab - Serial.print(pulses[i][1] * RESOLUTION / 10, DEC); - Serial.print(", "); - Serial.print(pulses[i+1][0] * RESOLUTION / 10, DEC); - Serial.println(","); - } - Serial.print("\t"); // tab - Serial.print(pulses[currentpulse-1][1] * RESOLUTION / 10, DEC); - Serial.print(", 0};"); -} diff --git a/rawirdecode/AUXAC.cpp b/rawirdecode/AUXAC.cpp new file mode 100644 index 0000000..e419259 --- /dev/null +++ b/rawirdecode/AUXAC.cpp @@ -0,0 +1,104 @@ +#include + +// AUX YKR-N/002E + +bool decodeAUX(byte *bytes, int byteCount) +{ + // If this looks like a AUX code... + if ( byteCount == 13 && bytes[0] == 0xC3 ) { + Serial.println(F("Looks like a AUX protocol")); + + // Power mode + switch (bytes[9] & 0x20) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x20: + Serial.println(F("POWER ON")); + break; + } + + // Turbo mode + switch (bytes[5] & 0x40) { + case 0x40: + Serial.println(F("TURBO: ON")); + break; + } + + // Operating mode + switch (bytes[6] & 0xE0) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x80: + Serial.println(F("MODE HEAT")); + break; + case 0x20: + Serial.println(F("MODE COOL")); + break; + case 0x40: + Serial.println(F("MODE DRY")); + break; + case 0xC0: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bytes[1] >> 3) + 8); + + // Fan speed + switch (bytes[04] & 0xE0) { + case 0xA0: + Serial.println(F("FAN: AUTO")); + break; + case 0x60: + Serial.println(F("FAN: 1")); + break; + case 0x40: + Serial.println(F("FAN: 2")); + break; + case 0x20: + Serial.println(F("FAN: 3")); + break; + } + + // Horizontal swing + switch (bytes[02] & 0xE0) { + case 0x00: + Serial.println(F("Horizontal swing: Off")); + break; + case 0xE0: + Serial.println(F("Horizontal swing: On")); + break; + } + + // Vertical swing + switch (bytes[01] & 0x07) { + case 0x00: + Serial.println(F("Vertical swing: Off")); + break; + case 0x07: + Serial.println(F("Vertical swing: On")); + break; + } + + // Check if the checksum matches + byte checksum = 0x00; + + for (byte i = 0; i < 12; i++) { + checksum += bytes[i]; + } + + if ( bytes[12] == checksum ) { + Serial.println(F("Checksum matches")); + } else { + Serial.println(F("Checksum does not match")); + } + + return true; + } + + return false; +} diff --git a/rawirdecode/Airwell.cpp b/rawirdecode/Airwell.cpp new file mode 100644 index 0000000..44480f6 --- /dev/null +++ b/rawirdecode/Airwell.cpp @@ -0,0 +1,107 @@ +#include + +// PoC Airwell RC-3 remote support. +// Works with https://www.aliexpress.com/item/A-C-controller-Air-Conditioner-air-conditioning-remote-control-FOR-YORK-Electra-Airwell-Emailair-RC-4/32634241533.html +// Reads fine in receive mode 1 or 2 +// Only supports 2 operation modes — ♺ and ❄ +// Reports: +// - Mode: 1 - ♺, 2 - ❄ +// - Fan speed: 1, 2, 3, Auto +// - Set temperature +// - If power button is pressed, reports it + +bool decodeAirwell(char* symbols, int bitCount) +{ + // If wrong package length + if (bitCount != 110) { return false; } + // If incorrect header + if (symbols[1] != 'H' || symbols[2] != 'h') { return false; } + // If incorrect first payload footer + if (symbols[35] != 'H' || symbols[36] != 'h') { return false; } + + Serial.println(F("Looks like an Airwell RC-3 remote")); + + if (symbols[3] == 'H' && symbols[4] == 'h' && (symbols[5] == '0' || symbols[5] == '1')) { + Serial.print(F("POWER")); + return true; + } + + int acMode; + if (symbols[3] == '0' && symbols[4] == '1' && symbols[5] == '0') { acMode = 1; } + else if (symbols[3] == '0' && symbols[4] == '0' && symbols[5] == '1') { acMode = 2; } + else { + Serial.println(F("Mode: UNKNOWN")); + return false; + } + + Serial.print(F("Mode: ")); + Serial.println(acMode); + + int acSpeed; + if (symbols[6] == 'H' && symbols[7] == 'h' && symbols[8] == '0' && symbols[9] == '0') { acSpeed = 1; } + else if (symbols[6] == 'H' && symbols[7] == 'h' && symbols[8] == 'H' && symbols[9] == 'h') { acSpeed = 2; } + else if (symbols[6] == '0' && symbols[7] == 'H' && symbols[8] == 'h' && symbols[9] == '0') { acSpeed = 3; } + else if (symbols[6] == '0' && symbols[7] == '0' && symbols[8] == 'H' && symbols[9] == 'h') { acSpeed = 0; } + else { + Serial.println(F("Speed: UNKNOWN")); + return false; + } + + Serial.print(F("Speed: ")); + Serial.println((acSpeed == 0) ? "AUTO" : String(acSpeed)); + + int acTemp; + if (symbols[13] == '0' && symbols[14] == '0' && symbols[15] == '0' && symbols[16] == '1' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 16; } + else if (symbols[13] == '0' && symbols[14] == '0' && symbols[15] == '1' && symbols[16] == 'H' && symbols[17] == 'h' && symbols[18] == '0') { acTemp = 17; } + else if (symbols[13] == '0' && symbols[14] == '0' && symbols[15] == '1' && symbols[16] == '0' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 18; } + else if (symbols[13] == '0' && symbols[14] == '1' && symbols[15] == 'H' && symbols[16] == 'h' && symbols[17] == '0' && symbols[18] == '0') { acTemp = 19; } + else if (symbols[13] == '0' && symbols[14] == '1' && symbols[15] == 'H' && symbols[16] == 'h' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 20; } + else if (symbols[13] == '0' && symbols[14] == '1' && symbols[15] == '0' && symbols[16] == 'H' && symbols[17] == 'h' && symbols[18] == '0') { acTemp = 21; } + else if (symbols[13] == '0' && symbols[14] == '1' && symbols[15] == '0' && symbols[16] == '0' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 22; } + else if (symbols[13] == '1' && symbols[14] == 'H' && symbols[15] == 'h' && symbols[16] == '0' && symbols[17] == '0' && symbols[18] == '0') { acTemp = 23; } + else if (symbols[13] == '1' && symbols[14] == 'H' && symbols[15] == 'h' && symbols[16] == '1' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 24; } + else if (symbols[13] == '1' && symbols[14] == 'H' && symbols[15] == 'h' && symbols[16] == 'H' && symbols[17] == 'h' && symbols[18] == '0') { acTemp = 25; } + else if (symbols[13] == '1' && symbols[14] == 'H' && symbols[15] == 'h' && symbols[16] == '0' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 26; } + else if (symbols[13] == '1' && symbols[14] == '0' && symbols[15] == 'H' && symbols[16] == 'h' && symbols[17] == '0' && symbols[18] == '0') { acTemp = 27; } + else if (symbols[13] == '1' && symbols[14] == '0' && symbols[15] == 'H' && symbols[16] == 'h' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 28; } + else if (symbols[13] == '1' && symbols[14] == '0' && symbols[15] == '0' && symbols[16] == 'H' && symbols[17] == 'h' && symbols[18] == '0') { acTemp = 29; } + else if (symbols[13] == '1' && symbols[14] == '0' && symbols[15] == '0' && symbols[16] == '0' && symbols[17] == 'H' && symbols[18] == 'h') { acTemp = 30; } + else { + Serial.println(F("Temp: UNKNOWN")); + return false; + } + + Serial.print(F("Temp: ")); + Serial.println(acTemp); + + return true; +} + + +/* Example payloads. They are repeated thrice in 110 symbol packet, only the first repeat is shown: + + M FAN TEMP + / \/ \ / \ +16/1: Hh010Hh000000001Hh0000000000000001Hh +17/1: Hh010Hh00000001Hh00000000000000001Hh +18/1: Hh010Hh000000010Hh0000000000000001Hh +19/1: Hh010Hh0000001Hh000000000000000001Hh +20/1: Hh010Hh0000001HhHh0000000000000001Hh +20/2: Hh010HhHh00001HhHh0000000000000001Hh +20/3: Hh0100Hh000001HhHh0000000000000001Hh +20/A: Hh01000Hh00001HhHh0000000000000001Hh +21/1: Hh010Hh00000010Hh00000000000000001Hh +22/1: Hh010Hh000000100Hh0000000000000001Hh +23/1: Hh010Hh000001Hh0000000000000000001Hh +24/1: Hh010Hh000001Hh1Hh0000000000000001Hh +25/1: Hh010Hh000001HhHh00000000000000001Hh +26/1: Hh010Hh000001Hh0Hh0000000000000001Hh +27/1: Hh010Hh0000010Hh000000000000000001Hh +28/1: Hh010Hh0000010HhHh0000000000000001Hh +29/1: Hh010Hh00000100Hh00000000000000001Hh +30/1: Hh010Hh000001000Hh0000000000000001Hh +30/2: Hh010HhHh0001000Hh0000000000000001Hh +30/3: Hh0100Hh00001000Hh0000000000000001Hh +30/A: Hh01000Hh0001000Hh0000000000000001Hh + +*/ diff --git a/rawirdecode/Ballu.cpp b/rawirdecode/Ballu.cpp new file mode 100644 index 0000000..b8c56a2 --- /dev/null +++ b/rawirdecode/Ballu.cpp @@ -0,0 +1,126 @@ +#include + +bool decodeBallu(byte *bytes, int byteCount) +{ + // If this looks like a Ballu, BGH code... + // Remote control "J1-05(E)" + if ( byteCount == 21 && bytes[0] == 0x83 && bytes[1] == 0x06 ) { + Serial.println(F("Looks like a Ballu, BGH protocol")); + + // Power mode + //its send same signal for on/off its depends unit status + if((bytes[2] >> 2) == 0x01){ + Serial.println(F("On/Off action ")); + } + + // TBD + //bytes 6 and 7 are remote control time (hour and minutes) + + // Operating mode + //Serial.print(F("Hex Mode ")); Serial.print (bytes[3],BIN); Serial.print (" "); Serial.println (bytes[3] & 0x07); + switch (bytes[3] & 0x07) { + case 0x00: + Serial.println(F("MODE HEAT")); + break; + case 0x02: + Serial.println(F("MODE COOL")); + break; + case 0x03: + Serial.println(F("MODE DRY")); + break; + case 0x04: + Serial.println(F("MODE FAN")); + break; + } + + // swing + if((bytes[2] >> 4) == 0x08 && (bytes[8] >> 4) == 0x04 ){ + Serial.println(F("Vertical Swing")); + + } + + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bytes[3] >> 4) + 16); + + // Fan speed + switch (bytes[02] & 0x03) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x01: + Serial.println(F("FAN: 1")); + break; + case 0x02: + Serial.println(F("FAN: 2")); + break; + case 0x03: + Serial.println(F("FAN: 3")); + break; + } + + // pressed buton byte 15 + // Serial.print(F("Button Pressed: ")); Serial.print (bytes[15],BIN); Serial.print (" | "); Serial.println (bytes[15], HEX); + switch (bytes[15]) { + case 0x01: + Serial.println(F("Button: on/off")); + break; + case 0x02: + Serial.println(F("Button: Temp")); + break; + case 0x04: + Serial.println(F("Button: Super")); + break; + case 0x06: + Serial.println(F("Button: Modo")); + break; + case 0x07: + Serial.println(F("Button: Vertical Swing")); + break; + case 0x08: + Serial.println(F("Button: Horizontal Swing")); + break; + case 0x11: + Serial.println(F("Button: Fan")); + break; + case 0x17: + Serial.println(F("Button: Smart")); + break; + case 0x0D: + Serial.println(F("Button: iFeel")); + break; + } + + // Check if the checksum matches + // it has 2 checksums one at byte 13 and other at byte 20 + // first is XOR of bytes 2 to 12 + // second is XOR of bytes 14 to 19 + byte checksum = 0; + for (int i=2; i<13; i++) { + // Serial.print(bytes[i], HEX); Serial.print(" | "); Serial.println(bytes[i], BIN); + checksum ^= bytes[i]; + // Serial.print(checksum, HEX); Serial.print(" | "); Serial.println(checksum, BIN); + } + if (checksum == bytes[13]) { + Serial.println(F("Firs Checksum matches")); + } else { + Serial.println(F("Fist Checksum does not match")); + } + checksum = 0; + for (int i=14; i<20; i++) { + // Serial.print(bytes[i], HEX); Serial.print(" | "); Serial.println(bytes[i], BIN); + checksum ^= bytes[i]; + // Serial.print(checksum, HEX); Serial.print(" | "); Serial.println(checksum, BIN); + } + if (checksum == bytes[20]) { + Serial.println(F("Second Checksum matches")); + } else { + Serial.println(F("Second Checksum does not match")); + } + + return true; + } + + return false; +} diff --git a/rawirdecode/Carrier.cpp b/rawirdecode/Carrier.cpp new file mode 100644 index 0000000..70a2e70 --- /dev/null +++ b/rawirdecode/Carrier.cpp @@ -0,0 +1,317 @@ +#include + + +#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d" +#define BYTETOBINARY(byte) \ + (byte & 0x80 ? 1 : 0), \ + (byte & 0x40 ? 1 : 0), \ + (byte & 0x20 ? 1 : 0), \ + (byte & 0x10 ? 1 : 0), \ + (byte & 0x08 ? 1 : 0), \ + (byte & 0x04 ? 1 : 0), \ + (byte & 0x02 ? 1 : 0), \ + (byte & 0x01 ? 1 : 0) + +byte bitReverse(byte x) +{ + // 01010101 | 10101010 + x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa); + // 00110011 | 11001100 + x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc); + // 00001111 | 11110000 + x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0); + return x; +} + +bool decodeCarrier1(byte *bytes, int byteCount) +{ + // If this looks like a Carrier code... + if ( byteCount == 18 && bytes[0] == 0x4F && bytes[1] == 0xB0 && (memcmp(bytes, bytes+9, 9) == 0)) { + Serial.println(F("Looks like a Carrier protocol #1")); + + // Check if the checksum matches + byte checksum = 0x00; + + for (byte i = 0; i < 8; i++) { + checksum ^= bitReverse(bytes[i]); + } + + switch (bytes[6] & 0x0F) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x02: + Serial.println(F("FAN: 1")); + break; + case 0x06: + Serial.println(F("FAN: 2")); + break; + case 0x01: + Serial.println(F("FAN: 3")); + break; + case 0x05: + Serial.println(F("FAN: 4")); + break; + case 0x03: + Serial.println(F("FAN: 5")); + break; + } + + switch (bytes[6] & 0xF0) { + case 0xE0: + Serial.println(F("MODE: OFF")); + break; + case 0x00: + Serial.println(F("MODE: AUTO")); + break; + case 0x80: + Serial.println(F("MODE: COOL")); + break; + case 0x40: + Serial.println(F("MODE: DRY")); + break; + case 0xC0: + Serial.println(F("MODE: HEAT")); + break; + case 0x20: + Serial.println(F("MODE: FAN")); + break; + } + + checksum = bitReverse(checksum); + + const byte temperatures[] = { 17, 25, 21, 29, 19, 27, 23, 00, 18, 26, 22, 30, 20, 28, 24 }; + + + Serial.print(F("Temperature: ")); + Serial.println(temperatures[bytes[5]]); + + char bin1[9]; + char bin2[9]; + char bin3[9]; + + snprintf(bin1, sizeof(bin1), BYTETOBINARYPATTERN, BYTETOBINARY(checksum)); + snprintf(bin2, sizeof(bin2), BYTETOBINARYPATTERN, BYTETOBINARY(bytes[8])); + snprintf(bin3, sizeof(bin3), BYTETOBINARYPATTERN, BYTETOBINARY(bytes[6])); + + + Serial.print(F("ModeFan ")); + Serial.println(bin3); + + + Serial.print(F("Checksum ")); + Serial.print(bin1); + + if (checksum == bytes[8]) { + Serial.println(F(" matches")); + } else { + Serial.println(F(" does not match real")); + Serial.print(F("checksum ")); + Serial.println(bin2); + } + return true; + } + + return false; +} + +bool decodeCarrier2(byte *bytes, int byteCount) +{ + // If this looks like a Carrier code... + if ( byteCount == 12 && ((bytes[0] == 0x4D && bytes[1] == 0xB2) || (bytes[0] == 0xAD && bytes[1] == 0x52)) && (memcmp(bytes, bytes+6, 6) == 0)) { + Serial.println(F("Looks like a Carrier protocol #2")); + + if (bytes[0] == 0xAD && bytes[1] == 0x52) + { + if (bytes[4] == 0x55) + { + Serial.println(F("MODE: Frost guard")); + } + else + { + Serial.println(F("MODE: Turbo mode")); + } + } + else + { + switch (bytes[2] & 0x20) { + case 0x00: + Serial.println(F("POWER: OFF")); + break; + case 0x20: + Serial.println(F("POWER: ON")); + break; + } + + switch (bytes[2] & 0x07) { + case 0x05: + Serial.println(F("FAN: AUTO")); + break; + case 0x00: + Serial.println(F("FAN: AUTO/DRY AUTO")); + break; + case 0x01: + Serial.println(F("FAN: 1")); + break; + case 0x02: + Serial.println(F("FAN: 2")); + break; + case 0x04: + Serial.println(F("FAN: 3")); + break; + } + + switch (bytes[4] & 0x30) { + case 0x10: + Serial.println(F("MODE: AUTO")); + break; + case 0x00: + Serial.println(F("MODE: COOL")); + break; + case 0x30: + Serial.println(F("MODE: HEAT")); + break; + case 0x20: + if ((bytes[4] & 0x0F) == 0x07) { + Serial.println(F("MODE: FAN")); + } else { + Serial.println(F("MODE: DRY")); + } + break; + } + + const byte temperatures[] = { 17, 28, 24, 25, 20, 29, 21, 31, 18, 27, 23, 26, 19, 30, 22 }; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + + Serial.print(F("Temperature: ")); + Serial.println(temperatures[bytes[4] & 0x0F]); + } + + // Check if the checksum matches + uint8_t checksum1 = ~bytes[2]; + uint8_t checksum2 = ~bytes[4]; + + if (checksum1 == bytes[3] && checksum2 == bytes[5]) { + Serial.println(F("Checksum matches")); + } else { + Serial.println(F("Checksum does not match")); + } + + return true; + } + + return false; +} + +bool decodeCarrier3(byte *bytes, int byteCount) +{ + // If this looks like a Carrier code... + if ( byteCount == 20 && bytes[0] == 0x4F && bytes[1] == 0xB0 && (memcmp(bytes, bytes+10, 10) == 0)) { + Serial.println(F("Looks like a Carrier protocol #3")); + + // Check if the checksum matches + byte checksum = 0x00; + + for (byte i = 0; i < 9; i++) { + checksum ^= bitReverse(bytes[i]); + } + + switch (bytes[6] & 0x0F) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x02: + Serial.println(F("FAN: 1")); + break; + case 0x06: + Serial.println(F("FAN: 2")); + break; + case 0x01: + Serial.println(F("FAN: 3")); + break; + case 0x05: + Serial.println(F("FAN: 4")); + break; + case 0x03: + Serial.println(F("FAN: 5")); + break; + } + + switch (bytes[6] & 0xF0) { + case 0xE0: + Serial.println(F("MODE: OFF")); + break; + case 0x00: + Serial.println(F("MODE: AUTO")); + break; + case 0x80: + Serial.println(F("MODE: COOL")); + break; + case 0x40: + Serial.println(F("MODE: DRY")); + break; + case 0xC0: + Serial.println(F("MODE: HEAT")); + break; + case 0x20: + Serial.println(F("MODE: FAN")); + break; + } + + if (bytes[2] == 0x20 && bytes[3] == 0xDF) + { + if (bytes[8] == 0x20) + { + Serial.println(F("ADVANCED: Frost guard (+8°C) Mode")); + } + else if (bytes[8] == 0xC0) + { + Serial.println(F("ADVANCED: Eco/Sleep Mode")); + } + else if (bytes[8] == 0x80) + { + Serial.println(F("ADVANCED: Hi POWER Mode")); + } + } + + checksum = bitReverse(checksum); + + const byte temperatures[] = { 17, 25, 21, 29, 19, 27, 23, 00, 18, 26, 22, 30, 20, 28, 24 }; + + + Serial.print(F("Temperature: ")); + Serial.println(temperatures[bytes[5]]); + + char bin1[9]; + char bin2[9]; + char bin3[9]; + + snprintf(bin1, sizeof(bin1), BYTETOBINARYPATTERN, BYTETOBINARY(checksum)); + snprintf(bin2, sizeof(bin2), BYTETOBINARYPATTERN, BYTETOBINARY(bytes[9])); + snprintf(bin3, sizeof(bin3), BYTETOBINARYPATTERN, BYTETOBINARY(bytes[6])); + + + Serial.print(F("ModeFan ")); + Serial.println(bin3); + + + Serial.print(F("Checksum ")); + Serial.print(bin1); + + if (checksum == bytes[9]) { + Serial.println(F(" matches")); + } else { + Serial.println(F(" does not match real")); + Serial.print(F("checksum ")); + Serial.println(bin2); + } + return true; + } + + return false; +} + +bool decodeCarrier(byte *bytes, int byteCount) +{ + return decodeCarrier1(bytes, byteCount) || decodeCarrier2(bytes, byteCount) || decodeCarrier3(bytes, byteCount); +} diff --git a/rawirdecode/Daikin.cpp b/rawirdecode/Daikin.cpp new file mode 100644 index 0000000..3a070b7 --- /dev/null +++ b/rawirdecode/Daikin.cpp @@ -0,0 +1,116 @@ +#include + +bool decodeDaikin(byte *bytes, int byteCount) +{ + // If this looks like a Daikin code... + if ( byteCount == 35 && bytes[0] == 0x11 && bytes[1] == 0xDA && bytes[2] == 0x27 ) { + Serial.println(F("Looks like a Daikin protocol")); + + // Power mode + switch (bytes[21] & 0x01) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x01: + Serial.println(F("POWER ON")); + break; + } + + // Operating mode + switch (bytes[21] & 0x70) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x40: + Serial.println(F("MODE HEAT")); + break; + case 0x30: + Serial.println(F("MODE COOL")); + break; + case 0x20: + Serial.println(F("MODE DRY")); + break; + case 0x60: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println(bytes[22] / 2); + + // Fan speed + switch (bytes[24] & 0xF0) { + case 0xB0: + // Outdoor unit quiet + Serial.println(F("FAN: QUIET")); + break; + case 0xA0: + Serial.println(F("FAN: AUTO")); + break; + case 0x30: + Serial.println(F("FAN: 1")); + break; + case 0x40: + Serial.println(F("FAN: 2")); + break; + case 0x50: + Serial.println(F("FAN: 3")); + break; + case 0x60: + Serial.println(F("FAN: 4")); + break; + case 0x70: + Serial.println(F("FAN: 5")); + break; + } + + // Other flags + Serial.print(F("FLAGS: ")); + if (bytes[29] & 0x01) Serial.print(F("POWERFUL ")); + if (bytes[32] & 0x04) Serial.print(F("ECONO ")); + if (bytes[24] & 0x0F) Serial.print(F("SWING ")); + if (bytes[29] & 0x20) Serial.print(F("QUIET")); + Serial.println(); + + // Check if the checksum matches + byte checksum = 0x00; + + for (byte i = 0; i < 7; i++) { + checksum += bytes[i]; + } + + if ( bytes[7] == checksum ) { + Serial.println(F("Checksum 1 matches")); + } else { + Serial.println(F("Checksum 1 does not match")); + } + + checksum = 0x00; + + for (byte i = 8; i < 15; i++) { + checksum += bytes[i]; + } + + if ( bytes[15] == checksum ) { + Serial.println(F("Checksum 2 matches")); + } else { + Serial.println(F("Checksum 2 does not match")); + } + + checksum = 0x00; + + for (byte i = 16; i < 34; i++) { + checksum += bytes[i]; + } + + if ( bytes[34] == checksum ) { + Serial.println(F("Checksum 3 matches")); + } else { + Serial.println(F("Checksum 3 does not match")); + } + return true; + } + + return false; +} diff --git a/rawirdecode/Fuego.cpp b/rawirdecode/Fuego.cpp new file mode 100644 index 0000000..a928ded --- /dev/null +++ b/rawirdecode/Fuego.cpp @@ -0,0 +1,105 @@ +#include + +bool decodeFuego(byte *bytes, int byteCount) +{ + // If this looks like a Chinese Fuego, Vivax, Classe, NEO, Galanz, Simbio, Beko code... + // Remote control GZ-1002B-E3 + if ( byteCount == 14 && + bytes[0] == 0x23 && + bytes[1] == 0xCB && + bytes[2] == 0x26 ) { + Serial.println(F("Looks like a Fuego etc. protocol")); + + // Check if the checksum matches + byte checksum = 0; + + for (int i=0; i<13; i++) { + checksum += bytes[i]; + } + + if (checksum == bytes[13]) { + Serial.println(F("Checksum matches")); + } else { + Serial.println(F("Checksum does not match")); + } + + // Power mode + switch (bytes[5] & 0x04) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x04: + Serial.println(F("POWER ON")); + break; + } + + // Operating mode + switch (bytes[6] & 0x07) { + case 0x00: //??? + Serial.println(F("MODE AUTO")); + break; + case 0x01: + Serial.println(F("MODE HEAT")); + break; + case 0x03: + Serial.println(F("MODE COOL")); + break; + case 0x02: + Serial.println(F("MODE DRY")); + break; + case 0x07: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println(31 - (bytes[7] & 0x0F)); + + // Fan speed + switch (bytes[8] & 0x07) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x02: + Serial.println(F("FAN: 1")); + break; + case 0x03: + Serial.println(F("FAN: 2")); + break; + case 0x05: + Serial.println(F("FAN: 3")); + break; + } + + // Vertical air direction + Serial.print(F("Vertical air direction: ")); + switch (bytes[8] & 0x38) { + case 0x00: + Serial.println(F("AUTO")); + break; + case 0x08: + Serial.println(F("UP")); + break; + case 0x10: + Serial.println(F("MIDDLE UP")); + break; + case 0x18: + Serial.println(F("MIDDLE")); + break; + case 0x20: + Serial.println(F("MIDDLE DOWN")); + break; + case 0x28: + Serial.println(F("DOWN")); + break; + case 0x38: + Serial.println(F("SWING")); + break; + } + + return true; + } + + return false; +} diff --git a/rawirdecode/Fujitsu.cpp b/rawirdecode/Fujitsu.cpp new file mode 100644 index 0000000..77c6eb8 --- /dev/null +++ b/rawirdecode/Fujitsu.cpp @@ -0,0 +1,639 @@ +#include + +/* +Decoder for Fujitsu remote control + +Pulls apart packet and shows the meaning of each part. + +Fujitsu Model AR-RAH1E remote control codes by Andrew Errington 2017 +Based on Fujitsu AR-RY16 codes by David Abrams 2009 + +Carrier frequency 38 kHz +/- 1% +Carrier period 26.3 us + +Basic unit 410 us +/- 3% (approx. 16 cycles of carrier) + +Leader: 125 (0x7C) cycles carrier on +followed by 62 (0x3E) cycles carrier off + +Trailer 16 (0x10) cycles carrier on +followed by at least 305 (0x130) cycles carrier off + +Data '1' bit 16 (0x10) cycles carrier on + 16 (0x10) cycles carrier off + +Data '0' bit 16 (0x10) cycles carrier on + 46 (0x2E) cycles carrier off + +There are two kinds of packet. A short packet is 7 bytes long and +usually encodes a simple command, such as toggling a feature on or off. +A full packet is 16 bytes long, and encodes the full state of the +remote control including the mode, fan speed, temperature, etc. + +Short packet (7 bytes) + +Byte 0 Marker code (M1) 0x14 +Byte 1 Marker code (M2) 0x63 +Byte 2 Signal code 0x00 +Byte 3 Custom code (C1) 0x10 +Byte 4 Sub custom code (C2) 0x10 +Byte 5 Command code (D) 0xXX +Byte 6 Checksum 0x?? + + +The a/c units and remote can be configured with one of four signal +codes (A, b, c, or d). This allows different a/c units to be controlled +by different remotes in the same room. + +Signal code (Byte 2<5:4>) + +0x00 Signal code A (default) +0x01 Signal code b +0x02 Signal code c +0x03 Signal code d + + +Byte 5 contains the command. If it's not 0xFE then we have a short +packet, with a simple function. + +Command codes (Byte 5<7:0>) + +0x02 Off +0x03 Test +0x09 Toggle economy mode +0x3F Filter Reset* +0x2D Super quiet* +0x6C Step horizontal vane up/down +0x79 Step vertical vane left/right +0xFE Full packet + +* Not for AR-RAH1E + + +Checksum (Byte 6<7:0>) (short packet) + +Checksum = 0xFF - Byte 5 + + + +Full packet (16 bytes) + +Byte 0 Marker code (M1) 0x14 +Byte 1 Marker code (M2) 0x63 +Byte 2 Signal code 0x00 +Byte 3 Custom code (C1) 0x10 +Byte 4 Sub custom code (C2) 0x10 +Byte 5 Command code (D) 0xFE (indicates full packet) +Byte 6 Unknown 0x09 +Byte 7 Unknown 0x30 +Byte 8 <7:4> temperature + <3:1> unknown + <0> on/off state +Byte 9 <7:4> timer mode + <3:0> mode +Byte 10 <7:4> swing mode + <3:0> fan mode +Byte 11 Timer off value low byte +Byte 12 <7:4> timer on value bits 3:0 + <3> new timer off value + <2:0> timer off value bits 10:8 +Byte 13 <7> new timer on value + <6:0> timer on value bits 10:4 +Byte 14 Unknown 0x20 +Byte 15 Checksum 0x?? + + +Temperature (Byte 8<7:4>) + +0x00 16C (60F)* 0x08 24C (76F) +0x01 17C (62F)* 0x09 25C (78F) +0x02 18C (64F) 0x0A 26C (80F) +0x03 19C (66F) 0x0B 27C (82F) +0x04 20C (68F) 0x0C 28C (84F) +0x05 21C (70F) 0x0D 29C (86F) +0x06 22C (72F) 0x0E 30C (88F) +0x07 23C (74F) 0x0F Unknown + +Byte 8<0>, if 1, unit was off and is now on, if 0 unit was already on + +* For AR-RAH1E, heat setting is 16-30C, cool setting is 18-30C + + +Timer mode (Byte 9<7:4>) + +0x00 Timer off +0x01 Sleep mode +0x02 Timer off time +0x03 Timer on time +0x04 Off -> On + + +Mode (Byte 9<3:0>) + +0x00 Auto +0x01 Cool +0x02 Dry +0x03 Fan +0x04 Heat + + +Swing mode (Byte 10<7:4>) + +0x00 Off +0x01 Horizontal vanes oscillation up/down +0x02 Vertical vanes oscillation left/right +0x03 Both hor. and vert. vanes oscillation + + +Fan mode (Byte 10<3:0>) + +0x00 Auto +0x01 High +0x02 Medium +0x03 Low +0x04 Quiet + + +Timer modes (Byte 11-13) + +Timer off + +Timer off value (Byte 12<2:0> Byte 11<7:0>) +11-bit value (0 to 595) +For Sleep mode, number of minutes to run before turning off. +For Timer Off mode, number of minutes from now until turn off. + +New timer off value (Byte 12<3>) +Set when new Sleep or Timer Off value is sent. + +Timer on + +Timer on value (Byte 13<6:0> Byte 12<7:4>) +11-bit value, number of minutes from now until turn on. + +New timer on value (Byte 13<7>) +Set when new Timer On value is sent. + + +Unknown (Byte 14) + +For AR-RY16 and AR-RAH1E constant 0x20 +Some reports indicate this may be an Eco setting for some models + + +Checksum (Byte 15) + +Checksum = 0 - (sum of Bytes 7 to 14) + +*/ + +static byte header[] = {0x14,0x63}; + +bool decodeFujitsu(byte *bytes, int byteCount) +{ + if(byteCount > sizeof(header) && memcmp(bytes, header, sizeof(header)) == 0) { + Serial.println(F("Looks like a Fujitsu protocol")); + + byte checksum = 0; + + //Calculate checksum + if (byteCount == 7) { + checksum = 0xFF - bytes[5]; + } else { + for (int i=7; i: 0x")); + Serial.print((bytes[2] & 0x30) >> 4, HEX); + Serial.print(F("\t\t")); + + switch ((bytes[2] & 0x30) >> 4) { + case 0x00: + Serial.println(F("A (Default)")); + break; + case 0x01: + Serial.println(F("b")); + break; + case 0x02: + Serial.println(F("c")); + break; + case 0x03: + Serial.println(F("d")); + break; + } + + + Serial.print(F("Byte 3: 0x")); + Serial.print(bytes[3], HEX); + Serial.print(F(" expected 0x10 ")); + if (bytes[3] == 0x10) { + Serial.println(F("[OK]")); + } else { + Serial.println(F("[Error]")); + } + + Serial.print(F("Byte 4: 0x")); + Serial.print(bytes[4], HEX); + Serial.print(F(" expected 0x10 ")); + if (bytes[4] == 0x10) { + Serial.println(F("[OK]")); + } else { + Serial.println(F("[Error]")); + } + + + // Byte 5 encodes the command. + // If it's not 0xFE then it's a short command (byte 5 is the command) + // If it is 0xFE then it's a long command and data is encoded + // in the following bytes. + + Serial.print(F("Byte 5: 0x")); + Serial.print(bytes[5], HEX); + + if (bytes[5] != 0xFE) { + // Short command + Serial.print(F(" Short command:\t\t")); + + switch (bytes[5]) { + case 0x02: + Serial.println(F("POWER OFF")); + break; + case 0x03: + Serial.println(F("TEST")); + break; + case 0x09: + Serial.println(F("TOGGLE ECONOMY MODE")); + break; + case 0x2D: + // Not AR-RAH1E + Serial.println(F("SUPER QUIET")); + break; + case 0x39: + // Not AR-RAH1E + Serial.println(F("HIGH POWER")); + break; + case 0x3F: + // Not AR-RAH1E + Serial.println(F("FILTER RESET")); + break; + case 0x6C: + Serial.println(F("STEP HORIZONTAL VANE UP/DOWN")); + break; + case 0x79: + Serial.println(F("STEP VERTICAL VANE LEFT/RIGHT")); + break; + default: + Serial.println(F("UNKNOWN")); + break; + } + + // Byte 6 is checksum in short command mode + + Serial.print(F("Byte 6 (Checksum): 0x")); + Serial.println(bytes[6], HEX); + + return true; + + } + + + // Otherwise, long command + + Serial.println(F(" Long command")); + + + // Bytes 6 and 7 are constant + + Serial.print(F("Byte 6: 0x")); + Serial.print(bytes[6], HEX); + Serial.print(F(" expected 0x09 ")); + if (bytes[6] == 0x09) { + Serial.println(F("[OK]")); + } else { + Serial.println(F("[Error]")); + } + + Serial.print(F("Byte 7: 0x")); + Serial.print(bytes[7], HEX); + Serial.print(F(" expected 0x30 ")); + if (bytes[7] == 0x30) { + Serial.println(F("[OK]")); + } else { + Serial.println(F("[Error]")); + } + + + // Byte 8 encodes temperature + + Serial.print(F("Byte 8: 0x")); + Serial.println(bytes[8], HEX); + + Serial.print(F("Temperature, byte 8<7:4>: 0x")); + Serial.print(bytes[8] >> 4, HEX); + + if ((bytes[8] >> 4) != 0x0F) { + Serial.print(F("\t\t")); + Serial.print((bytes[8] >> 4) + 16); + Serial.print(F("C (")); + Serial.print((bytes[8] >> 3) + 60); + Serial.println(F("F)")); + } else { + Serial.println(F(" UNKNOWN")); + } + + Serial.print(F("Unit state, byte 8 <0>: 0x")); + Serial.print(bytes[8] & 0x01,HEX); + + if ((bytes[8] & 0x01) == 0x01) { + Serial.println(F("\t\tUnit was off and is now on.")); + } else { + Serial.println(F("\t\tUnit was already on.")); + } + + Serial.print(F("Unknown bits, Byte 8 <3:1>: 0x")); + Serial.println(bytes[8] & 0x0E >> 1,HEX); + + + // Byte 9 encodes timer and main operating mode + + Serial.print(F("Byte 9: 0x")); + Serial.println(bytes[9], HEX); + + // Timer mode + Serial.print(F("Timer mode, byte 9<7:4>: 0x")); + Serial.print(bytes[9] >> 4, HEX); + Serial.print(F("\t\t")); + + switch (bytes[9] >> 4) { + case 0x00: + Serial.println(F("TIMER OFF")); + break; + case 0x01: + Serial.println(F("TIMER SLEEP")); + break; + case 0x02: + Serial.println(F("SET TIMER OFF TIME")); + break; + case 0x03: + Serial.println(F("SET TIMER ON TIME")); + break; + case 0x04: + Serial.println(F("SET TIMER (OFF -> ON)")); + break; + default: + Serial.println(F("UNKNOWN")); + break; + } + + // Main mode + Serial.print(F("Main mode, byte 9<3:0>: 0x")); + Serial.print(bytes[9] & 0x0F, HEX); + Serial.print(F("\t\t")); + + switch (bytes[9] & 0x0F) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x01: + Serial.println(F("MODE COOL")); + break; + case 0x02: + Serial.println(F("MODE DRY")); + break; + case 0x03: + Serial.println(F("MODE FAN")); + break; + case 0x04: + Serial.println(F("MODE HEAT")); + break; + case 0x08: + Serial.println(F("MODE COIL DRY")); + break; + case 0x0B: + Serial.println(F("MODE 10C HEAT")); + break; + default: + Serial.println(F("MODE UNKNOWN")); + break; + } + + + // Byte 10 encodes swing mode and fan speed + + Serial.print(F("Byte 10: 0x")); + Serial.println(bytes[10], HEX); + + // Swing mode + // This may actually be two individual bits + // i.e. byte 10<4> 1 = oscillate horizontal vane 0 = horizontal vane still + // byte 10<5> 1 = oscillate vertical vane 0 = vertical vane still + // byte 10(7:6> unknown + // For now it is treated as a 4-bit field with four known values + Serial.print(F("Swing mode, byte 10<7:4>: 0x")); + Serial.print(bytes[10] >> 4, HEX); + Serial.print(F("\t\t")); + + switch (bytes[10] >> 4) { + case 0x00: + Serial.println(F("OSCILLATION OFF/VANES STILL")); + break; + case 0x01: + Serial.println(F("OSCILLATE HORIZONTAL VANE UP/DOWN")); + break; + case 0x02: + Serial.println(F("OSCILLATE VERTICAL VANE LEFT/RIGHT")); + break; + case 0x03: + Serial.println(F("OSCILLATE HORIZONTAL AND VERTICAL VANES")); + break; + default: + Serial.println(F("VANE UNKNOWN")); + break; + } + + // Fan speed + Serial.print(F("Fan speed, byte 9<10:0>: 0x")); + Serial.print(bytes[10] & 0x0F, HEX); + Serial.print(F("\t\t")); + + switch (bytes[10] & 0x07) { + case 0x00: + Serial.println(F("FAN AUTO")); + break; + case 0x01: + Serial.println(F("FAN HIGH")); + break; + case 0x02: + Serial.println(F("FAN MED")); + break; + case 0x03: + Serial.println(F("FAN LOW")); + break; + case 0x04: + Serial.println(F("FAN QUIET")); + break; + default: + Serial.println(F("FAN UNKNOWN")); + break; + } + + + // Bytes 11, 12 and 13 encode the timer operation + + Serial.print(F("Byte 11: 0x")); + Serial.println(bytes[11], HEX); + + Serial.print(F("Byte 12: 0x")); + Serial.println(bytes[12], HEX); + + Serial.print(F("Byte 13: 0x")); + Serial.println(bytes[13], HEX); + + + Serial.print(F("Timer off, byte 12<2:0> 0x")); + Serial.print(bytes[12] & 0x03, HEX); + + Serial.print(F(" byte 11<7:0>: 0x")); + Serial.print(bytes[11], HEX); + Serial.print(F(" 0x")); + Serial.print(bytes[12] & 0x03, HEX); + Serial.println(bytes[11], HEX); + + unsigned int timer_off = (unsigned int) (bytes[12] & 0x03); + timer_off = (timer_off << 8) + bytes[11]; + Serial.print(F("Off time: 0x")); + Serial.print(timer_off, HEX); + Serial.print(F("\t\t\t\t")); + Serial.print((int)timer_off/60); + Serial.print(F("h ")); + Serial.print(timer_off%60); + Serial.print(F("m (")); + Serial.print(timer_off); + Serial.println(F(" minutes)")); + + + Serial.print(F("Timer off value flag, byte 12<3>: 0x")); + Serial.print(bytes[12] & 0x08 >> 3,HEX); + + if ((bytes[12] & 0x08) == 0x08) { + Serial.println(F("\tNew timer OFF value.")); + } else { + Serial.println(F("\tTimer OFF value unchanged.")); + } + + + Serial.print(F("Timer on, byte 13<6:0> 0x")); + Serial.print(bytes[13] & 0x3F, HEX); + Serial.print(F(" byte 12<7:4>: 0x")); + Serial.print(bytes[12]>>4, HEX); + Serial.print(F(" 0x")); + Serial.print(bytes[13] & 0x3F << 4); + Serial.println(bytes[12] >> 4, HEX); + + unsigned int timer_on = (unsigned int) (bytes[13] & 0x3F); + timer_on = (timer_on << 4) + (bytes[12] >> 4); + Serial.print(F("On time: 0x")); + Serial.print(timer_on, HEX); + Serial.print(F("\t\t\t\t")); + Serial.print((int)timer_on/60); + Serial.print(F("h ")); + Serial.print(timer_on%60); + Serial.print(F("m (")); + Serial.print(timer_on); + Serial.println(F(" minutes)")); + + + Serial.print(F("Timer on value flag, byte 13<7>: 0x")); + Serial.print(bytes[13] & 0x80 >> 7,HEX); + + if ((bytes[13] & 0x80) == 0x80) { + Serial.println(F("\tNew timer ON value.")); + } else { + Serial.println(F("\tTimer ON value unchanged.")); + } + + + // Byte 14 is constant + // Might be a bit for ECO mode on certain models. + Serial.print(F("Byte 14: 0x")); + Serial.print(bytes[14], HEX); + Serial.print(F(" expected 0x20 ")); + if (bytes[14] == 0x20) { + Serial.println(F("[OK]")); + } else { + Serial.println(F("[Error]")); + } + + + // Byte 15 is the checksum + + Serial.print(F("Byte 15 (Checksum): 0x")); + Serial.println(bytes[15], HEX); + + return true; + + } + + return false; + +} diff --git a/rawirdecode/Gree.cpp b/rawirdecode/Gree.cpp new file mode 100644 index 0000000..fde8cb8 --- /dev/null +++ b/rawirdecode/Gree.cpp @@ -0,0 +1,99 @@ +#include + +bool decodeGree(byte *bytes, int pulseCount) +{ + // If this looks like a Gree code... + if ( pulseCount == 71 ) { + Serial.println(F("Looks like a Gree protocol")); + + // Check if the checksum matches + uint8_t checksum = ( + (bytes[0] & 0x0F) + + (bytes[1] & 0x0F) + + (bytes[2] & 0x0F) + + (bytes[3] & 0x0F) + + ((bytes[5] & 0xF0) >> 4) + + ((bytes[6] & 0xF0) >> 4) + + ((bytes[7] & 0xF0) >> 4) + + 0x0A) & 0xF0; + + if (checksum == bytes[8]) { + Serial.println(F("Checksum matches")); + } else { + Serial.println(F("Checksum does not match")); + } + + // Power mode + switch (bytes[0] & 0x08) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x08: + Serial.println(F("POWER ON")); + break; + } + + // Operating mode + switch (bytes[0] & 0x07) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x04: + Serial.println(F("MODE HEAT")); + break; + case 0x01: + Serial.println(F("MODE COOL")); + break; + case 0x02: + Serial.println(F("MODE DRY")); + break; + case 0x03: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bytes[1] & 0x0F) + 16); + + // Fan speed + switch (bytes[0] & 0x30) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x10: + Serial.println(F("FAN: 1")); + break; + case 0x20: + Serial.println(F("FAN: 2")); + break; + case 0x30: + Serial.println(F("FAN: 3")); + break; + } + + // Sleep mode + switch (bytes[0] & 0x80) { + case 0x80: + Serial.println(F("SLEEP: ON")); + break; + case 0x00: + Serial.println(F("SLEEP: OFF")); + break; + } + + // Air direction + switch (bytes[0] & 0x40) { + case 0x40: + Serial.println(F("SWING: ON")); + break; + case 0x00: + Serial.println(F("SWING: OFF")); + break; + } + + return true; + } + + return false; +} diff --git a/rawirdecode/Gree_YAC.cpp b/rawirdecode/Gree_YAC.cpp new file mode 100644 index 0000000..fc72313 --- /dev/null +++ b/rawirdecode/Gree_YAC.cpp @@ -0,0 +1,269 @@ +#include + +bool decodeGree_YAC(byte *bytes, int pulseCount) +{ + // If this looks like a Gree code... + if ( pulseCount == 19 ) { + Serial.println(F("Looks like a Gree YAC protocol for I-Feel")); + Serial.print(F("I-Feel Temperature: ")); + Serial.println(bytes[0]); + return true; + } + + if ( pulseCount == 142 || pulseCount == 161 ) { + Serial.println(F("Looks like a Gree YAC protocol")); + + // Check if the checksum matches + uint8_t checksum0 = ( + (bytes[0] & 0x0F) + + (bytes[1] & 0x0F) + + (bytes[2] & 0x0F) + + (bytes[3] & 0x0F) + + ((bytes[4] & 0xF0) >> 4) + + ((bytes[5] & 0xF0) >> 4) + + ((bytes[6] & 0xF0) >> 4) + + 0x0A) & 0xF; + + uint8_t checksum1 = ( + (bytes[8] & 0x0F) + + (bytes[9] & 0x0F) + + (bytes[10] & 0x0F) + + (bytes[11] & 0x0F) + + ((bytes[12] & 0xF0) >> 4) + + ((bytes[13] & 0xF0) >> 4) + + ((bytes[14] & 0xF0) >> 4) + + 0x0A) & 0xF; + + + //byte 7[7:4] contais checksum for bytes 0-6 + //byte 15[7:4] contais checksum for bytes 8-14 + if ((checksum0 == (bytes[7]>>4)) && (checksum1 == (bytes[15]>>4))) { + Serial.println(F("Checksum matches")); + } else { + Serial.println(F("Checksum does not match")); + } + + // Power mode + // in bytes[0] and bytes[8] + if (bytes[0] == bytes[8]){ + switch (bytes[0] & 0x08) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x08: + Serial.println(F("POWER ON")); + break; + } + } + + // Eco mode (8C) + // bit2 of byte 7 (bits 7:4 are checksum) + switch (bytes[7] & 0x04) { + case 0x00: + Serial.println(F("ECO OFF")); + break; + case 0x04: + Serial.println(F("ECO ON")); + break; + } + + // Turbo mode + // byte[2] bit 4 + switch (bytes[2] & 0x10) { + case 0x00: + Serial.println(F("TURBO OFF")); + break; + case 0x10: + Serial.println(F("TURBO ON")); + break; + } + + // Operating mode + // in bytes[0] and bytes[8] + if (bytes[0] == bytes[8]){ + switch (bytes[0] & 0x07) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x04: + Serial.println(F("MODE HEAT")); + break; + case 0x01: + Serial.println(F("MODE COOL")); + break; + case 0x02: + Serial.println(F("MODE DRY")); + break; + case 0x03: + Serial.println(F("MODE FAN")); + break; + } + } + + // Temperature + // temp in two identical bytes 1 and 9, bits [3:0] + // bits [7:4] are used with timers but can be left as 0 + if (bytes[1]==bytes[9]){ + Serial.print(F("Temperature: ")); + Serial.println((bytes[1] & 0x0F) + 16); + } + + // Fan speed + if (bytes[0] == bytes[8]){ //same fan speed in two bytes + // note: fan speed 0-5 also in byte 14 bits [6:4] as 0-5 + // speed 0-3 in bytes[0] and bytes[8] bits [5:4] 0-3, speed 4-5 set as 3. + switch (bytes[0] & 0x30) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x10: + Serial.println(F("FAN: 1")); + break; + case 0x20: + Serial.println(F("FAN: 2")); + break; + case 0x30: + switch (bytes[14] & 0x70) { + case 0x30: + Serial.println(F("FAN: 3")); + break; + case 0x40: + Serial.println(F("FAN: 4")); + break; + case 0x50: + Serial.println(F("FAN: 5")); + break; + } + break; + } + } + + // Sleep mode + // Note that YAC1FB has also Sleep 3-mode which sends 24 bytes and is not decoded here + switch (bytes[0] & 0x80) { + case 0x80: + Serial.println(F("SLEEP: ON 1")); + break; + case 0x00: + if ((bytes[12] & 0x01) == 1){ + Serial.println(F("SLEEP: ON 2")); + } else { + Serial.println(F("SLEEP: OFF")); + } + break; + } + + // Air direction swing + // in bytes[0] and bytes[8] + if (bytes[0] == bytes[8]){ + switch (bytes[0] & 0x40) { + case 0x40: + Serial.println(F("SWING: ON")); + break; + case 0x00: + Serial.println(F("SWING: OFF")); + break; + } + } + + // Vertical air direction + switch (bytes[4] & 0x0F){ + case 0x00: + Serial.println(F("VERT. VANE STOP")); + break; + case 0x01: + Serial.println(F("VERT. VANE SWEEP UP-DOWN (1-5)")); + break; + case 0x02: + Serial.println(F("VERT. VANE UP (1)")); + break; + case 0x03: + Serial.println(F("VERT. VANE HIGH (2)")); + break; + case 0x04: + Serial.println(F("VERT. VANE CENTER (3)")); + break; + case 0x05: + Serial.println(F("VERT. VANE LOW (4)")); + break; + case 0x06: + Serial.println(F("VERT. VANE DOWN (5) ")); + break; + case 0x07: + Serial.println(F("VERT. VANE SWEEP 3-5")); + break; + case 0x09: + Serial.println(F("VERT. VANE SWEEP 2-4")); + break; + case 0x0B: + Serial.println(F("VERT. VANE SWEEP 1-3")); + break; + } + + // Horizontal air direction + switch (bytes[4] & 0xF0){ + case 0x00: + Serial.println(F("HORIZ. VANE STOP")); + break; + case 0x10: + Serial.println(F("HORIZ. VANE SWEEP LEFT-RIGHT (1-5)")); + break; + case 0x20: + Serial.println(F("HORIZ. VANE FAR LEFT (1)")); + break; + case 0x30: + Serial.println(F("HORIZ. VANE LEFT (2)")); + break; + case 0x40: + Serial.println(F("HORIZ. VANE CENTER (3)")); + break; + case 0x50: + Serial.println(F("HORIZ. VANE RIGHT (4)")); + break; + case 0x60: + Serial.println(F("HORIZ. VANE FAR RIGHT (5)")); + break; + case 0xC0: + Serial.println(F("HORIZ. VANE LEFT (1) AND RIGHT (5)")); + break; + case 0xD0: + Serial.println(F("HORIZ. VANE SWEEP TO CENTER")); + break; + } + + // Display temperature + // bytes[5] bit [0:1] + switch (bytes[5] & 0x3) { + case 0x00: + Serial.println(F("DISPLAY: SET TEMP")); + break; + case 0x01: + Serial.println(F("DISPLAY: SET TEMP")); + break; + case 0x02: + Serial.println(F("DISPLAY: INDOOR TEMP")); + break; + case 0x03: + Serial.println(F("DISPLAY: OUTDOOR TEMP")); + break; + + } + + switch (bytes[5] & 0x4) { + case 0x00: + Serial.println(F("I-FEEL: OFF")); + break; + case 0x04: + Serial.println(F("I-FEEL: ON")); + break; + } + + if (pulseCount == 161) { + Serial.print(F("I-FEEL TEMPERATURE: ")); + Serial.println(bytes[16]); + } + return true; + } + + return false; +} diff --git a/rawirdecode/Hitachi.cpp b/rawirdecode/Hitachi.cpp new file mode 100644 index 0000000..4cdd5b9 --- /dev/null +++ b/rawirdecode/Hitachi.cpp @@ -0,0 +1,109 @@ +#include // Hitachi RAR-5E1 remote + +bool decodeHitachi(byte *bytes, int byteCount) +{ + if (byteCount == 28 && bytes[0] == 0x01 && bytes[1] == 0x10) { + Serial.println(F("Looks like a Hitachi protocol")); + + // Operating mode + Serial.print(F("Mode: ")); + switch (bytes[10]) { + case 0x02: + Serial.println(F("Auto")); + break; + case 0x03: + Serial.println(F("Heat")); + break; + case 0x04: + Serial.println(F("Cool")); + break; + case 0x05: + Serial.println(F("Dry")); + break; + } + + // Fan speed + Serial.print(F("Fan speed: ")); + switch (bytes[13]) { + case 0x01: + Serial.println(F("Auto")); + break; + case 0x02: + Serial.println(F("1")); + break; + case 0x03: + Serial.println(F("2")); + break; + case 0x04: + Serial.println(F("3")); + break; + case 0x05: + Serial.println(F("4")); + break; + } + + // Vertical air swing + Serial.print(F("Vertical air swing: ")); + switch (bytes[14] & 0x01) { + case 0x00: + Serial.println(F("Off")); + break; + case 0x01: + Serial.println(F("On")); + break; + } + + // Horisontal air Swing + Serial.print(F("Horisontal air swing: ")); + switch (bytes[15] & 0x01) { + case 0x00: + Serial.println(F("Off")); + break; + case 0x01: + Serial.println(F("On")); + break; + } + + // Power + Serial.print(F("Power: ")); + switch (bytes[17]) { + case 0x00: + Serial.println(F("Off")); + break; + case 0x80: + Serial.println(F("On")); + break; + } + + // ECO mode + Serial.print(F("ECO: ")); + switch (bytes[25]) { + case 0x00: + Serial.println(F("Off")); + break; + case 0x02: + Serial.println(F("On")); + break; + } + + //Temperature + Serial.print("Temperature: "); + Serial.println(bytes[11] >> 1); + + int checksum = 1086; + + for (byte i = 0; i < 27; i++) { + checksum -= bytes[i]; + } + + if ( bytes[27] == checksum ) { + Serial.println(F("Checksum matches")); + } else { + Serial.println(F("Checksum does not match")); + } + return true; + } + + return false; +} + diff --git a/rawirdecode/Hyundai.cpp b/rawirdecode/Hyundai.cpp new file mode 100644 index 0000000..0a353f5 --- /dev/null +++ b/rawirdecode/Hyundai.cpp @@ -0,0 +1,83 @@ +#include + +bool decodeHyundai(byte *bytes, int pulseCount) +{ + // If this looks like a Hyundai code... + // Remote control Y512F2, Hyundai split-unit air conditioner + if ( pulseCount == 38 ) { + Serial.println(F("Looks like a Hyundai protocol")); + + // Power mode + switch (bytes[0] & 0x08) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x08: + Serial.println(F("POWER ON")); + break; + } + + // Operating mode + switch (bytes[0] & 0x07) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x04: + Serial.println(F("MODE HEAT")); + break; + case 0x01: + Serial.println(F("MODE COOL")); + break; + case 0x02: + Serial.println(F("MODE DRY")); + break; + case 0x03: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bytes[1] & 0x0F) + 16); + + // Fan speed + switch (bytes[0] & 0x30) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x10: + Serial.println(F("FAN: 1")); + break; + case 0x20: + Serial.println(F("FAN: 2")); + break; + case 0x30: + Serial.println(F("FAN: 3")); + break; + } + + // Sleep mode + switch (bytes[0] & 0x80) { + case 0x80: + Serial.println(F("SLEEP: ON")); + break; + case 0x00: + Serial.println(F("SLEEP: OFF")); + break; + } + + // Air direction + switch (bytes[0] & 0x40) { + case 0x40: + Serial.println(F("SWING: ON")); + break; + case 0x00: + Serial.println(F("SWING: OFF")); + break; + } + + return true; + } + + return false; +} diff --git a/rawirdecode/KY26Remote.cpp b/rawirdecode/KY26Remote.cpp new file mode 100644 index 0000000..e9694c0 --- /dev/null +++ b/rawirdecode/KY26Remote.cpp @@ -0,0 +1,158 @@ +#include + +// KY-26 Remote protocol +// +// The KY-26 remote control is used in locally branded air conditioners. +// I used a ZAICON air conditioner, which also seems to be rebranded as +// SACOM, SENCYS, and possibly others. +// +// The remote sends a 4-byte message which contains all possible settings every +// time. +// +// Byte 0 contains the a power signal, operating mode, and fan speed. +// Byte 1 contains the timer setting. +// Byte 2 contains the temperature setting. +// Byte 3 contains a checksum. + +bool decodeKY26Remote(byte *bytes, int byteCount) { + // If this looks like a KY-26 code... + if (byteCount != 4 // + || (bytes[0] & 0xC4) != 0x00 // Bits 3, 7, and 8 must be 0 + || (bytes[1] & 0xC0) != 0x80 // Bits 7 must be 0, bit 8 must be 1 + || (bytes[2] & 0xE0) != 0x00 // Bits 6, 7, and 8 must be 0 + || (bytes[3] & 0x80) != 0x80 // Bit 8 must be 1 + ) { + return false; + } + + Serial.println(F("Looks like a KY-26 remote control protocol")); + + // Power mode + // It sends the same signal for on/off, it depends on the unit status. + if (bytes[0] & 0x08) { + Serial.println(F("On/Off action")); + } + + // Operating mode + Serial.print(F("MODE: ")); + switch (bytes[0] & 0x03) { + case 0x00: + Serial.println(F("AUTO")); + break; + case 0x01: + Serial.println(F("COOL")); + break; + case 0x03: + Serial.println(F("FAN")); + break; + case 0x02: + Serial.println(F("DRY")); + break; + } + + // Fan speed + switch (bytes[0] & 0x30) { + case 0x10: + Serial.println(F("FAN: 1")); + break; + case 0x20: + Serial.println(F("FAN: 2")); + break; + case 0x30: + Serial.println(F("FAN: 3")); + break; + } + + // Timer + // First 4 bits are hours, last 4 bits are half hours. + Serial.print(F("Timer: ")); + if (!(bytes[1] & 0x20)) { + Serial.println(F("OFF")); + } else { + Serial.print(bytes[1] & 0x0F); + if (bytes[1] & 0x10) { + Serial.print(F(".5")); + } + Serial.println(F("h")); + } + + // Temperature + Serial.print(F("Temperature: ")); + int temp = bytes[2] & 0x1F; + Serial.println(temp == 0 ? 15 : temp); // 0 is 15 degrees + + // CheckSum8 Modulo 256 + byte checksum = 0; + for (int i = 0; i < 3; i++) { + checksum += bytes[i]; + } + checksum = checksum % 256; + if (checksum != bytes[3]) { + Serial.print(F("Checksum error: ")); + Serial.print(checksum, HEX); + Serial.print(F(" != ")); + Serial.println(bytes[3], HEX); + } else { + Serial.println(F("Checksum matches")); + } + + return true; +} + +/* + x +Hh 00011000 00000001 00000001 11110101 h POWER + xx +Hh 00001000 00000001 11101000 11100101 h SPEED 1 +Hh 00000100 00000001 11101000 11101101 h SPEED 2 +Hh 00001100 00000001 11101000 11100011 h SPEED 3 + xx +Hh 00001000 00000001 11101000 11100101 h MODE 1 +Hh 10001000 00000001 11011000 00110101 h MODE 2 +Hh 11001000 00000001 11011000 01110101 h MODE 3 +Hh 01001000 00000001 10011000 11010101 h MODE 4 + xxxxxx +Hh 00001000 00000001 00000000 00001001 h TEMP 15 +Hh 00001000 00000001 00001000 00000101 h TEMP 16 +Hh 00001000 00000001 10001000 10000101 h TEMP 17 +Hh 00001000 00000001 01001000 01000101 h TEMP 18 +Hh 00001000 00000001 11001000 11000101 h TEMP 19 +Hh 00001000 00000001 00101000 00100101 h TEMP 20 +Hh 00001000 00000001 10101000 10100101 h TEMP 21 +Hh 00001000 00000001 01101000 01100101 h TEMP 22 +Hh 00001000 00000001 11101000 11100101 h TEMP 23 +Hh 00001000 00000001 00011000 00010101 h TEMP 24 +Hh 00001000 00000001 10011000 10010101 h TEMP 25 +Hh 00001000 00000001 01011000 01010101 h TEMP 26 +Hh 00001000 00000001 11011000 11010101 h TEMP 27 +Hh 00001000 00000001 00111000 00110101 h TEMP 28 +Hh 00001000 00000001 10111000 10110101 h TEMP 29 +Hh 00001000 00000001 01111000 01110101 h TEMP 30 +Hh 00001000 00000001 11111000 11110101 h TEMP 31 + xxxxxx +Hh 00001100 00000001 00000000 00001101 h Timer OFF +Hh 00001100 00001101 00000000 00000111 h Timer 0.5h +Hh 00001100 10000101 00000000 10001011 h Timer 1h +Hh 00001100 10001101 00000000 10000111 h Timer 1.5h +Hh 00001100 01000101 00000000 01001011 h Timer 2h +Hh 00001100 01001101 00000000 01000111 h Timer 2.5h +Hh 00001100 11000101 00000000 11001011 h Timer 3h +Hh 00001100 11001101 00000000 11000111 h Timer 3.5h +Hh 00001100 00100101 00000000 00101011 h Timer 4h +Hh 00001100 00101101 00000000 00100111 h Timer 4.5h +Hh 00001100 10100101 00000000 10101011 h Timer 5h +Hh 00001100 10101101 00000000 10100111 h Timer 5.5h +Hh 00001100 01100101 00000000 01101011 h Timer 6h +Hh 00001100 01101101 00000000 01100111 h Timer 6.5h +Hh 00001100 11100101 00000000 11101011 h Timer 7h +Hh 00001100 11101101 00000000 11100111 h Timer 7.5h +Hh 00001100 00010101 00000000 00011011 h Timer 8h +Hh 00001100 00011101 00000000 00010111 h Timer 8.5h +Hh 00001100 10010101 00000000 10011011 h Timer 9h +Hh 00001100 10011101 00000000 10010111 h Timer 9.5h +Hh 00001100 01010101 00000000 01011011 h Timer 10h +Hh 00001100 01011101 00000000 01010111 h Timer 10.5h +Hh 00001100 11010101 00000000 11011011 h Timer 11h +Hh 00001100 11011101 00000000 11010111 h Timer 11.5h +Hh 00001100 00110101 00000000 00111011 h Timer 12h +*/ \ No newline at end of file diff --git a/rawirdecode/MitsubishiElectric.cpp b/rawirdecode/MitsubishiElectric.cpp new file mode 100644 index 0000000..81f82c9 --- /dev/null +++ b/rawirdecode/MitsubishiElectric.cpp @@ -0,0 +1,232 @@ +#include + +bool decodeMitsubishiElectric(byte *bytes, int byteCount) +{ + // If this looks like a Mitsubishi FD-25 or FE code... + if ( byteCount == 36 && bytes[0] == 0x23 && + ( (memcmp(bytes, bytes+18, 17) == 0) || + ((memcmp(bytes, bytes+18, 14) == 0) && bytes[32] == 0x24) ) ){ + Serial.println(F("Looks like a Mitsubishi FD / FE / MSY series protocol")); + + // Check if the checksum matches + byte checksum = 0; + + for (int i=0; i<17; i++) { + checksum += bytes[i]; + } + + if (checksum == bytes[17]) { + Serial.println(F("Checksum matches")); + } else { + Serial.println(F("Checksum does not match")); + } + + // Power mode + switch (bytes[5]) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x20: + Serial.println(F("POWER ON")); + break; + default: + Serial.println(F("POWER unknown")); + break; + } + + // Operating mode + Serial.print(F("MODE ")); + switch (bytes[6] & 0x38) { // 0b00111000 + case 0x38: + Serial.println(F("FAN")); + break; + case 0x20: + Serial.println(F("AUTO")); + break; + case 0x08: + if (bytes[15] == 0x20) { + Serial.println(F("MAINTENANCE HEAT (FE only)")); + } else { + Serial.println(F("HEAT")); + } + break; + case 0x18: + Serial.println(F("COOL")); + break; + case 0x10: + Serial.println(F("DRY")); + break; + default: + Serial.println(F("unknown")); + break; + } + + // I-See + Serial.print(F("I-See: ")); + switch (bytes[6] & 0x40) { // 0b01000000 + case 0x40: + Serial.println(F("ON")); + break; + case 0x00: + Serial.println(F("OFF")); + break; + } + + // Clean + Serial.print(F("Clean: ")); + switch (bytes[14] & 0x04) { // 0b00000100 + case 0x04: + Serial.println(F("ON")); + break; + case 0x00: + Serial.println(F("OFF")); + break; + } + + + // Plasma + Serial.print(F("Plasma: ")); + switch (bytes[15] & 0x40) { // 0b01000000 + case 0x40: + Serial.println(F("ON")); + break; + case 0x00: + Serial.println(F("OFF")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + if (bytes[7] == 0x00) { + Serial.println(F("10")); + } else { + Serial.println(bytes[7] + 16); + } + + // Fan speed + Serial.print(F("FAN ")); + switch (bytes[9] & 0x07) { // 0b00000111 + case 0x00: + Serial.println(F("AUTO")); + break; + case 0x01: + Serial.println(F("1")); + break; + case 0x02: + Serial.println(F("2")); + break; + case 0x03: + Serial.println(F("3")); + break; + case 0x04: + Serial.println(F("4")); + break; + default: + Serial.println(F("unknown")); + break; + } + + // Vertical air direction + Serial.print(F("VANE: ")); + switch (bytes[9] & 0xF8) { // 0b11111000 + case 0x40: // 0b01000 + Serial.println(F("AUTO1?")); + break; + case 0x48: // 0b01001 + Serial.println(F("UP")); + break; + case 0x50: // 0b01010 + Serial.println(F("UP-1")); + break; + case 0x58: // 0b01011 + Serial.println(F("UP-2")); + break; + case 0x60: // 0b01100 + Serial.println(F("UP-3")); + break; + case 0x68: // 0b01101 + Serial.println(F("DOWN")); + break; + case 0x78: // 0b01111 + Serial.println(F("SWING")); + break; + case 0x80: // 0b10000 + Serial.println(F("AUTO2?")); + break; + case 0xB8: // 0b10111 + Serial.println(F("AUTO3?")); + break; + default: + Serial.println(F("unknown")); + break; + } + + // Horizontal air direction + Serial.print(F("WIDE VANE: ")); + switch (bytes[8] & 0xF0) { // 0b11110000 + case 0x00: + Serial.println(F("AREA")); + break; + case 0x10: + Serial.println(F("LEFT")); + break; + case 0x20: + Serial.println(F("MIDDLE LEFT")); + break; + case 0x30: + Serial.println(F("MIDDLE")); + break; + case 0x40: + Serial.println(F("MIDDLE RIGHT")); + break; + case 0x50: + Serial.println(F("RIGHT")); + break; + case 0xC0: + Serial.println(F("SWING")); + break; + default: + Serial.println(F("unknown")); + break; + } + + // Horizontal air direction, area mode + Serial.print(F("AREA MODE: ")); + switch (bytes[13] & 0xC0) { // 0b11000000 + case 0x00: + Serial.println(F("OFF")); + break; + case 0x40: + Serial.println(F("LEFT")); + break; + case 0x80: + Serial.println(F("AUTO")); + break; + case 0xC0: + Serial.println(F("RIGHT")); + break; + } + + // Installation position + Serial.print(F("INSTALL POSITION: ")); + switch (bytes[14] & 0x18) { // 0b00011000 + case 0x08: + Serial.println(F("LEFT")); + break; + case 0x10: + Serial.println(F("CENTER")); + break; + case 0x18: + Serial.println(F("RIGHT")); + break; + default: + Serial.println(F("unknown")); + break; + } + + + return true; + } + + return false; +} diff --git a/rawirdecode/MitsubishiHeavy.cpp b/rawirdecode/MitsubishiHeavy.cpp new file mode 100644 index 0000000..15b8683 --- /dev/null +++ b/rawirdecode/MitsubishiHeavy.cpp @@ -0,0 +1,381 @@ +#include + +bool decodeMitsubishiHeavy(byte *bytes, int byteCount) +{ + // If this looks like a Mitsubishi Heavy... + if ( byteCount == 11 && bytes[0] == 0x52 && bytes[1] == 0xAE && bytes[2] == 0xC3 && bytes[3] == 0x26 && bytes[4] == 0xD9) { + Serial.println(F("Looks like a Mitsubishi Heavy ZJ-S protocol")); + Serial.println(F("Model SRKxxZJ-S Remote Control RKX502A001C")); + + // Power mode + switch (bytes[9] & 0x08) { + case 0x00: + Serial.println(F("POWER ON")); + break; + case 0x08: + Serial.println(F("POWER OFF")); + break; + } + + // Operating mode + switch (bytes[9] & 0x07) { + case 0x07: + Serial.println(F("MODE AUTO")); + break; + case 0x03: + Serial.println(F("MODE HEAT")); + break; + case 0x06: + Serial.println(F("MODE COOL")); + break; + case 0x05: + Serial.println(F("MODE DRY")); + break; + case 0x04: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((~((bytes[9] & 0xF0) >> 4) & 0x0F) + 17); + + // Fan speed + switch (bytes[7] & 0xE0) { + case 0xE0: + Serial.println(F("FAN AUTO")); + break; + case 0xA0: + Serial.println(F("FAN 1")); + break; + case 0x80: + Serial.println(F("FAN 2")); + break; + case 0x60: + Serial.println(F("FAN 3")); + break; + case 0x20: + Serial.println(F("HI POWER MODE")); + break; + case 0x00: + Serial.println(F("ECONO MODE")); + break; + } + + // Vertical air direction + Serial.print(F("Vertical air direction: ")); + switch ((bytes[5] & 0b00000010) | (bytes[7] & 0b00011000)) { + case 0x0A: + Serial.println(F("SWING")); + break; + case 0x02: + Serial.println(F("UP")); + break; + case 0x18: + Serial.println(F("MIDDLE UP")); + break; + case 0x10: + Serial.println(F("MIDDLE")); + break; + case 0x08: + Serial.println(F("MIDDLE DOWN")); + break; + case 0x00: + Serial.println(F("DOWN")); + break; + case 0x1A: + Serial.println(F("STOP")); + break; + } + + // Horizontal air direction + Serial.print(F("Horizontal air direction: ")); + switch (bytes[5] & 0b11001100) { + case 0xC8: + Serial.println(F("LEFT")); + break; + case 0x88: + Serial.println(F("MIDDLE LEFT")); + break; + case 0x48: + Serial.println(F("MIDDLE")); + break; + case 0x08: + Serial.println(F("MIDDLE RIGHT")); + break; + case 0xC4: + Serial.println(F("RIGHT")); + break; + case 0x84: + Serial.println(F("LEFT RIGHT")); + break; + case 0x44: + Serial.println(F("RIGHT LEFT")); + break; + case 0x4C: + Serial.println(F("SWING")); + break; + case 0xCC: + Serial.println(F("STOP")); + break; + case 0x04: + Serial.println(F("3D AUTO")); + break; + } + + // Clean + Serial.print(F("Clean: ")); + switch (bytes[5] & 0x20) { + case 0x00: + Serial.println(F("ON")); + break; + case 0x20: + Serial.println(F("OFF")); + break; + } + + return true; + } else if ( byteCount == 19 && bytes[0] == 0x52 && bytes[1] == 0xAE && bytes[2] == 0xC3 && bytes[3] == 0x1A && bytes[4] == 0xE5) { + Serial.println(F("Looks like a Mitsubishi Heavy ZM-S protocol (Model SRKxxZM-S Remote Control RLA502A700B) or")); + Serial.println(F("looks like a Mitsubishi Heavy ZS-S protocol (Model SRKxxZS-S Remote Control RLA502A700L)")); + + // Power mode + switch (bytes[5] & 0x08) { + case 0x00: + Serial.println(F("POWER ON")); + break; + case 0x08: + Serial.println(F("POWER OFF")); + break; + } + + // Operating mode + switch (bytes[5] & 0x07) { + case 0x07: + Serial.println(F("MODE AUTO")); + break; + case 0x03: + Serial.println(F("MODE HEAT")); + break; + case 0x06: + Serial.println(F("MODE COOL")); + break; + case 0x05: + Serial.println(F("MODE DRY")); + break; + case 0x04: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((~(bytes[7]) & 0x0F) + 17); + + // Fan speed + switch (bytes[9] & 0x0F) { + case 0x0F: + Serial.println(F("FAN AUTO")); + break; + case 0x0E: + Serial.println(F("FAN 1")); + break; + case 0x0D: + Serial.println(F("FAN 2")); + break; + case 0x0C: + Serial.println(F("FAN 3")); + break; + case 0x0B: + Serial.println(F("FAN 4")); + break; + case 0x07: + Serial.println(F("HI POWER MODE")); + break; + case 0x09: + Serial.println(F("ECONO MODE")); + break; + } + + // Vertical air direction + Serial.print(F("Vertical air direction: ")); + switch ((bytes[11] & 0b11100000)) { + case 0xE0: + Serial.println(F("SWING")); + break; + case 0xC0: + Serial.println(F("UP")); + break; + case 0xA0: + Serial.println(F("MIDDLE UP")); + break; + case 0x80: + Serial.println(F("MIDDLE")); + break; + case 0x60: + Serial.println(F("MIDDLE DOWN")); + break; + case 0x40: + Serial.println(F("DOWN")); + break; + case 0x20: + Serial.println(F("STOP")); + break; + } + + // Horizontal air direction + Serial.print(F("Horizontal air direction: ")); + switch (bytes[13] & 0x0F) { + case 0x0E: + Serial.println(F("LEFT")); + break; + case 0x0D: + Serial.println(F("MIDDLE LEFT")); + break; + case 0x0C: + Serial.println(F("MIDDLE")); + break; + case 0x0B: + Serial.println(F("MIDDLE RIGHT")); + break; + case 0x0A: + Serial.println(F("RIGHT")); + break; + case 0x08: + Serial.println(F("LEFT RIGHT")); + break; + case 0x09: + Serial.println(F("RIGHT LEFT")); + break; + case 0x0F: + Serial.println(F("SWING")); + break; + case 0x07: + Serial.println(F("STOP")); + break; + } + + // 3D Auto + Serial.print(F("3D Auto: ")); + switch (bytes[11] & 0b00010010) { + case 0x00: + Serial.println(F("ON")); + break; + case 0x12: + Serial.println(F("OFF")); + break; + } + + // Night setback + Serial.print(F("Night setback: ")); + switch (bytes[15] & 0x40) { + case 0x00: + Serial.println(F("ON")); + break; + case 0x40: + Serial.println(F("OFF")); + break; + } + + // Silent mode + Serial.print(F("Silent mode: ")); + switch (bytes[15] & 0x80) { + case 0x00: + Serial.println(F("ON")); + break; + case 0x80: + Serial.println(F("OFF")); + break; + } + + // Clean and alergen + Serial.print(F("Clean or Alergen: ")); + switch (bytes[5] & 0x60) { + case 0x00: + Serial.println(F("CLEAN")); + break; + case 0x20: + Serial.println(F("ALERGEN")); + break; + case 0x60: + Serial.println(F("OFF")); + break; + } + return true; + } else if ( byteCount == 8 && bytes[0] == 0x0A) { + Serial.println(F("Looks like a Mitsubishi Heavy FDTC protocol (Model FDTCxxVF Remote Control PJA502A704AA)")); + + // Power mode + switch (bytes[2] & 0x80) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x80: + Serial.println(F("POWER ON")); + break; + } + + // Operating mode + switch (bytes[2] & 0x70) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x40: + Serial.println(F("MODE HEAT")); + break; + case 0x20: + Serial.println(F("MODE COOL")); + break; + case 0x10: + Serial.println(F("MODE DRY")); + break; + case 0x30: + Serial.println(F("MODE FAN")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bytes[2] & 0x0F) + 16); + + // Fan speed + switch (bytes[1] & 0x30) { + case 0x00: + Serial.println(F("FAN 1")); + break; + case 0x10: + Serial.println(F("FAN 2")); + break; + case 0x20: + Serial.println(F("FAN 3")); + break; + } + + // Vertical air direction + Serial.print(F("Vertical air direction: ")); + if ((bytes[1] & 0b01000000) == 0x40 ) + { + Serial.println(F("SWING")); + } + else + { + switch ((bytes[3] & 0x30)) { + case 0x00: + Serial.println(F("UP")); + break; + case 0x10: + Serial.println(F("MIDDLE UP")); + break; + case 0x20: + Serial.println(F("MIDDLE DOWN")); + break; + case 0x30: + Serial.println(F("DOWN")); + break; + } + } + + return true; + } + return false; +} diff --git a/rawirdecode/Nibe.cpp b/rawirdecode/Nibe.cpp new file mode 100644 index 0000000..a168f9e --- /dev/null +++ b/rawirdecode/Nibe.cpp @@ -0,0 +1,207 @@ +#include + +bool decodeNibe(byte *originalBytes, char* symbols, int bitCount) +{ + byte bytes[11]; + byte currentBit = 0; + byte currentByte = 0; + byte byteCount = 0; + + // If this looks like a Nibe code... + if ( bitCount == 93 ) + { + // Decode the string of bits to a byte array + for (uint16_t i = 5; i < bitCount; i++) { + if (symbols[i] == '0' || symbols[i] == '1') { + currentByte >>= 1; + currentBit++; + + if (symbols[i] == '1') { + currentByte |= 0x80; + } + + Serial.print(symbols[i]); + + if (currentBit >= 8) { + bytes[byteCount++] = currentByte; + currentBit = 0; + currentByte = 0; + + Serial.print(" "); + } + } + } + + // Print the byte array + Serial.print(F("\nNibe bytes: ")); + for (int i = 0; i < byteCount; i++) { + if (bytes[i] < 0x10) { + Serial.print(F("0")); + } + Serial.print(bytes[i], HEX); + if ( i < byteCount - 1 ) { + Serial.print(F(",")); + } + } + Serial.println(); + + // Night (low night temp I think) + switch (bytes[9] & 0x01) { + case 0x00: + Serial.println(F("NIGHT MODE OFF")); + break; + case 0x01: + Serial.println(F("NIGHT MODE ON")); + break; + } + + // Filter + switch (bytes[9] & 0x04) { + case 0x00: + Serial.println(F("FILTER OFF")); + break; + case 0x04: + Serial.println(F("FILTER ON")); + break; + } + + // Power mode + switch (bytes[9] & 0x08) { + case 0x00: + Serial.println(F("POWER OFF")); + break; + case 0x08: + Serial.println(F("POWER ON")); + break; + } + + // iFeel + switch (bytes[9] & 0x32) { + case 0x00: + Serial.println(F("IFEEL OFF")); + break; + case 0x32: + Serial.println(F("IFEEL ON")); + break; + } + + // Operating mode + switch (bytes[2] & 0x0F) { + case 0x00: + Serial.println(F("MODE COOL")); + break; + case 0x02: + Serial.println(F("MODE DRY")); + break; + case 0x04: + Serial.println(F("MODE COOL AUTO")); + break; + case 0x08: + Serial.println(F("MODE HEAT")); + break; + } + + // Fan speed + Serial.print(F("Fan speed: ")); + switch (bytes[3] & 0x06) { + case 0x00: + Serial.println(F("AUTO")); + break; + case 0x02: + Serial.println(F("1")); + break; + case 0x04: + Serial.println(F("2")); + break; + case 0x06: + Serial.println(F("3")); + break; + } + + // Vertical air direction + Serial.print(F("Vertical air direction: ")); + switch (bytes[4]) { + case 0x00: + Serial.println(F("AUTO")); + break; + case 0x01: + Serial.println(F("UP")); + break; + case 0x02: + Serial.println(F("MIDDLE UP")); + break; + case 0x03: + Serial.println(F("MIDDLE")); + break; + case 0x04: + Serial.println(F("MIDDLE")); + break; + case 0x05: + Serial.println(F("MIDDLE DOWN")); + break; + case 0x06: + Serial.println(F("DOWN")); + break; + case 0x07: + Serial.println(F("SWING")); + break; + } + + Serial.print("Temperature: "); + int8_t temperature = (((bytes[2] & 0xF0) >> 4) + ((bytes[3] & 0x01) << 4)) + 4; + Serial.println(temperature); + + + + // TODO: How to calculate the checksum? This algorithm seems to get bits 0, 6 and 7 right, but how about the other bits? + // Here's some material for testing: + // + // Hh001101011010111100000101100001010101000000000111000000001111100110100000000001000101001001 + // Hh001101011010111100000111100001010101000000000111000000001111100110100000000001000101001011 + // Hh001101011010111100000100010001010101000000000111000000001110010110100000000001000111011000 + // Hh001101011010111100000110010001010101000000000111000000001110010110100000000001000111011010 + // Hh001101011010111100000101010001010101000000000111000000001110010110100000000001000111011001 + // Hh001101011010111100000111010001010101000000000111000000001110010110100000000001000111011011 + // Hh001101011010111100000100110001010101000000000111000000001110010110100000000001000100111000 + // Hh001101011010111100000110110001010101000000000111000000001110010110100000000001000100111010 + // Hh001101011010111100000101110001010101000000000111000000001110010110100000000001000100111001 + // Hh001101011010111100000111110001010101000000000111000000001110010110100000000001000100111011 + // Hh001101011010111100000100001001010101000000000111000000001111010110100000000001000110100100 + // Hh001101011010111100000110001001010101000000000111000000001111010110100000000001000110100110 + // Hh001101011010111100000101001001010101000000000111000000001111010110100000000001000110100101 + // Hh001101011010111100000111001001010101000000000111000000001111010110100000000001000110100111 + // Hh001101011010111100000100101001010101000000000111000000001111010110100000000001000101100100 + // Hh001101011010111100000110101001010101000000000111000000001111010110100000000001000101100110 + // Hh001101011010111100000101101001010101000000000111000000001111010110100000000001000101100101 + // Hh001101011010111100000111101001010101000000000111000000001111110110100000000001000101101111 + // Hh001101011010111100000100011001010101000000000111000000001111110110100000000001000111101100 + // Hh001101011010111100000110011001010101000000000111000000001111110110100000000001000111101110 + // Hh001101011010111100000101011001010101000000000111000000001111110110100000000001000111101101 + // Hh001101011010111100000111011001010101000000000111000000001111110110100000000001000111101111 + // Hh001101011010111100000100111001010101000000000111000000001111110110100000000001000100011100 + + byte checksum = 0b10011010; + + for (int i=0; i<10; i++) { + checksum ^= originalBytes[i]; + } + + Serial.print(bytes[10], BIN); + Serial.println(F(" <- original checksum")); + Serial.print(checksum, BIN); + Serial.println(F(" <- calculated checksum")); + + if (((checksum & 0b11000000) == (bytes[10] & 0b11000000)) && ((checksum & 0b00000001) == (bytes[10] & 0b00000001))) { + Serial.println(F("Checksum matches at least partially")); + } else { + Serial.println(F("Checksum does not match")); + } + + return true; + } + + return false; +} + + + diff --git a/rawirdecode/OlimpiaMaestro.cpp b/rawirdecode/OlimpiaMaestro.cpp new file mode 100644 index 0000000..1fc68d0 --- /dev/null +++ b/rawirdecode/OlimpiaMaestro.cpp @@ -0,0 +1,106 @@ +#include + +// from Carrier.cpp +#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d" +#define BYTETOBINARY(byte) \ + (byte & 0x80 ? 1 : 0), \ + (byte & 0x40 ? 1 : 0), \ + (byte & 0x20 ? 1 : 0), \ + (byte & 0x10 ? 1 : 0), \ + (byte & 0x08 ? 1 : 0), \ + (byte & 0x04 ? 1 : 0), \ + (byte & 0x02 ? 1 : 0), \ + (byte & 0x01 ? 1 : 0) + +bool decodeOlimpiaMaestro(byte *bytes, int byteCount) +{ + // If this looks like an Olimpia code... + if (byteCount == 11 && bytes[0] == 0x5A) { + Serial.println(F("Looks like an Olimpia-Splendid Maestro protocol")); + + // Determine if the unit is off or in a specific mode (bits 8–10) + switch ((bytes[1] & 0x07)) { + case 0b001: + Serial.println(F("POWER ON")); + Serial.println(F("MODE COOL")); + break; + case 0b010: + Serial.println(F("POWER ON")); + Serial.println(F("MODE HEAT")); + break; + case 0b011: + Serial.println(F("POWER ON")); + Serial.println(F("MODE DRY")); + break; + case 0b100: + Serial.println(F("POWER ON")); + Serial.println(F("MODE FAN")); + break; + case 0b101: + Serial.println(F("POWER ON")); + Serial.println(F("MODE AUTO")); + break; + case 0b000: + Serial.println(F("POWER OFF")); + break; + default: + Serial.println(F("MODE UNKNOWN")); + } + + // Temperature (bits 72–76) + Serial.print(F("Temperature: ")); + if (bytes[9] & 0x20) { + uint8_t encodedTemp = ((bytes[9] & 0x1F)); + Serial.print(((((float) encodedTemp) / 2) + 15) * (1.8) + 32); + Serial.println(F("°F")); + } else { + uint8_t encodedTemp = ((bytes[9] & 0x1F)); + Serial.print((((float) encodedTemp) / 2) + 15); + Serial.println(F("°C")); + } + + // Fan speed + switch ((bytes[1] & 0x18) >> 3) { + case 0b00: + Serial.println(F("FAN: LOW")); + break; + case 0b01: + Serial.println(F("FAN: MEDIUM")); + break; + case 0b10: + Serial.println(F("FAN: HIGH")); + break; + case 0b11: + Serial.println(F("FAN: AUTO")); + break; + } + + // Other flags + Serial.print(F("FLAGS: ")); + if (bytes[1] & 0x04) Serial.print(F("FLAP SWING ")); + if (bytes[7] & 0x04) Serial.print(F("ECONO ")); + if (bytes[1] & 0x02) Serial.print(F("LOW NOISE ")); + Serial.println(); + + // Check if the checksum matches + byte checksum = 0x00; + + for (int x = 0; x < 10; x++) { + checksum += bytes[x]; + } + + if ( bytes[10] == checksum ) { + Serial.println(F("Checksum matches")); + } else { + Serial.print(F("Checksum does not match.\nCalculated | Received\n ")); + Serial.printf(BYTETOBINARYPATTERN, BYTETOBINARY(checksum)); + Serial.print(F(" | ")); + Serial.printf(BYTETOBINARYPATTERN, BYTETOBINARY(bytes[10])); + Serial.println(); + } + + return true; + } + + return false; +} diff --git a/rawirdecode/PanasonicCPK.cpp b/rawirdecode/PanasonicCPK.cpp new file mode 100644 index 0000000..c00e50b --- /dev/null +++ b/rawirdecode/PanasonicCPK.cpp @@ -0,0 +1,63 @@ +#include + +bool decodePanasonicCKP(byte *bytes, int byteCount) +{ + // If this looks like a Panasonic CKP code... + if ( byteCount == 16 && bytes[10] == 0x36 ) { + Serial.println(F("Looks like a Panasonic CKP protocol")); + + switch (bytes[2] & 0x07) + { + case 0x06: + Serial.println(F("AUTO")); + break; + case 0x04: + Serial.println(F("HEAT")); + break; + case 0x02: + Serial.println(F("COOL")); + break; + case 0x03: + Serial.println(F("DRY")); + break; + case 0x01: + Serial.println(F("FAN")); + break; + } + + if ((bytes[2] & 0x08) != 0x08) + { + Serial.println(F("POWER SWITCH")); + } + + switch (bytes[0] & 0xF0) + { + case 0xF0: + Serial.println(F("FAN AUTO")); + break; + case 0x20: + Serial.println(F("FAN 1")); + break; + case 0x30: + Serial.println(F("FAN 2")); + break; + case 0x40: + Serial.println(F("FAN 3")); + break; + case 0x50: + Serial.println(F("FAN 4")); + break; + case 0x60: + Serial.println(F("FAN 5")); + break; + } + + Serial.print(F("Temperature: ")); + Serial.println((bytes[0] & 0x0F) + 15); + + return true; + } + + return false; +} + diff --git a/rawirdecode/PanasonicCS.cpp b/rawirdecode/PanasonicCS.cpp new file mode 100644 index 0000000..8d95498 --- /dev/null +++ b/rawirdecode/PanasonicCS.cpp @@ -0,0 +1,175 @@ +/** + * Code based on examples + * Checked with Panasonic Inverter Air Conditioner CS-RE18GKE indoor and CU-RE18GKE outdoor split unit + * Model known as WA-18HP/I/ST (year 2007-2008) + * Remote: A75C3010 + * + * designed with help off http://www.instructables.com/id/Reverse-engineering-of-an-Air-Conditioning-control/ + * + */ +#include + +bool decodePanasonicCS(byte *bytes, int byteCount) +{ + // Test if looks like Pansonic code + // check via byteCount and fixed values + if (byteCount == 27 && bytes[10 == 0xE0]){ // I just picked a value here + Serial.println(F("Look like a Panasonic CS protocol")); + + // check on-off + // 13th byte from start, first byte with info + // this is not a toggle, but a state + if ((bytes[13] & 0x01) == 0x01) { // check if the right bit set + Serial.println(F("Powering ON!")); + } else { + Serial.println(F("Powering OFF")); + } + + // check mode + // if this byte is used for other things, first mask the correct bits + Serial.print(F("Mode: ")); + switch ((bytes[13]) & 0xF0){ // masks the first 4 bits 1111 0000 + case 0x00: + Serial.println(F("Auto")); + break; + case 0x40: + Serial.println(F("Heat")); + break; + case 0x30: + Serial.println(F("Cool")); + break; + case 0x20: + Serial.println(F("Dry")); + break; + default: + Serial.println(F("Error")); + break; + } + // check temp + Serial.print(F("Temperature: ")); + byte temp = (bytes[14] & 0x1E); + temp = temp >> 1; + Serial.println((temp) + 16); // masks the middle 5 bits: 0x1E = 0001 1110 + + // check fanspeed + Serial.print(F("Fan speed: ")); + switch (bytes[16] & 0xF0){ // 0xF0 = 1111 0000 eerste 4 bits + case 0xA0: + Serial.println(F("FAN AUTO")); + break; + case 0x30: + Serial.println(F("FAN 1")); + break; + case 0x40: + Serial.println(F("FAN 2")); + break; + case 0x50: + Serial.println(F("FAN 3")); + break; + case 0x60: + Serial.println(F("FAN 4")); + break; + case 0x70: + Serial.println(F("FAN 5")); + break; + default: + Serial.println(F("Error")); + break; + } + + // check vertical swing + Serial.print(F("Vertical swing: ")); + switch (bytes[16] & 0x0F){ // 0x0F = 0000 1111 + case 0x0F: + Serial.println(F("AUTO")); + break; + case 0x01: + Serial.println(F("Straight")); + break; + case 0x02: + Serial.println(F("Down 1")); + break; + case 0x03: + Serial.println(F("Down 2")); + break; + case 0x04: + Serial.println(F("Down 3")); + break; + case 0x05: + Serial.println(F("Down 4")); + break; + default: + Serial.println(F("Error")); + break; + } + + // check horizontal swing + Serial.print(F("Horizontal swing: ")); + switch (bytes[17] & 0x0F){ // 0x0F = 0000 1111 + case 0x0D: + Serial.println(F("AUTO")); + break; + case 0x06: + Serial.println(F("Center")); + break; + case 0x09: + Serial.println(F("Left")); + break; + case 0x0A: + Serial.println(F("Left center")); + break; + case 0x0B: + Serial.println(F("Right center")); + break; + case 0x0C: + Serial.println(F("Right")); + break; + default: + Serial.println(F("Error")); + break; + } + + // timer A (start) + Serial.print(F("Timer A active: ")); + if((bytes[13] & 0x02) == 0x02){ //check if second bit in byte is active + Serial.println(F("Yes")); + } else { + Serial.println(F("No")); + } + + // timer B (end) + Serial.print(F("Timer B active: ")); + if((bytes[13] & 0x04) == 0x04){ //check if third bit in byte is active + Serial.println(F("Yes")); + } else { + Serial.println(F("No")); + } + // no need to investigate actual timings if you're going to automate :-) + + // checksum algorithm + // Assumption: bitwise sum of payload bytes (so ignore header, and last byte) + byte checksum = 0x06; // found differce, so start with 0x06 + Serial.print(F("Add bytes: ")); + for (int i = 13; i < 26; i++){ + if (bytes[i] < 0x10) { + Serial.print(F("0")); + } + Serial.print(bytes[i],HEX); + Serial.print(F("+")); + checksum = checksum + bytes[i]; + } + Serial.println(F(".")); + Serial.print(F("Checksum Truncate: ")); + checksum = (checksum & 0xFF); // mask out only first byte) + Serial.println(checksum,HEX); + + if (checksum == bytes[26]){ + Serial.println(F("----- Checksum OK ------")); + } + + // good end + return true; + } + + return false; +} diff --git a/rawirdecode/Philco.cpp b/rawirdecode/Philco.cpp new file mode 100644 index 0000000..3110fd7 --- /dev/null +++ b/rawirdecode/Philco.cpp @@ -0,0 +1,331 @@ +#include + +// Philco with remote KT-L12010C + +bool decodePhilco(byte *bytes, int byteCount) +{ + if (byteCount == 21 && bytes[0] == 0x83 && bytes[1] == 0x06) { + Serial.println(F("Looks like a Philco protocol")); + + if (bytes[2] & 0x04 && bytes[15] == 0x01) { + Serial.println(F("POWER ON")); + } + + { + Serial.print(F("Current time: ")); + Serial.print(bytes[6] & 0x5F); + Serial.print(F(":")); + Serial.println(bytes[7]); + } + + // Operating mode + if((bytes[4] & 0xF) == 0x02) { + switch (bytes[13]) { + case 0x07: + Serial.println(F("MODE COOL")); + break; + case 0xF1: + Serial.println(F("MODE DRY")); + break; + case 0xF7: + Serial.println(F("MODE FAN")); + break; + case 0xD2: + Serial.println(F("MODE HEAT")); + break; + } + } else if((bytes[3] & 0xF) == 0x1) { + Serial.println(F("MODE SMART")); + + if((bytes[2] & 0xF0) == 0x00) { + Serial.println(F("SMART: --")); + } else if((bytes[2] & 0xF0) == 0x10 && bytes[16] == 0x00) { + Serial.println(F("SMART: 1")); + } else if((bytes[2] & 0xF0) == 0x50 && bytes[16] == 0x00) { + Serial.println(F("SMART: -1")); + } else if((bytes[2] & 0xF0) == 0x20) { + switch (bytes[16]) { + case 0x00: + Serial.println(F("SMART: 2")); + break; + case 0x04: + Serial.println(F("SMART: 3")); + break; + case 0x08: + Serial.println(F("SMART: 4")); + break; + case 0x0C: + Serial.println(F("SMART: 5")); + break; + case 0x10: + Serial.println(F("SMART: 6")); + break; + case 0x14: + Serial.println(F("SMART: 7")); + break; + } + } else if((bytes[2] & 0xF0) == 0x60) { + switch (bytes[16]) { + case 0x00: + Serial.println(F("SMART: -2")); + break; + case 0x04: + Serial.println(F("SMART: -3")); + break; + case 0x08: + Serial.println(F("SMART: -4")); + break; + case 0x0C: + Serial.println(F("SMART: -5")); + break; + case 0x10: + Serial.println(F("SMART: -6")); + break; + case 0x14: + Serial.println(F("SMART: -7")); + break; + } + } + } + + if (bytes[3] == 0x73) { + Serial.println(F("MODE DRY")); + + if((bytes[2] & 0xF0) == 0x00) { + Serial.println(F("DRY: --")); + } else if((bytes[2] & 0xF0) == 0x10 && bytes[16] == 0x00) { + Serial.println(F("DRY: 1")); + } else if((bytes[2] & 0xF0) == 0x50 && bytes[16] == 0x00) { + Serial.println(F("DRY: -1")); + } else if((bytes[2] & 0xF0) == 0x20) { + switch (bytes[16]) { + case 0x00: + Serial.println(F("DRY: 2")); + break; + case 0x04: + Serial.println(F("DRY: 3")); + break; + case 0x08: + Serial.println(F("DRY: 4")); + break; + case 0x0C: + Serial.println(F("DRY: 5")); + break; + case 0x10: + Serial.println(F("DRY: 6")); + break; + case 0x14: + Serial.println(F("DRY: 7")); + break; + } + } else if((bytes[2] & 0xF0) == 0x60) { + switch (bytes[16]) { + case 0x00: + Serial.println(F("DRY: -2")); + break; + case 0x04: + Serial.println(F("DRY: -3")); + break; + case 0x08: + Serial.println(F("DRY: -4")); + break; + case 0x0C: + Serial.println(F("DRY: -5")); + break; + case 0x10: + Serial.println(F("DRY: -6")); + break; + case 0x14: + Serial.println(F("DRY: -7")); + break; + } + } + } + + // Temperature target + if ((bytes[3] & 0xF) == 0x2 || (bytes[3] & 0xF) == 0x0) { + Serial.print(F("Selected temperature: ")); + Serial.print((bytes[3] >> 4) + 16); + Serial.println(F("°")); + } + + // Temperature real + if(bytes[11] == 0x80) { + Serial.print(F("Current temperature: ")); + Serial.print((bytes[12] & 0x0F) + 16); + Serial.println(F("°")); + } + + // Fan speed + switch (bytes[2] & 0x03) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x03: + Serial.println(F("FAN: 1 (low)")); + break; + case 0x02: + Serial.println(F("FAN: 2 (medium)")); + break; + case 0x01: + Serial.println(F("FAN: 3 (high)")); + break; + } + + if (bytes[2] == 0x03 && bytes[14] == 0x04 && bytes[15] == 0x0B) { + Serial.println(F("QUIET: ON")); + } else if (bytes[14] == 0x00 && bytes[15] == 0x0B) { + Serial.println(F("QUIET: OFF)")); + } + + // Air direction + if(bytes[8] == 0x80 && bytes[15] == 0x08) { + Serial.println(F("FIN: left to right")); + } + + if(bytes[8] == 0x40 && bytes[15] == 0x07) { + Serial.println(F("FIN: up and down")); + } + + if((bytes[2] & 0x0F) == 0x0B) { + // Sleep mode + switch (bytes[14]) { + case 0x00: + Serial.println(F("SLEEP: 1")); + break; + case 0x40: + Serial.println(F("SLEEP: 2")); + break; + case 0x80: + Serial.println(F("SLEEP: 3")); + break; + case 0xC0: + Serial.println(F("SLEEP: 4")); + break; + } + } else { + Serial.println(F("SLEEP: OFF")); + } + + // Turbo mode + Serial.print(F("TURBO: ")); + switch (bytes[5]) { + case 0x90: + case 0x10: + Serial.println(F("ON")); + break; + case 0x00: + Serial.println(F("OFF")); + break; + } + + Serial.print(F("ECONOMY: ")); + switch (bytes[14]) { + case 0x20: + Serial.println(F("ON")); + break; + case 0x00: + Serial.println(F("OFF")); + break; + } + + // Ifeel mode + Serial.print(F("IFEEL: ")); + switch (bytes[11]) { + case 0x80: + Serial.println(F("ON")); + break; + default: + Serial.println(F("OFF")); + break; + } + + //Dimmer + if ((bytes[6] & 0x20)) { + Serial.println(F("DIMMER: ON")); + } else { + Serial.println(F("DIMMER: OFF")); + } + + // Timer + byte hoursOff = bytes[8]; + byte minutesOff = bytes[9] & 0x7F; + byte hoursOn = bytes[10]; + byte minutesOn = bytes[11] & 0x7F; + + if (bytes[9] & 0x80 && bytes[15] == 0x05) { + { + Serial.print(F("Activate TIMER ON at ")); + Serial.print(hoursOn); + Serial.print(F(":")); + Serial.println(minutesOn); + } + + { + Serial.print(F("TIMER OFF at ")); + Serial.print(hoursOff); + Serial.print(F(":")); + Serial.println(minutesOff); + } + } else if (bytes[15] == 0x1D) { + { + Serial.print(F("Activate TIMER OFF at ")); + Serial.print(hoursOff); + Serial.print(F(":")); + Serial.println(minutesOff); + } + + { + Serial.print(F("TIMER ON at ")); + Serial.print(hoursOn); + Serial.print(F(":")); + Serial.println(minutesOn); + } + } else if (bytes[10] == 0x00 && bytes[11] == 0x80 && bytes[15] == 0x05) { + Serial.println(F("Deactivate TIMER ON")); + } else if (bytes[8] == 0x00 && bytes[9] == 0x80 && bytes[15] == 0x1D) { + Serial.println(F("Deactivate TIMER OFF")); + } + + { + byte originalChecksum = bytes[13]; + byte calcuatedChecksum = 0x00; + + for (int i = 2; i < 13; i++) { + calcuatedChecksum ^= bytes[i]; + } + + Serial.print(F("First checksum '0x")); + Serial.print(calcuatedChecksum, HEX); + + if ( originalChecksum == calcuatedChecksum ) { + Serial.println(F("' matches")); + } else { + Serial.print(F("' does not match 0x")); + Serial.println(originalChecksum, HEX); + } + } + + { + byte originalChecksum = bytes[20]; + byte calcuatedChecksum = 0x00; + + for (int i = 14; i < 20; i++) { + calcuatedChecksum ^= bytes[i]; + } + + Serial.print(F("Second checksum '0x")); + Serial.print(calcuatedChecksum, HEX); + + if ( originalChecksum == calcuatedChecksum ) { + Serial.println(F("' matches")); + } else { + Serial.print(F("' does not match 0x")); + Serial.println(originalChecksum, HEX); + } + } + return true; + } + + return false; +} diff --git a/rawirdecode/Samsung.cpp b/rawirdecode/Samsung.cpp new file mode 100644 index 0000000..890d737 --- /dev/null +++ b/rawirdecode/Samsung.cpp @@ -0,0 +1,203 @@ +#include + +// Samsung with remote ARH-465 or remote ARH-1362 + +bool decodeSamsung(byte *bytes, int byteCount) +{ + // If this looks like a Samsung code... + if (bytes[0] == 0x02 + && (byteCount == 14 && bytes[1] == 0x92) + && bytes[2] == 0x0F) + { + Serial.println(F("Looks like a short 14 bytes Samsung protocol")); + + Serial.println(F("POWER ON")); + + // Operating mode + switch (bytes[12] & 0xF0) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x10: + Serial.println(F("MODE COOL")); + break; + case 0x20: + Serial.println(F("MODE DRY")); + break; + case 0x30: + Serial.println(F("MODE FAN")); + break; + case 0x40: + Serial.println(F("MODE HEAT")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bytes[11] >> 4) + 16); + + // Fan speed + switch (bytes[12] & 0x0F) { + case 0x01: + Serial.println(F("FAN: AUTO")); + break; + case 0x05: + Serial.println(F("FAN: 1 (low)")); + break; + case 0x09: + Serial.println(F("FAN: 2 (medium)")); + break; + case 0x0B: + Serial.println(F("FAN: 3 (high)")); + break; + case 0x0F: + Serial.println(F("FAN: 4")); + break; + } + + // Airflow mode + Serial.print(F("Airflow: ")); + switch (bytes[9] & 0xF0) { + case 0xA0: + Serial.println(F("ON")); + break; + case 0xF0: + Serial.println(F("OFF")); + break; + } + + // Turbo mode + Serial.print(F("Turbo mode: ")); + switch (bytes[10] & 0x0F) { + case 0x07: + Serial.println(F("ON")); + break; + case 0x01: + Serial.println(F("OFF")); + break; + } + + // Check if the checksum matches + byte originalChecksum = bytes[8]; + byte checksum = 0x00; + + // Calculate the byte 8 checksum + // Count the number of ONE bits + bytes[9] &= 0b11111110; + for (uint8_t j=9; j<13; j++) { + uint8_t samsungByte = bytes[j]; + for (uint8_t i=0; i<8; i++) { + if ( (samsungByte & 0x01) == 0x01 ) { + checksum++; + } + samsungByte >>= 1; + } + } + + checksum = 28 - checksum; + checksum <<= 4; + checksum |= 0x02; + + Serial.print(F("Checksum '0x")); + Serial.print(checksum, HEX); + + if ( originalChecksum == checksum ) { + Serial.println(F("' matches")); + } else { + Serial.print(F("' does not match 0x")); + Serial.println(originalChecksum, HEX); + } + return true; + } + + + if (bytes[0] == 0x02 + && ((byteCount == 21 && bytes[1] == 0xB2) || (byteCount == 21 && bytes[1] == 0x92)) + && bytes[2] == 0x0F) + { + Serial.println(F("Looks like a 21 bytes long Samsung protocol")); + + // Power mode + if (byteCount == 21 && bytes[1] == 0xB2) + { + Serial.println(F("POWER OFF")); + } + else + Serial.println(F("POWER ON")); + + // Operating mode | fan speed auto + switch (bytes[19] & 0xF0) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x10: + Serial.println(F("MODE COOL")); + break; + case 0x20: + Serial.println(F("MODE DRY")); + break; + case 0x40: + Serial.println(F("MODE HEAT")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bytes[18] >> 4) + 16); + + // Fan speed + switch (bytes[19] & 0x0F) { + case 0x0D: + case 0x01: + Serial.println(F("FAN: AUTO")); + break; + case 0x05: + Serial.println(F("FAN: 1 (low)")); + break; + case 0x09: + Serial.println(F("FAN: 2 (medium)")); + break; + case 0x0B: + Serial.println(F("FAN: 3 (high)")); + break; + } + + // calculate the checksum + uint8_t checksum = 0; + byte originalChecksum = bytes[15]; + + // Calculate the byte 15 checksum + // Count the number of ONE bits on message uint8_ts 15-20 + for (uint8_t j=16; j<20; j++) { + uint8_t Samsungbyte = bytes[j]; + for (uint8_t i=0; i<8; i++) { + if ( (Samsungbyte & 0x01) == 0x01 ) { + checksum++; + } + Samsungbyte >>= 1; + } + } + + // Transform the number of ONE bits to the actual checksum + checksum = 28 - checksum; + checksum <<= 4; + checksum |= (byteCount == 21 && bytes[1] == 0xB2) ? 0x22 : 0x02; + + // incredible hack if power off and temp = 20 and mode heat or cool or dry + if (byteCount == 21 && bytes[1] == 0xB2 && bytes[18] == 0x40 && ((bytes[19] & 0xF0) == 0x40 || (bytes[19] & 0xF0) == 0x20 || (bytes[19] & 0xF0) == 0x10)) + checksum = 0x02; + + Serial.print(F("Checksum '0x")); + Serial.print(checksum, HEX); + + if ( originalChecksum == checksum ) { + Serial.println(F("' matches")); + } else { + Serial.print(F("' does not match 0x")); + Serial.println(originalChecksum, HEX); + } + return true; + } + + return false; +} diff --git a/rawirdecode/Sharp.cpp b/rawirdecode/Sharp.cpp new file mode 100644 index 0000000..dd7fdc7 --- /dev/null +++ b/rawirdecode/Sharp.cpp @@ -0,0 +1,83 @@ +#include + +bool decodeSharp(byte *bytes, int byteCount) +{ + // If this looks like a Sharp code... + if ( byteCount == 13 && bytes[0] == 0xAA && bytes[1] == 0x5A && bytes[2] == 0xCF ) { + Serial.println(F("Looks like a Sharp protocol")); + + // Power mode + switch (bytes[5]) { + case 0x21: + Serial.println(F("POWER OFF")); + break; + case 0x31: + Serial.println(F("POWER ON")); + break; + } + + // Operating mode + switch (bytes[6] & 0x0F) { + case 0x01: + if (bytes[4] == 0x00) { + Serial.println(F("MODE MAINTENANCE HEAT")); + } else { + Serial.println(F("MODE HEAT")); + } + break; + case 0x02: + Serial.println(F("MODE COOL")); + break; + case 0x03: + Serial.println(F("MODE DRY")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + if (bytes[4] == 0x00) { + Serial.println(F("10")); + } else { + Serial.println(bytes[4] + 17); + } + + switch (bytes[6] & 0xF0) { + case 0x20: + Serial.println(F("FAN: AUTO")); + break; + case 0x30: + Serial.println(F("FAN: 1")); + break; + case 0x50: + Serial.println(F("FAN: 2")); + break; + case 0x70: + Serial.println(F("FAN: 3")); + break; + } + + // Check if the checksum matches + byte checksum = 0x00; + + for (byte i = 0; i < 12; i++) { + checksum ^= bytes[i]; + } + + checksum ^= bytes[12] & 0x0F; + checksum ^= (checksum >> 4); + checksum &= 0x0F; + + Serial.print(F("Checksum '0x")); + Serial.print(checksum, HEX); + + if ( ((bytes[12] & 0xF0) >> 4) == checksum ) { + Serial.println(F("' matches")); + } else { + Serial.print(F(" does not match ")); + Serial.println(((bytes[12] & 0xF0) >> 4), HEX); + } + return true; + } + + return false; +} diff --git a/rawirdecode/Toshiba.cpp b/rawirdecode/Toshiba.cpp new file mode 100644 index 0000000..27acc7a --- /dev/null +++ b/rawirdecode/Toshiba.cpp @@ -0,0 +1,78 @@ +#include + +byte bitReverse(byte x); + +// Toshiba RAS-10PKVP-ND + +bool decodeToshiba(byte *bytes, int byteCount) +{ + // If this looks like a Toshiba code... + if ( byteCount == 9 && bytes[0] == 0x4F && bytes[1] == 0xB0 && bytes[2] == 0xC0 && bytes[3] == 0x3F && bytes[4] == 0x80 ) { + Serial.println(F("Looks like a Toshiba protocol")); + + // Power & operating mode + switch (bytes[6] & 0xF0) { + case 0x00: + Serial.println(F("MODE AUTO")); + break; + case 0x80: + Serial.println(F("MODE COOL")); + break; + case 0x40: + Serial.println(F("MODE DRY")); + break; + case 0xC0: + Serial.println(F("MODE HEAT")); + break; + case 0xE0: + Serial.println(F("POWER OFF")); + break; + } + + // Temperature + Serial.print(F("Temperature: ")); + Serial.println((bitReverse((bytes[5] & 0x07)) >> 4) + 17); + + // Fan speed + switch (bytes[6] & 0x07) { + case 0x00: + Serial.println(F("FAN: AUTO")); + break; + case 0x02: + Serial.println(F("FAN: 1")); + break; + case 0x06: + Serial.println(F("FAN: 2")); + break; + case 0x01: + Serial.println(F("FAN: 3")); + break; + case 0x05: + Serial.println(F("FAN: 4")); + break; + case 0x03: + Serial.println(F("FAN: 5")); + break; + } + + // Check if the checksum matches + byte checksum = 0x00; + + for (byte i = 0; i < 8; i++) { + checksum ^= bytes[i]; + } + + Serial.print(F("Checksum '0x")); + Serial.print(checksum, HEX); + + if ( bytes[8] == checksum ) { + Serial.println(F("' matches")); + } else { + Serial.print(F(" does not match ")); + Serial.println(bytes[8], HEX); + } + return true; + } + + return false; +} diff --git a/rawirdecode/ZHJG01Remote.cpp b/rawirdecode/ZHJG01Remote.cpp new file mode 100644 index 0000000..6f48f06 --- /dev/null +++ b/rawirdecode/ZHJG01Remote.cpp @@ -0,0 +1,208 @@ + +/******************************************************************************** + * Airconditional remote control decoder for: + * + * ZH/JG-01 Remote control https://www.google.com/search?q=zh/JG-01 + * + * The ZH/JG-01 remote control is used for many locally branded Split airconditioners, + * so it is better to name this protocol by the name of the protocol rather then the + * name of the Airconditioner. For this project I used a TACHIAIR airconditioner. + * + * For airco-brands: + * Tachiair + * Chigo + * Etc. + * + *********************************************************************************** + * SUMMARY FUNCTIONAL DESCRIPTION + ********************************************************************************** + * The remote sends a 6 Byte message which contains all possible settings every + * time. + * + * Every UNeven Byte (01,03,05,07 and 09) hold command data + * Every EVEN Byte (00,02,04,06,08 and 10) hold a checksum of the corresponding + * command by inverting the bits, for example: + * + * The checksum byte[0] = 0x2A = B0010 1010 + * The identifier byte[1] = 0xD5 = B1101 0101 + * + * So, you can check the message by: + * - inverting the bits of the checksum byte with the corresponding command, they + * should be the same, or + * - Summing up the checksum byte and the corresponding command, + * they should always add up to 0xFF = B11111111 = 255 + * + * TIMINGS + * (Only ZH/JG-01 (option 6) work) + * Space: Not used + * Header Mark: 6550 us + * Header Space: 0 us + * Bit Mark: 560 us + * Zero Space: 1530 us + * One Space: 3750 us + * + * ****************************************************************************** + * Written by: Abílio Costa + * Date: 2023-07-03 + * Version: 1.0 + *******************************************************************************/ + +#include + +byte reverseByte(byte input) { + byte output = 0; + for (int i = 0; i < 8; ++i) { + output <<= 1; + output |= (input & 1); + input >>= 1; + } + return output; +} + +bool decodeZHJG01remote(byte *bytes, int byteCount) +{ + +/******************************************************************************** + * Is this a ZH/JG-01 Remote? + * Message should by 6 bytes, 52 symbols, with valid checksum. + * + *******************************************************************************/ + + if (byteCount != 6) { + return false; + } + + bool checksum = true; + for (int i = 0; i < 6; i+=2) { + checksum &= (bytes[i] + bytes[i+1] == 255); + } + + if (!checksum) { + return false; + } + + Serial.println(F("Looks like a ZH/JG-01 remote control protocol")); + +/******************************************************************************** + * Byte[1]: Turbo, Eco, Fan, Vertical Swing + * TURBO ON: B1x0xxxxx + * ECO ON: B1x1xxxxx + * TURBO/ECO OFF: B0xxxxxxx + * FAN1: Bx11xxxxx + * FAN2: Bx10xxxxx + * FAN3: Bx01xxxxx + * FAN AUTO: Bx00xxxxx + * VERTICAL FIXED: Bxxx10xxx + * VERTICAL SWING: Bxxx01xxx + * VERTICAL WIND: Bxxx00xxx + *******************************************************************************/ + // TURBO ON/OFF + Serial.print(F("bytes[1] bit0+2 Turbo: ")); + switch (bytes[1]& B10100000) { + case B10000000: + Serial.println(F("On")); + break; + default: + Serial.println(F("Off")); + } + + // FAN ON/OFF + Serial.print(F("bytes[1] bit0+2 Eco: ")); + switch (bytes[1]& B10100000) { + case B10100000: + Serial.println(F("On")); + break; + default: + Serial.println(F("Off")); + } + + Serial.print(F("bytes[1] bit1-2 Fan Speed: ")); + switch (bytes[1] & B01100000) { + case B01100000: + Serial.println(F("1")); + break; + case B01000000: + Serial.println(F("2")); + break; + case B00100000: + Serial.println(F("3")); + break; + case B00000000: + Serial.println(F("Auto")); + break; + default: + Serial.println(F("Unknown")); + } + + // Vertical air swing + Serial.print(F("bytes[1] bit3-4 Vertical: ")); + switch (bytes[1] & B00011000) { + case B00001000: + Serial.println(F("Swing")); + break; + case B00000000: + Serial.println(F("Wind")); + break; + case B00010000: + Serial.println(F("Fixed")); + break; + default: + Serial.println(F("Unknown")); + } + +/******************************************************************************** + * Byte[3]: Temp, Power, Mode + * TEMP: Bttttxxxx + * POWER ON: Bxxxx1xxx + * POWER OFF: Bxxxx0xxx + * MODE HEAT: Bxxxxx100 + * MODE VENT: Bxxxxx011 + * MODE DRY: Bxxxxx010 + * MODE COOL: Bxxxxx001 + * MODE AUTO: Bxxxxx000 + *******************************************************************************/ + + // TEMPERATURE + byte tempByte = 0; + Serial.print("bytes[3] Temperature: "); + Serial.print(((bytes[3] >> 4) & 0b1111) + 17); + Serial.println("°C"); + + // POWER ON/OFF + Serial.print(F("bytes[3] bit4 Power: ")); + switch (bytes[3] & B00001000) { + case B00000000: + Serial.println(F("Off")); + break; + case B00001000: + Serial.println(F("On")); + break; + default: + Serial.println(F("Unknown")); + } + + // MODE + Serial.print(F("bytes[3] bit5-7 Mode: ")); + switch (bytes[3] & B00000111) { + case B00000000: + Serial.println(F("Auto")); + break; + case B00000001: + Serial.println(F("Cool")); + break; + case B00000011: + Serial.println(F("Ventilate")); + break; + case B00000010: + Serial.println(F("Dry")); + break; + case B00000100: + Serial.println(F("Heat")); + break; + default: + Serial.println("Unknown"); + } + + return true; +} + diff --git a/rawirdecode/ZHLT01Remote.cpp b/rawirdecode/ZHLT01Remote.cpp new file mode 100644 index 0000000..5361775 --- /dev/null +++ b/rawirdecode/ZHLT01Remote.cpp @@ -0,0 +1,375 @@ + +/******************************************************************************** + * Airconditional remote control decoder for: + * + * ZH/LT-01 Remote control https://www.google.com/search?q=zh/lt-01 + * + * The ZH/LT-01 remote control is used for many locally branded Split airconditioners, + * so it is better to name this protocol by the name of the protocol rather then the + * name of the Airconditioner. For this project I used a Eurom-airconditioner, which is + * Dutch-branded and sold in the Netherlands at Hornbach. + * + * For airco-brands: + * Eurom + * Chigo + * Tristar + * Tecnomaster + * Elgin + * Geant + * Tekno + * Topair + * Proma + * Sumikura + * JBS + * Turbo Air + * Nakatomy + * Celestial Air + * Ager + * Blueway + * Airlux + * Etc. + * + *********************************************************************************** + * SUMMARY FUNCTIONAL DESCRIPTION + ********************************************************************************** + * The remote sends a 12 Byte message which contains all possible settings every + * time. + * + * Byte 11 (and 10) contain the remote control identifier and are always 0xD5 and + * 0x2A respectively for the ZH/LT-01 remote control. + * Every UNeven Byte (01,03,05,07 and 09) hold command data + * Every EVEN Byte (00,02,04,06,08 and 10) hold a checksum of the corresponding + * command-, or identifier-byte by inverting the bits, for example: + * + * The identifier byte[11] = 0xD5 = B1101 0101 + * The checksum byte[10] = 0x2A = B0010 1010 + * + * So, you can check the message by: + * - inverting the bits of the checksum byte with the corresponding command-, or + * identifier byte, they should me the same, or + * - Summing up the checksum byte and the corresponding command-, or identifier byte, + * they should always add up to 0xFF = B11111111 = 255 + * + * Control bytes: + * [01] - Timer (1-24 hours, Off) + * [03] - LAMP ON/OFF, TURBO ON/OFF, HOLD ON/OFF + * [05] - Indicates which button the user pressed on the remote control + * [07] - POWER ON/OFF, FAN AUTO/3/2/1, SLEEP ON/OFF, AIRFLOW ON/OFF, + * VERTICAL SWING/WIND/FIXED + * [09] - MODE AUTO/COOL/VENT/DRY/HEAT, TEMPERATURE (16 - 32°C) + * + * TIMINGS + * (Both Hyundai (option 4) and Samsung (option 5) timings work) + * Space: Not used + * Header Mark: 6200 us + * Header Space: 7500 us + * Bit Mark: 475 us + * Zero Space: 525 us + * One Space: 1650 us + * + * ****************************************************************************** + * Written by: Marcel van der Kooij + * Date: 2018-06-07 + * Version: 1.0 + *******************************************************************************/ + +#include + +bool decodeZHLT01remote(byte *bytes, int byteCount) +{ + +/******************************************************************************** + * Is this a ZH/LT-01 Remote? + * Message should by 12 bytes, 100 symbols. + * Remote identifier + * Byte[11]: Always 0xD5, which makes Byte[10] always 0x2A (see checksum) + * + *******************************************************************************/ + + if (byteCount == 12 && bytes[10] == 0x2A && bytes[11] == 0xD5) { + Serial.println(F("Looks like a ZH/LT-01 remote control protocol")); + +/******************************************************************************* + * Byte[01]: Timer (1-24 hours, Not used, Off, Changed, Set) + * TIMER ON/OFF: B1xxxxxxx + * TIMER CHANGED: Bxx1xxxxx + * + * Number of hours is determined by bit0-4: + * 0x01 = 1 hour + * 0x18 = 24 hours + *******************************************************************************/ + + Serial.print(F("bytes[1] bit5-7 Timer: ")); + switch (bytes[1] & B10100000) { + case 0x00: + Serial.println(F("Not used")); + break; + case B00100000: + Serial.println(F("Off")); + break; + case B10000000: + Serial.println(F("Set")); + break; + case B10100000: + Serial.println(F("Changed")); + break; + default: + Serial.println("Unknown"); + } + + Serial.print(F("bytes[1] bit0-4 Hours: ")); + Serial.println(bytes[1] & B00011111); + + + +/******************************************************************************** + * Byte[03]: LAMP, TURBO, HOLD ON/OFF + * LAMP ON: Bxxxxxxx1 (bit0) + * LAMP OFF: Bxxxxxxx0 + * HOLD ON: Bxxxxx1xx (bit2) + * HOLD ON: Bxxxxx0xx + * TURBO ON: Bxxxx1xxx (bit3) + * TURBO ON: Bxxxx0xxx + *******************************************************************************/ + // LAMP ON/OFF + Serial.print(F("bytes[3] bit0 Lamp: ")); + switch (bytes[3]& B00000001) { + case B00000000: + Serial.println(F("Off")); + break; + case B00000001: + Serial.println(F("On")); + break; + default: + Serial.println(F("Unknown")); + } + + // HOLD ON/OFF + Serial.print(F("bytes[3] bit2 Hold: ")); + switch (bytes[3]& B00000100) { + case B00000000: + Serial.println(F("Off")); + break; + case B00000100: + Serial.println(F("On")); + break; + default: + Serial.println(F("Unknown")); + } + + // TURBO ON/OFF + Serial.print(F("bytes[3] bit3 Turbo: ")); + switch (bytes[3]& B00001000) { + case B00000000: + Serial.println(F("Off")); + break; + case B00001000: + Serial.println(F("On")); + break; + default: + Serial.println(F("Unknown")); + } + +/******************************************************************************** + * Byte[05]: Button pressed + * 0x00: POWER + * 0x01: MODE + * 0x02: Temperature Up + * 0x03: Temperature Down + * 0x04: Vertical Swing + * 0x05: FAN + * 0x06: TIMER + * 0x07: Horizontal swing + * 0x08: HOLD + * 0x09: SLEEP + * 0x0A: TURBO + * 0x0B: LAMP + *******************************************************************************/ + + Serial.print(F("bytes[5] Button pressed: ")); + switch (bytes[5]) { + case 0x00: + Serial.println(F("POWER")); + break; + case 0x01: + Serial.println(F("MODE")); + break; + case 0x02: + Serial.println(F("TEMP UP")); + break; + case 0x03: + Serial.println(F("TEMP DOWN")); + break; + case 0x04: + Serial.println(F("SWING (vertical)")); + break; + case 0x05: + Serial.println(F("FAN SPEED")); + break; + case 0x06: + Serial.println(F("TIMER")); + break; + case 0x07: + Serial.println(F("AIR FLOW (horizontal)")); + break; + case 0x08: + Serial.println(F("HOLD")); + break; + case 0x09: + Serial.println(F("SLEEP")); + break; + case 0x0A: + Serial.println(F("TURBO")); + break; + case 0x0B: + Serial.println(F("LAMP")); + break; + default: + Serial.println(F("Unknown")); + } + +/******************************************************************************** + * Byte[07]: POWER, FAN, SLEEP, HORIZONTAL, VERTICAL + * SLEEP ON: Bxxxxxxx1 (LSB) + * SLEEP OFF: Bxxxxxxx0 + * POWER ON: Bxxxxxx1x + * POWER OFF: Bxxxxxx0x + * VERTICAL SWING: Bxxxx01xx + * VERTICAL WIND: Bxxxx00xx + * VERTICAL FIXED: Bxxxx10xx + * HORIZONTAL SWING: Bxxx0xxxx + * HORIZONTAL OFF: Bxxx1xxxx + * FAN AUTO: Bx00xxxxx + * FAN3: Bx01xxxxx + * FAN2: Bx10xxxxx + * FAN1: Bx11xxxxx + *******************************************************************************/ + + // SLEEP ON/OFF + Serial.print(F("bytes[7] bit0 Sleep: ")); + switch (bytes[7]& B00000001) { + case B00000000: + Serial.println(F("Off")); + break; + case B00000001: + Serial.println(F("On")); + break; + default: + Serial.println(F("Unknown")); + } + + // POWER ON/OFF + Serial.print(F("bytes[7] bit1 Power: ")); + switch (bytes[7]& B00000010) { + case B00000000: + Serial.println(F("Off")); + break; + case B00000010: + Serial.println(F("On")); + break; + default: + Serial.println(F("Unknown")); + } + + // Vertical air swing + Serial.print(F("bytes[7] bit2-3 Vertical: ")); + switch (bytes[7] & B00001100) { + case B00000100: + Serial.println(F("Swing")); + break; + case 0x00: + Serial.println(F("Wind")); + break; + case B00001000: + Serial.println(F("Fixed")); + break; + default: + Serial.println(F("Unknown")); + } + + // Air flow (Horizontal) ON/OFF + Serial.print(F("bytes[7] bit4 Air flow: ")); + switch (bytes[7]& B00010000) { + case B00000000: + Serial.println(F("Swing")); + break; + case B00010000: + Serial.println(F("Fixed")); + break; + default: + Serial.println(F("Unknown")); + } + + // Fan Speed + Serial.print(F("bytes[7] bit5-6 Fan Speed: ")); + switch (bytes[7] & B01100000) { + case B00000000: + Serial.println(F("Auto")); + break; + case B00100000: + Serial.println(F("3")); + break; + case B01000000: + Serial.println(F("2")); + break; + case B01100000: + Serial.println(F("1")); + break; + default: + Serial.println(F("Unknown")); + } + +/******************************************************************************** + * Byte[09]: Mode, Temperature + * MODE AUTO: B000xxxxx + * MODE COOL: B001xxxxx + * MODE VENT: B011xxxxx + * MODE DRY: B010xxxxx + * MODE HEAT: B100xxxxx + * Temperature is determined by bit0-4: + * 0x00 = 16C + * 0x10 = 32C + *******************************************************************************/ + + // MODE + Serial.print(F("bytes[9] bit5-7 Mode: ")); + switch (bytes[9] & B11100000) { + case 0x00: + Serial.println(F("Auto")); + break; + case B00100000: + Serial.println(F("Cool")); + break; + case B01100000: + Serial.println(F("Ventilate")); + break; + case B01000000: + Serial.println(F("Dry")); + break; + case B10000000: + Serial.println(F("Heat")); + break; + default: + Serial.println("Unknown"); + } + + //Temperature + Serial.print("Bytes[9] Temperature: "); + Serial.print((bytes[9] & B00011111)+16); + Serial.println("°C"); + +// Checksum + bool checksum = true; + + for (int i = 0; i < 12; i+=2) { + checksum &= (bytes[i] + bytes[i+1] == 255); + } + + Serial.println(checksum?"Checksum: MATCHES":"Checksum: DOES NOT MATCH"); + + return true; + } + + return false; +} + diff --git a/rawirdecode/rawirdecode.ino b/rawirdecode/rawirdecode.ino index e493326..61009e1 100644 --- a/rawirdecode/rawirdecode.ino +++ b/rawirdecode/rawirdecode.ino @@ -1,14 +1,144 @@ +#include + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(ESP32) || defined(ESP8266) + #define MITSUBISHI_ELECTRIC + #define FUJITSU + #define MITSUBISHI_HEAVY + #define DAIKIN + #define SHARP_ + #define CARRIER + #define PANASONIC_CKP + #define PANASONIC_CS + #define HYUNDAI + #define OLIMPIA // try model choice 3 + #define GREE + #define GREE_YAC + #define FUEGO + #define TOSHIBA + #define NIBE + #define AIRWELL + #define HITACHI + #define SAMSUNG + #define BALLU + #define AUX + #define ZHLT01_REMOTE + #define ZHJG01_REMOTE + #define KY26_REMOTE +#else + // low-memory device, + // uncomment the define corresponding with your remote + //#define MITSUBISHI_ELECTRIC + //#define FUJITSU + //#define MITSUBISHI_HEAVY + //#define DAIKIN + //#define SHARP_ + //#define CARRIER + //#define PANASONIC_CKP + //#define PANASONIC_CS + //#define HYUNDAI + //#define OLIMPIA + //#define GREE + //#define GREE_YAC + //#define FUEGO + //#define TOSHIBA + //#define NIBE + //#define AIRWELL + //#define HITACHI + //#define SAMSUNG + //#define BALLU + //#define AUX + //#define ZHLT01_REMOTE + //#define PHILCO + //#define KY26_REMOTE +#endif + +#if !defined(MITSUBISHI_ELECTRIC)&&!defined(FUJITSU)&&!defined(MITSUBISHI_HEAVY)&&!defined(DAIKIN)&&!defined(SHARP_)&&!defined(CARRIER)&&!defined(PANASONIC_CKP)&&!defined(PANASONIC_CS)&&!defined(HYUNDAI)&&!defined(GREE)&&!defined(GREE_YAC)&&!defined(FUEGO)&&!defined(TOSHIBA)&&!defined(NIBE)&&!defined(AIRWELL)&&!defined(HITACHI)&&!defined(SAMSUNG)&&!defined(BALLU)&&!defined(AUX)&&!defined(ZHLT01_REMOTE)&&!defined(ZHJG01_REMOTE)&&!defined(PHILCO)&&!defined(KY26_REMOTE) + #error You must uncomment at least one brand define!! +#endif + + +#if defined(MITSUBISHI_ELECTRIC) + bool decodeMitsubishiElectric(byte *bytes, int byteCount); +#endif +#if defined(FUJITSU) + bool decodeFujitsu(byte *bytes, int byteCount); +#endif +#if defined(MITSUBISHI_HEAVY) + bool decodeMitsubishiHeavy(byte *bytes, int byteCount); +#endif +#if defined(DAIKIN) + bool decodeDaikin(byte *bytes, int byteCount); +#endif +#if defined(SHARP_) + bool decodeSharp(byte *bytes, int byteCount); +#endif +#if defined(CARRIER) + bool decodeCarrier(byte *bytes, int byteCount); +#endif +#if defined(PANASONIC_CKP) + bool decodePanasonicCKP(byte *bytes, int byteCount); +#endif +#if defined(PANASONIC_CS) + bool decodePanasonicCS(byte *bytes, int byteCount); +#endif +#if defined(HYUNDAI) + bool decodeHyundai(byte *bytes, int pulseCount); +#endif +#if defined(OLIMPIA) + bool decodeOlimpiaMaestro(byte *bytes, int pulseCount); +#endif +#if defined(GREE) or defined(GREE_YAC) + bool decodeGree(byte *bytes, int pulseCount); + bool decodeGree_YAC(byte *bytes, int pulseCount); +#endif +#if defined(FUEGO) + bool decodeFuego(byte *bytes, int byteCount); +#endif +#if defined(TOSHIBA) + bool decodeToshiba(byte *bytes, int byteCount); +#endif +#if defined(NIBE) + bool decodeNibe(byte *bytes, char* symbols, int bitCount); +#endif +#if defined(AIRWELL) + bool decodeAirwell(char* symbols, int bitCount); +#endif +#if defined(HITACHI) + bool decodeHitachi(byte *bytes, int byteCount); +#endif +#if defined(SAMSUNG) + bool decodeSamsung(byte *bytes, int byteCount); +#endif +#if defined(BALLU) + bool decodeBallu(byte *bytes, int byteCount); +#endif +#if defined(AUX) + bool decodeAUX(byte *bytes, int byteCount); +#endif +#if defined(ZHLT01_REMOTE) + bool decodeZHLT01remote(byte *bytes, int byteCount); +#endif +#if defined(ZHJG01_REMOTE) + bool decodeZHJG01remote(byte *bytes, int byteCount); +#endif +#if defined(PHILCO) + bool decodePhilco(byte *bytes, int byteCount); +#endif +#if defined(KY26_REMOTE) + bool decodeKY26Remote(byte *bytes, int byteCount); +#endif + /* Raw IR decoder sketch! - - This sketch/program uses the Arduno and a PNA4602 to + + This sketch/program uses the Arduno and a PNA4602 to decode IR received. This can be used to make a IR receiver (by looking for a particular code) or transmitter (by pulsing an IR LED at ~38KHz for the - durations detected - + durations detected + Code is public domain, check out www.ladyada.net and adafruit.com - for more tutorials! + for more tutorials! */ // We need to use the 'raw' pin reading methods @@ -17,8 +147,18 @@ //uint8_t IRpin = 2; // Digital pin #2 is the same as Pin D2 see // http://arduino.cc/en/Hacking/PinMapping168 for the 'raw' pin mapping -#define IRpin_PIN PIND -#define IRpin 2 + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + #define IRpin_PIN PINE + #define IRpin 4 +#elif defined(ESP32) + #define IRpin 25 // G25 on M5STACK ATOM LITE +#elif defined(ESP8266) + #define IRpin 2 +#else + #define IRpin_PIN PIND + #define IRpin 2 +#endif // the maximum pulse we'll listen for - 65 milliseconds is a long time #define MAXPULSE 65000 @@ -26,86 +166,462 @@ // what our timing resolution should be, larger is better // as its more 'precise' - but too large and you wont get // accurate timing -#define RESOLUTION 20 +uint16_t RESOLUTION=5; + +// The thresholds for different symbols +uint16_t MARK_THRESHOLD_BIT_HEADER = 0; // Value between BIT MARK and HEADER MARK +uint16_t SPACE_THRESHOLD_ZERO_ONE = 0; // Value between ZERO SPACE and ONE SPACE +uint16_t SPACE_THRESHOLD_ONE_HEADER = 0; // Value between ONE SPACE and HEADER SPACE +uint16_t SPACE_THRESHOLD_HEADER_PAUSE = 0; // Value between HEADER SPACE and PAUSE SPACE (Panasonic/Midea only) -// we will store up to 100 pulse pairs (this is -a lot-) -uint16_t pulses[100][2]; // pair is high and low pulse -uint8_t currentpulse = 0; // index for pulses we're storing + +uint32_t mark_header_avg = 0; +uint16_t mark_header_cnt = 0; +uint32_t mark_bit_avg = 0; +uint16_t mark_bit_cnt = 0; +uint32_t space_zero_avg = 0; +uint16_t space_zero_cnt = 0; +uint32_t space_one_avg = 0; +uint16_t space_one_cnt = 0; +uint32_t space_header_avg = 0; +uint16_t space_header_cnt = 0; +uint32_t space_pause_avg = 0; +uint16_t space_pause_cnt = 0; + +// we will store up to 1024 symbols +char symbols[1024]; // decoded symbols +uint16_t currentpulse = 0; // index for pulses we're storing + +uint8_t modelChoice = 0; + +// Decoded bytes +byte byteCount = 0; +byte bytes[128]; void setup(void) { + + pinMode(IRpin, INPUT); + Serial.begin(9600); - Serial.println("Ready to decode IR!"); + delay(1000); + Serial.println(F("Select model to decode (this affects the IR signal timings detection):")); + Serial.println(F("* '1' for Panasonic DKE>, Mitsubishi Electric, Fujitsu etc. codes")); + Serial.println(F("* '2' for Panasonic CKP, Midea etc. codes")); + Serial.println(F("* '3' for Mitsubishi Heavy etc. codes")); + Serial.println(F("* '4' for Hyundai etc. codes")); + Serial.println(F("* '5' for Samsung etc. codes")); + Serial.println(F("* '6' for ZH/JG-01 codes")); + Serial.println(F("* '9' for entering the bit sequence on the serial monitor (instead of the IR receiver)")); + Serial.println(); + Serial.print(F("Enter choice: ")); + + while (modelChoice == 0) { + #if defined(ESP8266) + yield(); + #endif + + int selection = Serial.read(); + + if ( selection != -1 ) { + Serial.print((char)selection); + + switch ((char)selection) { + case '1': + modelChoice = 1; + break; + case '2': + modelChoice = 2; + break; + case '3': + modelChoice = 3; + break; + case '4': + modelChoice = 4; + break; + case '5': + modelChoice = 5; + break; + case '6': + modelChoice = 6; + break; + case '9': + modelChoice = 9; + break; + } + } + } + + Serial.print(F("\n\nReady to decode IR for choice '")); + Serial.print(modelChoice); + Serial.println(F("'\n\n")); + + if (modelChoice == 1) { + MARK_THRESHOLD_BIT_HEADER = 2000; + SPACE_THRESHOLD_ZERO_ONE = 800; + SPACE_THRESHOLD_ONE_HEADER = 1500; + SPACE_THRESHOLD_HEADER_PAUSE = 8000; + } else if (modelChoice == 2) { + MARK_THRESHOLD_BIT_HEADER = 2000; + SPACE_THRESHOLD_ZERO_ONE = 1800; + SPACE_THRESHOLD_ONE_HEADER = 3200; + SPACE_THRESHOLD_HEADER_PAUSE = 8000; + } else if (modelChoice == 3) { + MARK_THRESHOLD_BIT_HEADER = 2000; + SPACE_THRESHOLD_ZERO_ONE = 800; + SPACE_THRESHOLD_ONE_HEADER = 1400; + SPACE_THRESHOLD_HEADER_PAUSE = 8000; + } else if (modelChoice == 4) { + MARK_THRESHOLD_BIT_HEADER = 2000; + SPACE_THRESHOLD_ZERO_ONE = 800; + SPACE_THRESHOLD_ONE_HEADER = 2400; + SPACE_THRESHOLD_HEADER_PAUSE = 8000; + } else if (modelChoice == 5) { + MARK_THRESHOLD_BIT_HEADER = 2000; + SPACE_THRESHOLD_ZERO_ONE = 800; + SPACE_THRESHOLD_ONE_HEADER = 2400; + SPACE_THRESHOLD_HEADER_PAUSE = 10000; + } else if (modelChoice == 6) { + MARK_THRESHOLD_BIT_HEADER = 6500; + SPACE_THRESHOLD_ZERO_ONE = 2100; + SPACE_THRESHOLD_ONE_HEADER = 7750; + SPACE_THRESHOLD_HEADER_PAUSE = 9000; + } } void loop(void) { + + memset(symbols, 0, sizeof(symbols)); + memset(bytes, 0, sizeof(bytes)); + + currentpulse=0; + byteCount=0; + + if (modelChoice != 9) { + receivePulses(); + } else { + while ((currentpulse = Serial.readBytesUntil('\n', symbols+1, sizeof(symbols)-1)) == 0) { + #if defined(ESP8266) + yield(); + #endif + } + currentpulse++; + } + + // avoid false receiving positives + if (currentpulse > 1) + { + Serial.println("################# Start"); + printPulses(); + decodeProtocols(); + Serial.println("################# End "); + Serial.println(); + } +} + +void receivePulses(void) { uint16_t highpulse, lowpulse; // temporary storage timing - highpulse = lowpulse = 0; // start out with no pulse length - - -// while (digitalRead(IRpin)) { // this is too slow! - while (IRpin_PIN & (1 << IRpin)) { - // pin is still HIGH - - // count off another few microseconds - highpulse++; - delayMicroseconds(RESOLUTION); - - // If the pulse is too long, we 'timed out' - either nothing - // was received or the code is finished, so print what - // we've grabbed so far, and then reset - if ((highpulse >= MAXPULSE) && (currentpulse != 0)) { - printpulses(); - currentpulse=0; - return; - } + + // Initialize the averages every time + mark_header_avg = 0; + mark_header_cnt = 0; + mark_bit_avg = 0; + mark_bit_cnt = 0; + space_zero_avg = 0; + space_zero_cnt = 0; + space_one_avg = 0; + space_one_cnt = 0; + space_header_avg = 0; + space_header_cnt = 0; + space_pause_avg = 0; + space_pause_cnt = 0; + + // Only Panasonic seems to use the pause + space_pause_avg = 0; + space_pause_cnt = 0; + + unsigned long start = micros(); + unsigned long last = start; + + while (currentpulse < sizeof(symbols)) + { + #if defined(ESP8266) + yield(); + #endif + + highpulse = 0; + + while (getPinState()) { + #if defined(ESP8266) + yield(); + #endif + + // pin is still HIGH + + // count off another few microseconds + highpulse++; + delayMicroseconds(RESOLUTION); + + // If the pulse is too long, we 'timed out' - either nothing + // was received or the code is finished, so print what + // we've grabbed so far, and then reset + if ((highpulse >= MAXPULSE) && (currentpulse != 0)) { + return; + } + } + + // Instructions below here also take time, and need to be counted before the next high pulse. + unsigned long ts = micros(); + highpulse = ts - last; + last = ts; + + if (currentpulse > 0) + { + // this is a SPACE + if ( highpulse > SPACE_THRESHOLD_HEADER_PAUSE ) { + symbols[currentpulse] = 'W'; + // Cumulative moving average, see http://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average + space_pause_avg = (highpulse + space_pause_cnt * space_pause_avg) / (space_pause_cnt + 1); + space_pause_cnt++; + } else if ( (currentpulse > 0 && symbols[currentpulse-1] == 'H') || highpulse > SPACE_THRESHOLD_ONE_HEADER ) { + symbols[currentpulse] = 'h'; + // Cumulative moving average, see http://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average + if (highpulse > SPACE_THRESHOLD_ONE_HEADER) + space_header_avg = (highpulse + space_header_cnt * space_header_avg) / (space_header_cnt + 1); + space_header_cnt++; + } else if ( highpulse > SPACE_THRESHOLD_ZERO_ONE ) { + symbols[currentpulse] = '1'; + space_one_avg = (highpulse + space_one_cnt * space_one_avg) / (space_one_cnt + 1); + space_one_cnt++; + } else { + symbols[currentpulse] = '0'; + space_zero_avg = (highpulse + space_zero_cnt * space_zero_avg) / (space_zero_cnt + 1); + space_zero_cnt++; + } + } + currentpulse++; + + // same as above + lowpulse = 0; + while (! getPinState()) { + #if defined(ESP8266) + yield(); + #endif + + // pin is still LOW + lowpulse++; + delayMicroseconds(RESOLUTION); + if ((lowpulse >= MAXPULSE) && (currentpulse != 0)) { + return; + } + } + + // this is a MARK + + ts = micros(); + lowpulse = ts - last; + last = ts; + + if ( lowpulse > MARK_THRESHOLD_BIT_HEADER ) { + symbols[currentpulse] = 'H'; + currentpulse++; + mark_header_avg = (lowpulse + mark_header_cnt * mark_header_avg) / (mark_header_cnt + 1); + mark_header_cnt++; + } else { + mark_bit_avg = (lowpulse + mark_bit_cnt * mark_bit_avg) / (mark_bit_cnt + 1); + mark_bit_cnt++; + } + + // we read one high-low pulse successfully, continue! + } +} + +bool getPinState() { +#if defined(ESP32) + return gpio_get_level(gpio_num_t(IRpin)); +#elif defined(ESP8266) + return digitalRead(IRpin); +#else + return (IRpin_PIN & _BV(IRpin)); +#endif +} + +void printPulses(void) { + + int bitCount = 0; + byte currentByte = 0; + + Serial.print(F("\nNumber of symbols: ")); + Serial.println(currentpulse); + + // Print the symbols (0, 1, H, h, W) + Serial.println(F("Symbols:")); + //Serial.println("--1-------2-------3-------4-------5-------6-------7-------8-------9-------0-------1-------2-------3-------4-------5-------"); + //Serial.println("--123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678"); + Serial.println(symbols+1); + + // Print the decoded bytes + Serial.println(F("Bytes:")); + + // Decode the string of bits to a byte array from LSB first to MSB last for each byte + for (uint16_t i = 0; i < currentpulse; i++) { + + if (symbols[i] == '0' || symbols[i] == '1') { + + if (bitCount == 0) { + if (byteCount < 10) + Serial.print("0"); + Serial.print(byteCount); Serial.print(": "); + } + + currentByte >>= 1; + bitCount++; + + if (symbols[i] == '1') { + currentByte |= 0x80; + } + + Serial.print(symbols[i]); + if (bitCount == 4) { + Serial.print("|"); + + } + if (bitCount == 8) { + bytes[byteCount++] = currentByte; + bitCount = 0; + Serial.print(" | "); + printByte(currentByte); + Serial.print(" | "); + + for (int mask = 0x80; mask > 0; mask >>= 1) { + Serial.print((currentByte & mask) ? "1" : "0"); + } + Serial.println(" "); + } + } else { // Ignore bits which do not form octets + bitCount = 0; + currentByte = 0; + } } - // we didn't time out so lets stash the reading - pulses[currentpulse][0] = highpulse; - - // same as above - while (! (IRpin_PIN & _BV(IRpin))) { - // pin is still LOW - lowpulse++; - delayMicroseconds(RESOLUTION); - if ((lowpulse >= MAXPULSE) && (currentpulse != 0)) { - printpulses(); - currentpulse=0; - return; - } + + // Print the byte array + for (int i = 0; i < byteCount; i++) { + // if (bytes[i] < 0x10) { + // Serial.print(F("0")); + // } + // Serial.print(bytes[i],HEX); + + printByte(bytes[i]); + if ( i < byteCount - 1 ) { + Serial.print(F(",")); + } } - pulses[currentpulse][1] = lowpulse; + Serial.println(); - // we read one high-low pulse successfully, continue! - currentpulse++; + // Print the timing constants + Serial.println(F("Timings (in us): ")); + Serial.print(F("PAUSE SPACE: ")); + Serial.println(space_pause_avg); + Serial.print(F("HEADER MARK: ")); + Serial.println(mark_header_avg); + Serial.print(F("HEADER SPACE: ")); + Serial.println(space_header_avg); + Serial.print(F("BIT MARK: ")); + Serial.println(mark_bit_avg); + Serial.print(F("ZERO SPACE: ")); + Serial.println(space_zero_avg); + Serial.print(F("ONE SPACE: ")); + Serial.println(space_one_avg); } -void printpulses(void) { - Serial.println("\n\r\n\rReceived: \n\rOFF \tON"); - for (uint8_t i = 0; i < currentpulse; i++) { - Serial.print(pulses[i][0] * RESOLUTION, DEC); - Serial.print(" usec, "); - Serial.print(pulses[i][1] * RESOLUTION, DEC); - Serial.println(" usec"); +void printByte(byte bytetoprint) { + if (bytetoprint < 0x10) { + Serial.print(F("0")); } - - // print it in a 'array' format - Serial.println("int IRsignal[] = {"); - Serial.println("// ON, OFF "); - for (uint8_t i = 0; i < currentpulse-1; i++) { - //Serial.print("\t"); // tab - Serial.print("pulseIR("); - Serial.print(pulses[i][1] * RESOLUTION , DEC); - Serial.print(");"); - Serial.println(""); - //Serial.print("\t"); - Serial.print("delayMicroseconds("); - Serial.print(pulses[i+1][0] * RESOLUTION , DEC); - Serial.println(");"); + Serial.print(bytetoprint, HEX); +} + + +void decodeProtocols() +{ + Serial.println(F("Decoding known protocols...")); + + bool knownProtocol; + +#if defined(MITSUBISHI_ELECTRIC) + knownProtocol = decodeMitsubishiElectric(bytes, byteCount); +#endif +#if defined(FUJITSU) + knownProtocol = decodeFujitsu(bytes, byteCount); +#endif +#if defined(MITSUBISHI_HEAVY) + knownProtocol = decodeMitsubishiHeavy(bytes, byteCount); +#endif +#if defined(SHARP_) + knownProtocol = decodeSharp(bytes, byteCount); +#endif +#if defined(DAIKIN) + knownProtocol = decodeDaikin(bytes, byteCount); +#endif +#if defined(CARRIER) + knownProtocol = decodeCarrier(bytes, byteCount); +#endif +#if defined(PANASONIC_CKP) + knownProtocol = decodePanasonicCKP(bytes, byteCount); +#endif +#if defined(PANASONIC_CS) + knownProtocol = decodePanasonicCS(bytes, byteCount); +#endif +#if defined(HYUNDAI) + knownProtocol = decodeHyundai(bytes, currentpulse); +#endif +#if defined(OLIMPIA) + knownProtocol = decodeOlimpiaMaestro(bytes, byteCount); +#endif +#if defined(GREE) or defined(GREE_YAC) + knownProtocol = decodeGree(bytes, currentpulse) || decodeGree_YAC(bytes, currentpulse); +#endif +#if defined(FUEGO) + knownProtocol = decodeFuego(bytes, byteCount); +#endif +#if defined(TOSHIBA) + knownProtocol = decodeToshiba(bytes, byteCount); +#endif +#if defined(NIBE) + knownProtocol = decodeNibe(bytes, symbols, currentpulse); +#endif +#if defined(AIRWELL) + knownProtocol = decodeAirwell(symbols, currentpulse); +#endif +#if defined(HITACHI) + knownProtocol = decodeHitachi(bytes, byteCount); +#endif +#if defined(SAMSUNG) + knownProtocol = decodeSamsung(bytes, byteCount); +#endif +#if defined(BALLU) + knownProtocol = decodeBallu(bytes, byteCount); +#endif +#if defined(AUX) + knownProtocol = decodeAUX(bytes, byteCount); +#endif +#if defined(ZHLT01_REMOTE) + knownProtocol = decodeZHLT01remote(bytes, byteCount); +#endif +#if defined(ZHJG01_REMOTE) + knownProtocol = decodeZHJG01remote(bytes, byteCount); +#endif +#if defined(PHILCO) + knownProtocol = decodePhilco(bytes, byteCount); +#endif +#if defined(KY26_REMOTE) + knownProtocol = decodeKY26Remote(bytes, byteCount); +#endif + if (!knownProtocol) + { + Serial.println(F("Unknown protocol")); + Serial.print("Bytecount: "); + Serial.println(byteCount); } - //Serial.print("\t"); // tab - Serial.print("pulseIR("); - Serial.print(pulses[currentpulse-1][1] * RESOLUTION, DEC); - Serial.print(");"); } diff --git a/rawirdecode/rawirdecodestruct.ino b/rawirdecode/rawirdecodestruct.ino deleted file mode 100644 index e493326..0000000 --- a/rawirdecode/rawirdecodestruct.ino +++ /dev/null @@ -1,111 +0,0 @@ - -/* Raw IR decoder sketch! - - This sketch/program uses the Arduno and a PNA4602 to - decode IR received. This can be used to make a IR receiver - (by looking for a particular code) - or transmitter (by pulsing an IR LED at ~38KHz for the - durations detected - - Code is public domain, check out www.ladyada.net and adafruit.com - for more tutorials! - */ - -// We need to use the 'raw' pin reading methods -// because timing is very important here and the digitalRead() -// procedure is slower! -//uint8_t IRpin = 2; -// Digital pin #2 is the same as Pin D2 see -// http://arduino.cc/en/Hacking/PinMapping168 for the 'raw' pin mapping -#define IRpin_PIN PIND -#define IRpin 2 - -// the maximum pulse we'll listen for - 65 milliseconds is a long time -#define MAXPULSE 65000 - -// what our timing resolution should be, larger is better -// as its more 'precise' - but too large and you wont get -// accurate timing -#define RESOLUTION 20 - -// we will store up to 100 pulse pairs (this is -a lot-) -uint16_t pulses[100][2]; // pair is high and low pulse -uint8_t currentpulse = 0; // index for pulses we're storing - -void setup(void) { - Serial.begin(9600); - Serial.println("Ready to decode IR!"); -} - -void loop(void) { - uint16_t highpulse, lowpulse; // temporary storage timing - highpulse = lowpulse = 0; // start out with no pulse length - - -// while (digitalRead(IRpin)) { // this is too slow! - while (IRpin_PIN & (1 << IRpin)) { - // pin is still HIGH - - // count off another few microseconds - highpulse++; - delayMicroseconds(RESOLUTION); - - // If the pulse is too long, we 'timed out' - either nothing - // was received or the code is finished, so print what - // we've grabbed so far, and then reset - if ((highpulse >= MAXPULSE) && (currentpulse != 0)) { - printpulses(); - currentpulse=0; - return; - } - } - // we didn't time out so lets stash the reading - pulses[currentpulse][0] = highpulse; - - // same as above - while (! (IRpin_PIN & _BV(IRpin))) { - // pin is still LOW - lowpulse++; - delayMicroseconds(RESOLUTION); - if ((lowpulse >= MAXPULSE) && (currentpulse != 0)) { - printpulses(); - currentpulse=0; - return; - } - } - pulses[currentpulse][1] = lowpulse; - - // we read one high-low pulse successfully, continue! - currentpulse++; -} - -void printpulses(void) { - Serial.println("\n\r\n\rReceived: \n\rOFF \tON"); - for (uint8_t i = 0; i < currentpulse; i++) { - Serial.print(pulses[i][0] * RESOLUTION, DEC); - Serial.print(" usec, "); - Serial.print(pulses[i][1] * RESOLUTION, DEC); - Serial.println(" usec"); - } - - // print it in a 'array' format - Serial.println("int IRsignal[] = {"); - Serial.println("// ON, OFF "); - for (uint8_t i = 0; i < currentpulse-1; i++) { - //Serial.print("\t"); // tab - Serial.print("pulseIR("); - Serial.print(pulses[i][1] * RESOLUTION , DEC); - Serial.print(");"); - Serial.println(""); - //Serial.print("\t"); - Serial.print("delayMicroseconds("); - Serial.print(pulses[i+1][0] * RESOLUTION , DEC); - Serial.println(");"); - - } - //Serial.print("\t"); // tab - Serial.print("pulseIR("); - Serial.print(pulses[currentpulse-1][1] * RESOLUTION, DEC); - Serial.print(");"); - -}