diff --git a/chip_test_example/lora_rx.cpp b/chip_test_example/lora_rx.cpp index bbe5240..54e5374 100644 --- a/chip_test_example/lora_rx.cpp +++ b/chip_test_example/lora_rx.cpp @@ -1,12 +1,20 @@ #include +#include #include #include "lr1121_malnus.hpp" +static volatile std::sig_atomic_t g_stop = 0; + +static void onSignal(int) +{ + g_stop = 1; +} + static void applyPaPreset(lr1121::Config &cfg, bool hp_mode) { cfg.pa_sel = hp_mode ? lr1121::PA_HP : lr1121::PA_LP; - cfg.tx_dbm = hp_mode ? 14 : 10; + cfg.tx_dbm = hp_mode ? 14 : 8; } static bool parseArgs(int argc, char **argv, bool &verbose, bool &do_reset, bool &hp_mode) @@ -27,6 +35,9 @@ static bool parseArgs(int argc, char **argv, bool &verbose, bool &do_reset, bool int main(int argc, char **argv) { + std::signal(SIGINT, onSignal); + std::signal(SIGTERM, onSignal); + lr1121::Config cfg; constexpr uint32_t rx_timeout_ms = 1000; bool verbose = false; @@ -60,17 +71,21 @@ int main(int argc, char **argv) return 1; } } - std::puts("Radio OK - listening..."); + if (!radio.startListening()) { + std::fprintf(stderr, "ERROR: start listening failed: %s\n", + lr1121::Radio::errorString(radio.lastError())); + return 1; + } + std::puts("Radio OK - listening (continuous RX). Press Ctrl+C to stop."); uint8_t buf[256]; int pkt = 0; uint32_t timeout_total = 0; uint32_t crc_total = 0; - for (;;) { + while (!g_stop) { lr1121::RxInfo info{}; - const int r = radio.receive(buf, uint8_t(sizeof(buf) - 1), - rx_timeout_ms, &info); + const int r = radio.receive(buf, uint8_t(sizeof(buf) - 1), rx_timeout_ms, &info); if (r > 0) { buf[r] = '\0'; std::printf("[%04d] %3d B rssi=%4d snr=%3d '%s'\n", @@ -86,4 +101,10 @@ int main(int argc, char **argv) lr1121::Radio::errorString(radio.lastError())); } } + + std::puts("\nStopping listener..."); + radio.stopListening(); + radio.end(); + std::puts("Radio OK - stopped listening..."); + return 0; } diff --git a/chip_test_example/lora_tx.cpp b/chip_test_example/lora_tx.cpp index 4956500..32cf740 100644 --- a/chip_test_example/lora_tx.cpp +++ b/chip_test_example/lora_tx.cpp @@ -8,7 +8,7 @@ static void applyPaPreset(lr1121::Config &cfg, bool hp_mode) { cfg.pa_sel = hp_mode ? lr1121::PA_HP : lr1121::PA_LP; - cfg.tx_dbm = hp_mode ? 14 : 10; + cfg.tx_dbm = hp_mode ? 14 : 8; } static bool parseArgs(int argc, char **argv, bool &verbose, bool &do_reset, bool &hp_mode) diff --git a/chip_test_example/lr1121_malnus.hpp b/chip_test_example/lr1121_malnus.hpp index 6d5a1e7..ba72cd9 100644 --- a/chip_test_example/lr1121_malnus.hpp +++ b/chip_test_example/lr1121_malnus.hpp @@ -112,10 +112,17 @@ public: // Returns true on TX_DONE, false otherwise; check lastError() for reason. bool send(const uint8_t *data, uint8_t n); + // One-shot RX: arm, wait timeout_ms, return to standby. // Returns bytes received (>=0), -1 on timeout/error, -2 on CRC error. int receive(uint8_t *buf, uint8_t cap, uint32_t timeout_ms, RxInfo *rx_info = nullptr); + // Continuous RX: arm once, call receive() in a loop — chip stays in RX + // between packets. timeout_ms in receive() becomes a per-call poll window + // (0 = wait forever). Call stopListening() to return to standby. + bool startListening(); + void stopListening(); + // Runtime tuning. Call from standby (the driver leaves the chip in // standby_xosc after begin()/send()/receive()). bool setFrequency(uint32_t hz); @@ -126,7 +133,7 @@ public: ChipVersion chipVersion(); uint16_t chipErrors(); uint8_t vbatRaw(); - float vbatVolts() { return vbatRaw() / 34.0f; } + float vbatVolts() { return vbatRaw() / 56.4f; } const Config &config() const { return cfg_; } Error lastError() const { return last_err_; } static const char *errorString(Error e); @@ -135,7 +142,8 @@ public: private: Config cfg_{}; - int spi_fd_ = -1, busy_fd_ = -1, reset_fd_ = -1, dio9_fd_ = -1; + int spi_fd_ = -1, busy_fd_ = -1, reset_fd_ = -1, dio9_fd_ = -1; + bool listening_ = false; Error last_err_ = Error::NotReady; bool fail(Error e) { last_err_ = e; return false; } @@ -314,11 +322,12 @@ inline void Radio::imgCalFreqs(uint32_t hz, uint8_t &f1, uint8_t &f2) { const uint32_t mhz = hz / 1'000'000u; uint32_t lo, hi; + // 2.4 GHz values are hardcoded (lo/4 would overflow uint8_t). + if (mhz >= 2000) { f1 = 0xD7; f2 = 0xDB; return; } if (mhz < 446) { lo = 430; hi = 440; } else if (mhz < 740) { lo = 470; hi = 510; } else if (mhz < 890) { lo = 860; hi = 876; } - else if (mhz < 2000) { lo = 902; hi = 928; } - else { lo = 2400; hi = 2500; } + else { lo = 902; hi = 928; } f1 = uint8_t(lo / 4); f2 = uint8_t((hi + 3) / 4); } @@ -587,28 +596,53 @@ inline bool Radio::send(const uint8_t *data, uint8_t n) return fail(Error::TxLbd); } +inline bool Radio::startListening() +{ + if (!setStandbyXosc()) return false; + if (!clearIrq()) return false; + const uint32_t mask = IRQ_RX_DONE | IRQ_CRC_ERR; + if (!setIrqMask(mask, mask)) return false; + // 0xFFFFFF = continuous RX, no radio timeout + if (!wcmd(OC_SET_RX, {0xFF, 0xFF, 0xFF})) return false; + listening_ = true; + return true; +} + +inline void Radio::stopListening() +{ + (void)setStandbyXosc(); + (void)clearIrq(); + (void)setIrqMask(0, 0); + listening_ = false; +} + inline int Radio::receive(uint8_t *buf, uint8_t cap, uint32_t timeout_ms, RxInfo *rx_info) { - if (!setStandbyXosc()) return -1; - if (!clearIrq()) return -1; - const uint32_t mask = IRQ_RX_DONE | IRQ_CRC_ERR | IRQ_TIMEOUT; - if (!setIrqMask(mask, mask)) return -1; - - const uint32_t steps = timeoutMsToRtcSteps(timeout_ms); - if (!wcmd(OC_SET_RX, { - uint8_t(steps >> 16), - uint8_t(steps >> 8), - uint8_t(steps), - })) return -1; + if (!listening_) { + // one-shot: arm the chip, it will time out on its own + if (!setStandbyXosc()) return -1; + if (!clearIrq()) return -1; + const uint32_t mask = IRQ_RX_DONE | IRQ_CRC_ERR | IRQ_TIMEOUT; + if (!setIrqMask(mask, mask)) return -1; + const uint32_t steps = timeoutMsToRtcSteps(timeout_ms); + if (!wcmd(OC_SET_RX, { + uint8_t(steps >> 16), + uint8_t(steps >> 8), + uint8_t(steps), + })) return -1; + } + // continuous: timeout_ms is a per-call poll window, 0 means wait forever + const uint32_t poll_ms = listening_ ? (timeout_ms == 0 ? 0 : timeout_ms) + : (timeout_ms == 0 ? 60000 : timeout_ms + 500); + const bool has_deadline = poll_ms > 0; const auto deadline = std::chrono::steady_clock::now() + - std::chrono::milliseconds(timeout_ms == 0 ? 60000 : timeout_ms + 500); + std::chrono::milliseconds(poll_ms); for (;;) { - if (std::chrono::steady_clock::now() >= deadline) { - (void)clearIrq(); - (void)setIrqMask(0, 0); + if (has_deadline && std::chrono::steady_clock::now() >= deadline) { + if (!listening_) { (void)clearIrq(); (void)setIrqMask(0, 0); } last_err_ = Error::RxTimeout; return -1; } @@ -618,8 +652,7 @@ inline int Radio::receive(uint8_t *buf, uint8_t cap, uint32_t timeout_ms, } const uint32_t irq = getIrq(); if (irq & IRQ_TIMEOUT) { - (void)clearIrq(); - (void)setIrqMask(0, 0); + if (!listening_) { (void)clearIrq(); (void)setIrqMask(0, 0); } last_err_ = Error::RxTimeout; return -1; } @@ -645,7 +678,13 @@ inline int Radio::receive(uint8_t *buf, uint8_t cap, uint32_t timeout_ms, (void)wcmd(OC_CLEAR_RXBUF); (void)clearIrq(); - (void)setIrqMask(0, 0); + if (listening_) { + // re-arm IRQ mask but leave chip in RX + const uint32_t mask = IRQ_RX_DONE | IRQ_CRC_ERR; + (void)setIrqMask(mask, mask); + } else { + (void)setIrqMask(0, 0); + } if (irq & IRQ_CRC_ERR) { last_err_ = Error::RxCrc; return -2; } last_err_ = Error::Ok; return int(len);