IRremote
ir_MagiQuest.hpp
Go to the documentation of this file.
1 /*
2  * ir_MagiQuest.hpp
3  *
4  * Contains functions for receiving and sending MagiQuest Protocol
5  * Based off the Magiquest fork of Arduino-IRremote by mpflaga https://github.com/mpflaga/Arduino-IRremote/
6  *
7  * RESULT:
8  * The 31 bit wand ID is available in decodedRawData.
9  * The lower 16 bit of the ID is available in address.
10  * The magnitude is available in command.
11  *
12  * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
13  *
14  ************************************************************************************
15  * MIT License
16  *
17  * Copyright (c) 2017-2025 E. Stuart Hicks <ehicks@binarymagi.com>, Armin Joachimsmeyer
18  *
19  * Permission is hereby granted, free of charge, to any person obtaining a copy
20  * of this software and associated documentation files (the "Software"), to deal
21  * in the Software without restriction, including without limitation the rights
22  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23  * copies of the Software, and to permit persons to whom the Software is furnished
24  * to do so, subject to the following conditions:
25  *
26  * The above copyright notice and this permission notice shall be included in all
27  * copies or substantial portions of the Software.
28  *
29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
30  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
31  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
33  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
34  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35  *
36  ************************************************************************************
37  */
38 #ifndef _IR_MAGIQUEST_HPP
39 #define _IR_MAGIQUEST_HPP
40 
41 #if defined(DEBUG)
42 #define LOCAL_DEBUG
43 #else
44 //#define LOCAL_DEBUG // This enables debug output only for this file
45 #endif
46 //
47 //==============================================================================
48 //
49 // M M AA GGG III QQQ U U EEEE SSS TTTTTT
50 // MM MM A A G I Q Q U U E S TT
51 // M M M AAAA G GG I Q Q U U EEE SSS TT
52 // M M A A G G I Q QQ U U E S TT
53 // M M A A GGG III QQQQ UUU EEEE SSSS TT
54 // Q
55 //==============================================================================
56 /*
57  * https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
58  * https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1015#issuecomment-1222247231
59 
60  Protocol=MagiQuest Address=0xFF00 Command=0x176 Raw-Data=0x6BCDFF00 56 bits MSB first
61  + 250,- 800 + 250,- 850 + 250,- 850 + 250,- 850 // 8 zero start bits
62  + 250,- 850 + 300,- 800 + 250,- 850 + 250,- 850
63 
64  // 31 ID bits
65  + 550,- 600 + 550,- 550 + 350,- 800 + 600,- 600 // 110 1 6
66  + 200,- 950 + 550,- 600 + 550,- 600 + 550,- 600 // 011 1 B - 1(from above)011 => B
67  + 550,- 600 + 250,- 900 + 300,- 850 + 550,- 600 // 100 1 C
68  + 550,- 600 + 300,- 850 + 550,- 600 + 550,- 600
69  + 550,- 600 + 550,- 600 + 550,- 600 + 550,- 600
70  + 550,- 600 + 550,- 600 + 550,- 600 + 300,- 800
71  + 350,- 850 + 300,- 850 + 300,- 850 + 300,- 850
72  + 300,- 850 + 300,- 850 + 300,- 850 + 550,- 600 // 000 1 - 3 LSB ID bits 000 + 1 MSB magnitude bit 1
73 
74  // 8 bit magnitude
75  + 300,- 850 + 550,- 600 + 550,- 600 + 550,- 600
76  + 300,- 850 + 550,- 600 + 550,- 600 + 250,- 900
77 
78  // Checksum (+ sum of the 5 bytes before == 0)
79  + 250,- 900 + 300,- 900 + 250,- 850 + 550,- 600
80  + 600,- 550 + 300,- 900 + 250,- 850 + 550
81 
82  // No stop bit!
83  */
84 // MSB first, 8 start bits (zero), 31 wand id bits, 9 magnitude bits 8 checksum bits and no stop bit => 56 bits
85 #define MAGIQUEST_CHECKSUM_BITS 8 // magiquest_t.cmd.checksum
86 #define MAGIQUEST_MAGNITUDE_BITS 9 // magiquest_t.cmd.magnitude
87 #define MAGIQUEST_WAND_ID_BITS 31 // magiquest_t.cmd.wand_id -> wand-id is handled as 32 bit and always even
88 #define MAGIQUEST_START_BITS 8 // magiquest_t.cmd.StartBits
89 
90 #define MAGIQUEST_PERIOD 1150 // Time for a full MagiQuest "bit" (1100 - 1200 usec)
91 
92 #define MAGIQUEST_DATA_BITS (MAGIQUEST_CHECKSUM_BITS + MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS) // 48 Size of the command without the start bits
93 #define MAGIQUEST_BITS (MAGIQUEST_CHECKSUM_BITS + MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS + MAGIQUEST_START_BITS) // 56 Size of the command with the start bits
94 
95 /*
96  * Protocol is Pulse Distance Width not pure Pulse Distance and a longer space is not a one but a zero
97  *
98  * 0 = 25% mark & 75% space across 1 period
99  * 1150 * 0.25 = 288 usec mark
100  * 1150 - 288 = 862 usec space
101  * 1 = 50% mark & 50% space across 1 period
102  * 1150 * 0.5 = 575 usec mark
103  * 1150 - 575 = 575 usec space
104  */
105 #define MAGIQUEST_UNIT (MAGIQUEST_PERIOD / 4) // 287.5
106 
107 #define MAGIQUEST_ONE_MARK (2 * MAGIQUEST_UNIT) // 576
108 #define MAGIQUEST_ONE_SPACE (2 * MAGIQUEST_UNIT) // 576
109 #define MAGIQUEST_ZERO_MARK MAGIQUEST_UNIT // 287.5
110 #define MAGIQUEST_ZERO_SPACE (3 * MAGIQUEST_UNIT) // 864
111 
112 // assume 110 as repeat period.
113 struct PulseDistanceWidthProtocolConstants const MagiQuestProtocolConstants PROGMEM = {MAGIQUEST, 38, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE,
116 //+=============================================================================
117 //
122 void IRsend::sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude) {
123 
124  // Set IR carrier frequency
125  enableIROut(38);
126 
127  aMagnitude &= 0x1FF; // we have 9 bit
128  LongUnion tWandId;
129  tWandId.ULong = aWandId << 1;
130  uint8_t tChecksum = (tWandId.Bytes[0]) + tWandId.Bytes[1] + tWandId.Bytes[2] + tWandId.Bytes[3];
131  tChecksum += aMagnitude + (aMagnitude >> 8);
132  tChecksum = ~tChecksum + 1;
133 
134  // 8 start bits
135  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, 0, 8);
136  // 48 bit data
137  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, aWandId, MAGIQUEST_WAND_ID_BITS); // send only 31 bit, do not send MSB here
138  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, aMagnitude, MAGIQUEST_MAGNITUDE_BITS);
139  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, tChecksum, MAGIQUEST_CHECKSUM_BITS);
140 #if defined(LOCAL_DEBUG)
141  // must be after sending, in order not to destroy the send timing
142  Serial.print(F("MagiQuest checksum=0x"));
143  Serial.println(tChecksum, HEX);
144 #endif
145 }
146 
147 //+=============================================================================
148 //
149 /*
150  * decodes a 56 bit result, which is not really compatible with standard decoder layout
151  * magnitude is stored in command
152  * 31 bit wand_id is stored in decodedRawData
153  * lower 16 bit of wand_id is stored in address
154  */
156 
157  // Check we have the right amount of data, magnitude and ID bits and 8 start bits + 0 stop bit
158  if (decodedIRData.rawlen != (2 * MAGIQUEST_BITS)) {
159  IR_DEBUG_PRINT(F("MagiQuest: "));
160  IR_DEBUG_PRINT(F("Data length="));
162  IR_DEBUG_PRINTLN(F(" is not 112"));
163  return false;
164  }
165 
166  /*
167  * Check for 8 zero header bits
168  */
169  decodePulseDistanceWidthData_P(&MagiQuestProtocolConstants, MAGIQUEST_START_BITS, 1);
170 #if defined(USE_THRESHOLD_DECODER)
171  if (decodedIRData.decodedRawData != 0xFF) // For Magiquest a small pause is a 1 which is inverse to threshold decoding
172 #else
173  if (decodedIRData.decodedRawData != 0)
174 #endif
175  {
176 #if defined(LOCAL_DEBUG)
177  Serial.print(F("MagiQuest: "));
178  Serial.print(F("Not 8 leading zero start bits received, RawData=0x"));
179  Serial.println(decodedIRData.decodedRawData, HEX);
180 #endif
181  return false;
182  }
183 
184  /*
185  * Decode the 31 bit ID
186  */
187  decodePulseDistanceWidthData_P(&MagiQuestProtocolConstants, MAGIQUEST_WAND_ID_BITS, (MAGIQUEST_START_BITS * 2) + 1);
188 #if defined(USE_THRESHOLD_DECODER)
189  decodedIRData.decodedRawData = decodedIRData.decodedRawData ^ 0x7FFFFFFF; // We have 31 bit. For Magiquest a small pause is a 1 which is inverse to threshold decoding
190 #endif
191 
192  LongUnion tDecodedRawData;
193  uint32_t tWandId = decodedIRData.decodedRawData; // save tWandId for later use
194  tDecodedRawData.ULong = decodedIRData.decodedRawData << 1; // shift for checksum computation
195  uint8_t tChecksum = tDecodedRawData.Bytes[0] + tDecodedRawData.Bytes[1] + tDecodedRawData.Bytes[2] + tDecodedRawData.Bytes[3];
196 #if defined(LOCAL_DEBUG)
197  Serial.print(F("31 bit WandId=0x"));
198  Serial.print(decodedIRData.decodedRawData, HEX);
199  Serial.print(F(" shifted=0x"));
200  Serial.println(tDecodedRawData.ULong, HEX);
201 #endif
202  /*
203  * Decode the 9 bit Magnitude + 8 bit checksum
204  */
207 
208 #if defined(USE_THRESHOLD_DECODER)
209  decodedIRData.decodedRawData = decodedIRData.decodedRawData ^ 0x0001FFFF; // We have 17 bit. For Magiquest a small pause is a 1 which is inverse to threshold decoding
210 
211 #endif
212 
213 #if defined(LOCAL_DEBUG)
214  Serial.print(F("Magnitude + checksum=0x"));
215  Serial.println(decodedIRData.decodedRawData, HEX);
216 #endif
217  tDecodedRawData.ULong = decodedIRData.decodedRawData;
218  decodedIRData.command = tDecodedRawData.UBytes[1] | tDecodedRawData.UBytes[2] << 8; // Values observed are 0x102,01,04,37,05,38,2D| 02,06,04|03,103,12,18,0E|09
219 
220  tChecksum += tDecodedRawData.UBytes[2] /* only one bit */+ tDecodedRawData.UBytes[1] + tDecodedRawData.UBytes[0];
221  if (tChecksum != 0) {
223 #if defined(LOCAL_DEBUG)
224  Serial.print(F("Checksum 0x"));
225  Serial.print(tChecksum, HEX);
226  Serial.println(F(" is not 0"));
227 #endif
228  }
229 
230  // Success
231  decodedIRData.decodedRawData = tWandId; // 31 bit wand_id
232  decodedIRData.address = tWandId; // lower 16 bit of wand_id
233  decodedIRData.extra = tWandId >> 16; // upper 15 bit of wand_id
234 
238 
239  return true;
240 }
241 #if defined(LOCAL_DEBUG)
242 #undef LOCAL_DEBUG
243 #endif
244 #endif // _IR_MAGIQUEST_HPP
IRData::address
uint16_t address
Decoded address, Distance protocol (tMarkTicksLong (if tMarkTicksLong == 0, then tMarkTicksShort) << ...
Definition: IRremoteInt.h:152
SUPPRESS_STOP_BIT
#define SUPPRESS_STOP_BIT
Definition: IRProtocol.h:177
LongUnion
Union to specify parts / manifestations of a 32 bit Long without casts and shifts.
Definition: LongUnion.h:59
IRsend::sendMagiQuest
void sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude)
Definition: ir_MagiQuest.hpp:122
PROTOCOL_IS_MSB_FIRST
#define PROTOCOL_IS_MSB_FIRST
Definition: IRProtocol.h:178
IRData::numberOfBits
uint16_t numberOfBits
Number of bits received for data (address + command + parity) - to determine protocol length if diffe...
Definition: IRremoteInt.h:161
IRrecv::decodePulseDistanceWidthData_P
void decodePulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset=3)
Definition: IRReceive.hpp:1117
MAGIQUEST_ONE_SPACE
#define MAGIQUEST_ONE_SPACE
Definition: ir_MagiQuest.hpp:108
LongUnion::UBytes
uint8_t UBytes[4]
Definition: LongUnion.h:91
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
MAGIQUEST
@ MAGIQUEST
Definition: IRProtocol.h:123
MAGIQUEST_ZERO_MARK
#define MAGIQUEST_ZERO_MARK
Definition: ir_MagiQuest.hpp:109
IRDATA_FLAGS_PARITY_FAILED
#define IRDATA_FLAGS_PARITY_FAILED
The current (autorepeat) frame violated parity check.
Definition: IRProtocol.h:149
PulseDistanceWidthProtocolConstants
Definition: IRProtocol.h:161
IRsend::sendPulseDistanceWidthData_P
void sendPulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, IRRawDataType aData, uint_fast8_t aNumberOfBits)
Definition: IRSend.hpp:586
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
MAGIQUEST_ZERO_SPACE
#define MAGIQUEST_ZERO_SPACE
Definition: ir_MagiQuest.hpp:110
PROGMEM
struct PulseDistanceWidthProtocolConstants const MagiQuestProtocolConstants PROGMEM
Definition: ir_MagiQuest.hpp:113
MAGIQUEST_CHECKSUM_BITS
#define MAGIQUEST_CHECKSUM_BITS
Definition: ir_MagiQuest.hpp:85
MAGIQUEST_ONE_MARK
#define MAGIQUEST_ONE_MARK
Definition: ir_MagiQuest.hpp:107
MAGIQUEST_MAGNITUDE_BITS
#define MAGIQUEST_MAGNITUDE_BITS
Definition: ir_MagiQuest.hpp:86
IRData::command
uint16_t command
Decoded command, Distance protocol (tMarkTicksShort << 8) | tSpaceTicksShort.
Definition: IRremoteInt.h:153
LongUnion::ULong
uint32_t ULong
Definition: LongUnion.h:95
MAGIQUEST_WAND_ID_BITS
#define MAGIQUEST_WAND_ID_BITS
Definition: ir_MagiQuest.hpp:87
IRDATA_FLAGS_IS_MSB_FIRST
#define IRDATA_FLAGS_IS_MSB_FIRST
Value is mainly determined by the (known) protocol.
Definition: IRProtocol.h:155
IRData::extra
uint16_t extra
Contains upper 16 bit of Magiquest WandID, Kaseikyo unknown vendor ID and Distance protocol (HeaderMa...
Definition: IRremoteInt.h:154
IRrecv::decodeMagiQuest
bool decodeMagiQuest()
Definition: ir_MagiQuest.hpp:155
PROTOCOL_IS_PULSE_DISTANCE_WIDTH
#define PROTOCOL_IS_PULSE_DISTANCE_WIDTH
Definition: IRProtocol.h:174
LongUnion::Bytes
int8_t Bytes[4]
Definition: LongUnion.h:92
IRData::rawlen
IRRawlenType rawlen
Counter of entries in rawbuf of last received frame.
Definition: IRremoteInt.h:170
MAGIQUEST_START_BITS
#define MAGIQUEST_START_BITS
Definition: ir_MagiQuest.hpp:88
IR_DEBUG_PRINTLN
#define IR_DEBUG_PRINTLN(...)
If DEBUG, print the arguments as a line, otherwise do nothing.
Definition: IRremoteInt.h:190
IRData::protocol
decode_type_t protocol
UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ...
Definition: IRremoteInt.h:151
MAGIQUEST_BITS
#define MAGIQUEST_BITS
Definition: ir_MagiQuest.hpp:93
IRsend::enableIROut
void enableIROut(uint_fast8_t aFrequencyKHz)
Enables IR output.
Definition: IRSend.hpp:1404