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