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