ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
magiquest_protocol.cpp
Go to the documentation of this file.
2#include "esphome/core/log.h"
3
4/* Based on protocol analysis from
5 * https://arduino-irremote.github.io/Arduino-IRremote/ir__MagiQuest_8hpp_source.html
6 */
7
8namespace esphome::remote_base {
9
10ESPHOME_LOG_TAG(TAG, "remote.magiquest");
11
12static constexpr uint32_t MAGIQUEST_UNIT = 288; // us
13// Half a unit is the widest window that still tells a one unit mark or space from a two unit one.
14static constexpr uint32_t MAGIQUEST_TOLERANCE = MAGIQUEST_UNIT / 2;
15static constexpr uint32_t MAGIQUEST_ONE_MARK = 2 * MAGIQUEST_UNIT;
16static constexpr uint32_t MAGIQUEST_ONE_SPACE = 2 * MAGIQUEST_UNIT;
17static constexpr uint32_t MAGIQUEST_ZERO_MARK = MAGIQUEST_UNIT;
18static constexpr uint32_t MAGIQUEST_ZERO_SPACE = 3 * MAGIQUEST_UNIT;
19
20static bool checksum_is_valid(uint32_t wand_id, uint32_t magnitude_and_checksum) {
21 // The frame's six payload bytes, which start at the wand id's top bit, must sum to zero.
22 const uint32_t id_bytes = wand_id << 1;
23 uint8_t sum = 0;
24 for (uint8_t shift = 0; shift < 32; shift += 8) {
25 sum += (id_bytes >> shift) + (magnitude_and_checksum >> shift);
26 }
27 return sum == 0;
28}
29
31 // Still the old frame layout, which the decoder no longer reads; changing it would alter what existing configs send.
32 dst->reserve(101); // 2 start bits, 48 data bits, 1 stop bit
33 dst->set_carrier_frequency(38000);
34
35 // 2 start bits
36 dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
37 dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
38 for (uint32_t mask = 1 << 31; mask; mask >>= 1) {
39 if (data.wand_id & mask) {
40 dst->item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE);
41 } else {
42 dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
43 }
44 }
45
46 for (uint16_t mask = 1 << 15; mask; mask >>= 1) {
47 if (data.magnitude & mask) {
48 dst->item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE);
49 } else {
50 dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
51 }
52 }
53
54 dst->mark(MAGIQUEST_UNIT);
55}
57 src.set_tolerance(MAGIQUEST_TOLERANCE, TOLERANCE_MODE_TIME);
58
59 MagiQuestData data{};
60
61 // 8 bit header, always zero
62 for (uint8_t i = 0; i < 8; i++) {
63 if (!src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
64 return {};
65 }
66 }
67
68 // 31 bit wand id
69 for (uint32_t mask = 1 << 30; mask; mask >>= 1) {
70 if (src.expect_item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE)) {
71 data.wand_id |= mask;
72 } else if (!src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
73 return {};
74 }
75 }
76
77 // 9 bit magnitude, then 8 bit checksum
78 uint32_t magnitude_and_checksum = 0;
79 for (uint32_t mask = 1 << 16; mask > 1; mask >>= 1) {
80 if (src.expect_item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE)) {
81 magnitude_and_checksum |= mask;
82 } else if (!src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
83 return {};
84 }
85 }
86 // The last bit has no trailing space. A wrong or missing mark here is caught by the checksum.
87 if (src.expect_mark(MAGIQUEST_ONE_MARK)) {
88 magnitude_and_checksum |= 1;
89 }
90
91 if (!checksum_is_valid(data.wand_id, magnitude_and_checksum)) {
92 return {};
93 }
94 data.magnitude = magnitude_and_checksum >> 8;
95 return data;
96}
98 ESP_LOGI(TAG, "Received MagiQuest: wand_id=0x%08" PRIX32 ", magnitude=%u", data.wand_id, data.magnitude);
99}
100
101} // namespace esphome::remote_base
optional< MagiQuestData > decode(RemoteReceiveData src)
void dump(const MagiQuestData &data)
void encode(RemoteTransmitData *dst, const MagiQuestData &data)
void set_carrier_frequency(uint32_t carrier_frequency)
Definition remote_base.h:64
void item(uint32_t mark, uint32_t space)
Definition remote_base.h:43
ESPHOME_LOG_TAG(TAG, "remote.abbwelcome")
const void * src
Definition hal.h:68
static void uint32_t