ESPHome 2025.11.0-dev
Loading...
Searching...
No Matches
symphony_protocol.cpp
Go to the documentation of this file.
1#include "symphony_protocol.h"
2#include "esphome/core/log.h"
3
4namespace esphome {
5namespace remote_base {
6
7static const char *const TAG = "remote.symphony";
8
9// Reference implementation and timing details:
10// IRremoteESP8266 ir_Symphony.cpp
11// https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp
12// The implementation below mirrors the constant bit-time mapping and
13// footer-gap handling used there.
14
15// Symphony protocol timing specifications (tuned to handset captures)
16static const uint32_t BIT_ZERO_HIGH_US = 460; // short
17static const uint32_t BIT_ZERO_LOW_US = 1260; // long
18static const uint32_t BIT_ONE_HIGH_US = 1260; // long
19static const uint32_t BIT_ONE_LOW_US = 460; // short
20static const uint32_t CARRIER_FREQUENCY = 38000;
21
22// IRremoteESP8266 reference: kSymphonyFooterGap = 4 * (mark + space)
23static const uint32_t FOOTER_GAP_US = 4 * (BIT_ZERO_HIGH_US + BIT_ZERO_LOW_US);
24// Typical inter-frame gap (~34.8 ms observed)
25static const uint32_t INTER_FRAME_GAP_US = 34760;
26
28 dst->set_carrier_frequency(CARRIER_FREQUENCY);
29 ESP_LOGD(TAG, "Sending Symphony: data=0x%0*X nbits=%u repeats=%u", (data.nbits + 3) / 4, (uint32_t) data.data,
30 data.nbits, data.repeats);
31 // Each bit produces a mark+space (2 entries). We fold the inter-frame/footer gap
32 // into the last bit's space of each frame to avoid over-length gaps.
33 dst->reserve(data.nbits * 2u * data.repeats);
34
35 for (uint8_t repeats = 0; repeats < data.repeats; repeats++) {
36 // Data bits (MSB first)
37 for (uint32_t mask = 1UL << (data.nbits - 1); mask != 0; mask >>= 1) {
38 const bool is_last_bit = (mask == 1);
39 const bool is_last_frame = (repeats == (data.repeats - 1));
40 if (is_last_bit) {
41 // Emit last bit's mark; replace its space with the proper gap
42 if (data.data & mask) {
43 dst->mark(BIT_ONE_HIGH_US);
44 } else {
45 dst->mark(BIT_ZERO_HIGH_US);
46 }
47 dst->space(is_last_frame ? FOOTER_GAP_US : INTER_FRAME_GAP_US);
48 } else {
49 if (data.data & mask) {
50 dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US);
51 } else {
52 dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US);
53 }
54 }
55 }
56 }
57}
58
60 auto is_valid_len = [](uint8_t nbits) -> bool { return nbits == 8 || nbits == 12 || nbits == 16; };
61
62 RemoteReceiveData s = src; // copy
63 SymphonyData out{0, 0, 1};
64
65 for (; out.nbits < 32; out.nbits++) {
66 if (s.expect_mark(BIT_ONE_HIGH_US)) {
67 if (!s.expect_space(BIT_ONE_LOW_US)) {
68 // Allow footer gap immediately after the last mark
69 if (s.peek_space_at_least(FOOTER_GAP_US)) {
70 uint8_t bits_with_this = out.nbits + 1;
71 if (is_valid_len(bits_with_this)) {
72 out.data = (out.data << 1UL) | 1UL;
73 out.nbits = bits_with_this;
74 return out;
75 }
76 }
77 return {};
78 }
79 // Successfully consumed a '1' bit (mark + space)
80 out.data = (out.data << 1UL) | 1UL;
81 continue;
82 } else if (s.expect_mark(BIT_ZERO_HIGH_US)) {
83 if (!s.expect_space(BIT_ZERO_LOW_US)) {
84 // Allow footer gap immediately after the last mark
85 if (s.peek_space_at_least(FOOTER_GAP_US)) {
86 uint8_t bits_with_this = out.nbits + 1;
87 if (is_valid_len(bits_with_this)) {
88 out.data = (out.data << 1UL) | 0UL;
89 out.nbits = bits_with_this;
90 return out;
91 }
92 }
93 return {};
94 }
95 // Successfully consumed a '0' bit (mark + space)
96 out.data = (out.data << 1UL) | 0UL;
97 continue;
98 } else {
99 // Completed a valid-length frame followed by a footer gap
100 if (is_valid_len(out.nbits) && s.peek_space_at_least(FOOTER_GAP_US)) {
101 return out;
102 }
103 return {};
104 }
105 }
106
107 if (is_valid_len(out.nbits) && s.peek_space_at_least(FOOTER_GAP_US)) {
108 return out;
109 }
110
111 return {};
112}
113
115 const int32_t hex_width = (data.nbits + 3) / 4; // pad to nibble width
116 ESP_LOGI(TAG, "Received Symphony: data=0x%0*X, nbits=%d", hex_width, (uint32_t) data.data, data.nbits);
117}
118
119} // namespace remote_base
120} // namespace esphome
bool peek_space_at_least(uint32_t length, uint32_t offset=0) const
void set_carrier_frequency(uint32_t carrier_frequency)
Definition remote_base.h:30
void item(uint32_t mark, uint32_t space)
Definition remote_base.h:25
void encode(RemoteTransmitData *dst, const SymphonyData &data) override
optional< SymphonyData > decode(RemoteReceiveData src) override
void dump(const SymphonyData &data) override
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7