ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
nexus_protocol.cpp
Go to the documentation of this file.
1#include "nexus_protocol.h"
2#include "esphome/core/log.h"
3
4#include <algorithm>
5#include <cinttypes>
6#include <cmath>
7
8namespace esphome::remote_base {
9
10ESPHOME_LOG_TAG(TAG, "remote.nexus");
11
23static constexpr uint8_t NBITS = 36;
24
25static constexpr uint32_t SYNC_HIGH_US = 500;
26static constexpr uint32_t SYNC_LOW_US = 3900;
27static constexpr uint32_t BIT_HIGH_US = 500;
28static constexpr uint32_t BIT_ZERO_LOW_US = 980;
29static constexpr uint32_t BIT_ONE_LOW_US = 1950;
30
32 // A templated value may be out of range or NaN (an unavailable sensor); send 0 for NaN
33 const float temperature = std::isnan(data.temperature) ? 0.0f : std::clamp(data.temperature, -204.8f, 204.7f);
34 const auto t = static_cast<int16_t>(lroundf(temperature * 10.0f));
35 const uint64_t packet = uint64_t(data.address) << 28 | uint64_t(data.battery_level) << 27 |
36 uint64_t(data.force_update) << 26 | uint64_t((data.channel - 1) & 0b11) << 24 |
37 uint64_t(t & 0x0FFF) << 12 | 0b1111 << 8 | data.humidity;
38
39 dst->reserve(NBITS * 2 + 2);
40 for (int8_t i = NBITS - 1; i >= 0; i--)
41 dst->item(BIT_HIGH_US, (packet >> i) & 1 ? BIT_ONE_LOW_US : BIT_ZERO_LOW_US);
42 // The sync follows the bits, as the sensors send it: its mark ends the last space, its gap separates repeats
43 dst->item(SYNC_HIGH_US, SYNC_LOW_US);
44}
45
47 // A buffer may start with the previous frame's sync when the receiver did not split on it
48 src.expect_item(SYNC_HIGH_US, SYNC_LOW_US);
49 if (src.size() < NBITS * 2)
50 return {};
51
52 uint64_t packet = 0;
53 for (uint8_t i = 0; i < NBITS; i++) {
54 packet <<= 1;
55 if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
56 packet |= 1;
57 } else if (!src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
58 return {};
59 }
60 }
61 if (((packet >> 8) & 0xF) != 0xF)
62 return {};
63 ESP_LOGV(TAG, "Nexus packet: 0x%09" PRIX64, packet);
64
65 return NexusData{
66 // The 12 bit field sits at bits 12-23; shift it to the top of an int16_t and back to sign extend.
67 .temperature = (static_cast<int16_t>((packet >> 8) & 0xFFF0) >> 4) / 10.0f,
68 .address = static_cast<uint8_t>(packet >> 28),
69 .battery_level = static_cast<bool>((packet >> 27) & 1),
70 .force_update = static_cast<bool>((packet >> 26) & 1),
71 .channel = static_cast<uint8_t>(((packet >> 24) & 0b11) + 1),
72 .humidity = static_cast<uint8_t>(packet),
73 };
74}
75
76void NexusProtocol::dump(const NexusData &data) {
77 ESP_LOGI(TAG,
78 "Received Nexus: address=0x%02X, channel=%u, temperature=%.1f, humidity=%u, battery_level=%d, "
79 "force_update=%d",
80 data.address, data.channel, data.temperature, data.humidity, data.battery_level, data.force_update);
81}
82
83} // namespace esphome::remote_base
uint8_t address
Definition bl0906.h:4
void dump(const NexusData &data)
void encode(RemoteTransmitData *dst, const NexusData &data)
optional< NexusData > decode(RemoteReceiveData src)
void item(uint32_t mark, uint32_t space)
Definition remote_base.h:26
ESPHOME_LOG_TAG(TAG, "remote.abbwelcome")
const void * src
Definition hal.h:68
static void uint32_t
uint16_t temperature
Definition sun_gtil2.cpp:12