Improved LG protocol and added class Aircondition_LG. Improved ir_DistanceProtocol.cpp to support more than 32 bits.

This commit is contained in:
Armin 2021-09-27 23:46:13 +02:00
parent ed948955e8
commit 94fa49a0f8
16 changed files with 709 additions and 288 deletions

View File

@ -112,12 +112,12 @@ jobs:
- arduino-boards-fqbn: megaTinyCore:megaavr:atxy4:chip=1604,clock=16internal
platform-url: http://drazzy.com/package_drazzy.com_index.json
sketches-exclude: MinimalReceiver,IRDispatcherDemo,MicroGirs # digitalWriteFast.h not available for this board
sketches-exclude: MinimalReceiver,IRDispatcherDemo,MicroGirs,UnitTest # digitalWriteFast.h not available for this board
- arduino-boards-fqbn: digistump:avr:digispark-tiny:clock=clock16
platform-url: https://raw.githubusercontent.com/ArminJo/DigistumpArduino/master/package_digistump_index.json
required-libraries: ATtinySerialOut
sketch-names: MinimalReceiver.ino,IRremoteInfo.ino,SimpleReceiver.ino,ReceiveDemo.ino,ControlRelay.ino,SimpleSender.ino,SendDemo.ino,SendRawDemo.ino,SendAndReceive.ino
sketch-names: MinimalReceiver.ino,IRremoteInfo.ino,SimpleReceiver.ino,ControlRelay.ino,SimpleSender.ino,SendDemo.ino,SendRawDemo.ino,SendAndReceive.ino
- arduino-boards-fqbn: ATTinyCore:avr:attinyx5micr:LTO=enable,sketchclock=16pll
platform-url: http://drazzy.com/package_drazzy.com_index.json

View File

