Skip to content

Commit

Permalink
Add Lego Power Functions send protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
philipphenkel committed Apr 27, 2016
1 parent d064c7d commit 34e5cd8
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 3 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ env:
- PLATFORMIO_CI_SRC=examples/IRtest PLATFORMIO_BUILD_FLAGS="-DSEND_NEC -DSEND_SONY -DSEND_RC5 -DSEND_RC6"
- PLATFORMIO_CI_SRC=examples/IRtest2 PLATFORMIO_BUILD_FLAGS="-DSEND_NEC -DSEND_SONY -DSEND_RC5 -DSEND_RC6"
- PLATFORMIO_CI_SRC=examples/JVCPanasonicSendDemo PLATFORMIO_BUILD_FLAGS="-DSEND_JVC -DSEND_PANASONIC"
- PLATFORMIO_CI_SRC=examples/LegoPowerFunctionsSendDemo PLATFORMIO_BUILD_FLAGS="-DSEND_LEGO_PF"
- PLATFORMIO_CI_SRC=examples/IRremoteInfo

install:
Expand Down
14 changes: 13 additions & 1 deletion IRremote.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
#define SEND_AIWA_RC_T501 1

#define DECODE_LG 1
#define SEND_LG 1
#define SEND_LG 1

#define DECODE_SANYO 1
#define SEND_SANYO 0 // NOT WRITTEN
Expand All @@ -76,6 +76,9 @@
#define DECODE_PRONTO 0 // This function doe not logically make sense
#define SEND_PRONTO 1

#define DECODE_LEGO_PF 0 // NOT WRITTEN
#define SEND_LEGO_PF 1

//------------------------------------------------------------------------------
// When sending a Pronto code we request to send either the "once" code
// or the "repeat" code
Expand Down Expand Up @@ -115,6 +118,7 @@ typedef
SHARP,
DENON,
PRONTO,
LEGO_PF,
}
decode_type_t;

Expand Down Expand Up @@ -243,6 +247,10 @@ class IRrecv
# if DECODE_DENON
bool decodeDenon (decode_results *results) ;
# endif
//......................................................................
# if DECODE_LEGO_PF
bool decodeLegoPowerFunctions (decode_results *results) ;
# endif
} ;

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -327,6 +335,10 @@ class IRsend
# if SEND_PRONTO
void sendPronto (char* code, bool repeat, bool fallback) ;
# endif
//......................................................................
# if SEND_LEGO_PF
void sendLegoPowerFunctions (uint16_t data, bool repeat = true) ;
# endif
} ;

#endif
22 changes: 22 additions & 0 deletions examples/LegoPowerFunctionsSendDemo/LegoPowerFunctionsSendDemo.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* LegoPowerFunctionsSendDemo: LEGO Power Functions
* Copyright (c) 2016 Philipp Henkel
*/

#include <IRremote.h>
#include <IRremoteInt.h>

IRsend irsend;

void setup() {
}

void loop() {
// Send repeated command "channel 1, blue forward, red backward"
irsend.sendLegoPowerFunctions(0x197);
delay(2000);

// Send single command "channel 1, blue forward, red backward"
irsend.sendLegoPowerFunctions(0x197, false);
delay(2000);
}
9 changes: 7 additions & 2 deletions irRecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ int IRrecv::decode (decode_results *results)
if (decodeDenon(results)) return true ;
#endif

#if DECODE_LEGO_PF
DBG_PRINTLN("Attempting Lego Power Functions");
if (decodeLegoPowerFunctions(results)) return true ;
#endif

// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
// If you add any decodes, add them before this.
Expand Down Expand Up @@ -145,8 +150,8 @@ void IRrecv::blink13 (int blinkflag)

//+=============================================================================
// Return if receiving new IR signals
//
bool IRrecv::isIdle ( )
//
bool IRrecv::isIdle ( )
{
return (irparams.rcvstate == STATE_IDLE || irparams.rcvstate == STATE_STOP) ? true : false;
}
Expand Down
130 changes: 130 additions & 0 deletions ir_Lego_PF.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include "IRremote.h"
#include "IRremoteInt.h"

