ESPHome 2025.5.0
Loading...
Searching...
No Matches
nexa_protocol.cpp
Go to the documentation of this file.
1#include "nexa_protocol.h"
2#include "esphome/core/log.h"
3
4namespace esphome {
5namespace remote_base {
6
7static const char *const TAG = "remote.nexa";
8
9static const uint8_t NBITS = 32;
10static const uint32_t HEADER_HIGH_US = 319;
11static const uint32_t HEADER_LOW_US = 2610;
12static const uint32_t BIT_HIGH_US = 319;
13static const uint32_t BIT_ONE_LOW_US = 1000;
14static const uint32_t BIT_ZERO_LOW_US = 140;
15
16static const uint32_t TX_HEADER_HIGH_US = 250;
17static const uint32_t TX_HEADER_LOW_US = TX_HEADER_HIGH_US * 10;
18static const uint32_t TX_BIT_HIGH_US = 250;
19static const uint32_t TX_BIT_ONE_LOW_US = TX_BIT_HIGH_US * 5;
20static const uint32_t TX_BIT_ZERO_LOW_US = TX_BIT_HIGH_US * 1;
21
23 // '1' => '10'
24 dst->item(TX_BIT_HIGH_US, TX_BIT_ONE_LOW_US);
25 dst->item(TX_BIT_HIGH_US, TX_BIT_ZERO_LOW_US);
26}
27
29 // '0' => '01'
30 dst->item(TX_BIT_HIGH_US, TX_BIT_ZERO_LOW_US);
31 dst->item(TX_BIT_HIGH_US, TX_BIT_ONE_LOW_US);
32}
33
34void NexaProtocol::sync(RemoteTransmitData *dst) const { dst->item(TX_HEADER_HIGH_US, TX_HEADER_LOW_US); }
35
38
39 // Send SYNC
40 this->sync(dst);
41
42 // Device (26 bits)
43 for (int16_t i = 26 - 1; i >= 0; i--) {
44 if (data.device & (1 << i)) {
45 this->one(dst);
46 } else {
47 this->zero(dst);
48 }
49 }
50
51 // Group (1 bit)
52 if (data.group != 0) {
53 this->one(dst);
54 } else {
55 this->zero(dst);
56 }
57
58 // State (1 bit)
59 if (data.state == 2) {
60 // Special case for dimmers...send 00 as state
61 dst->item(TX_BIT_HIGH_US, TX_BIT_ZERO_LOW_US);
62 dst->item(TX_BIT_HIGH_US, TX_BIT_ZERO_LOW_US);
63 } else if (data.state == 1) {
64 this->one(dst);
65 } else {
66 this->zero(dst);
67 }
68
69 // Channel (4 bits)
70 for (int16_t i = 4 - 1; i >= 0; i--) {
71 if (data.channel & (1 << i)) {
72 this->one(dst);
73 } else {
74 this->zero(dst);
75 }
76 }
77
78 // Level (4 bits)
79 if (data.state == 2) {
80 for (int16_t i = 4 - 1; i >= 0; i--) {
81 if (data.level & (1 << i)) {
82 this->one(dst);
83 } else {
84 this->zero(dst);
85 }
86 }
87 }
88
89 // Send finishing Zero
90 dst->item(TX_BIT_HIGH_US, TX_BIT_ZERO_LOW_US);
91}
92
94 NexaData out{
95 .device = 0,
96 .group = 0,
97 .state = 0,
98 .channel = 0,
99 .level = 0,
100 };
101
102 // From: http://tech.jolowe.se/home-automation-rf-protocols/
103 // New data: http://tech.jolowe.se/old-home-automation-rf-protocols/
104 /*
105
106 SHHHH HHHH HHHH HHHH HHHH HHHH HHGO EE BB DDDD 0 P
107
108 S = Sync bit.
109 H = The first 26 bits are transmitter unique codes, and it is this code that the receiver "learns" to recognize.
110 G = Group code, set to one for the whole group.
111 O = On/Off bit. Set to 1 for on, 0 for off.
112 E = Unit to be turned on or off. The code is inverted, i.e. '11' equals 1, '00' equals 4.
113 B = Button code. The code is inverted, i.e. '11' equals 1, '00' equals 4.
114 D = Dim level bits.
115 0 = packet always ends with a zero.
116 P = Pause, a 10 ms pause in between re-send.
117
118 Update: First of all the '1' and '0' bit seems to be reversed (and be the same as Jula I protocol below), i.e.
119
120 */
121
122 // Require a SYNC pulse + long gap
123 if (!src.expect_pulse_with_gap(HEADER_HIGH_US, HEADER_LOW_US))
124 return {};
125
126 // Device
127 for (uint8_t i = 0; i < 26; i++) {
128 out.device <<= 1UL;
129 if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US) &&
130 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US))) {
131 // '1' => '10'
132 out.device |= 0x01;
133 } else if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US) &&
134 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US))) {
135 // '0' => '01'
136 out.device |= 0x00;
137 } else {
138 // This should not happen...failed command
139 return {};
140 }
141 }
142
143 // GROUP
144 for (uint8_t i = 0; i < 1; i++) {
145 out.group <<= 1UL;
146 if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US) &&
147 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US))) {
148 // '1' => '10'
149 out.group |= 0x01;
150 } else if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US) &&
151 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US))) {
152 // '0' => '01'
153 out.group |= 0x00;
154 } else {
155 // This should not happen...failed command
156 return {};
157 }
158 }
159
160 // STATE
161 for (uint8_t i = 0; i < 1; i++) {
162 out.state <<= 1UL;
163
164 // Special treatment as we should handle 01, 10 and 00
165 // We need to care for the advance made in the expect functions
166 // hence take them one at a time so that we do not get out of sync
167 // in decoding
168
169 if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US)) {
170 // Starts with '1'
171 if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
172 // '10' => 1
173 out.state |= 0x01;
174 } else if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US)) {
175 // '11' => NOT OK
176 // This case is here to make sure we advance through the correct index
177 // This should not happen...failed command
178 return {};
179 }
180 } else if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
181 // Starts with '0'
182 if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US)) {
183 // '01' => 0
184 out.state |= 0x00;
185 } else if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
186 // '00' => Special case for dimmer! => 2
187 out.state |= 0x02;
188 }
189 }
190 }
191
192 // CHANNEL (EE and BB bits)
193 for (uint8_t i = 0; i < 4; i++) {
194 out.channel <<= 1UL;
195 if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US) &&
196 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US))) {
197 // '1' => '10'
198 out.channel |= 0x01;
199 } else if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US) &&
200 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US))) {
201 // '0' => '01'
202 out.channel |= 0x00;
203 } else {
204 // This should not happen...failed command
205 return {};
206 }
207 }
208
209 // Optional to transmit LEVEL data (8 bits more)
210 if (int32_t(src.get_index() + 8) >= src.size()) {
211 return out;
212 }
213
214 // LEVEL
215 for (uint8_t i = 0; i < 4; i++) {
216 out.level <<= 1UL;
217 if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US) &&
218 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US))) {
219 // '1' => '10'
220 out.level |= 0x01;
221 } else if (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ZERO_LOW_US) &&
222 (src.expect_pulse_with_gap(BIT_HIGH_US, BIT_ONE_LOW_US))) {
223 // '0' => '01'
224 out.level |= 0x00;
225 } else {
226 // This should not happen...failed command
227 break;
228 }
229 }
230
231 return out;
232}
233
234void NexaProtocol::dump(const NexaData &data) {
235 ESP_LOGI(TAG, "Received NEXA: device=0x%04" PRIX32 " group=%d state=%d channel=%d level=%d", data.device, data.group,
236 data.state, data.channel, data.level);
237}
238
239} // namespace remote_base
240} // namespace esphome
void dump(const NexaData &data) override
void zero(RemoteTransmitData *dst) const
void encode(RemoteTransmitData *dst, const NexaData &data) override
void one(RemoteTransmitData *dst) const
optional< NexaData > decode(RemoteReceiveData src) override
void sync(RemoteTransmitData *dst) const
bool expect_pulse_with_gap(uint32_t mark, uint32_t space)
void set_carrier_frequency(uint32_t carrier_frequency)
Definition remote_base.h:34
void item(uint32_t mark, uint32_t space)
Definition remote_base.h:29
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t sync
Definition sun_gtil2.cpp:0