Initial commit
This commit is contained in:
476
components/lora_radio/lora_radio.c
Normal file
476
components/lora_radio/lora_radio.c
Normal file
@@ -0,0 +1,476 @@
|
||||
#include "lora_radio.h"
|
||||
#include <string.h>
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_lora_1121.h"
|
||||
#include "lr1121_common/lr1121_common.h"
|
||||
#include "lr11xx_driver/lr11xx_hal.h"
|
||||
#include "lr11xx_driver/lr11xx_radio.h"
|
||||
#include "lr11xx_driver/lr11xx_regmem.h"
|
||||
#include "lr11xx_driver/lr11xx_system.h"
|
||||
|
||||
static const char *TAG = "lora_radio";
|
||||
|
||||
#define LR1121_SPI_FREQ_HZ (8 * 1000 * 1000)
|
||||
#define LR1121_SPI_MODE (0)
|
||||
#define LR1121_RX_CONTINUOUS (0xFFFFFF)
|
||||
#define LR1121_SYNC_WORD (0x12) // Private network
|
||||
#define LR1121_PA_RAMP (LR11XX_RADIO_RAMP_48_US)
|
||||
|
||||
static lr1121_t s_lr = {0};
|
||||
static spi_device_handle_t s_spi = NULL;
|
||||
static bool s_is_tx = false;
|
||||
static lora_params_t s_params;
|
||||
static lora_metrics_t s_metrics = { .rssi_dbm = -120, .snr_db = -20, .last_status = 0 };
|
||||
static char s_last_payload[96] = "(no data)";
|
||||
static size_t s_last_payload_len = 0;
|
||||
static int64_t s_last_rx_restart_us = 0;
|
||||
static bool s_tx_in_progress = false;
|
||||
static int64_t s_last_tx_us = 0;
|
||||
|
||||
static inline esp_err_t lr_status_to_esp(lr11xx_status_t st)
|
||||
{
|
||||
return (st == LR11XX_STATUS_OK) ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
static uint32_t cmhz_to_hz(int cmhz)
|
||||
{
|
||||
if (cmhz <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return (uint32_t)cmhz * 10000U;
|
||||
}
|
||||
|
||||
static lr11xx_radio_lora_sf_t map_sf(int sf)
|
||||
{
|
||||
switch (sf) {
|
||||
case 5: return LR11XX_RADIO_LORA_SF5;
|
||||
case 6: return LR11XX_RADIO_LORA_SF6;
|
||||
case 7: return LR11XX_RADIO_LORA_SF7;
|
||||
case 8: return LR11XX_RADIO_LORA_SF8;
|
||||
case 9: return LR11XX_RADIO_LORA_SF9;
|
||||
case 10: return LR11XX_RADIO_LORA_SF10;
|
||||
case 11: return LR11XX_RADIO_LORA_SF11;
|
||||
case 12: return LR11XX_RADIO_LORA_SF12;
|
||||
default: return LR11XX_RADIO_LORA_SF7;
|
||||
}
|
||||
}
|
||||
|
||||
static lr11xx_radio_lora_bw_t map_bw(int bw_khz)
|
||||
{
|
||||
switch (bw_khz) {
|
||||
case 125: return LR11XX_RADIO_LORA_BW_125;
|
||||
case 250: return LR11XX_RADIO_LORA_BW_250;
|
||||
case 500: return LR11XX_RADIO_LORA_BW_500;
|
||||
default: return LR11XX_RADIO_LORA_BW_125;
|
||||
}
|
||||
}
|
||||
|
||||
static lr11xx_radio_lora_cr_t map_cr(int cr)
|
||||
{
|
||||
switch (cr) {
|
||||
case 5: return LR11XX_RADIO_LORA_CR_4_5;
|
||||
case 6: return LR11XX_RADIO_LORA_CR_4_6;
|
||||
case 7: return LR11XX_RADIO_LORA_CR_4_7;
|
||||
case 8: return LR11XX_RADIO_LORA_CR_4_8;
|
||||
default: return LR11XX_RADIO_LORA_CR_4_5;
|
||||
}
|
||||
}
|
||||
|
||||
static void sanitize_params(lora_params_t *p)
|
||||
{
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
if (p->freq_centi_mhz < 15000) p->freq_centi_mhz = 15000; // 150 MHz мінімум
|
||||
if (p->freq_centi_mhz > 250000) p->freq_centi_mhz = 250000; // 2.5 GHz максимум
|
||||
if (p->bw_khz != 125 && p->bw_khz != 250 && p->bw_khz != 500) {
|
||||
p->bw_khz = 125;
|
||||
}
|
||||
if (p->sf < 5) p->sf = 5;
|
||||
if (p->sf > 12) p->sf = 12;
|
||||
if (p->cr < 5) p->cr = 5;
|
||||
if (p->cr > 8) p->cr = 8;
|
||||
int max_dbm = 22;
|
||||
// HF port (L/S/2.4 GHz) має нижчу допустиму потужність
|
||||
if (p->freq_centi_mhz >= 150000) {
|
||||
max_dbm = 13;
|
||||
}
|
||||
if (p->tx_power_dbm < -17) p->tx_power_dbm = -17;
|
||||
if (p->tx_power_dbm > max_dbm) p->tx_power_dbm = max_dbm;
|
||||
if (p->preamble_syms == 0) p->preamble_syms = 8;
|
||||
if (p->payload_len == 0 && p->header_implicit) {
|
||||
p->payload_len = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void lr1121_calibrate_image_for_freq(uint32_t freq_hz)
|
||||
{
|
||||
// Використовуємо універсальне калібрування за частотою в MHz ±10 MHz
|
||||
uint16_t mhz = (uint16_t)(freq_hz / 1000000U);
|
||||
uint16_t f1 = (mhz > 10) ? (uint16_t)(mhz - 10) : mhz;
|
||||
uint16_t f2 = (uint16_t)(mhz + 10);
|
||||
lr11xx_system_calibrate_image_in_mhz(&s_lr, f1, f2);
|
||||
}
|
||||
|
||||
static esp_err_t radio_setup_spi(void)
|
||||
{
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = CONFIG_LORA_PIN_MOSI,
|
||||
.miso_io_num = CONFIG_LORA_PIN_MISO,
|
||||
.sclk_io_num = CONFIG_LORA_PIN_SCK,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
};
|
||||
esp_err_t err = spi_bus_initialize(CONFIG_LORA_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (s_spi) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.clock_speed_hz = LR1121_SPI_FREQ_HZ,
|
||||
.mode = LR1121_SPI_MODE,
|
||||
.spics_io_num = -1, // CS керуємо вручну в HAL
|
||||
.queue_size = 2,
|
||||
};
|
||||
err = spi_bus_add_device(CONFIG_LORA_SPI_HOST, &devcfg, &s_spi);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lr1121_hw_init(void)
|
||||
{
|
||||
lora_init_io_context(&s_lr,
|
||||
CONFIG_LORA_PIN_CS,
|
||||
CONFIG_LORA_PIN_RST,
|
||||
CONFIG_LORA_PIN_BUSY,
|
||||
CONFIG_LORA_PIN_DIO1);
|
||||
lora_init_io(&s_lr);
|
||||
lora_spi_init(&s_lr, s_spi);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lr1121_system_boot(void)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_reset(&s_lr)), TAG, "reset");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_hal_wakeup(&s_lr)), TAG, "wakeup");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_standby(&s_lr, LR11XX_SYSTEM_STANDBY_CFG_XOSC)), TAG, "standby");
|
||||
const lr11xx_system_reg_mode_t reg_mode = smtc_shield_lr11xx_common_get_reg_mode();
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_reg_mode(&s_lr, reg_mode)), TAG, "reg mode");
|
||||
|
||||
const lr11xx_system_rfswitch_cfg_t *rf_switch = smtc_shield_lr11xx_common_get_rf_switch_cfg();
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_dio_as_rf_switch(&s_lr, rf_switch)), TAG, "rf switch");
|
||||
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_tcxo_mode(&s_lr, LR11XX_SYSTEM_TCXO_CTRL_3_0V, 300)), TAG, "tcxo");
|
||||
|
||||
const smtc_shield_lr11xx_lfclk_cfg_t *lf = smtc_shield_lr11xx_common_get_lfclk_cfg();
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_cfg_lfclk(&s_lr, lf->lf_clk_cfg, lf->wait_32k_ready)), TAG, "lfclk");
|
||||
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_clear_errors(&s_lr)), TAG, "clear errors");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_calibrate(&s_lr, 0x3F)), TAG, "calibrate");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_clear_irq_status(&s_lr, LR11XX_SYSTEM_IRQ_ALL_MASK)), TAG, "clear irq");
|
||||
|
||||
lr11xx_system_version_t ver = {0};
|
||||
if (lr11xx_system_get_version(&s_lr, &ver) == LR11XX_STATUS_OK) {
|
||||
ESP_LOGI(TAG, "LR1121 ver hw=0x%02X type=0x%02X fw=0x%04X",
|
||||
ver.hw, ver.type, ver.fw);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lr1121_apply_lora(const lora_params_t *p)
|
||||
{
|
||||
uint32_t freq_hz = cmhz_to_hz(p->freq_centi_mhz);
|
||||
lr11xx_radio_lora_bw_t bw = map_bw(p->bw_khz);
|
||||
lr11xx_radio_lora_sf_t sf = map_sf(p->sf);
|
||||
lr11xx_radio_lora_cr_t cr = map_cr(p->cr);
|
||||
|
||||
lr11xx_radio_mod_params_lora_t mod = {
|
||||
.sf = sf,
|
||||
.bw = bw,
|
||||
.cr = cr,
|
||||
.ldro = smtc_shield_lr11xx_common_compute_lora_ldro(sf, bw),
|
||||
};
|
||||
lr11xx_radio_pkt_params_lora_t pkt = {
|
||||
.preamble_len_in_symb = p->preamble_syms,
|
||||
.header_type = p->header_implicit ? LR11XX_RADIO_LORA_PKT_IMPLICIT : LR11XX_RADIO_LORA_PKT_EXPLICIT,
|
||||
.pld_len_in_bytes = p->payload_len,
|
||||
.crc = p->crc_on ? LR11XX_RADIO_LORA_CRC_ON : LR11XX_RADIO_LORA_CRC_OFF,
|
||||
.iq = p->iq_invert ? LR11XX_RADIO_LORA_IQ_INVERTED : LR11XX_RADIO_LORA_IQ_STANDARD,
|
||||
};
|
||||
|
||||
const lr11xx_radio_rssi_calibration_table_t *rssi_cal = smtc_shield_lr11xx_get_rssi_calibration_table(freq_hz);
|
||||
const smtc_shield_lr11xx_pa_pwr_cfg_t *pa_cfg = smtc_shield_lr1121mb1gis_get_pa_pwr_cfg(freq_hz, p->tx_power_dbm);
|
||||
if (pa_cfg == NULL || rssi_cal == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
lr1121_calibrate_image_for_freq(freq_hz);
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_pkt_type(&s_lr, LR11XX_RADIO_PKT_TYPE_LORA)), TAG, "pkt type");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_rf_freq(&s_lr, freq_hz)), TAG, "freq");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_rssi_calibration(&s_lr, rssi_cal)), TAG, "rssi cal");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_pa_cfg(&s_lr, &pa_cfg->pa_config)), TAG, "pa cfg");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_tx_params(&s_lr, pa_cfg->power, LR1121_PA_RAMP)), TAG, "tx params");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_rx_tx_fallback_mode(&s_lr, LR11XX_RADIO_FALLBACK_STDBY_RC)), TAG, "fallback");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_cfg_rx_boosted(&s_lr, true)), TAG, "rx boost");
|
||||
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_mod_params(&s_lr, &mod)), TAG, "mod params");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_pkt_params(&s_lr, &pkt)), TAG, "pkt params");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_sync_word(&s_lr, LR1121_SYNC_WORD)), TAG, "sync word");
|
||||
|
||||
lr11xx_system_irq_mask_t irq_mask = LR11XX_SYSTEM_IRQ_RX_DONE | LR11XX_SYSTEM_IRQ_TIMEOUT | LR11XX_SYSTEM_IRQ_HEADER_ERROR | LR11XX_SYSTEM_IRQ_CRC_ERROR;
|
||||
if (s_is_tx) {
|
||||
irq_mask |= LR11XX_SYSTEM_IRQ_TX_DONE;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_dio_irq_params(&s_lr, irq_mask, 0)), TAG, "irq mask");
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_clear_irq_status(&s_lr, LR11XX_SYSTEM_IRQ_ALL_MASK)), TAG, "clr irq");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lr1121_enter_rx_continuous(void)
|
||||
{
|
||||
s_last_rx_restart_us = esp_timer_get_time();
|
||||
return lr_status_to_esp(lr11xx_radio_set_rx_with_timeout_in_rtc_step(&s_lr, LR1121_RX_CONTINUOUS));
|
||||
}
|
||||
|
||||
bool lora_radio_init(bool is_tx_role)
|
||||
{
|
||||
s_is_tx = is_tx_role;
|
||||
s_params.freq_centi_mhz = CONFIG_LORA_FREQ_MHZ * 100;
|
||||
s_params.bw_khz = CONFIG_LORA_BW_KHZ;
|
||||
s_params.sf = CONFIG_LORA_SF;
|
||||
s_params.cr = CONFIG_LORA_CR;
|
||||
s_params.tx_power_dbm = 14;
|
||||
s_params.preamble_syms = 8;
|
||||
s_params.payload_len = 0;
|
||||
s_params.crc_on = true;
|
||||
s_params.iq_invert = false;
|
||||
s_params.header_implicit = false;
|
||||
sanitize_params(&s_params);
|
||||
|
||||
ESP_LOGI(TAG, "Init LR1121 over SPI host %d (MOSI=%d, MISO=%d, SCK=%d, CS=%d, RST=%d, BUSY=%d, DIO1=%d)",
|
||||
CONFIG_LORA_SPI_HOST,
|
||||
CONFIG_LORA_PIN_MOSI,
|
||||
CONFIG_LORA_PIN_MISO,
|
||||
CONFIG_LORA_PIN_SCK,
|
||||
CONFIG_LORA_PIN_CS,
|
||||
CONFIG_LORA_PIN_RST,
|
||||
CONFIG_LORA_PIN_BUSY,
|
||||
CONFIG_LORA_PIN_DIO1);
|
||||
if (CONFIG_LORA_PIN_BUSY < 0) {
|
||||
ESP_LOGE(TAG, "BUSY pin обов'язковий для LR1121, вкажіть коректний GPIO");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (radio_setup_spi() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI init failed");
|
||||
return false;
|
||||
}
|
||||
if (lr1121_hw_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "IO init failed");
|
||||
return false;
|
||||
}
|
||||
if (lr1121_system_boot() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "System boot failed");
|
||||
return false;
|
||||
}
|
||||
if (lr1121_apply_lora(&s_params) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Initial params failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!s_is_tx) {
|
||||
lr1121_enter_rx_continuous();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Radio ready: %.2f MHz BW %d kHz SF %d CR 4/%d TX %d dBm",
|
||||
s_params.freq_centi_mhz / 100.0f,
|
||||
s_params.bw_khz,
|
||||
s_params.sf,
|
||||
s_params.cr,
|
||||
s_params.tx_power_dbm);
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t lora_radio_apply_params(const lora_params_t *params)
|
||||
{
|
||||
if (!params) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
lora_params_t clean = *params;
|
||||
sanitize_params(&clean);
|
||||
esp_err_t err = lr1121_apply_lora(&clean);
|
||||
if (err == ESP_OK) {
|
||||
s_params = clean;
|
||||
if (!s_is_tx) {
|
||||
lr1121_enter_rx_continuous();
|
||||
}
|
||||
ESP_LOGI(TAG, "Apply LoRa params: %.2f MHz, BW %d kHz, SF %d, CR 4/%d, TX %d dBm",
|
||||
s_params.freq_centi_mhz / 100.0f,
|
||||
s_params.bw_khz,
|
||||
s_params.sf,
|
||||
s_params.cr,
|
||||
s_params.tx_power_dbm);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t lora_radio_set_tx_power_dbm(int dbm)
|
||||
{
|
||||
lora_params_t next = s_params;
|
||||
next.tx_power_dbm = dbm;
|
||||
return lora_radio_apply_params(&next);
|
||||
}
|
||||
|
||||
esp_err_t lora_radio_set_frequency_centi_mhz(int freq_centi_mhz)
|
||||
{
|
||||
lora_params_t next = s_params;
|
||||
next.freq_centi_mhz = freq_centi_mhz;
|
||||
return lora_radio_apply_params(&next);
|
||||
}
|
||||
|
||||
void lora_radio_get_params(lora_params_t *out)
|
||||
{
|
||||
if (!out) {
|
||||
return;
|
||||
}
|
||||
*out = s_params;
|
||||
}
|
||||
|
||||
void lora_radio_tick_tx(void)
|
||||
{
|
||||
if (!s_is_tx) {
|
||||
return;
|
||||
}
|
||||
lr11xx_system_irq_mask_t irq_status = 0;
|
||||
if (lr11xx_system_get_and_clear_irq_status(&s_lr, &irq_status) != LR11XX_STATUS_OK) {
|
||||
return;
|
||||
}
|
||||
if (irq_status == 0) {
|
||||
return;
|
||||
}
|
||||
s_metrics.last_status = (uint8_t)(irq_status & 0xFF);
|
||||
if (irq_status & LR11XX_SYSTEM_IRQ_TX_DONE) {
|
||||
s_tx_in_progress = false;
|
||||
ESP_LOGI(TAG, "TX done");
|
||||
}
|
||||
if (irq_status & LR11XX_SYSTEM_IRQ_TIMEOUT) {
|
||||
s_tx_in_progress = false;
|
||||
ESP_LOGW(TAG, "TX timeout");
|
||||
}
|
||||
}
|
||||
|
||||
void lora_radio_tick_rx(void)
|
||||
{
|
||||
if (s_is_tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
lr11xx_system_irq_mask_t irq_status = 0;
|
||||
if (lr11xx_system_get_and_clear_irq_status(&s_lr, &irq_status) != LR11XX_STATUS_OK) {
|
||||
return;
|
||||
}
|
||||
if (irq_status == 0) {
|
||||
int64_t now = esp_timer_get_time();
|
||||
if (now - s_last_rx_restart_us > 1000000) {
|
||||
lr1121_enter_rx_continuous();
|
||||
}
|
||||
return;
|
||||
}
|
||||
s_metrics.last_status = (uint8_t)(irq_status & 0xFF);
|
||||
|
||||
if (irq_status & LR11XX_SYSTEM_IRQ_RX_DONE) {
|
||||
lr11xx_radio_rx_buffer_status_t rx_status = {0};
|
||||
if (lr11xx_radio_get_rx_buffer_status(&s_lr, &rx_status) == LR11XX_STATUS_OK && rx_status.pld_len_in_bytes > 0) {
|
||||
uint8_t buf[sizeof(s_last_payload)];
|
||||
uint8_t copy_len = rx_status.pld_len_in_bytes;
|
||||
if (copy_len >= sizeof(buf)) {
|
||||
copy_len = sizeof(buf) - 1;
|
||||
}
|
||||
if (lr11xx_regmem_read_buffer8(&s_lr, buf, rx_status.buffer_start_pointer, copy_len) == LR11XX_STATUS_OK) {
|
||||
memcpy(s_last_payload, buf, copy_len);
|
||||
s_last_payload[copy_len] = '\0';
|
||||
s_last_payload_len = copy_len;
|
||||
|
||||
lr11xx_radio_pkt_status_lora_t pkt = {0};
|
||||
if (lr11xx_radio_get_lora_pkt_status(&s_lr, &pkt) == LR11XX_STATUS_OK) {
|
||||
s_metrics.rssi_dbm = pkt.rssi_pkt_in_dbm;
|
||||
s_metrics.snr_db = pkt.snr_pkt_in_db;
|
||||
}
|
||||
ESP_LOGI(TAG, "RX %u bytes RSSI=%ddBm SNR=%ddB \"%s\"",
|
||||
(unsigned)copy_len, s_metrics.rssi_dbm, s_metrics.snr_db, s_last_payload);
|
||||
}
|
||||
}
|
||||
lr1121_enter_rx_continuous();
|
||||
}
|
||||
|
||||
if (irq_status & (LR11XX_SYSTEM_IRQ_TIMEOUT | LR11XX_SYSTEM_IRQ_HEADER_ERROR | LR11XX_SYSTEM_IRQ_CRC_ERROR)) {
|
||||
ESP_LOGW(TAG, "RX irq=0x%04X (timeout/header/crc)", irq_status);
|
||||
lr1121_enter_rx_continuous();
|
||||
}
|
||||
}
|
||||
|
||||
void lora_radio_get_metrics(lora_metrics_t *out)
|
||||
{
|
||||
if (!out) {
|
||||
return;
|
||||
}
|
||||
*out = s_metrics;
|
||||
}
|
||||
|
||||
void lora_radio_get_last_payload(char *out, size_t out_len)
|
||||
{
|
||||
if (!out || out_len == 0) {
|
||||
return;
|
||||
}
|
||||
if (s_last_payload_len == 0) {
|
||||
strlcpy(out, "(no data)", out_len);
|
||||
return;
|
||||
}
|
||||
strlcpy(out, s_last_payload, out_len);
|
||||
}
|
||||
|
||||
esp_err_t lora_radio_send(const uint8_t *data, size_t len)
|
||||
{
|
||||
if (!s_is_tx) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!data || len == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (len > 255) {
|
||||
len = 255; // обмеження довжини пакета
|
||||
}
|
||||
|
||||
// Оновити pkt params під фактичну довжину
|
||||
lr11xx_radio_pkt_params_lora_t pkt = {
|
||||
.preamble_len_in_symb = s_params.preamble_syms,
|
||||
.header_type = s_params.header_implicit ? LR11XX_RADIO_LORA_PKT_IMPLICIT : LR11XX_RADIO_LORA_PKT_EXPLICIT,
|
||||
.pld_len_in_bytes = (uint8_t)len,
|
||||
.crc = s_params.crc_on ? LR11XX_RADIO_LORA_CRC_ON : LR11XX_RADIO_LORA_CRC_OFF,
|
||||
.iq = s_params.iq_invert ? LR11XX_RADIO_LORA_IQ_INVERTED : LR11XX_RADIO_LORA_IQ_STANDARD,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_pkt_params(&s_lr, &pkt)), TAG, "pkt params tx");
|
||||
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_regmem_write_buffer8(&s_lr, data, len)), TAG, "write buf");
|
||||
|
||||
// timeout 0 => без тайм-ауту, чекаємо TX_DONE
|
||||
ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_tx(&s_lr, 0)), TAG, "set tx");
|
||||
s_tx_in_progress = true;
|
||||
s_last_tx_us = esp_timer_get_time();
|
||||
s_params.payload_len = (uint8_t)len;
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user