Skip to content

Commit

Permalink
Add support for a second LG protocol variant.
Browse files Browse the repository at this point in the history
Ref: #548
  • Loading branch information
crankyoldgit committed Oct 11, 2018
1 parent 9aaa511 commit 667160a
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 28 deletions.
10 changes: 9 additions & 1 deletion examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,9 @@ void handleRoot() {
"<option value='13'>Dish</option>"
"<option value='43'>GICable</option>"
"<option value='6'>JVC</option>"
"<option value='10'>LG</option>"
"<option value='36'>Lasertag</option>"
"<option value='10'>LG</option>"
"<option value='51'>LG2</option>"
"<option value='47'>Lutron</option>"
"<option value='35'>MagiQuest</option>"
"<option value='34'>Midea</option>"
Expand Down Expand Up @@ -1446,6 +1447,13 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
bits = kLutronBits;
irsend.sendLutron(code, bits, repeat);
break;
#endif
#if SEND_LG
case LG2: // 51
if (bits == 0)
bits = kLgBits;
irsend.sendLG2(code, bits, repeat);
break;
#endif
default:
// If we got here, we didn't know how to send it.
Expand Down
2 changes: 2 additions & 0 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ enum decode_type_t {
LUTRON,
ELECTRA_AC,
PANASONIC_AC,
PIONEER,
LG2,
};

// Message lengths & required repeat values
Expand Down
3 changes: 3 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,9 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
#if SEND_LG
case LG: sendLG(data, nbits); break;
#endif
#if SEND_LG
case LG2: sendLG2(data, nbits); break;
#endif
#if SEND_WHYNTER
case WHYNTER: sendWhynter(data, nbits); break;
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
#if SEND_LG
void sendLG(uint64_t data, uint16_t nbits = kLgBits,
uint16_t repeat = kNoRepeat);
void sendLG2(uint64_t data, uint16_t nbits = kLgBits,
uint16_t repeat = kNoRepeat);
uint32_t encodeLG(uint16_t address, uint16_t command);
#endif
#if (SEND_SHARP || SEND_DENON)
Expand Down
1 change: 1 addition & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ std::string typeToString(const decode_type_t protocol,
case JVC: result = "JVC"; break;
case KELVINATOR: result = "KELVINATOR"; break;
case LG: result = "LG"; break;
case LG2: result = "LG2"; break;
case LASERTAG: result = "LASERTAG"; break;
case LUTRON: result = "LUTRON"; break;
case MAGIQUEST: result = "MAGIQUEST"; break;
Expand Down
136 changes: 109 additions & 27 deletions src/ir_LG.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2015 Darryl Smith
// Copyright 2015 cheaplin
// Copyright 2017 David Conran
// Copyright 2017, 2018 David Conran

#include "ir_LG.h"
#include <algorithm>
Expand All @@ -16,6 +16,11 @@

// LG decode originally added by Darryl Smith (based on the JVC protocol)
// LG send originally added by https://github.com/chaeplin
//
// Known supported devices:
// IR Remotes:
// 6711A20083V
// AKB74395308

// Constants
const uint16_t kLgTick = 50;
Expand All @@ -24,24 +29,31 @@ const uint16_t kLgHdrMark = kLgHdrMarkTicks * kLgTick; // 8500
const uint16_t kLgHdrSpaceTicks = 85;
const uint16_t kLgHdrSpace = kLgHdrSpaceTicks * kLgTick; // 4250
const uint16_t kLgBitMarkTicks = 11;
const uint16_t kLgBitMark = kLgBitMarkTicks * kLgTick;
const uint16_t kLgBitMark = kLgBitMarkTicks * kLgTick; // 550
const uint16_t kLgOneSpaceTicks = 32;
const uint16_t kLgOneSpace = kLgOneSpaceTicks * kLgTick;
const uint16_t kLgOneSpace = kLgOneSpaceTicks * kLgTick; // 1600
const uint16_t kLgZeroSpaceTicks = 11;
const uint16_t kLgZeroSpace = kLgZeroSpaceTicks * kLgTick;
const uint16_t kLgZeroSpace = kLgZeroSpaceTicks * kLgTick; // 550
const uint16_t kLgRptSpaceTicks = 45;
const uint16_t kLgRptSpace = kLgRptSpaceTicks * kLgTick;
const uint16_t kLgRptSpace = kLgRptSpaceTicks * kLgTick; // 2250
const uint16_t kLgMinGapTicks = 795;
const uint16_t kLgMinGap = kLgMinGapTicks * kLgTick;
const uint16_t kLgMinGap = kLgMinGapTicks * kLgTick; // 39750
const uint16_t kLgMinMessageLengthTicks = 2161;
const uint32_t kLgMinMessageLength = kLgMinMessageLengthTicks * kLgTick;

const uint16_t kLg32HdrMarkTicks = 90;
const uint16_t kLg32HdrMark = kLg32HdrMarkTicks * kLgTick;
const uint16_t kLg32HdrMark = kLg32HdrMarkTicks * kLgTick; // 4500
const uint16_t kLg32HdrSpaceTicks = 89;
const uint16_t kLg32HdrSpace = kLg32HdrSpaceTicks * kLgTick;
const uint16_t kLg32HdrSpace = kLg32HdrSpaceTicks * kLgTick; // 4450
const uint16_t kLg32RptHdrMarkTicks = 179;
const uint16_t kLg32RptHdrMark = kLg32RptHdrMarkTicks * kLgTick;
const uint16_t kLg32RptHdrMark = kLg32RptHdrMarkTicks * kLgTick; // 8950

const uint16_t kLg2HdrMarkTicks = 64;
const uint16_t kLg2HdrMark = kLg2HdrMarkTicks * kLgTick; // 3200
const uint16_t kLg2HdrSpaceTicks = 197;
const uint16_t kLg2HdrSpace = kLg2HdrSpaceTicks * kLgTick; // 9850
const uint16_t kLg2BitMarkTicks = 10;
const uint16_t kLg2BitMark = kLg2BitMarkTicks * kLgTick; // 500

#if (SEND_LG || DECODE_LG)
// Calculate the rolling 4-bit wide checksum over all of the data.
Expand All @@ -68,6 +80,8 @@ uint8_t calcLGChecksum(uint16_t data) {
//
// Notes:
// LG has a separate message to indicate a repeat, like NEC does.
// Supports:
// IR Remote models: 6711A20083V
void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
uint16_t repeatHeaderMark = 0;

Expand Down Expand Up @@ -98,13 +112,54 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
38, true, repeat - 1, 50);
}

// Construct a raw 28-bit LG message from the supplied address & command.
// Send an LG Variant-2 formatted message.
//
// Args:
// data: The contents of the message you want to send.
// nbits: The bit size of the message being sent.
// Typically kLgBits or kLg32Bits.
// repeat: The number of times you want the message to be repeated.
//
// Status: Beta / Should be working.
//
// Notes:
// LG has a separate message to indicate a repeat, like NEC does.
// Supports:
// IR Remote models: AKB74395308
void IRsend::sendLG2(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits >= kLg32Bits) {
// Let the original routine handle it.
sendLG(data, nbits, repeat); // Send it as a single Samsung message.
return;
}

// LGv2 (28-bit) protocol.
sendGeneric(kLg2HdrMark, kLg2HdrSpace,
kLgBitMark, kLgOneSpace,
kLgBitMark, kLgZeroSpace,
kLgBitMark,
kLgMinGap, kLgMinMessageLength,
data, nbits, 38, true, 0, // Repeats are handled later.
50);

// TODO(crackn): Verify the details of what repeat messages look like.
// Repeat
// Protocol has a mandatory repeat-specific code sent after every command.
if (repeat)
sendGeneric(kLg2HdrMark, kLgRptSpace,
0, 0, 0, 0, // No data is sent.
kLgBitMark, kLgMinGap, kLgMinMessageLength,
0, 0, // No data.
38, true, repeat - 1, 50);
}

