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 // This block must be located after the includes of other *.hpp files
42 //#define LOCAL_DEBUG // This enables debug output only for this file - only for development
43 #include "LocalDebugLevelStart.h"
44 
48 //
49 //==============================================================================
50 //
51 // M M AA GGG III QQQ U U EEEE SSS TTTTTT
52 // MM MM A A G I Q Q U U E S TT
53 // M M M AAAA G GG I Q Q U U EEE SSS TT
54 // M M A A G G I Q QQ U U E S TT
55 // M M A A GGG III QQQQ UUU EEEE SSSS TT
56 // Q
57 //==============================================================================
58 /*
59  * https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
60  * https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1015#issuecomment-1222247231
61 
62  Protocol=MagiQuest Address=0xFF00 Command=0x176 Raw-Data=0x6BCDFF00 56 bits MSB first
63  + 250,- 800 + 250,- 850 + 250,- 850 + 250,- 850 // 8 zero start bits
64  + 250,- 850 + 300,- 800 + 250,- 850 + 250,- 850
65 
66  // 31 ID bits
67  + 550,- 600 + 550,- 550 + 350,- 800 + 600,- 600 // 110 1 6
68  + 200,- 950 + 550,- 600 + 550,- 600 + 550,- 600 // 011 1 B - 1(from above)011 => B
69  + 550,- 600 + 250,- 900 + 300,- 850 + 550,- 600 // 100 1 C
70  + 550,- 600 + 300,- 850 + 550,- 600 + 550,- 600
71  + 550,- 600 + 550,- 600 + 550,- 600 + 550,- 600
72  + 550,- 600 + 550,- 600 + 550,- 600 + 300,- 800
73  + 350,- 850 + 300,- 850 + 300,- 850 + 300,- 850
74  + 300,- 850 + 300,- 850 + 300,- 850 + 550,- 600 // 000 1 - 3 LSB ID bits 000 + 1 MSB magnitude bit 1
75 
76  // 8 bit magnitude
77  + 300,- 850 + 550,- 600 + 550,- 600 + 550,- 600
78  + 300,- 850 + 550,- 600 + 550,- 600 + 250,- 900
79 
80  // Checksum (+ sum of the 5 bytes before == 0)
81  + 250,- 900 + 300,- 900 + 250,- 850 + 550,- 600
82  + 600,- 550 + 300,- 900 + 250,- 850 + 550
83 
84  // No stop bit!
85  */
86 // MSB first, 8 start bits (zero), 31 wand id bits, 9 magnitude bits 8 checksum bits and no stop bit => 56 bits
87 #define MAGIQUEST_CHECKSUM_BITS 8 // magiquest_t.cmd.checksum
88 #define MAGIQUEST_MAGNITUDE_BITS 9 // magiquest_t.cmd.magnitude
89 #define MAGIQUEST_WAND_ID_BITS 31 // magiquest_t.cmd.wand_id -> wand-id is handled as 32 bit and always even
90 #define MAGIQUEST_START_BITS 8 // magiquest_t.cmd.StartBits
91 
92 #define MAGIQUEST_PERIOD 1150 // Time for a full MagiQuest "bit" (1100 - 1200 usec)
93 
94 #define MAGIQUEST_DATA_BITS (MAGIQUEST_CHECKSUM_BITS + MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS) // 48 Size of the command without the start bits
95 #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
96 
97 /*
98  * Protocol is Pulse Distance Width not pure Pulse Distance and a longer space is not a one but a zero
99  *
100  * 0 = 25% mark & 75% space across 1 period
101  * 1150 * 0.25 = 288 usec mark
102  * 1150 - 288 = 862 usec space
103  * 1 = 50% mark & 50% space across 1 period
104  * 1150 * 0.5 = 575 usec mark
105  * 1150 - 575 = 575 usec space
106  */
107 #define MAGIQUEST_UNIT (MAGIQUEST_PERIOD / 4) // 287.5
108 
109 #define MAGIQUEST_ONE_MARK (2 * MAGIQUEST_UNIT) // 576
110 #define MAGIQUEST_ONE_SPACE (2 * MAGIQUEST_UNIT) // 576
111 #define MAGIQUEST_ZERO_MARK MAGIQUEST_UNIT // 287.5
112 #define MAGIQUEST_ZERO_SPACE (3 * MAGIQUEST_UNIT) // 864
113 
114 // assume 110 as repeat period.
115 struct PulseDistanceWidthProtocolConstants const MagiQuestProtocolConstants PROGMEM = {MAGIQUEST, 38, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE,
118 //+=============================================================================
119 //
124 void IRsend::sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude) {
125 
126  // Set IR carrier frequency
127  enableIROut(38);
128 
129  aMagnitude &= 0x1FF; // we have 9 bit
130  LongUnion tWandId;
131  tWandId.ULong = aWandId << 1;
132  uint8_t tChecksum = (tWandId.Bytes[0]) + tWandId.Bytes[1] + tWandId.Bytes[2] + tWandId.Bytes[3];
133  tChecksum += aMagnitude + (aMagnitude >> 8);
134  tChecksum = ~tChecksum + 1;
135 
136  // 8 start bits
137  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, 0, 8);
138  // 48 bit data
139  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, aWandId, MAGIQUEST_WAND_ID_BITS); // send only 31 bit, do not send MSB here
140  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, aMagnitude, MAGIQUEST_MAGNITUDE_BITS);
141  sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, tChecksum, MAGIQUEST_CHECKSUM_BITS);
142 
143  // must be after sending, in order not to destroy the send timing
144  DEBUG_PRINT(F("MagiQuest checksum=0x"));
145  DEBUG_PRINTLN(tChecksum, HEX);
146 }
147 
148 //+=============================================================================
149 //
150 /*
151  * decodes a 56 bit result, which is not really compatible with standard decoder layout
152  * magnitude is stored in command
153  * 31 bit wand_id is stored in decodedRawData
154  * lower 16 bit of wand_id is stored in address
155  */
157 
158  // Check we have the right amount of data, magnitude and ID bits and 8 start bits + 0 stop bit
159  if (decodedIRData.rawlen != (2 * MAGIQUEST_BITS)) {
160  DEBUG_PRINT(F("MagiQuest: Data length="));
162  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  DEBUG_PRINT(F("MagiQuest: Not 8 leading zero start bits received, RawData=0x"));
178  return false;
179  }
180 
181  /*
182  * Decode the 31 bit ID
183  */
184  decodePulseDistanceWidthData_P(&MagiQuestProtocolConstants, MAGIQUEST_WAND_ID_BITS, (MAGIQUEST_START_BITS * 2) + 1);
185 #if defined(USE_THRESHOLD_DECODER)
186  decodedIRData.decodedRawData = decodedIRData.decodedRawData ^ 0x7FFFFFFF; // We have 31 bit. For Magiquest a small pause is a 1 which is inverse to threshold decoding
187 #endif
188 
189  LongUnion tDecodedRawData;
190  uint32_t tWandId = decodedIRData.decodedRawData; // save tWandId for later use
191  tDecodedRawData.ULong = decodedIRData.decodedRawData << 1; // shift for checksum computation
192  uint8_t tChecksum = tDecodedRawData.Bytes[0] + tDecodedRawData.Bytes[1] + tDecodedRawData.Bytes[2] + tDecodedRawData.Bytes[3];
193 
194  DEBUG_PRINT(F("31 bit WandId=0x"));
196  DEBUG_PRINT(F(" shifted=0x"));
197  DEBUG_PRINTLN(tDecodedRawData.ULong, HEX);
198 
199  /*
200  * Decode the 9 bit Magnitude + 8 bit checksum
201  */
204 
205 #if defined(USE_THRESHOLD_DECODER)
206  decodedIRData.decodedRawData = decodedIRData.decodedRawData ^ 0x0001FFFF; // We have 17 bit. For Magiquest a small pause is a 1 which is inverse to threshold decoding
207 
208 #endif
209 
210  DEBUG_PRINT(F("Magnitude + checksum=0x"));
212 
213  tDecodedRawData.ULong = decodedIRData.decodedRawData;
214  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
215 
216  tChecksum += tDecodedRawData.UBytes[2] /* only one bit */+ tDecodedRawData.UBytes[1] + tDecodedRawData.UBytes[0];
217  if (tChecksum != 0) {
219  DEBUG_PRINT(F("Checksum 0x"));
220  DEBUG_PRINT(tChecksum, HEX);
221  DEBUG_PRINTLN(F(" is not 0"));
222  }
223 
224  // Success
225  decodedIRData.decodedRawData = tWandId; // 31 bit wand_id
226  decodedIRData.address = tWandId; // lower 16 bit of wand_id
227  decodedIRData.extra = tWandId >> 16; // upper 15 bit of wand_id
228 
232 
233  return true;
234 }
235 
237 #include "LocalDebugLevelEnd.h"
238 
239 #endif // _IR_MAGIQUEST_HPP
IRData::address
uint16_t address
Decoded address, Distance protocol (tMarkTicksLong (if tMarkTicksLong == 0, then tMarkTicksShort) << ...
Definition: IRremoteInt.h:164
SUPPRESS_STOP_BIT
#define SUPPRESS_STOP_BIT
Definition: IRProtocol.h:155
LongUnion
Union to specify parts / manifestations of a 32 bit Long without casts and shifts.
Definition: LongUnion.h:59
PROTOCOL_IS_MSB_FIRST
#define PROTOCOL_IS_MSB_FIRST
Definition: IRProtocol.h:156
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
MAGIQUEST_MAGNITUDE_BITS
#define MAGIQUEST_MAGNITUDE_BITS
Definition: ir_MagiQuest.hpp:88
IRrecv::decodePulseDistanceWidthData_P
void decodePulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset=3)
Definition: IRReceive.hpp:1112
MAGIQUEST_ZERO_MARK
#define MAGIQUEST_ZERO_MARK
Definition: ir_MagiQuest.hpp:111
LongUnion::UBytes
uint8_t UBytes[4]
Definition: LongUnion.h:91
IRsend::sendMagiQuest
void sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude)
Definition: ir_MagiQuest.hpp:124
MAGIQUEST
@ MAGIQUEST
Definition: IRProtocol.h:103
MAGIQUEST_ONE_MARK
#define MAGIQUEST_ONE_MARK
Definition: ir_MagiQuest.hpp:109
LocalDebugLevelStart.h
IRDATA_FLAGS_PARITY_FAILED
#define IRDATA_FLAGS_PARITY_FAILED
The current (autorepeat) frame violated parity check.
Definition: IRProtocol.h:127
MAGIQUEST_ZERO_SPACE
#define MAGIQUEST_ZERO_SPACE
Definition: ir_MagiQuest.hpp:112
PulseDistanceWidthProtocolConstants
Definition: IRProtocol.h:139
MAGIQUEST_WAND_ID_BITS
#define MAGIQUEST_WAND_ID_BITS
Definition: ir_MagiQuest.hpp:89
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
MAGIQUEST_BITS
#define MAGIQUEST_BITS
Definition: ir_MagiQuest.hpp:95
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
MAGIQUEST_ONE_SPACE
#define MAGIQUEST_ONE_SPACE
Definition: ir_MagiQuest.hpp:110
IRsend::sendPulseDistanceWidthData_P
void sendPulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, IRDecodedRawDataType aData, uint_fast8_t aNumberOfBits)
Definition: IRSend.hpp:670
LongUnion::ULong
uint32_t ULong
Definition: LongUnion.h:95
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
IRData::extra
uint16_t extra
Contains upper 16 bit of Magiquest WandID, Kaseikyo unknown vendor ID and Distance protocol (HeaderMa...
Definition: IRremoteInt.h:166
PROGMEM
struct PulseDistanceWidthProtocolConstants const MagiQuestProtocolConstants PROGMEM
Definition: ir_MagiQuest.hpp:115
PROTOCOL_IS_PULSE_DISTANCE_WIDTH
#define PROTOCOL_IS_PULSE_DISTANCE_WIDTH
Definition: IRProtocol.h:152
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:182
IRrecv::decodeMagiQuest
bool decodeMagiQuest()
Definition: ir_MagiQuest.hpp:156
MAGIQUEST_CHECKSUM_BITS
#define MAGIQUEST_CHECKSUM_BITS
Definition: ir_MagiQuest.hpp:87
IRData::protocol
decode_type_t protocol
UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ...
Definition: IRremoteInt.h:163
IRsend::enableIROut
void enableIROut(uint_fast8_t aFrequencyKHz)
Enables IR output.
Definition: IRSend.hpp:1498
LocalDebugLevelEnd.h
MAGIQUEST_START_BITS
#define MAGIQUEST_START_BITS
Definition: ir_MagiQuest.hpp:90