#pragma once #include #include "config.h" #include "data_model.h" constexpr size_t LORA_MAX_PAYLOAD = 230; constexpr size_t LORA_FRAME_HEADER_LEN = 3; // msg_kind + dev_id_short constexpr size_t LORA_FRAME_CRC_LEN = 2; constexpr size_t LORA_ACK_DOWN_PAYLOAD_LEN = 7; // flags(1) + batch_id(2) + epoch_utc(4) static_assert(LORA_ACK_DOWN_PAYLOAD_LEN == 7, "ACK_DOWN payload must remain 7 bytes"); constexpr size_t lora_frame_size(size_t payload_len) { return LORA_FRAME_HEADER_LEN + payload_len + LORA_FRAME_CRC_LEN; } enum class LoraMsgKind : uint8_t { BatchUp = 0, AckDown = 1 }; struct LoraPacket { LoraMsgKind msg_kind; uint16_t device_id_short; uint8_t payload[LORA_MAX_PAYLOAD]; size_t payload_len; int16_t rssi_dbm; float snr_db; }; void lora_init(); bool lora_send(const LoraPacket &pkt); bool lora_receive(LoraPacket &pkt, uint32_t timeout_ms); RxRejectReason lora_get_last_rx_reject_reason(); bool lora_get_last_rx_signal(int16_t &rssi_dbm, float &snr_db); void lora_idle(); void lora_sleep(); void lora_receive_continuous(); bool lora_receive_window(LoraPacket &pkt, uint32_t timeout_ms); uint32_t lora_airtime_ms(size_t packet_len);