// Construct a raw 28-bit LG message code from the supplied address & command.
//
// Args:
// address: The address code.
// command: The command code.
// Returns:
// A raw 28-bit LG message suitable for sendLG().
// A raw 28-bit LG message code suitable for sendLG() etc.
//
// Status: BETA / Should work.
//
Expand Down Expand Up @@ -136,6 +191,9 @@ uint32_t IRsend::encodeLG(uint16_t address, uint16_t command) {
// Note:
// LG 32bit protocol appears near identical to the Samsung protocol.
// They possibly differ on how they repeat and initial HDR mark.
//
// Supports:
// IR Remote models: 6711A20083V, AKB74395308

// Ref:
// https://funembedded.wordpress.com/2014/11/08/ir-remote-control-for-lg-conditioner-using-stm32f302-mcu-on-mbed-platform/
Expand All @@ -152,35 +210,56 @@ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) {

uint64_t data = 0;
uint16_t offset = kStartOffset;
bool isLg2 = false;

// Header
if (!matchMark(results->rawbuf[offset], kLgHdrMark) &&
!matchMark(results->rawbuf[offset], kLg32HdrMark)) return false;
uint32_t m_tick;
if (matchMark(results->rawbuf[offset], kLgHdrMark))
if (matchMark(results->rawbuf[offset], kLgHdrMark)) {
m_tick = results->rawbuf[offset++] * kRawTick / kLgHdrMarkTicks;
else
} else if (matchMark(results->rawbuf[offset], kLg2HdrMark)) {
m_tick = results->rawbuf[offset++] * kRawTick / kLg2HdrMarkTicks;
isLg2 = true;
} else if (matchMark(results->rawbuf[offset], kLg32HdrMark)) {
m_tick = results->rawbuf[offset++] * kRawTick / kLg32HdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], kLgHdrSpace) &&
!matchSpace(results->rawbuf[offset], kLg32HdrSpace)) return false;
} else {
return false;
}
uint32_t s_tick;
if (matchSpace(results->rawbuf[offset], kLgHdrSpace))
s_tick = results->rawbuf[offset++] * kRawTick / kLgHdrSpaceTicks;
else
s_tick = results->rawbuf[offset++] * kRawTick / kLg32HdrSpaceTicks;
if (isLg2) {
if (matchSpace(results->rawbuf[offset], kLg2HdrSpace))
s_tick = results->rawbuf[offset++] * kRawTick / kLg2HdrSpaceTicks;
else
return false;
} else {
if (matchSpace(results->rawbuf[offset], kLgHdrSpace))
s_tick = results->rawbuf[offset++] * kRawTick / kLgHdrSpaceTicks;
else if (matchSpace(results->rawbuf[offset], kLg2HdrSpace))
s_tick = results->rawbuf[offset++] * kRawTick / kLg32HdrSpaceTicks;
else
return false;
}