//==============================================================================
// L EEEEEE EEEE OOOO
// L E E O O
// L EEEE E EEE O O
// L E E E O O LEGO Power Functions
// LLLLLL EEEEEE EEEE OOOO Copyright (c) 2016 Philipp Henkel
//==============================================================================

//+=============================================================================
//
#if SEND_LEGO_PF

class BitStreamEncoder {
private:
uint16_t data;
bool repeatMessage;
int messageBitIdx;
int repeatCount;
int messageLength;

// HIGH data bit = IR mark + high pause
// LOW data bit = IR mark + low pause
static const int LOW_BIT_DURATION = 421;
static const int HIGH_BIT_DURATION = 711;
static const int START_BIT_DURATION = 1184;
static const int STOP_BIT_DURATION = 1184;
static const int IR_MARK_DURATION = 158;
static const int HIGH_PAUSE_DURATION = HIGH_BIT_DURATION - IR_MARK_DURATION;
static const int LOW_PAUSE_DURATION = LOW_BIT_DURATION - IR_MARK_DURATION;
static const int START_PAUSE_DURATION = START_BIT_DURATION - IR_MARK_DURATION;
static const int STOP_PAUSE_DURATION = STOP_BIT_DURATION - IR_MARK_DURATION;
static const int MESSAGE_BITS = 18;
static const int MAX_MESSAGE_LENGTH = 16000;

public:
void reset(uint16_t data, bool repeatMessage) {
this->data = data;
this->repeatMessage = repeatMessage;
messageBitIdx = 0;
repeatCount = 0;
messageLength = getMessageLength();
}

int getChannelId() const { return 1 + ((data >> 12) & 0x3); }

int getMessageLength() const {
// Sum up all marks
int length = MESSAGE_BITS * IR_MARK_DURATION;

// Sum up all pauses
length += START_PAUSE_DURATION;
for (unsigned long mask = 1UL << 15; mask; mask >>= 1) {
if (data & mask) {
length += HIGH_PAUSE_DURATION;
} else {
length += LOW_PAUSE_DURATION;
}
}
length += STOP_PAUSE_DURATION;
return length;
}

boolean next() {
messageBitIdx++;
if (messageBitIdx >= MESSAGE_BITS) {
repeatCount++;
messageBitIdx = 0;
}
if (repeatCount >= 1 && !repeatMessage) {
return false;
} else if (repeatCount >= 5) {
return false;
} else {
return true;
}
}

int getMarkDuration() const { return IR_MARK_DURATION; }

int getPauseDuration() const {
if (messageBitIdx == 0)
return START_PAUSE_DURATION;
else if (messageBitIdx < MESSAGE_BITS - 1) {
return getDataBitPause();
} else {
return getStopPause();
}
}

private:
int getDataBitPause() const {
const int pos = MESSAGE_BITS - 2 - messageBitIdx;
const bool isHigh = data & (1 << pos);
return isHigh ? HIGH_PAUSE_DURATION : LOW_PAUSE_DURATION;
}

int getStopPause() const {
if (repeatMessage) {
return getRepeatStopPause();
} else {
return STOP_PAUSE_DURATION;
}
}

int getRepeatStopPause() const {
if (repeatCount == 0 || repeatCount == 1) {
return STOP_PAUSE_DURATION + 5 * MAX_MESSAGE_LENGTH - messageLength;
} else if (repeatCount == 2 || repeatCount == 3) {
return STOP_PAUSE_DURATION
+ (6 + 2 * getChannelId()) * MAX_MESSAGE_LENGTH - messageLength;
} else {
return STOP_PAUSE_DURATION;
}
}
};

void IRsend::sendLegoPowerFunctions(uint16_t data, bool repeat)
{
enableIROut(38);
static BitStreamEncoder bitStreamEncoder;
bitStreamEncoder.reset(data, repeat);
do {
mark(bitStreamEncoder.getMarkDuration());
space(bitStreamEncoder.getPauseDuration());
} while (bitStreamEncoder.next());
}
#endif

0 comments on commit 34e5cd8

Please sign in to comment.