Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
radio_soft_phy.h
Go to the documentation of this file.
1#pragma once
2
3/// @file radio_soft_phy.h
4/// @brief Software PHY for radios without IoHomeOn hardware framing.
5/// @ingroup hioc_radio
6///
7/// Chips such as the SX1262 and LR1121 have no hardware mode equivalent to the SX1276's
8/// IoHomeOn: they provide generic GFSK framing only. This header
9/// declares the chip-agnostic pieces such a driver needs to reproduce IO-Homecontrol framing
10/// in software — UART bit-encoding for TX, and a UART-decode probe (with CRC validation) to
11/// recover frame boundaries from an unaligned raw RX bitstream.
12
13#include "radio_interface.h"
14
15#include <cstdint>
16
17namespace esphome {
18namespace home_io_control {
19
20/// @brief Result of the UART probe: best candidate frame within a raw capture.
22 bool valid{false}; ///< A plausible frame was found.
23 uint8_t bit_offset{0}; ///< Bit offset where the best decode started.
24 uint8_t decoded_len{0}; ///< Total number of bytes decoded at that offset.
25 uint8_t frame_start{0}; ///< Index into decoded buffer where the frame begins.
26 uint8_t frame_len{0}; ///< Length of the candidate IoFrame (decoded bytes).
27 uint8_t decoded[RADIO_PACKET_BUFFER_SIZE]{}; ///< Full decoded UART stream at the chosen offset.
28};
29
30/// @brief Decode a UART-encoded bitstream from the given bit offset.
31uint8_t decode_uart_probe(const uint8_t *raw, uint8_t raw_len, uint8_t bit_offset, uint8_t *decoded,
32 uint8_t decoded_max_len);
33
34/// @brief Search raw RX buffer for the best CRC-validated IO-Homecontrol frame.
35UartProbeResult find_uart_probe(const uint8_t *raw, uint8_t raw_len);
36
37/// @brief Check if a command ID is one of the known IO-Homecontrol commands.
38///
39/// This is a gate, not a directory: find_uart_probe() only accepts a CRC-valid candidate whose
40/// cmd passes this check (or whose CTRL0_PROTOCOL_1W bit is set), so a command missing here makes
41/// every SX1262/LR1121 reception of that opcode silently unrecoverable on this software PHY —
42/// the frame is on air, its CRC matches, and it still never reaches the parser. Exposed (out of
43/// radio_soft_phy.cpp's anonymous namespace) so tests can iterate every accepted command directly
44/// instead of hand-maintaining a parallel list that can drift out of sync with this one.
45/// @param cmd Command byte.
46/// @return true if cmd matches a known command constant.
47bool is_known_io_command(uint8_t cmd);
48
49/// @brief Bits an on-air UART cell spends per protocol byte: start(1) + data(8) + stop(1).
50static constexpr uint8_t UART_CELL_BITS = 10;
51
52/// @brief Raw on-air bytes needed to carry a whole frame: `frame_len` protocol bytes plus the
53/// two trailing CRC bytes, each UART-packed into a 10-bit cell.
54///
55/// This is what makes a length-driven receive possible at all: a frame's own size is knowable
56/// from its first decoded byte, so the raw byte count it will occupy is knowable too — no chip
57/// needs to tell us where the frame ends.
58/// @param frame_len Protocol frame length in bytes (CTRL0's own length field, +1).
59/// @return Raw byte count, rounded up to whole bytes.
60uint8_t soft_phy_raw_bytes_for_frame(uint8_t frame_len);
61
62/// @brief Recover a frame's total length from the very first UART cell of a reception.
63///
64/// CTRL0 bits [4:0] hold `frame_length - 1` (see proto_frame.h), and CTRL0 is the first byte
65/// after the sync word — so ten bits of air time are enough to learn how long the whole frame
66/// will be. Alignment is not yet known at that point, so every probe offset is tried and the
67/// largest plausible answer wins: over-waiting by a few bytes costs a little latency, whereas
68/// under-waiting would truncate the frame.
69/// @param raw Raw bytes read from the chip's data buffer, starting at the reception's own offset.
70/// @param raw_len Number of raw bytes available (three is enough at any alignment).
71/// @return Plausible frame length in bytes, or 0 when no offset yields one.
72uint8_t soft_phy_peek_frame_length(const uint8_t *raw, uint8_t raw_len);
73
74/// @brief UART-encode a buffer of bytes (start bit 0, 8 data bits LSB-first, stop bit 1).
75/// @return Number of encoded bytes, or 0 if the output buffer is too small.
76uint8_t uart_encode_packet(const uint8_t *data, uint8_t len, uint8_t *encoded, uint8_t encoded_max_len);
77
78} // namespace home_io_control
79} // namespace esphome
uint8_t decode_uart_probe(const uint8_t *raw, uint8_t raw_len, uint8_t bit_offset, uint8_t *decoded, uint8_t decoded_max_len)
Decode a raw UART‑encoded bitstream into bytes.
bool is_known_io_command(uint8_t cmd)
This list missing CMD_GET_GENERAL_INFO3_RESP (0x59) is exactly what turned a real Q2 probe reply into...
uint8_t soft_phy_peek_frame_length(const uint8_t *raw, uint8_t raw_len)
Recover a frame's total length from the very first UART cell of a reception.
uint8_t uart_encode_packet(const uint8_t *data, uint8_t len, uint8_t *encoded, uint8_t encoded_max_len)
UART-encode a buffer of bytes (start bit 0, 8 data bits LSB-first, stop bit 1).
UartProbeResult find_uart_probe(const uint8_t *raw, uint8_t raw_len)
Search raw RX buffer for the best CRC-validated IO-Homecontrol frame.
uint8_t soft_phy_raw_bytes_for_frame(uint8_t frame_len)
Raw on-air bytes needed to carry a whole frame: frame_len protocol bytes plus the two trailing CRC by...
constexpr uint8_t RADIO_PACKET_BUFFER_SIZE
Scratch buffer size for raw radio packets and recovered frames.
static constexpr uint8_t UART_CELL_BITS
Bits an on-air UART cell spends per protocol byte: start(1) + data(8) + stop(1).
Radio abstraction layer for IO-Homecontrol.
Result of the UART probe: best candidate frame within a raw capture.
uint8_t decoded_len
Total number of bytes decoded at that offset.
uint8_t frame_start
Index into decoded buffer where the frame begins.
bool valid
A plausible frame was found.
uint8_t bit_offset
Bit offset where the best decode started.
uint8_t frame_len
Length of the candidate IoFrame (decoded bytes).
uint8_t decoded[RADIO_PACKET_BUFFER_SIZE]
Full decoded UART stream at the chosen offset.