IRremote
ir_LG.hpp
Go to the documentation of this file.
1 /*
2  * ir_LG.hpp
3  *
4  * Contains functions for receiving and sending LG IR Protocol for air conditioner
5  *
6  * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
7  *
8  ************************************************************************************
9  * MIT License
10  *
11  * Copyright (c) 2017-2025 Darryl Smith, Armin Joachimsmeyer
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a copy
14  * of this software and associated documentation files (the "Software"), to deal
15  * in the Software without restriction, including without limitation the rights
16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17  * copies of the Software, and to permit persons to whom the Software is furnished
18  * to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included in all
21  * copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
24  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
25  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
28  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29  *
30  ************************************************************************************
31  */
32 #ifndef _IR_LG_HPP
33 #define _IR_LG_HPP
34 
35 #include "LocalDebugLevelStart.h"
36 
40 //==============================================================================
41 // L GGGG
42 // L G
43 // L G GG
44 // L G G
45 // LLLLL GGG
46 //==============================================================================
47 /*
48  * Protocol=LG Address=0xF1 Command=0x7776 Raw-Data=0xF17776B 28 bits MSB first
49  +8950,-4150
50  + 500,-1550 + 550,-1550 + 500,-1550 + 500,-1600
51  + 500,- 700 + 350,- 600 + 450,- 600 + 450,-1550
52  + 500,- 550 + 500,-1550 + 500,-1600 + 500,-1550
53  + 550,- 550 + 500,-1550 + 500,-1550 + 550,-1550
54  + 500,- 550 + 500,-1550 + 500,-1600 + 500,-1550
55  + 500,- 550 + 500,-1550 + 500,-1600 + 500,- 550
56  + 500,-1550 + 500,- 600 + 450,-1600 + 500,-1550
57  + 500
58  Sum: 62400
59  */
60 
61 // LG originally added by Darryl Smith (based on the JVC protocol)
62 // see: https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master/examples/LGAirConditionerSendDemo
63 // see: https://www.mikrocontroller.net/articles/IRMP_-_english#LGAIR
64 // MSB first, 1 start bit + 8 bit address + 16 bit command + 4 bit checksum + 1 stop bit (28 data bits).
65 // Bit and repeat timing is like NEC
66 // LG2 has different header timing and a shorter bit time
67 /*
68  * LG remote IR-LED measurements: Type AKB 73315611 for air conditioner, Ver1.1 from 2011.03.01
69  * Protocol: LG2
70  * Internal crystal: 4 MHz
71  * Header: 8.9 ms mark 4.15 ms space
72  * Data: 500 / 540 and 500 / 1580;
73  * Clock is not synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark
74  * Duty: 9 us on 17 us off => around 33 % duty
75  * NO REPEAT: If value like temperature has changed during long press, the last value is send at button release.
76  * If you do a double press, the next value can be sent after around 118 ms. Tested with the fan button.
77 
78  * LG remote IR-LED measurements: Type AKB 75095308 for LG TV
79  * Protocol: NEC!!!
80  * Frequency 37.88 kHz
81  * Header: 9.0 ms mark 4.5 ms space
82  * Data: 560 / 560 and 560 / 1680;
83  * Clock is synchronized with gate, mark always starts with a full period
84  * Duty: 13 us on 13 us off => 50 % duty
85  * Repeat: 110 ms 9.0 ms mark, 2250 us space, 560 stop
86  * LSB first!
87  *
88  * The codes of the LG air conditioner are documented in https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/ac_LG.cpp
89  */
90 #define LG_ADDRESS_BITS 8
91 #define LG_COMMAND_BITS 16
92 #define LG_CHECKSUM_BITS 4
93 #define LG_BITS (LG_ADDRESS_BITS + LG_COMMAND_BITS + LG_CHECKSUM_BITS) // 28
94 
95 #define LG_UNIT 500 // 19 periods of 38 kHz
96 
97 #define LG_HEADER_MARK (18 * LG_UNIT) // 9000
98 #define LG_HEADER_SPACE 4200 // 4200 | 84
99 
100 #define LG2_HEADER_MARK (19 * LG_UNIT) // 9500
101 #define LG2_HEADER_SPACE (6 * LG_UNIT) // 3000
102 
103 #define LG_BIT_MARK LG_UNIT
104 #define LG_ONE_SPACE 1580 // 60 periods of 38 kHz
105 #define LG_ZERO_SPACE 550
106 
107 #define LG_REPEAT_HEADER_SPACE (4 * LG_UNIT) // 2250
108 #define LG_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
109 //#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
110 //#define LG_REPEAT_DURATION (LG_HEADER_MARK + LG_REPEAT_HEADER_SPACE + LG_BIT_MARK)
111 //#define LG_REPEAT_DISTANCE (LG_REPEAT_PERIOD - LG_AVERAGE_DURATION) // 52 ms
112 
115 
118 
119 /************************************
120  * Start of send and decode functions
121  ************************************/
122 /*
123  * Send special LG2 repeat - not used internally
124  */
126  enableIROut (LG_KHZ); // 38 kHz
127  mark(LG2_HEADER_MARK); // + 3000
128  space(LG_REPEAT_HEADER_SPACE); // - 2250
129  mark(LG_BIT_MARK); // + 500
130 }
131 
137  IrSender.enableIROut(LG_KHZ); // 38 kHz
138  IrSender.mark(LG2_HEADER_MARK); // + 3000
140  IrSender.mark(LG_BIT_MARK); // + 500
141 }
142 
143 uint32_t IRsend::computeLGRawDataAndChecksum(uint8_t aAddress, uint16_t aCommand) {
144  uint32_t tRawData = ((uint32_t) aAddress << (LG_COMMAND_BITS + LG_CHECKSUM_BITS)) | ((uint32_t) aCommand << LG_CHECKSUM_BITS);
145  /*
146  * My guess of the 4 bit checksum
147  * Addition of all 4 nibbles of the 16 bit command
148  */
149  uint8_t tChecksum = 0;
150  uint16_t tTempForChecksum = aCommand;
151  for (int i = 0; i < 4; ++i) {
152  tChecksum += tTempForChecksum & 0xF; // add low nibble
153  tTempForChecksum >>= 4; // shift by a nibble
154  }
155  return (tRawData | (tChecksum & 0xF));
156 }
157 
161 void IRsend::sendLG(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
163 }
164 
168 void IRsend::sendLG2(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
170 }
171 
173  decode_type_t tProtocol = LG;
174  uint16_t tHeaderSpace = LG_HEADER_SPACE;
175 
176  /*
177  * First check for right data length
178  * Next check start bit
179  * Next try the decode
180  */
181 
182 // Check we have the right amount of data (60). The +4 is for initial gap, start bit mark and space + stop bit mark.
183  if (decodedIRData.rawlen != ((2 * LG_BITS) + 4) && (decodedIRData.rawlen != 4)) {
184  // Is only printed, if LOCAL_DEBUG is defined globally
185  DEBUG_PRINT(F("LG: Data length="));
187  DEBUG_PRINTLN(F(" is not 60 or 4"));
188  return false;
189  }
190 
191 // Check header "mark" this must be done for repeat and data
194  tProtocol = LG2;
195  tHeaderSpace = LG2_HEADER_SPACE;
196  } else {
197  DEBUG_PRINTLN(F("LG: Header mark is wrong"));
198  return false; // neither LG nor LG2 header
199  }
200  }
201 
202 // Check for repeat - here we have another header space length
203  if (decodedIRData.rawlen == 4) {
209  return true;
210  }
211  DEBUG_PRINTLN(F("LG: Repeat header space is wrong"));
212  return false;
213  }
214 
215 // Check command header space
216  if (!matchSpace(irparams.rawbuf[2], tHeaderSpace)) {
217  DEBUG_PRINTLN(F("LG: Header space length is wrong"));
218  return false;
219  }
220 
221  decodePulseDistanceWidthData_P(&LGProtocolConstants, LG_BITS);
222 
223 // Success
227 
228  /*
229  * My guess of the checksum
230  */
231  uint8_t tChecksum = 0;
232  uint16_t tTempForChecksum = decodedIRData.command;
233  for (int i = 0; i < 4; ++i) {
234  tChecksum += tTempForChecksum & 0xF; // add low nibble
235  tTempForChecksum >>= 4; // shift by a nibble
236  }
237 // Checksum check
238  if ((tChecksum & 0xF) != (decodedIRData.decodedRawData & 0xF)) {
239 
240  DEBUG_PRINT(F("LG: 4 bit checksum is not correct. expected=0x"));
241  DEBUG_PRINT(tChecksum, HEX);
242  DEBUG_PRINT(F(" received=0x"));
244  DEBUG_PRINT(F(" data=0x"));
246 
248  }
249 
250  decodedIRData.protocol = tProtocol; // LG or LG2
252 
253  return true;
254 }
255 
256 /*********************************************************************************
257  * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
258  *********************************************************************************/
259 
265 void IRsend::sendLGRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats) {
266  sendPulseDistanceWidth_P(&LGProtocolConstants, aRawData, LG_BITS, aNumberOfRepeats);
267 }
268 
270  unsigned int offset = 1; // Skip first space
271 
272 // Check we have enough data (60) - +4 for initial gap, start bit mark and space + stop bit mark
273  if (aResults->rawlen != (2 * LG_BITS) + 4) {
274  return false;
275  }
276 
277 // Initial mark/space
278  if (!matchMark(aResults->rawbuf[offset], LG_HEADER_MARK)) {
279  return false;
280  }
281  offset++;
282 
283  if (!matchSpace(aResults->rawbuf[offset], LG_HEADER_SPACE)) {
284  return false;
285  }
286  offset++;
287 
289 
290 // Stop bit
291  if (!matchMark(aResults->rawbuf[offset + (2 * LG_BITS)], LG_BIT_MARK)) {
292  DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
293  return false;
294  }
295 
296 // Success
297  aResults->value = decodedIRData.decodedRawData;
298  aResults->bits = LG_BITS;
299  aResults->decode_type = LG;
301  return true;
302 }
303 
304 //+=============================================================================
305 void IRsend::sendLG(unsigned long data, int nbits) {
306 // Set IR carrier frequency
308 #if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__))
310  F( "The function sendLG(data, nbits) is deprecated and may not work as expected! Use sendLGRaw(data, NumberOfRepeats) or better sendLG(Address, Command, NumberOfRepeats)."));
311 #endif
312 // Header
315 
316 // Data + stop bit
318 }
319 
321 #include "LocalDebugLevelEnd.h"
322 
323 #endif // _IR_LG_HPP
IRData::address
uint16_t address
Decoded address, Distance protocol (tMarkTicksLong (if tMarkTicksLong == 0, then tMarkTicksShort) << ...
Definition: IRremoteInt.h:164
LG_REPEAT_HEADER_SPACE
#define LG_REPEAT_HEADER_SPACE
Definition: ir_LG.hpp:107
IRsend::sendLGRaw
void sendLGRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats=NO_REPEATS)
Here you can put your raw data, even one with "wrong" checksum.
Definition: ir_LG.hpp:265
decode_results
Results returned from old decoders !!!deprecated!!!
Definition: IRremoteInt.h:193
decode_results::rawbuf
uint16_t * rawbuf
Definition: IRremoteInt.h:204
IRrecv::decodePulseDistanceWidthData
void decodePulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset=3)
Decode pulse distance protocols for PulseDistanceWidthProtocolConstants.
Definition: IRReceive.hpp:1076
PROTOCOL_IS_MSB_FIRST
#define PROTOCOL_IS_MSB_FIRST
Definition: IRProtocol.h:156
IRrecv::lastDecodedProtocol
decode_type_t lastDecodedProtocol
Definition: IRremoteInt.h:404
IRData::numberOfBits
uint16_t numberOfBits
Number of bits received for data (address + command + parity) - to determine protocol length if diffe...
Definition: IRremoteInt.h:173
DEBUG_PRINT
#define DEBUG_PRINT(...)
Definition: LocalDebugLevelStart.h:79
LG_REPEAT_PERIOD
#define LG_REPEAT_PERIOD
Definition: ir_LG.hpp:108
IRsend::aNumberOfRepeats
void int_fast8_t aNumberOfRepeats
Definition: IRremoteInt.h:528
IRrecv::decodePulseDistanceWidthData_P
void decodePulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset=3)
Definition: IRReceive.hpp:1112
MICROS_IN_ONE_MILLI
#define MICROS_IN_ONE_MILLI
Definition: IRremote.hpp:217
PROTOCOL_IS_PULSE_DISTANCE
#define PROTOCOL_IS_PULSE_DISTANCE
Definition: IRProtocol.h:151
IRsend::mark
void mark(uint16_t aMarkMicros)
Sends an IR mark for the specified number of microseconds.
Definition: IRSend.hpp:1247
IRDATA_FLAGS_IS_REPEAT
#define IRDATA_FLAGS_IS_REPEAT
The gap between the preceding frame is as smaller than the maximum gap expected for a repeat....
Definition: IRProtocol.h:125
decode_type_t
decode_type_t
An enum consisting of all supported formats.
Definition: IRProtocol.h:97
decode_results::bits
uint8_t bits
Definition: IRremoteInt.h:199
decode_results::decode_type
decode_type_t decode_type
Definition: IRremoteInt.h:196
matchSpace
bool matchSpace(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros)
Compensate for spaces shortened by demodulator hardware.
Definition: IRReceive.hpp:1375
sendLG2SpecialRepeat
void sendLG2SpecialRepeat()
Static function for sending special repeat frame.
Definition: ir_LG.hpp:136
IRrecv::decodeLGMSB
bool decodeLGMSB(decode_results *aResults)
Definition: ir_LG.hpp:269
LocalDebugLevelStart.h
IRDATA_FLAGS_PARITY_FAILED
#define IRDATA_FLAGS_PARITY_FAILED
The current (autorepeat) frame violated parity check.
Definition: IRProtocol.h:127
LG_CHECKSUM_BITS
#define LG_CHECKSUM_BITS
Definition: ir_LG.hpp:92
decode_results::value
uint32_t value
Definition: IRremoteInt.h:198
PulseDistanceWidthProtocolConstants
Definition: IRProtocol.h:139
IRsend::sendPulseDistanceWidth_P
void sendPulseDistanceWidth_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, IRDecodedRawDataType aData, uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats)
Definition: IRSend.hpp:1158
IRrecv::decodedIRData
IRData decodedIRData
Definition: IRremoteInt.h:401
IRsend::sendLG2Repeat
void sendLG2Repeat()
Definition: ir_LG.hpp:125
LG_COMMAND_BITS
#define LG_COMMAND_BITS
Definition: ir_LG.hpp:91
IRData::flags
uint8_t flags
IRDATA_FLAGS_IS_REPEAT, IRDATA_FLAGS_WAS_OVERFLOW etc. See IRDATA_FLAGS_* definitions above.
Definition: IRremoteInt.h:174
LG_ONE_SPACE
#define LG_ONE_SPACE
Definition: ir_LG.hpp:104
IRsend::computeLGRawDataAndChecksum
uint32_t computeLGRawDataAndChecksum(uint8_t aAddress, uint16_t aCommand)
Definition: ir_LG.hpp:143
IRData::command
uint16_t command
Decoded command, Distance protocol (tMarkTicksShort << 8) | tSpaceTicksShort.
Definition: IRremoteInt.h:165
LG_BITS
#define LG_BITS
Definition: ir_LG.hpp:93
PROGMEM
struct PulseDistanceWidthProtocolConstants const LGProtocolConstants PROGMEM
Definition: ir_LG.hpp:113
IRData::decodedRawData
IRDecodedRawDataType decodedRawData
Up to 32/64 bit decoded raw data, to be used for send<protocol>Raw functions.
Definition: IRremoteInt.h:167
matchMark
bool matchMark(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros)
Compensate for marks exceeded by demodulator hardware.
Definition: IRReceive.hpp:1327
irparams_struct::rawbuf
IRRawbufType rawbuf[RAW_BUFFER_LENGTH]
raw data / tick counts per mark/space. With 8 bit we can only store up to 12.7 ms....
Definition: IRremoteInt.h:147
LG_KHZ
#define LG_KHZ
Definition: IRProtocol.h:170
IRrecv::irparams
irparams_struct irparams
Definition: IRremoteInt.h:400
LG2_HEADER_MARK
#define LG2_HEADER_MARK
Definition: ir_LG.hpp:100
IRrecv::lastDecodedCommand
uint16_t lastDecodedCommand
Definition: IRremoteInt.h:406
DEBUG_PRINTLN
#define DEBUG_PRINTLN(...)
Definition: LocalDebugLevelStart.h:80
IrSender
IRsend IrSender
Definition: IRSend.hpp:62
IRDATA_FLAGS_IS_MSB_FIRST
#define IRDATA_FLAGS_IS_MSB_FIRST
Value is mainly determined by the (known) protocol.
Definition: IRProtocol.h:133
LG2_HEADER_SPACE
#define LG2_HEADER_SPACE
Definition: ir_LG.hpp:101
LG2
@ LG2
Definition: IRProtocol.h:98
IRsend::space
static void space(uint16_t aSpaceMicros)
Sends an IR space for the specified number of microseconds.
Definition: IRSend.hpp:1458
LG_HEADER_MARK
#define LG_HEADER_MARK
Definition: ir_LG.hpp:97
LG_ZERO_SPACE
#define LG_ZERO_SPACE
Definition: ir_LG.hpp:105
IRData::rawlen
IRRawlenType rawlen
Counter of entries in rawbuf of last received frame.
Definition: IRremoteInt.h:182
IRsend::sendPulseDistanceWidthData
void sendPulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRDecodedRawDataType aData, uint_fast8_t aNumberOfBits)
Sends PulseDistance from data contained in parameter using ProtocolConstants structure for timing etc...
Definition: IRSend.hpp:662
LG_HEADER_SPACE
#define LG_HEADER_SPACE
Definition: ir_LG.hpp:98
IRrecv::decodeLG
bool decodeLG()
Definition: ir_LG.hpp:172
LG_BIT_MARK
#define LG_BIT_MARK
Definition: ir_LG.hpp:103
decode_results::rawlen
uint_fast8_t rawlen
Definition: IRremoteInt.h:205
IRData::protocol
decode_type_t protocol
UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ...
Definition: IRremoteInt.h:163
IRsend::sendLG
void sendLG(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats)
LG uses the NEC repeat.
Definition: ir_LG.hpp:161
IRrecv::lastDecodedAddress
uint16_t lastDecodedAddress
Definition: IRremoteInt.h:405
IRsend::aCommand
void aCommand
Definition: IRremoteInt.h:617
LG
@ LG
Definition: IRProtocol.h:98
IRsend::enableIROut
void enableIROut(uint_fast8_t aFrequencyKHz)
Enables IR output.
Definition: IRSend.hpp:1498
LocalDebugLevelEnd.h
IRsend::sendLG2
void sendLG2(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats)
LG2 uses a special repeat.
Definition: ir_LG.hpp:168
sendNECSpecialRepeat
void sendNECSpecialRepeat()
Static function variant of IRsend::sendNECRepeat For use in ProtocolConstants.
Definition: ir_NEC.hpp:143