ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
nec_protocol.cpp
Go to the documentation of this file.
1#include "nec_protocol.h"
3#include "esphome/core/log.h"
4
5namespace esphome::remote_base {
6
7ESPHOME_LOG_TAG(TAG, "remote.nec");
8
9static constexpr uint32_t HEADER_HIGH_US = 9000;
10static constexpr uint32_t HEADER_LOW_US = 4500;
11static constexpr uint32_t BIT_HIGH_US = 560;
12static constexpr uint32_t BIT_ONE_LOW_US = 1690;
13static constexpr uint32_t BIT_ZERO_LOW_US = 560;
14static constexpr uint32_t REPEAT_HEADER_LOW_US = HEADER_LOW_US / 2;
15// A held button repeats every 108 ms: the first gap depends on the frame's own length, later ones follow a repeat frame
16static constexpr uint32_t REPEAT_PERIOD_US = 108000;
17static constexpr uint32_t REPEAT_FRAME_US = HEADER_HIGH_US + REPEAT_HEADER_LOW_US + BIT_HIGH_US;
18static constexpr uint32_t REPEAT_GAP_US = REPEAT_PERIOD_US - REPEAT_FRAME_US;
19// Transmitters without an RMT peripheral hold an interrupt lock for the whole send, about 108 ms per repeat
20static constexpr uint16_t MAX_COMMAND_REPEATS = 10;
21
23 ESP_LOGD(TAG, "Sending NEC: address=0x%04X, command=0x%04X command_repeats=%d", data.address, data.command,
24 data.command_repeats);
25
26 // The schema bounds literal values; a lambda can produce anything, so clamp here too
27 const uint16_t repeats = clamp<uint16_t>(data.command_repeats, 1, MAX_COMMAND_REPEATS);
28 // Frame: header pair, 32 bit pairs, stop mark. Each extra repeat: gap space, header pair, mark.
29 dst->reserve(2 + 64 + 1 + (repeats - 1) * 4);
30 dst->set_carrier_frequency(38000);
31
32 dst->item(HEADER_HIGH_US, HEADER_LOW_US);
33 uint32_t frame_us = HEADER_HIGH_US + HEADER_LOW_US + BIT_HIGH_US;
34
35 for (uint16_t value : {data.address, data.command}) {
36 for (uint16_t mask = 1; mask; mask <<= 1) {
37 const uint32_t low_us = (value & mask) ? BIT_ONE_LOW_US : BIT_ZERO_LOW_US;
38 dst->item(BIT_HIGH_US, low_us);
39 frame_us += BIT_HIGH_US + low_us;
40 }
41 }
42
43 dst->mark(BIT_HIGH_US);
44
45 for (uint16_t repeat = 1; repeat < repeats; repeat++) {
46 dst->space(repeat == 1 ? REPEAT_PERIOD_US - frame_us : REPEAT_GAP_US);
47 dst->item(HEADER_HIGH_US, REPEAT_HEADER_LOW_US);
48 dst->mark(BIT_HIGH_US);
49 }
50}
52 NECData data{
53 .address = 0,
54 .command = 0,
55 .command_repeats = 1,
56 };
57 if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
58 return {};
59
60 for (uint16_t mask = 1; mask; mask <<= 1) {
61 if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
62 data.address |= mask;
63 } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
64 data.address &= ~mask;
65 } else {
66 return {};
67 }
68 }
69
70 for (uint16_t mask = 1; mask; mask <<= 1) {
71 if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
72 data.command |= mask;
73 } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
74 data.command &= ~mask;
75 } else {
76 return {};
77 }
78 }
79
80 // Repeat frames arrive as separate captures (receiver idle is far below the repeat gap) and are not decoded
81 src.expect_mark(BIT_HIGH_US);
82 return data;
83}
84void NECProtocol::dump(const NECData &data) {
85 ESP_LOGI(TAG, "Received NEC: address=0x%04X, command=0x%04X command_repeats=%d", data.address, data.command,
86 data.command_repeats);
87}
88
89} // namespace esphome::remote_base
optional< NECData > decode(RemoteReceiveData src)
void dump(const NECData &data)
void encode(RemoteTransmitData *dst, const NECData &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