IRremote
ir_Lego.hpp
Go to the documentation of this file.
1 /*
2  * ir_Lego.hpp
3  *
4  * Contains functions for receiving and sending Lego Power Functions IR Protocol
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) 2020-2023 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_LEGO_HPP
33 #define _IR_LEGO_HPP
34 
35 #if defined(DEBUG)
36 #define LOCAL_DEBUG
37 #else
38 //#define LOCAL_DEBUG // This enables debug output only for this file
39 #endif
40 
44 //==============================================================================
45 // L EEEEEE EEEE OOOO
46 // L E E O O
47 // L EEEE E EEE O O
48 // L E E E O O
49 // LLLLLL EEEEEE EEEE OOOO
50 //==============================================================================
51 // from LEGO Power Functions RC Manual 26.02.2010 Version 1.20
52 // https://github.com/jurriaan/Arduino-PowerFunctions/raw/master/LEGO_Power_Functions_RC_v120.pdf
53 // https://oberguru.net/elektronik/ir/codes/lego_power_functions_train.lircd.conf
54 // For original LEGO receiver see: https://www.philohome.com/pfrec/pfrec.htm and https://www.youtube.com/watch?v=KCM4Ug1bPrM
55 //
56 // To ensure correct detection of IR messages six 38 kHz cycles are transmitted as mark.
57 // Low bit consists of 6 cycles of IR and 10 cycles of pause,
58 // High bit of 6 cycles IR and 21 cycles of pause,
59 // Start/stop of 6 cycles IR and 39 cycles of pause.
60 //
61 // If tm is the maximum message length (16ms) and Ch is the channel number, then
62 // The delay before transmitting the first message is: (4 - Ch) * tm
63 // The time from start to start for the next 2 messages is: 5 * tm
64 // The time from start to start for the following messages is: (6 + 2 * Ch) * tm
65 // Supported Devices
66 // LEGO Power Functions IR Receiver 8884
67 // MSB first, 1 start bit + 4 bit channel, 4 bit mode + 4 bit command + 4 bit parity + 1 stop bit.
68 /* Protocol=Lego Address=0x1, Command=0x16, Raw-Data=0x1169 ,16 bits, MSB first, Gap=1050600us, Duration=10000us
69  Send with: IrSender.sendLego(0x1, 0x16, <numberOfRepeats>);
70  rawData[36]:
71  -1050600
72  + 250,- 950
73  + 250,- 500 + 200,- 200 + 200,- 250 + 200,- 500
74  + 200,- 250 + 200,- 500 + 200,- 500 + 200,- 250
75  + 200,- 500 + 200,- 200 + 250,- 200 + 200,- 200
76  + 250,- 500 + 200,- 200 + 250,- 200 + 200,- 250
77  + 200
78  Duration=10000us
79  */
80 
81 #define LEGO_CHANNEL_BITS 4
82 #define LEGO_MODE_BITS 4
83 #define LEGO_COMMAND_BITS 4
84 #define LEGO_PARITY_BITS 4
85 
86 #define LEGO_BITS (LEGO_CHANNEL_BITS + LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS)
87 
88 #define LEGO_HEADER_MARK 158 // 6 cycles
89 #define LEGO_HEADER_SPACE 1026 // 39 cycles
90 
91 #define LEGO_BIT_MARK 158 // 6 cycles
92 #define LEGO_ONE_SPACE 553 // 21 cycles
93 #define LEGO_ZERO_SPACE 263 // 10 cycles
94 #define LEGO_ONE_THRESHOLD 408 // 15.5 cycles - not used, just for info
95 
96 #define LEGO_AVERAGE_DURATION 11000 // LEGO_HEADER_MARK + LEGO_HEADER_SPACE + 16 * 600 + 158
97 
98 #define LEGO_AUTO_REPEAT_PERIOD_MIN 110000 // Every frame is auto repeated 5 times.
99 #define LEGO_AUTO_REPEAT_PERIOD_MAX 230000 // space for channel 3
100 
101 #define LEGO_MODE_EXTENDED 0
102 #define LEGO_MODE_COMBO 1
103 #define LEGO_MODE_SINGLE 0x4 // here the 2 LSB have meanings like Output A / Output B
104 
105 // Cannot be constant, since we need to change RepeatPeriodMillis during sending
109 
110 /************************************
111  * Start of send and decode functions
112  ************************************/
113 /*
114  * Here we process the structured data, and call the send raw data function
115  * @param aMode one of LEGO_MODE_EXTENDED, LEGO_MODE_COMBO, LEGO_MODE_SINGLE
116  */
117 void IRsend::sendLegoPowerFunctions(uint8_t aChannel, uint8_t aCommand, uint8_t aMode, bool aDoSend5Times) {
118  aChannel &= 0x0F; // allow toggle and escape bits too
119  aCommand &= 0x0F;
120  aMode &= 0x0F;
121  uint8_t tParity = 0xF ^ aChannel ^ aMode ^ aCommand;
122  // send 4 bit channel, 4 bit mode, 4 bit command, 4 bit parity
123  uint16_t tRawData = (((aChannel << LEGO_MODE_BITS) | aMode) << (LEGO_COMMAND_BITS + LEGO_PARITY_BITS))
124  | (aCommand << LEGO_PARITY_BITS) | tParity;
125  sendLegoPowerFunctions(tRawData, aChannel, aDoSend5Times);
126 }
127 
128 void IRsend::sendLegoPowerFunctions(uint16_t aRawData, uint8_t aChannel, bool aDoSend5Times) {
129 
130 #if defined(LOCAL_DEBUG)
131  Serial.print(F("sendLego aRawData=0x"));
132  Serial.println(aRawData, HEX);
133 #endif
134 
135  aChannel &= 0x03; // we have 4 channels
136 
137  uint_fast8_t tNumberOfRepeats = 0;
138  if (aDoSend5Times) {
139  tNumberOfRepeats = 4;
140  }
141 // required for repeat timing, see http://www.hackvandedam.nl/blog/?page_id=559
142  uint8_t tRepeatPeriod = (LEGO_AUTO_REPEAT_PERIOD_MIN / MICROS_IN_ONE_MILLI) + (aChannel * 40); // from 110 to 230
144  sendPulseDistanceWidth(&LegoProtocolConstants, aRawData, LEGO_BITS, tNumberOfRepeats);
145 }
146 
147 /*
148  * Mode is stored in the upper nibble of command
149  */
151 
152  /*
153  * Check header timings
154  * Since LEGO_HEADER_MARK is just 158 us use a relaxed threshold compare (237) for it instead of matchMark()
155  */
157  return false;
158  }
159 
160  // Check we have enough data - +4 for initial gap, start bit mark and space + stop bit mark
161  if (decodedIRData.rawlen != (2 * LEGO_BITS) + 4) {
162  IR_DEBUG_PRINT(F("LEGO: "));
163  IR_DEBUG_PRINT(F("Data length="));
165  IR_DEBUG_PRINTLN(F(" is not 36"));
166  return false;
167  }
168 
170 
171  // Stop bit, use threshold decoding - not required :-)
172 // if (irparams.rawbuf[3 + (2 * LEGO_BITS)] > (2 * LEGO_BIT_MARK)) {
173 //#if defined(LOCAL_DEBUG)
174 // Serial.print(F("LEGO: "));
175 // Serial.println(F("Stop bit mark length is wrong"));
176 //#endif
177 // return false;
178 // }
179 
180  // Success
182  uint16_t tDecodedValue = decodedIRData.decodedRawData;
183  uint8_t tToggleEscapeChannel = tDecodedValue >> (LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS);
184  uint8_t tMode = (tDecodedValue >> (LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) & 0xF;
185  uint8_t tData = (tDecodedValue >> LEGO_PARITY_BITS) & 0xF; // lego calls this field "data"
186  uint8_t tParityReceived = tDecodedValue & 0xF;
187 
188  // This is parity as defined in the specifications
189  // But in some scans I saw 0x9 ^ .. as parity formula
190  uint8_t tParityComputed = 0xF ^ tToggleEscapeChannel ^ tMode ^ tData;
191 
192  // parity check
193  if (tParityReceived != tParityComputed) {
194 #if defined(LOCAL_DEBUG)
195  Serial.print(F("LEGO: "));
196  Serial.print(F("Parity is not correct. expected=0x"));
197  Serial.print(tParityComputed, HEX);
198  Serial.print(F(" received=0x"));
199  Serial.print(tParityReceived, HEX);
200  Serial.print(F(", raw=0x"));
201  Serial.print(tDecodedValue, HEX);
202  Serial.print(F(", 3 nibbles are 0x"));
203  Serial.print(tToggleEscapeChannel, HEX);
204  Serial.print(F(", 0x"));
205  Serial.print(tMode, HEX);
206  Serial.print(F(", 0x"));
207  Serial.println(tData, HEX);
208 #endif
209  // might not be an error, so just continue
211  }
212 
213  /*
214  * Check for autorepeat (should happen 4 times for one press)
215  */
218  }
219  decodedIRData.address = tToggleEscapeChannel;
220  decodedIRData.command = tData | (tMode << LEGO_COMMAND_BITS);
223 
224  return true;
225 }
226 
227 /*********************************************************************************
228  * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
229  *********************************************************************************/
230 
231 void IRsend::sendLegoPowerFunctions(uint16_t aRawData, bool aDoSend5Times) {
232  sendLegoPowerFunctions(aRawData, (aRawData >> (LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) & 0x3, aDoSend5Times);
233 }
234 
236 #if defined(LOCAL_DEBUG)
237 #undef LOCAL_DEBUG
238 #endif
239 #endif // _IR_LEGO_HPP
IRData::address
uint16_t address
Decoded address, Distance protocol (tMarkTicksLong (if tMarkTicksLong == 0, then tMarkTicksShort) << ...
Definition: IRremoteInt.h:152
MICROS_PER_TICK
#define MICROS_PER_TICK
microseconds per clock interrupt tick
Definition: IRremote.hpp:207
IRrecv::decodePulseDistanceWidthData
void decodePulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset=3)
Decode pulse distance protocols for PulseDistanceWidthProtocolConstants.
Definition: IRReceive.hpp:1081
LEGO_COMMAND_BITS
#define LEGO_COMMAND_BITS
Definition: ir_Lego.hpp:83
IRData::numberOfBits
uint16_t numberOfBits
Number of bits received for data (address + command + parity) - to determine protocol length if diffe...
Definition: IRremoteInt.h:161
LEGO_ZERO_SPACE
#define LEGO_ZERO_SPACE
Definition: ir_Lego.hpp:93
MICROS_IN_ONE_MILLI
#define MICROS_IN_ONE_MILLI
Definition: IRremote.hpp:212
PROTOCOL_IS_PULSE_DISTANCE
#define PROTOCOL_IS_PULSE_DISTANCE
Definition: IRProtocol.h:173
IRsend::sendPulseDistanceWidth
void sendPulseDistanceWidth(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData, uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats)
Sends PulseDistance frames and repeats.
Definition: IRSend.hpp:833
IRDATA_FLAGS_IS_AUTO_REPEAT
#define IRDATA_FLAGS_IS_AUTO_REPEAT
The current repeat frame is a repeat, that is always sent after a regular frame and cannot be avoided...
Definition: IRProtocol.h:148
IRData::decodedRawData
IRRawDataType decodedRawData
Up to 32/64 bit decoded raw data, to be used for send<protocol>Raw functions.
Definition: IRremoteInt.h:155
IR_DEBUG_PRINT
#define IR_DEBUG_PRINT(...)
If DEBUG, print the arguments, otherwise do nothing.
Definition: IRremoteInt.h:186
matchSpace
bool matchSpace(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros)
Compensate for spaces shortened by demodulator hardware.
Definition: IRReceive.hpp:1459
IRDATA_FLAGS_PARITY_FAILED
#define IRDATA_FLAGS_PARITY_FAILED
The current (autorepeat) frame violated parity check.
Definition: IRProtocol.h:149
LEGO_MODE_BITS
#define LEGO_MODE_BITS
Definition: ir_Lego.hpp:82
IRrecv::decodeLegoPowerFunctions
bool decodeLegoPowerFunctions()
Definition: ir_Lego.hpp:150
PulseDistanceWidthProtocolConstants
Definition: IRProtocol.h:161
irparams_struct::rawlen
IRRawlenType rawlen
counter of entries in rawbuf
Definition: IRremoteInt.h:138
IRrecv::decodedIRData
IRData decodedIRData
Definition: IRremoteInt.h:409
IRData::flags
uint8_t flags
IRDATA_FLAGS_IS_REPEAT, IRDATA_FLAGS_WAS_OVERFLOW etc. See IRDATA_FLAGS_* definitions above.
Definition: IRremoteInt.h:162
PROTOCOL_IS_LSB_FIRST
#define PROTOCOL_IS_LSB_FIRST
Definition: IRProtocol.h:179
LEGO_BITS
#define LEGO_BITS
Definition: ir_Lego.hpp:86
IRData::command
uint16_t command
Decoded command, Distance protocol (tMarkTicksShort << 8) | tSpaceTicksShort.
Definition: IRremoteInt.h:153
LEGO_PF
@ LEGO_PF
Definition: IRProtocol.h:122
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:140
IRrecv::irparams
irparams_struct irparams
Definition: IRremoteInt.h:408
IRDATA_FLAGS_IS_MSB_FIRST
#define IRDATA_FLAGS_IS_MSB_FIRST
Value is mainly determined by the (known) protocol.
Definition: IRProtocol.h:155
LEGO_HEADER_SPACE
#define LEGO_HEADER_SPACE
Definition: ir_Lego.hpp:89
IRData::rawlen
IRRawlenType rawlen
Counter of entries in rawbuf of last received frame.
Definition: IRremoteInt.h:170
LEGO_HEADER_MARK
#define LEGO_HEADER_MARK
Definition: ir_Lego.hpp:88
LegoProtocolConstants
struct PulseDistanceWidthProtocolConstants LegoProtocolConstants
Definition: ir_Lego.hpp:106
LEGO_AUTO_REPEAT_PERIOD_MIN
#define LEGO_AUTO_REPEAT_PERIOD_MIN
Definition: ir_Lego.hpp:98
LEGO_BIT_MARK
#define LEGO_BIT_MARK
Definition: ir_Lego.hpp:91
LEGO_ONE_SPACE
#define LEGO_ONE_SPACE
Definition: ir_Lego.hpp:92
LEGO_PARITY_BITS
#define LEGO_PARITY_BITS
Definition: ir_Lego.hpp:84
LEGO_AUTO_REPEAT_PERIOD_MAX
#define LEGO_AUTO_REPEAT_PERIOD_MAX
Definition: ir_Lego.hpp:99
IR_DEBUG_PRINTLN
#define IR_DEBUG_PRINTLN(...)
If DEBUG, print the arguments as a line, otherwise do nothing.
Definition: IRremoteInt.h:190
PulseDistanceWidthProtocolConstants::RepeatPeriodMillis
unsigned int RepeatPeriodMillis
Definition: IRProtocol.h:166
IRsend::sendLegoPowerFunctions
void sendLegoPowerFunctions(uint8_t aChannel, uint8_t tCommand, uint8_t aMode, bool aDoSend5Times=true)
Definition: ir_Lego.hpp:117
IRData::protocol
decode_type_t protocol
UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ...
Definition: IRremoteInt.h:151
IRsend::aCommand
void aCommand
Definition: IRremoteInt.h:688
IRData::initialGapTicks
uint16_t initialGapTicks
Contains the initial gap (pre 4.4: the value in rawbuf[0]) of the last received frame.
Definition: IRremoteInt.h:171