// Set up the expected tick sizes based on variant.
uint16_t bitmarkticks;
if (isLg2) {
bitmarkticks = kLg2BitMarkTicks;
} else {
bitmarkticks = kLgBitMarkTicks;
}

// Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
kLgBitMarkTicks * m_tick,
bitmarkticks * m_tick,
kLgOneSpaceTicks * s_tick,
kLgBitMarkTicks * m_tick,
kLgZeroSpaceTicks * s_tick);
bitmarkticks * m_tick,
kLgZeroSpaceTicks * s_tick,
kTolerance, 0);
if (data_result.success == false) return false;
data = data_result.data;
offset += data_result.used;

// Footer
if (!matchMark(results->rawbuf[offset++], kLgBitMarkTicks * m_tick))
if (!matchMark(results->rawbuf[offset++], bitmarkticks * m_tick))
return false;
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], kLgMinGapTicks * s_tick))
Expand All @@ -195,7 +274,7 @@ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) {
return false;
if (!matchSpace(results->rawbuf[offset++], kLgRptSpaceTicks * s_tick))
return false;
if (!matchMark(results->rawbuf[offset++], kLgBitMarkTicks * m_tick))
if (!matchMark(results->rawbuf[offset++], bitmarkticks * m_tick))
return false;
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], kLgMinGapTicks * s_tick))
Expand All @@ -209,7 +288,10 @@ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) {
return false; // The last 4 bits sent are the expected checksum.

// Success
results->decode_type = LG;
if (isLg2)
results->decode_type = LG2;
else
results->decode_type = LG;
results->bits = nbits;
results->value = data;
results->command = command;
Expand Down
56 changes: 56 additions & 0 deletions test/ir_LG_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,59 @@ TEST(TestDecodeLG, FailToDecodeNonLGExample) {
ASSERT_FALSE(irrecv.decodeLG(&irsend.capture));
ASSERT_FALSE(irrecv.decodeLG(&irsend.capture, kLgBits, false));
}

// Tests for sendLG2().

// Test sending typical data only.
TEST(TestSendLG2, SendDataOnly) {
IRsendTest irsend(0);
irsend.begin();

irsend.reset();
irsend.sendLG2(0x880094D);
EXPECT_EQ(
"m3200s9850"
"m550s1600m550s550m550s550m550s550m550s1600m550s550m550s550m550s550"
"m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550"
"m550s1600m550s550m550s550m550s1600m550s550m550s1600m550s550m550s550"
"m550s1600m550s1600m550s550m550s1600"
"m550s55250", irsend.outputStr());
}

TEST(TestDecodeLG2, SyntheticExample) {
IRsendTest irsend(0);
IRrecv irrecv(0);
irsend.begin();

irsend.reset();
irsend.sendLG2(0x880094D);
irsend.makeDecodeResult();

ASSERT_TRUE(irrecv.decodeLG(&irsend.capture));
ASSERT_EQ(LG2, irsend.capture.decode_type);
EXPECT_EQ(kLgBits, irsend.capture.bits);
EXPECT_EQ(0x880094D, irsend.capture.value);
}


// Verify decoding of LG variant 2 messages.
TEST(TestDecodeLG2, RealLG2Example) {
IRsendTest irsend(0);
IRrecv irrecv(0);
irsend.begin();

irsend.reset();
// From issue #548
uint16_t rawData[59] = {3154, 9834, 520, 1634, 424, 606, 424, 568, 462, 570,
462, 1564, 508, 568, 458, 544, 500, 546, 508, 530, 508, 532, 506, 566,
464, 568, 460, 578, 464, 568, 464, 532, 506, 552, 474, 1592, 506, 568,
460, 570, 462, 1564, 506, 606, 424, 1640, 424, 616, 422, 570, 462, 1616,
460, 1584, 500, 544, 506, 1598, 490}; // UNKNOWN F6D13AE8
irsend.sendRaw(rawData, 59, 38000);
irsend.makeDecodeResult();

ASSERT_TRUE(irrecv.decodeLG(&irsend.capture));
ASSERT_EQ(LG2, irsend.capture.decode_type);
EXPECT_EQ(kLgBits, irsend.capture.bits);
EXPECT_EQ(0x880094D, irsend.capture.value);
}

0 comments on commit 667160a

Please sign in to comment.