@ -50,14 +50,17 @@ If you use an (old) Arduino core that does not use the `-flto` flag for compile,
- Now there is an **IRreceiver** and **IRsender** object like the well known Arduino **Serial** object.
- Just remove the line `IRrecv IrReceiver(IR_RECEIVE_PIN);` and/or `IRsend IrSender;` in your program, and replace all occurrences of `IRrecv.` or `irrecv.` with `IrReceiver`.
- Since the decoded values are now in `IrReceiver.decodedIRData` and not in `results` any more, remove the line `decode_results results` or similar.
- Like for the Serial object, call [`IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L38) or `IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK);` instead of the `IrReceiver.enableIRIn();` or `irrecv.enableIRIn();` in setup().
- Like for the Serial object, call [`IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L38)
or `IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK);` instead of the `IrReceiver.enableIRIn();` or `irrecv.enableIRIn();` in setup().
- Old `decode(decode_results *aResults)` function is replaced by simple `decode()`. So if you have a statement `if(irrecv.decode(&results))` replace it with `if (IrReceiver.decode())`.
- The decoded result is now in in `IrReceiver.decodedIRData` and not in `results` any more, therefore replace any occurrences of `results.value` and `results.decode_type` (and similar) to `IrReceiver.decodedIRData.decodedRawData` and `IrReceiver.decodedIRData.protocol`.
- The decoded result is now in in `IrReceiver.decodedIRData` and not in `results` any more, therefore replace any occurrences of `results.value` and `results.decode_type` (and similar) to
`IrReceiver.decodedIRData.decodedRawData` and `IrReceiver.decodedIRData.protocol`.
- Overflow, Repeat and other flags are now in [`IrReceiver.receivedIRData.flags`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.h#L126).
- Seldom used: `results.rawbuf` and `results.rawlen` must be replaced by `IrReceiver.decodedIRData.rawDataPtr->rawbuf` and `IrReceiver.decodedIRData.rawDataPtr->rawlen`.
# Do not want to convert your 2.x program and use the 3.x library version?
The 3.x versions try to be backwards compatible, so you can easily run your old examples. But some functions like e.g. `sendNEC()` -see below- could not made backwards compatible, so in this cases you must revisit your code and adapt it to the 3.x library.<br/>
The 3.x versions try to be backwards compatible, so you can easily run your old examples. But some functions like e.g. `sendNEC()` -see below- could not made backwards compatible,
so in this cases you must revisit your code and adapt it to the 3.x library.<br/>
If you program look like:
```
IRrecv irrecv(RECV_PIN);
@ -80,7 +83,8 @@ void loop() {
```
it runs on the 3.x version as before. But only the following decoders are available then: Denon, JVC, LG, NEC, Panasonic, RC5, RC6, Samsung, Sony.
The `results.value` is set by the decoders for **NEC, Panasonic, Sony, Samsung and JVC** as MSB first like in 2.x!<br/>
- The old functions `sendNEC()` and `sendJVC()` are deprecated and renamed to `sendNECMSB()` and `sendJVCMSB()` to make it clearer that they send data with MSB first, which is not the standard for NEC and JVC. Use them to send your **old MSB-first 32 bit IR data codes**.
- The old functions `sendNEC()` and `sendJVC()` are deprecated and renamed to `sendNECMSB()` and `sendJVCMSB()` to make it clearer that they send data with MSB first,
which is not the standard for NEC and JVC. Use them to send your **old MSB-first 32 bit IR data codes**.
In the new version you will send NEC (and other) commands not by 32 bit codes but by a (constant) 8 bit address and an 8 bit command.
# How to convert old MSB first 32 bit IR data codes to new LSB first 32 bit IR data codes
@ -98,7 +102,8 @@ Example:
Please do not use the old send*Raw() functions for sending like e.g. `IrSender.sendNECRaw(0xE61957A8,2)`,
even if this functions are used in a lot of **(old)** tutorials. They are only kept for backward compatibility and unsupported and error prone.<br/>
**Much better** is to use the **new structured functions** with address and command parameters like e.g. `IrSender.sendNEC(0xA8, 0x19, 2)`.
Especially if you are able to receive these remote codes and get the address and command values. You will discover that **the address is a constant** and the commands sometimes are sensibly grouped.
Especially if you are able to receive these remote codes and get the address and command values.
You will discover that **the address is a constant** and the commands sometimes are sensibly grouped.
# FAQ
- IR does not work right when I use **Neopixels** (aka WS2811/WS2812/WS2812B) or other libraries blocking interrupts for a longer time (> 50 us).<br/>
@ -112,7 +117,7 @@ In turn, this stops the IR interrupt handler from running when it needs to. Ther
- The **minimal CPU frequency** for receiving is 4 MHz, since the 50 us timer ISR takes around 12 us on a 16 MHz ATmega.
# Minimal version
For applications only requiring NEC protocol, there is a receiver which has very **small codesize of 500 bytes and does NOT require any timer**. See the MinimalReceiver and IRDispatcherDemo example how to use it. Mapping of pins to interrupts can be found [here](https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master/src/TinyIRReceiver.hpp#L307).
For applications only requiring NEC protocol, there is a receiver which has very **small code size of 500 bytes and does NOT require any timer**. See the MinimalReceiver and IRDispatcherDemo example how to use it. Mapping of pins to interrupts can be found [here](https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master/src/TinyIRReceiver.hpp#L307).
# Handling unknown Protocols
## Disclaimer
@ -154,8 +159,11 @@ In order to fit the examples to the 8K flash of ATtiny85 and ATtiny88, the [Ardu
### SimpleReceiver + SimpleSender
This examples are a good starting point.
### ReceiveDemo + SendDemo
More complete examples for the advanced user.
### ReceiveDemo
Receives all protocols and play a beep on each packet received. By connecting pin 5 to ground, you can see the raw values for each packet.
### SendDemo
Sends all available protocols at least once.
### ReceiveAndSend + UnitTest
ReceiveDemo + SendDemo in one program. **Receiving while sending**.
@ -180,6 +188,12 @@ Control a relay (connected to an output pin) with your remote.
### IRremoteExtensionTest
Example for a user defined class, which itself uses the IRrecv class from IRremote.
### SendLGAirConditionerDemo
Example for sending LG air conditioner IR codes controlled by Serial input.<br/>
By just using the function `bool Aircondition_LG::sendCommandAndParameter(char aCommand, int aParameter)` you can control the air conditioner by any other command source.<br/>
The file *acLG.h* contains the command documentation of the LG air conditioner IR protocol. Based on reverse engineering of the LG AKB73315611 remote.
![LG AKB73315611 remote](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/LG_AKB73315611.jpg)
### ReceiverTimingAnalysis
This example analyzes the signal delivered by your IR receiver module.
Values can be used to determine the stability of the received signal as well as a hint for determining the protocol.<br/>
@ -189,7 +203,8 @@ Click on the receiver while simulation is running to specify individual NEC IR c
# Compile options / macros for this library
To customize the library to different requirements, there are some compile options / macros available.<br/>
Modify it by commenting them out or in, or change the values if applicable. Or define the macro with the -D compiler option for global compile (the latter is not possible with the Arduino IDE, so consider using [Sloeber](https://eclipse.baeyens.it).
Modify it by commenting them out or in, or change the values if applicable.
Or define the macro with the -D compiler option for global compile (the latter is not possible with the Arduino IDE, so consider using [Sloeber](https://eclipse.baeyens.it).
| Name | File | Default value | Description |
|-|-|-|-|
@ -209,7 +224,6 @@ Modify it by commenting them out or in, or change the values if applicable. Or d
| `IR_SEND_DUTY_CYCLE` | IRremoteInt.h | 30 | Duty cycle of IR send signal. |
| `MICROS_PER_TICK` | IRremoteInt.h | 50 | Resolution of the raw input buffer data. |
| `IR_USE_AVR_TIMER*` | private/IRTimer.hpp | | Selection of timer to be used for generating IR receiving sample interval. |
|-|-|-|-|
| `IR_INPUT_PIN` | TinyIRReceiver.h | 2 | The pin number for TinyIRReceiver IR input, which gets compiled in. |
| `IR_FEEDBACK_LED_PIN` | TinyIRReceiver.h | `LED_BUILTIN` | The pin number for TinyIRReceiver feedback LED, which gets compiled in. |
@ -224,7 +238,7 @@ The modification must be renewed for each new IRremote library version!
### Modifying compile options with Sloeber IDE
If you are using Sloeber as your IDE, you can easily define global symbols with *Properties > Arduino > CompileOptions*.<br/>
![Sloeber settings](https://github.com/ArminJo/ServoEasing/blob/master/pictures/SloeberDefineSymbols.png)
![Sloeber settings](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/SloeberDefineSymbols.png)
# Supported Boards
Digispark boards are tested with the recommended [ATTinyCore](https://github.com/SpenceKonde/ATTinyCore) using `New Style` pin mapping for the pro board.
@ -277,7 +291,7 @@ For the AVR platform the code to modify looks like:
You **just have to modify the comments** of the current and desired timer line.
But be aware that the new timer in turn might be incompatible with other libraries or commands.<br/>
The modification must be renewed for each new IRremote library version, or you use an IDE like [Sloeber](https://github.com/Arduino-IRremote/Arduino-IRremote#modifying-compile-options-with-sloeber-ide).<br/>
For other platforms you must modify the approriate section guarded by e.g. `#elif defined(ESP32)`.
For other platforms you must modify the appropriate section guarded by e.g. `#elif defined(ESP32)`.
Another approach can be to share the timer **sequentially** if their functionality is used only for a short period of time like for the **Arduino tone() command**.
An example can be seen [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/21b5747a58e9d47c9e3f1beb056d58c875a92b47/examples/ReceiveDemo/ReceiveDemo.ino#L159-L169), where the timer settings for IR receive are restored after the tone has stopped.

View File

@ -10,6 +10,8 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I
- Compiler switch USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN added.
- Moved blink13() back to IRrecv class.
- Added Kaseikyo convenience functions like sendKaseikyo_Denon().
- Improved LG protocol and added class Aircondition_LG based on real hardware supplied by makerspace 201 (https://wiki.hackerspaces.org/ZwoNullEins) from Cologne.
- Improved universal decoder for pulse width or pulse distance protocols to support more than 32 bits.
## 3.3.0
- Fix errors if LED_BUILTIN is not defined.

View File

@ -55,10 +55,11 @@
// MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding,
// to compensate for the signal forming of different IR receiver modules.
#define MARK_EXCESS_MICROS 20 // 20 is recommended for the cheap VS1838 modules
//#define MARK_EXCESS_MICROS 20 // 20 is recommended for the cheap VS1838 modules
#define RECORD_GAP_MICROS 12000 // Activate it for some LG air conditioner protocols
//#define RECORD_GAP_MICROS 12000 // Activate it for some LG air conditioner protocols
//#define
/*
* First define macros for input and output pin etc.
*/
@ -134,8 +135,9 @@ void loop() {
Serial.println();
#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604
if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) {
IrReceiver.decodedIRData.flags = false; // yes we have recognized the flag :-)
Serial.println(F("Overflow detected"));
Serial.println(F("Try to increase the \"RAW_BUFFER_LENGTH\" value in IRremoteInt.h to 750."));
// see also https://github.com/Arduino-IRremote/Arduino-IRremote#modifying-compile-options-with-sloeber-ide
# if !defined(ESP32) && !defined(ESP8266) && !defined(NRF5)
/*
* do double beep
@ -143,6 +145,9 @@ void loop() {
IrReceiver.stop();
tone(TONE_PIN, 1100, 10);
delay(50);
tone(TONE_PIN, 1100, 10);
delay(50);
IrReceiver.start(100000); // to compensate for 100 ms stop of receiver. This enables a correct gap measurement.
# endif
} else {

View File

@ -131,24 +131,30 @@ void loop() {
Serial.println(F("Send NEC 16 bit address=0xFB04 and command 0x08 with exact timing (16 bit array format)"));
Serial.flush();
const uint16_t irSignal[] = { 9000, 4500/*Start bit*/, 560, 560, 560, 560, 560, 1690, 560,
560/*0010 0x4 of 16 bit address LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000*/, 560, 1690, 560, 1690, 560,
560, 560, 1690/*1101 0xB*/, 560, 1690, 560, 1690, 560, 1690, 560, 1690/*1111*/, 560, 560, 560, 560, 560, 560, 560,
1690/*0001 0x08 of command LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000 0x00*/, 560, 1690, 560, 1690, 560,
1690, 560, 560/*1110 Inverted 8 of command*/, 560, 1690, 560, 1690, 560, 1690, 560, 1690/*1111 inverted 0 of command*/,
560 /*stop bit*/}; // Using exact NEC timing
560/*0010 0x4 of 16 bit address LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000*/, 560, 1690, 560, 1690,
560, 560, 560, 1690/*1101 0xB*/, 560, 1690, 560, 1690, 560, 1690, 560, 1690/*1111*/, 560, 560, 560, 560, 560, 560,
560, 1690/*0001 0x08 of command LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000 0x00*/, 560, 1690, 560,
1690, 560, 1690, 560, 560/*1110 Inverted 8 of command*/, 560, 1690, 560, 1690, 560, 1690, 560,
1690/*1111 inverted 0 of command*/, 560 /*stop bit*/}; // Using exact NEC timing
IrSender.sendRaw(irSignal, sizeof(irSignal) / sizeof(irSignal[0]), NEC_KHZ); // Note the approach used to automatically calculate the size of the array.
delay(DELAY_AFTER_SEND);
#endif
/*
* With sendNECRaw() you can send 32 bit combined codes
*/
Serial.println(
F(
"Send NEC / ONKYO with 16 bit address 0x0102 and 16 bit command 0x0304 with NECRaw(0x03040102)"));
Serial.println(F("Send NEC / ONKYO with 16 bit address 0x0102 and 16 bit command 0x0304 with NECRaw(0x03040102)"));
Serial.flush();
IrSender.sendNECRaw(0x03040102, sRepeats);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send NEC with 16 bit address 0x0102 and 16 bit command 0x0304 with sendPulseDistanceWidthData()"));
// Header
IrSender.mark(9000);
IrSender.space(4500);
// LSB first + stop bit
IrSender.sendPulseDistanceWidthData(560, 1680, 560, 560, 0x03040102, 32, PROTOCOL_IS_LSB_FIRST, SEND_STOP_BIT);
delay(DELAY_AFTER_SEND);
/*
* With Send sendNECMSB() you can send your old 32 bit codes.
* To convert one into the other, you must reverse the byte positions and then reverse all positions of each byte.

View File

@ -32,8 +32,15 @@
*/
#include <Arduino.h>
// LG2 has different header timing and a shorter bit time
/*
* LG2 has different header timing and a shorter bit time
* Known LG remote controls, which uses LG2 protocol are:
* AKB75215403
* AKB74955603
* AKB73757604:
*/
//#define USE_LG2_PROTOCOL // Try it if you do not have success with the default LG protocol
#define NUMBER_OF_COMMANDS_BETWEEN_PRINT_OF_MENU 5
/*
* Define macros for input and output pin etc.
@ -41,7 +48,7 @@
#include "PinDefinitionsAndMore.h"
#include <IRremote.h>
#include "LongUnion.h"
#include "ac_LG.h"
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut"
@ -52,140 +59,11 @@
#define Serial SerialUSB
#endif
bool ACIsWallType = false; // false : TOWER, true : WALL
bool ACIsHeating = false; // false : cooling, true : heating
bool ACPowerIsOn = false;
bool ACStateIsAirClean = false; // false : off, 1 : true --> power on
uint8_t ACRequestedFanIntensity = 1; // 0 : low, 1 : mid, 2 : high - if ACIsWallType==Wall then 3 -> cycle
uint8_t ACRequestedTemperature = 25; // temperature : 18 ~ 30
const int AC_FAN_TOWER[3] = { 0, 4, 6 };
const int AC_FAN_WALL[4] = { 0, 2, 4, 5 }; // 5 -> cycle
// from https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h
union LGProtocol{
uint32_t raw; ///< The state of the IR remote in IR code form.
struct {
uint32_t Sum :4;
uint32_t Fan :3;
uint32_t FanExt :1;
uint32_t Temp :4;
uint32_t Mode :3;
uint32_t :3;
uint32_t Power :2;
uint32_t Signature :8; /*=0x88*/
};
};
void ACSendCode(uint16_t aCommand) {
Serial.print(F("Send code="));
Serial.print(aCommand, HEX);
Serial.print(F(" | "));
Serial.println(aCommand, BIN);
Serial.flush();
#if defined(USE_LG2_PROTOCOL)
IrSender.sendLG((uint8_t) 0x88, aCommand, 0, true);
#else
IrSender.sendLG((uint8_t) 0x88, aCommand, 0, false);
#endif
}
void sendCommand(uint8_t aTemperature, uint8_t aFanIntensity) {
Serial.print(F("Send temperature="));
Serial.print(aTemperature);
Serial.print(F(" fan intensity="));
Serial.println(aFanIntensity);
WordUnion tCommand;
tCommand.UWord = 0;
if (ACIsHeating) {
// heating
tCommand.UByte.HighByte = 0x4; // maybe cooling is 0x08????
}
// Temperature is coded in the upper nibble of the LowByte
tCommand.UByte.LowByte = ((aTemperature - 15) << 4); // 16 -> 0x00, 18 -> 0x30, 30 -> 0xF0
// Fan intensity is coded in the lower nibble of the LowByte
if (ACIsWallType) {
tCommand.UByte.LowByte |= AC_FAN_WALL[aFanIntensity];
} else {
tCommand.UByte.LowByte |= AC_FAN_TOWER[aFanIntensity];
}
ACSendCode(tCommand.UWord);
ACPowerIsOn = true;
ACRequestedTemperature = aTemperature;
ACRequestedFanIntensity = aFanIntensity;
}
void sendAirSwing(bool aSwing) {
Serial.print(F("Send air swing="));
Serial.println(aSwing);
if (ACIsWallType) {
if (aSwing) {
ACSendCode(0x1314);
} else {
ACSendCode(0x1315);
}
} else {
if (aSwing) {
ACSendCode(0x1316);
} else {
ACSendCode(0x1317);
}
}
}
void SendPowerDown() {
Serial.println(F("Send power down"));
ACSendCode(0xC005);
ACPowerIsOn = false;
}
void sendAirClean(bool aStateAirClean) {
Serial.print(F("Send air clean="));
Serial.println(aStateAirClean);
if (aStateAirClean) {
ACSendCode(0xC000);
} else {
ACSendCode(0xC008);
}
ACStateIsAirClean = aStateAirClean;
}
void sendJet(bool aJetOn) {
Serial.print(F("Send jet on="));
Serial.println(aJetOn);
if (aJetOn) {
ACSendCode(0x1008);
} else {
ACSendCode(0x0834);
}
}
void printMenu() {
Serial.println();
Serial.println();
Serial.println(F("Type command and optional parameter without a separator"));
Serial.println(F("0 Off"));
Serial.println(F("1 On"));
Serial.println(F("s Swing <0 or 1>"));
Serial.println(F("c Air clean <0 or 1>"));
Serial.println(F("j Jet <0 or 1>"));
Serial.println(F("f Fan <0 to 2 or 3 for cycle>"));
Serial.println(F("t Temperature <18 to 30>"));
Serial.println(F("+ Temperature + 1"));
Serial.println(F("- Temperature - 1"));
Serial.println(F("e.g. \"s1\" or \"t23\" or \"+\""));
Serial.println();
}
#define SIZE_OF_RECEIVE_BUFFER 10
char sRequestString[SIZE_OF_RECEIVE_BUFFER];
Aircondition_LG MyLG_Aircondition;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
@ -203,87 +81,70 @@ delay(4000); // To be able to connect Serial monitor after reset or power up and
Serial.print(F("Ready to send IR signals at pin "));
Serial.println(IR_SEND_PIN);
Serial.println();
MyLG_Aircondition.setType(LG_IS_WALL_TYPE);
MyLG_Aircondition.printMenu();
delay(1000);
// test
// sendCommand(25, 1);
// MyLG_Aircondition.sendCommandAndParameter('j', 1);
// delay(5000);
// sendCommand(27, 2);
// MyLG_Aircondition.sendCommandAndParameter('f', 3);
// delay(5000);
printMenu();
}
void loop() {
// Test
// sendCommand(25, 1);
// delay(5000);
// sendCommand(27, 0);
// delay(5000);
static uint8_t sShowmenuConter = 0;
if (Serial.available()) {
/*
* Get parameters from serial
*/
uint8_t tNumberOfBytesReceived = Serial.readBytesUntil('\n', sRequestString, SIZE_OF_RECEIVE_BUFFER - 1);
// handle CR LF
if (sRequestString[tNumberOfBytesReceived - 1] == '\r') {
tNumberOfBytesReceived--;
}
sRequestString[tNumberOfBytesReceived] = '\0'; // terminate as string
char tCommand = sRequestString[0];
uint8_t tParameter = 0;
/*
* Handle parameter numbers which can be greater 9
*/
int tParameter = 0;
if (tNumberOfBytesReceived >= 2) {
tParameter = sRequestString[1] - '0';
if (tCommand == LG_COMMAND_TEMPERATURE || tCommand == LG_COMMAND_SWING || tCommand == LG_COMMAND_SLEEP
|| tCommand == LG_COMMAND_TIMER_ON || tCommand == LG_COMMAND_TIMER_OFF) {
tParameter = atoi(&sRequestString[1]);
}
}
/*
* Print command to send
*/
Serial.print(F("Command="));
Serial.println(tCommand);
Serial.print(tCommand);
if (tParameter != 0) {
Serial.print(F(" Parameter="));
Serial.print(tParameter);
}
Serial.println();
switch (tCommand) {
case 0: // off
SendPowerDown();
break;
case 1: // on
sendCommand(ACRequestedTemperature, ACRequestedFanIntensity);
break;
case 's':
sendAirSwing(tParameter);
break;
case 'c': // 1 : clean on, power on
sendAirClean(tParameter);
break;
case 'j':
sendJet(tParameter);
break;
case 'f':
if (tParameter <= 2) {
sendCommand(ACRequestedTemperature, tParameter);
}
break;
case 't':
tParameter = atoi(&sRequestString[1]);
if (18 <= tParameter && tParameter <= 30) {
sendCommand(tParameter, ACRequestedFanIntensity);
}
break;
case '+':
if (18 <= ACRequestedTemperature && ACRequestedTemperature <= 29) {
sendCommand((ACRequestedTemperature + 1), ACRequestedFanIntensity);
}
break;
case '-':
if (19 <= ACRequestedTemperature && ACRequestedTemperature <= 30) {
sendCommand((ACRequestedTemperature - 1), ACRequestedFanIntensity);
}
break;
default:
Serial.print(F("Error: unknown command \""));
if (!MyLG_Aircondition.sendCommandAndParameter(tCommand, tParameter)) {
Serial.print(F("Error: unknown command or invalid parameter in \""));
Serial.print(sRequestString);
Serial.println('\"');
}
if (tParameter != 0) {
Serial.print(F("Parameter="));
Serial.println(tParameter);
if (sShowmenuConter == 0) {
MyLG_Aircondition.printMenu();
sShowmenuConter = NUMBER_OF_COMMANDS_BETWEEN_PRINT_OF_MENU;
} else {
sShowmenuConter--;
}
printMenu();
}
delay(100);
}

BIN
pictures/LG_AKB73315611.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -122,7 +122,7 @@ void IRrecv::start() {
* Configures the timer and the state machine for IR reception.
* @param aMicrosecondsToAddToGapCounter To compensate for microseconds the timer was stopped / disabled.
*/
void IRrecv::start(uint16_t aMicrosecondsToAddToGapCounter) {
void IRrecv::start(uint32_t aMicrosecondsToAddToGapCounter) {
enableIRIn();
noInterrupts();
irparams.TickCounterForISR += aMicrosecondsToAddToGapCounter / MICROS_PER_TICK;
@ -197,7 +197,7 @@ void IRrecv::resume() {
void IRrecv::initDecodedIRData() {
if (irparams.OverflowFlag) {
// Copy overflow flag to decodedIRData.flags
// Copy overflow flag to decodedIRData.flags and reset it
irparams.OverflowFlag = false;
irparams.rawlen = 0; // otherwise we have OverflowFlag again at next ISR call
decodedIRData.flags = IRDATA_FLAGS_WAS_OVERFLOW;
@ -689,8 +689,12 @@ bool IRrecv::decodeHash() {
if (decodedIRData.rawDataPtr->rawlen < 6) {
return false;
}
for (unsigned int i = 1; (i + 2) < decodedIRData.rawDataPtr->rawlen; i++) {
#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program space and speeds up ISR
uint8_t i;
#else
uint16_t i;
#endif
for (i = 1; (i + 2) < decodedIRData.rawDataPtr->rawlen; i++) {
uint8_t value = compare(decodedIRData.rawDataPtr->rawbuf[i], decodedIRData.rawDataPtr->rawbuf[i + 2]);
// Add value into the hash
hash = (hash * FNV_PRIME_32) ^ value;
@ -1007,8 +1011,12 @@ void IRrecv::printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsI
}
aSerial->print(F(" -"));
aSerial->println(tDurationMicros, DEC);
for (uint8_t i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program space and speeds up ISR
uint8_t i;
#else
uint16_t i;
#endif
for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
if (aOutputMicrosecondsInsteadOfTicks) {
tDurationMicros = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK;
} else {
@ -1066,7 +1074,12 @@ void IRrecv::compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicr
aSerial->print(F("] = {")); // Start declaration
// Dump data
for (unsigned int i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program space and speeds up ISR
uint8_t i;
#else
uint16_t i;
#endif
for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK;
if (i & 1) {
@ -1111,8 +1124,13 @@ void IRrecv::compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicr
*/
void IRrecv::compensateAndStoreIRResultInArray(uint8_t *aArrayPtr) {
// Store data, skip leading space
for (unsigned int i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
// Store data, skip leading space#
#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program space and speeds up ISR
uint8_t i;
#else
uint16_t i;
#endif
for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK;
if (i & 1) {
// Mark
@ -1312,7 +1330,7 @@ ISR () // for functions definitions which are called by separate (board specific
irparams.TickCounterForISR = 0;// reset counter in both cases
}
} else if (irparams.StateForISR == IR_REC_STATE_MARK) { // Timing Mark
} else if (irparams.StateForISR == IR_REC_STATE_MARK) { // Timing mark
if (tIRInputLevel != INPUT_MARK) { // Mark ended; Record time
#if defined(IR_MEASURE_TIMING) && defined(IR_TIMING_TEST_PIN)
// digitalWriteFast(IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
@ -1322,10 +1340,10 @@ ISR () // for functions definitions which are called by separate (board specific
irparams.TickCounterForISR = 0;
}
} else if (irparams.StateForISR == IR_REC_STATE_SPACE) { // Timing Space
} else if (irparams.StateForISR == IR_REC_STATE_SPACE) { // Timing space
if (tIRInputLevel == INPUT_MARK) { // Space just ended; Record time
if (irparams.rawlen >= RAW_BUFFER_LENGTH) {
// Flag up a read OverflowFlag; Stop the State Machine
// Flag up a read OverflowFlag; Stop the state machine
irparams.OverflowFlag = true;
irparams.StateForISR = IR_REC_STATE_STOP;
} else {

View File

@ -57,7 +57,6 @@
/****************************************************
* PROTOCOLS
****************************************************/
/*
* Supported IR protocols
* Each protocol you include costs memory and, during decode, costs time
@ -98,10 +97,10 @@
//#define DEBUG // Activate this for lots of lovely debug output from the IRremote core.
/****************************************************
* RECEIVING
****************************************************/
/**
* MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding,
* to compensate for the signal forming of different IR receiver modules
@ -175,7 +174,6 @@
* Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin!
*/
//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN
/**
* This amount is subtracted from the on-time of the pulses generated for software PWM generation.
* It should be the time used for digitalWrite(sendPin, LOW) and the call to delayMicros()

View File

@ -34,8 +34,13 @@
#include <Arduino.h>
#if ! defined(RAW_BUFFER_LENGTH)
/*
* The length of the buffer where the IR timing data is stored before decoding
* 100 is sufficient for most standard protocols, but air conditioners often send a longer protocol data stream
*/
#if !defined(RAW_BUFFER_LENGTH)
#define RAW_BUFFER_LENGTH 100 ///< Maximum length of raw duration buffer. Must be even. 100 supports up to 48 bit codings inclusive 1 start and 1 stop bit.
//#define RAW_BUFFER_LENGTH 750 // Value for air condition remotes.
#endif
#if RAW_BUFFER_LENGTH % 2 == 1
#error RAW_BUFFER_LENGTH must be even, since the array consists of space / mark pairs.
@ -63,7 +68,7 @@
#define IR_REC_STATE_IDLE 0
#define IR_REC_STATE_MARK 1
#define IR_REC_STATE_SPACE 2
#define IR_REC_STATE_STOP 3
#define IR_REC_STATE_STOP 3 // set to IR_REC_STATE_IDLE only by resume()
/**
* This struct contains the data and control used for receiver static functions and the ISR (interrupt service routine)
@ -80,7 +85,7 @@ struct irparams_struct {
uint16_t TickCounterForISR; ///< Counts 50uS ticks. The value is copied into the rawbuf array on every transition.
bool OverflowFlag; ///< Raw buffer OverflowFlag occurred
#if RAW_BUFFER_LENGTH <= 255 // saves around 75 bytes program space and speeds up ISR
#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program space and speeds up ISR
uint8_t rawlen; ///< counter of entries in rawbuf
#else
unsigned int rawlen; ///< counter of entries in rawbuf
@ -88,6 +93,24 @@ struct irparams_struct {
uint16_t rawbuf[RAW_BUFFER_LENGTH]; ///< raw data / tick counts per mark/space, first entry is the length of the gap between previous and current command
};
/*
* Info directives
* Can be disabled to save program space
*/
#ifdef INFO
# define INFO_PRINT(...) Serial.print(__VA_ARGS__)
# define INFO_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
/**
* If INFO, print the arguments, otherwise do nothing.
*/
# define INFO_PRINT(...) void()
/**
* If INFO, print the arguments as a line, otherwise do nothing.
*/
# define INFO_PRINTLN(...) void()
#endif
/*
* Debug directives
*/
@ -165,7 +188,7 @@ struct IRData {
uint16_t address; ///< Decoded address
uint16_t command; ///< Decoded command
uint16_t extra; ///< Used by MagiQuest and for Kaseikyo unknown vendor ID. Ticks used for decoding Distance protocol.
uint8_t numberOfBits; ///< Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible.
uint16_t numberOfBits; ///< Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible.
uint8_t flags; ///< See IRDATA_FLAGS_* definitions above
uint32_t decodedRawData; ///< Up to 32 bit decoded raw data, used for sendRaw functions.
irparams_struct *rawDataPtr; ///< Pointer of the raw timing data to be decoded. Mainly the data buffer filled by receiving ISR.
@ -195,7 +218,7 @@ public:
*/
void begin(uint8_t aReceivePin, bool aEnableLEDFeedback = false, uint8_t aFeedbackLEDPin = USE_DEFAULT_FEEDBACK_LED_PIN);
void start(); // alias for enableIRIn
void start(uint16_t aMicrosecondsToAddToGapCounter);
void start(uint32_t aMicrosecondsToAddToGapCounter);
bool available();
IRData* read(); // returns decoded data
// write is a method of class IRsend below

304
src/ac_LG.cpp Normal file
View File

@ -0,0 +1,304 @@
/*
* ac_LG.cpp
*
* Contains functions for receiving and sending LG air conditioner IR Protocol
* There is no state plausibility check, e.g. you can send fan speed in Mode D and change temperature in mode F
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2021 Armin Joachimsmeyer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
************************************************************************************
*/
#include <Arduino.h>
#define INFO // Deactivate this to save program space and suppress info output.
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h"
#include "ac_LG.h" // evaluates the DEBUG for DEBUG_PRINT
#include "LongUnion.h"
/** \addtogroup Airconditoners Air conditioner special code
* @{
*/
/*
* LG remote measurements: Type AKB73315611, Ver1.1 from 2011.03.01
* Internal crystal: 4 MHz
* Header: 8.9 ms mark 4.15 ms space
* Data: 500 / 540 and 500 / 1580;
* Clock is nor synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark
* Duty: 9 us on 17 us off => around 33 % duty
* NO REPEAT: If value like temperature has changed during long press, the last value is send at button release
* If you do a double press -tested with the fan button-, the next value can be sent after 118 ms
*/
const int AC_FAN_TOWER[3] = { 0, 4, 6 };
const int AC_FAN_WALL[4] = { 0, 2, 4, 5 }; // 0 -> low, 4 high, 5 -> cycle
void Aircondition_LG::setType(bool aIsWallType) {
ACIsWallType = aIsWallType;
}
void Aircondition_LG::printMenu() {
Serial.println();
Serial.println();
Serial.println(F("Type command and optional parameter without a separator"));
Serial.println(F("0 Off"));
Serial.println(F("1 On"));
Serial.println(F("s Swing <0 or 1>"));
Serial.println(F("a Auto clean <0 or 1>"));
Serial.println(F("j Jet <0 or 1>"));
Serial.println(F("e Energy saving <0 or 1>"));
Serial.println(F("l Light"));
Serial.println(F("f Fan speed <0 to 4 or 5 for cycle>"));
Serial.println(F("t Temperature <18 to 30> degree"));
Serial.println(F("+ Temperature + 1"));
Serial.println(F("- Temperature - 1"));
Serial.println(F("M <C(ool) or A(uto) or D(ehumidifying) or H(eating) or F(an) mode>"));
Serial.println(F("S Sleep after <0 to 420> minutes"));
Serial.println(F("T Timer on after <0 to 1439> minutes"));
Serial.println(F("O Timer off after <0 to 1439> minutes"));
Serial.println(F("C Clear all timer and sleep"));
Serial.println(F("e.g. \"s1\" or \"t23\" or \"O60\" or \"+\""));
Serial.println();
}
/*
* Send repeat
* Repeat commands should be sent in a 110 ms raster.
* @param aCommand one of LG_COMMAND_OFF, LG_COMMAND_ON etc.
*/
bool Aircondition_LG::sendCommandAndParameter(char aCommand, int aParameter) {
// Commands without parameter
switch (aCommand) {
case LG_COMMAND_OFF: // off
sendIRCommand(LG_POWER_DOWN);
PowerIsOn = false;
return true;
case LG_COMMAND_ON: // on
PowerIsOn = false; // set to false in order to suppress on bit
sendTemperatureFanSpeedAndMode();
return true;
case LG_COMMAND_JET:
DEBUG_PRINTLN(F("Send jet on"));
sendIRCommand(LG_JET_ON);
return true;
case LG_COMMAND_LIGHT:
sendIRCommand(LG_LIGHT);
return true;
case LG_COMMAND_CLEAR_ALL:
sendIRCommand(LG_CLEAR_ALL);
return true;
case LG_COMMAND_TEMPERATURE_PLUS:
if (18 <= Temperature && Temperature <= 29) {
Temperature++;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
return true;
case LG_COMMAND_TEMPERATURE_MINUS:
if (19 <= Temperature && Temperature <= 30) {
Temperature--;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
return true;
}
PowerIsOn = true;
/*
* Now the commands which require a parameter
*/
if (aParameter < 0) {
DEBUG_PRINT(F("Error: Parameter is less than 0"));
return false;
}
switch (aCommand) {
case LG_COMMAND_MODE:
Mode = aParameter + '0';
sendTemperatureFanSpeedAndMode();
break;
case LG_COMMAND_SWING:
DEBUG_PRINT(F("Send air swing="));
DEBUG_PRINTLN(aParameter);
if (ACIsWallType) {
if (aParameter) {
sendIRCommand(LG_WALL_SWING_ON);
} else {
sendIRCommand(LG_WALL_SWING_OFF);
}
} else {
if (aParameter) {
sendIRCommand(LG_SWING_ON);
} else {
sendIRCommand(LG_SWING_OFF);
}
}
break;
case LG_COMMAND_AUTO_CLEAN:
DEBUG_PRINT(F("Send auto clean="));
DEBUG_PRINTLN(aParameter);
if (aParameter) {
sendIRCommand(LG_AUTO_CLEAN_ON);
} else {
sendIRCommand(LG_AUTO_CLEAN_OFF);
}
break;
case LG_COMMAND_ENERGY:
DEBUG_PRINT(F("Send energy saving on="));
DEBUG_PRINTLN(aParameter);
if (aParameter) {
sendIRCommand(LG_ENERGY_SAVING_ON);
} else {
sendIRCommand(LG_ENERGY_SAVING_OFF);
}
break;
case LG_COMMAND_FAN_SPEED:
if (aParameter <= 3) {
FanIntensity = aParameter;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
break;
case LG_COMMAND_TEMPERATURE:
if (18 <= aParameter && aParameter <= 30) {
Temperature = aParameter;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
break;
case LG_COMMAND_SLEEP:
if (aParameter <= 420) {
sendIRCommand(LG_SLEEP + aParameter);
} else {
return false;
}
break;
case LG_COMMAND_TIMER_ON:
if (aParameter <= 1439) {
sendIRCommand(LG_TIMER_ON + aParameter);
} else {
return false;
}
break;
case LG_COMMAND_TIMER_OFF:
if (aParameter <= 1439) {
sendIRCommand(LG_TIMER_OFF + aParameter);
} else {
return false;
}
break;
default:
return false;
}
return true;
}
void Aircondition_LG::sendIRCommand(uint16_t aCommand) {
INFO_PRINT(F("Send code=0x"));
INFO_PRINT(aCommand, HEX);
INFO_PRINT(F(" | 0b"));
INFO_PRINTLN(aCommand, BIN);
IrSender.sendLG((uint8_t) LG_ADDRESS, aCommand, 0, false, useLG2Protocol);
}
/*
* Takes values from static variables
*/
void Aircondition_LG::sendTemperatureFanSpeedAndMode() {
uint8_t tTemperature = Temperature;
INFO_PRINT(F("Send temperature="));
INFO_PRINT(tTemperature);
INFO_PRINT(F(" fan intensity="));
INFO_PRINT(FanIntensity);
INFO_PRINT(F(" mode="));
INFO_PRINTLN((char )Mode);
WordUnion tIRCommand;
tIRCommand.UWord = 0;
// Temperature is coded in the upper nibble of the LowByte
tIRCommand.UByte.LowByte = ((tTemperature - 15) << 4); // 16 -> 0x00, 18 -> 0x30, 30 -> 0xF0
// Fan intensity is coded in the lower nibble of the LowByte
if (ACIsWallType) {
tIRCommand.UByte.LowByte |= AC_FAN_WALL[FanIntensity];
} else {
tIRCommand.UByte.LowByte |= AC_FAN_TOWER[FanIntensity];
}
switch (Mode) {
case 'C':
tIRCommand.UByte.HighByte = LG_MODE_COOLING >> 8;
break;
case 'H':
tIRCommand.UByte.HighByte = LG_MODE_HEATING >> 8;
break;
case 'A':
tIRCommand.UByte.HighByte = LG_MODE_AUTO >> 8;
break;
case 'F':
tTemperature = 18;
tIRCommand.UByte.HighByte = LG_MODE_FAN >> 8;
break;
case 'D':
tIRCommand.UWord = LG_MODE_DEHUMIDIFIYING;
break;
default:
break;
}
if (!PowerIsOn) {
// switch on requires masked bit
tIRCommand.UByte.HighByte &= ~(LG_SWITCH_ON_MASK >> 8);
}
PowerIsOn = true;
sendIRCommand(tIRCommand.UWord);
}
/** @}*/

134
src/ac_LG.h Normal file
View File

@ -0,0 +1,134 @@
/*
* ac_LG.h
*
* Contains definitions for receiving and sending LG air conditioner IR Protocol
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2021 Armin Joachimsmeyer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
************************************************************************************
*/
// see also: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h
#include <Arduino.h>
/** \addtogroup Airconditoners Air conditioner special code
* @{
*/
#define LG_ADDRESS 0x88
/*
* The basic IR command codes
* Parts of the codes (especially the lower nibbles) may be modified to contain
* additional information like temperature, fan speed and minutes.
*/
#define LG_SWITCH_ON_MASK 0x0800 // This bit is masked if we switch Power on
#define LG_MODE_COOLING 0x0800 // Temperature and fan speed in lower nibbles
#define LG_MODE_DEHUMIDIFIYING 0x0990 // sets also temperature to 24 and fan speed to 0
#define LG_MODE_FAN 0x0A30 // sets also temperature to 18
#define LG_MODE_AUTO 0x0B00 // The remote initially sets also temperature to 22 and fan speed to 4
#define LG_MODE_HEATING 0x0C00 // Temperature and fan speed in lower nibbles
#define LG_ENERGY_SAVING_ON 0x1004
#define LG_ENERGY_SAVING_OFF 0x1005
#define LG_JET_ON 0x1008
#define LG_WALL_SWING_ON 0x1314
#define LG_WALL_SWING_OFF 0x1315
#define LG_SWING_ON 0x1316 // not verified, for AKB73757604
#define LG_SWING_OFF 0x1317 // not verified, for AKB73757604
#define LG_TIMER_ON 0x8000 // relative minutes in lower nibbles
#define LG_TIMER_OFF 0x9000 // relative minutes in lower nibbles
#define LG_SLEEP 0xA000 // relative minutes in lower nibbles
#define LG_CLEAR_ALL 0xB000 // Timers and sleep
#define LG_POWER_DOWN 0xC005
#define LG_LIGHT 0xC00A
#define LG_AUTO_CLEAN_ON 0xC00B
#define LG_AUTO_CLEAN_OFF 0xC00C
/*
* Commands as printed in menu and uses as first parameter for sendCommandAndParameter
*/
#define LG_COMMAND_OFF '0'
#define LG_COMMAND_ON '1'
#define LG_COMMAND_SWING 's'
#define LG_COMMAND_AUTO_CLEAN 'a'
#define LG_COMMAND_JET 'j'
#define LG_COMMAND_ENERGY 'e'
#define LG_COMMAND_LIGHT 'l'
#define LG_COMMAND_FAN_SPEED 'f'
#define LG_COMMAND_TEMPERATURE 't'
#define LG_COMMAND_TEMPERATURE_PLUS '+'
#define LG_COMMAND_TEMPERATURE_MINUS '-'
#define LG_COMMAND_MODE 'M'
#define LG_COMMAND_SLEEP 'S'
#define LG_COMMAND_TIMER_ON 'T'
#define LG_COMMAND_TIMER_OFF 'O'
#define LG_COMMAND_CLEAR_ALL 'C'
/*
* The modes are encoded as character values for easy printing :-)
*/
#define AC_MODE_COOLING 'C'
#define AC_MODE_DEHUMIDIFIYING 'D'
#define AC_MODE_FAN 'F'
#define AC_MODE_AUTO 'A'
#define AC_MODE_HEATING 'H'
// see https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h
union LGProtocol {
uint32_t raw; ///< The state of the IR remote in IR code form.
struct {
uint32_t Checksum :4;
uint32_t Fan :3;
uint32_t FanExt :1;
uint32_t Temp :4;
uint32_t Mode :4;
uint32_t Function :3;
uint32_t SwitchOnMask :1; /* Content is 0 when switching from off to on */
uint32_t Signature :8; /* Content is 0x88, LG_ADDRESS */
};
};
class Aircondition_LG {
public:
bool sendCommandAndParameter(char aCommand, int aParameter);
void setType(bool aIsWallType);
void printMenu();
void sendIRCommand(uint16_t aCommand);
void sendTemperatureFanSpeedAndMode();
/*
* Internal state of the air condition
*/
#define LG_IS_WALL_TYPE true
#define LG_IS_TOWER_TYPE false
bool ACIsWallType; // false : TOWER, true : WALL
bool PowerIsOn;
// These value are encoded and sent by AC_LG_SendCommandAndParameter()
uint8_t FanIntensity = 1; // 0 -> low, 4 high, 5 -> cycle
uint8_t Temperature = 22; // temperature : 18 ~ 30
uint8_t Mode = AC_MODE_COOLING;
bool useLG2Protocol = false;
};
/** @}*/

View File

@ -1,6 +1,19 @@
/*
* ir_DistanceProtocol.cpp
*
* This decoder tries to decode a pulse width or pulse distance protocol.
* 1. Analyze all space and mark length
* 2. Decide if we have an pulse width or distance protocol
* 3. Try to decode with the mark and space data found in step 1
* No data and address decoding, only raw data as result.
*
* Pulse distance data can be sent with the generic function:
* void sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros,
* unsigned int aZeroSpaceMicros, uint32_t aData, uint8_t aNumberOfBits, bool aMSBfirst, bool aSendStopBit = false)
* The header must be sent manually with:
* IrSender.mark(MarkMicros)
* IrSender.space(SpaceMicros);
* see also: SendDemo example line 150
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
@ -35,6 +48,7 @@
#define DISTANCE_DO_MSB_DECODING PROTOCOL_IS_LSB_FIRST // this results in the same decodedRawData as e.g. the NEC and Kaseikyo/Panasonic decoder
//#define DISTANCE_DO_MSB_DECODING PROTOCOL_IS_MSB_FIRST // this resembles the JVC, Denon
#define INFO // Deactivate this to save program space and suppress info output.
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for DEBUG_PRINT
//#include "LongUnion.h"
@ -96,10 +110,10 @@ bool aggregateArrayCounts(uint8_t aArray[], uint8_t aMaxIndex, uint8_t *aShortIn
/*
* Try to decode a pulse width or pulse distance protocol.
* 1. analyze all space and mark length
* 2. decide if we have an pulse width or distance protocol
* 3. try to decode with the mark and space data found in step 1.
* No data and address, only raw data as result.
* 1. Analyze all space and mark length
* 2. Decide if we have an pulse width or distance protocol
* 3. Try to decode with the mark and space data found in step 1
* No data and address decoding, only raw data as result.
*/
bool IRrecv::decodeDistance() {
uint8_t tDurationArray[DURATION_ARRAY_SIZE];
@ -172,15 +186,10 @@ bool IRrecv::decodeDistance() {
printDurations(tDurationArray, tMaxDurationIndex);
#endif
// skip leading start and trailing stop bit.
uint8_t tNumberOfBits = (decodedIRData.rawDataPtr->rawlen / 2) - 2;
uint16_t tNumberOfBits = (decodedIRData.rawDataPtr->rawlen / 2) - 2;
uint8_t tStartIndex = 3;
decodedIRData.numberOfBits = tNumberOfBits;
// adjust for longer data like Kaseikyo
if (tNumberOfBits > 32) {
tNumberOfBits = 32;
tStartIndex = decodedIRData.rawDataPtr->rawlen - 65;
}
uint8_t tNumberOfAdditionalLong = (tNumberOfBits - 1) / 32;
/*
* decide, if we have an pulse width or distance protocol
@ -196,23 +205,45 @@ bool IRrecv::decodeDistance() {
// tMarkTicksShort * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
// tNumberOfBits++;
// }
// decode without leading start bit. Currently only seen for sony protocol
if (!decodePulseWidthData(tNumberOfBits, tStartIndex, tMarkTicksLong * MICROS_PER_TICK,
// decode without leading start bit. Currently only seen for sony protocol
for (uint8_t i = 0; i <= tNumberOfAdditionalLong; ++i) {
uint8_t tNumberOfBitsForOneDecode = tNumberOfBits;
if (tNumberOfBitsForOneDecode > 32) {
tNumberOfBitsForOneDecode = 32;
}
if (!decodePulseWidthData(tNumberOfBitsForOneDecode, tStartIndex, tMarkTicksLong * MICROS_PER_TICK,
tMarkTicksShort * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
DEBUG_PRINT(F("PULSE_WIDTH: "));
DEBUG_PRINTLN(F("Decode failed"));
return false;
}
DEBUG_PRINT(F("PULSE_WIDTH: "));
DEBUG_PRINT(F(" OneMarkMicros="));
DEBUG_PRINT(tMarkTicksLong * MICROS_PER_TICK);
DEBUG_PRINT(F(" ZeroMarkMicros="));
DEBUG_PRINT(tMarkTicksShort* MICROS_PER_TICK);
DEBUG_PRINT(F(" ZeroSpaceMicros="));
DEBUG_PRINTLN(tSpaceTicksShort* MICROS_PER_TICK);
// Store ticks used for decoding in extra
decodedIRData.extra = (tMarkTicksShort << 8) | tMarkTicksLong;
decodedIRData.protocol = PULSE_WIDTH;
if (i == 0) {
// Print protocol timing only once
INFO_PRINTLN();
INFO_PRINT(F("PULSE_WIDTH:"));
INFO_PRINT(F(" HeaderMarkMicros="));
INFO_PRINT(decodedIRData.rawDataPtr->rawbuf[1] * MICROS_PER_TICK);
INFO_PRINT(F(" HeaderSpaceMicros="));
INFO_PRINT(decodedIRData.rawDataPtr->rawbuf[2] * MICROS_PER_TICK);
INFO_PRINT(F(" OneMarkMicros="));
INFO_PRINT(tMarkTicksLong * MICROS_PER_TICK);
INFO_PRINT(F(" ZeroMarkMicros="));
INFO_PRINT(tMarkTicksShort * MICROS_PER_TICK);
INFO_PRINT(F(" SpaceMicros="));
INFO_PRINTLN(tSpaceTicksShort * MICROS_PER_TICK);
}
if (tNumberOfAdditionalLong > 0) {
// print only if we have more than 32 bits for decode
INFO_PRINT(F(" 0x"));
INFO_PRINT(decodedIRData.decodedRawData, HEX);
tStartIndex += 64;
tNumberOfBits -= 32;
}
}
// Store ticks used for decoding in extra
decodedIRData.extra = (tMarkTicksShort << 8) | tMarkTicksLong;
decodedIRData.protocol = PULSE_WIDTH;
} else {
// // check if last bit can be decoded as data or not, in this case take it as a stop bit
// if (decodePulseDistanceData(1, decodedIRData.rawDataPtr->rawlen - 3, tMarkTicksShort * MICROS_PER_TICK,
@ -221,19 +252,42 @@ bool IRrecv::decodeDistance() {
// tNumberOfBits++;
// }
if (!decodePulseDistanceData(tNumberOfBits, tStartIndex, tMarkTicksShort * MICROS_PER_TICK,
tSpaceTicksLong * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
DEBUG_PRINT(F("PULSE_DISTANCE: "));
DEBUG_PRINTLN(F("Decode failed"));
return false;
for (uint8_t i = 0; i <= tNumberOfAdditionalLong; ++i) {
uint8_t tNumberOfBitsForOneDecode = tNumberOfBits;
if (tNumberOfBitsForOneDecode > 32) {
tNumberOfBitsForOneDecode = 32;
}
if (!decodePulseDistanceData(tNumberOfBitsForOneDecode, tStartIndex, tMarkTicksShort * MICROS_PER_TICK,
tSpaceTicksLong * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
DEBUG_PRINT(F("PULSE_DISTANCE: "));
DEBUG_PRINTLN(F("Decode failed"));
return false;
} else {
if (i == 0) {
// Print protocol timing only once
INFO_PRINTLN();
INFO_PRINT(F("PULSE_DISTANCE:"));
INFO_PRINT(F(" HeaderMarkMicros="));
INFO_PRINT(decodedIRData.rawDataPtr->rawbuf[1] * MICROS_PER_TICK);
INFO_PRINT(F(" HeaderSpaceMicros="));
INFO_PRINT(decodedIRData.rawDataPtr->rawbuf[2] * MICROS_PER_TICK);
INFO_PRINT(F(" MarkMicros="));
INFO_PRINT(tMarkTicksShort * MICROS_PER_TICK);
INFO_PRINT(F(" OneSpaceMicros="));
INFO_PRINT(tSpaceTicksLong * MICROS_PER_TICK);
INFO_PRINT(F(" ZeroSpaceMicros="));
INFO_PRINTLN(tSpaceTicksShort * MICROS_PER_TICK);
}
if (tNumberOfAdditionalLong > 0) {
// print only if we have more than 32 bits for decode
INFO_PRINT(F(" 0x"));
INFO_PRINT(decodedIRData.decodedRawData, HEX);
tStartIndex += 64;
tNumberOfBits -= 32;
}
}
}
DEBUG_PRINT(F("PULSE_DISTANCE: "));
DEBUG_PRINT(F("BitMarkMicros="));
DEBUG_PRINT(tMarkTicksShort* MICROS_PER_TICK);
DEBUG_PRINT(F(" OneSpaceMicros="));
DEBUG_PRINT(tSpaceTicksLong* MICROS_PER_TICK);
DEBUG_PRINT(F(" ZeroSpaceMicros="));
DEBUG_PRINTLN(tSpaceTicksShort* MICROS_PER_TICK);
// Store ticks used for decoding in extra
decodedIRData.extra = (tSpaceTicksShort << 8) | tSpaceTicksLong;
decodedIRData.protocol = PULSE_DISTANCE;

View File

@ -50,29 +50,34 @@
// MSB first, 1 start bit + 8 bit address + 16 bit command + 4 bit checksum + 1 stop bit (28 data bits).
// Bit and repeat timing is like NEC
// LG2 has different header timing and a shorter bit time
/*
* LG remote measurements: Type AKB73315611, Ver1.1 from 2011.03.01
* Internal crystal: 4 MHz
* Header: 8.9 ms mark 4.15 ms space
* Data: 500 / 540 and 500 / 1580;
* Clock is nor synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark
* Duty: 9 us on 17 us off => around 33 % duty
* NO REPEAT: If value like temperature has changed during long press, the last value is send at button release
* If you do a double press -tested with the fan button-, the next value can be sent after 118 ms
*
* The codes of the LG air conditioner are documented in https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/ac_LG.cpp
*/
#define LG_ADDRESS_BITS 8
#define LG_COMMAND_BITS 16
#define LG_CHECKSUM_BITS 4
#define LG_BITS (LG_ADDRESS_BITS + LG_COMMAND_BITS + LG_CHECKSUM_BITS) // 28
#define LG_UNIT 560 // like NEC
#define LG_UNIT 500 // 19 periods of 38 kHz
#define LG_HEADER_MARK (16 * LG_UNIT) // 9000
#define LG_HEADER_SPACE (8 * LG_UNIT) // 4500
#define LG_HEADER_MARK (18 * LG_UNIT) // 9000
#define LG_HEADER_SPACE 4200
// used for some LG air conditioners e.g. AKB75215403
#define LG2_UNIT 500 // 19 periods of 38 kHz
#define LG2_HEADER_MARK (6 * LG2_UNIT) // 3000
#define LG2_HEADER_SPACE (19 * LG2_UNIT) // 9500
#define LG2_HEADER_MARK (6 * LG_UNIT) // 3000
#define LG2_HEADER_SPACE (19 * LG_UNIT) // 9500
#define LG_BIT_MARK LG_UNIT
#define LG_ONE_SPACE (3 * LG_UNIT) // 1690
#define LG_ZERO_SPACE LG_UNIT
#define LG2_BIT_MARK LG2_UNIT
#define LG2_ONE_SPACE (3 * LG2_UNIT) // 1500
#define LG2_ZERO_SPACE LG2_UNIT
#define LG_ONE_SPACE 1580 // 60 periods of 38 kHz
#define LG_ZERO_SPACE 550
#define LG_REPEAT_HEADER_SPACE (4 * LG_UNIT) // 2250
#define LG_AVERAGE_DURATION 58000 // LG_HEADER_MARK + LG_HEADER_SPACE + 32 * 2,5 * LG_UNIT) + LG_UNIT // 2.5 because we assume more zeros than ones
@ -133,7 +138,7 @@ void IRsend::sendLGRaw(uint32_t aRawData, uint_fast8_t aNumberOfRepeats, bool aI
mark(LG2_HEADER_MARK);
space(LG2_HEADER_SPACE);
// MSB first
sendPulseDistanceWidthData(LG2_BIT_MARK, LG2_ONE_SPACE, LG2_BIT_MARK, LG2_ZERO_SPACE, aRawData, LG_BITS, PROTOCOL_IS_MSB_FIRST,
sendPulseDistanceWidthData(LG_BIT_MARK, LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, aRawData, LG_BITS, PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT);
} else {
mark(LG_HEADER_MARK);
@ -167,7 +172,6 @@ void IRsend::sendLGRaw(uint32_t aRawData, uint_fast8_t aNumberOfRepeats, bool aI
bool IRrecv::decodeLG() {
decode_type_t tProtocol = LG;
uint16_t tHeaderSpace = LG_HEADER_SPACE;
uint16_t tUnit = LG_UNIT;
// Check we have the right amount of data (60). The +4 is for initial gap, start bit mark and space + stop bit mark.
if (decodedIRData.rawDataPtr->rawlen != ((2 * LG_BITS) + 4) && (decodedIRData.rawDataPtr->rawlen != 4)) {
@ -187,7 +191,6 @@ bool IRrecv::decodeLG() {
} else {
tProtocol = LG2;
tHeaderSpace = LG2_HEADER_SPACE;
tUnit = LG2_UNIT;
}
}
@ -213,15 +216,14 @@ bool IRrecv::decodeLG() {
return false;
}
// if (!decodePulseDistanceData(LG_BITS, 3, LG_BIT_MARK, LG_ONE_SPACE, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
if (!decodePulseDistanceData(LG_BITS, 3, tUnit, 3 * tUnit, tUnit, PROTOCOL_IS_MSB_FIRST)) { // costs 20 bytes program space, compared with using constants for 1 protocol
if (!decodePulseDistanceData(LG_BITS, 3, LG_BIT_MARK, LG_ONE_SPACE, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
DEBUG_PRINT(F("LG: "));
DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * LG_BITS)], tUnit)) {
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * LG_BITS)], LG_BIT_MARK)) {
DEBUG_PRINT(F("LG: "));
DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
return false;