wireless/lpwan/sx127x: fix the frequency step, the band and the LoRa only build

- The frequency step was truncated to 61 Hz, while it is FXOSC/(2**19),
  about 61.035 Hz.  The error puts a 915 MHz channel more than 500 kHz away
  from the requested frequency, outside its own bandwidth.

- The low or high frequency front end was left at its reset value, so a board
  wired for 868 or 915 MHz neither transmitted nor received.

- sx127x_rx_watchdog() is only used by the FSK and OOK path but was compiled
  whenever receive support was on, so a LoRa only configuration failed to
  build with -Werror.  nrf52840-dk:sx127x is such a configuration.

Adds the sync word, the default bandwidth and the default spreading factor as
configuration options, all defaulting to the previous behaviour, and a page
for the driver under components/drivers.

Assisted-by: Claude Code 4.8
Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
This commit is contained in:
Jorge Guzman 2026-08-05 13:36:35 -03:00 committed by Alan C. Assis
parent f37bc4546e
commit 7bda3b7c8b
10 changed files with 202 additions and 8 deletions

View file

@ -7,4 +7,5 @@ LPWAN
lora_gw.rst
sx126x.rst
sx127x.rst

View file

@ -0,0 +1,132 @@
.. _sx127x:
=================
SX127x LoRa radio
=================
The SX127x family, which includes the SX1272 and the SX1276 found on most
modules and development boards, is a sub-GHz transceiver doing LoRa as well
as FSK and OOK. Unlike a concentrator it demodulates one channel at a time,
so frequency, spreading factor and bandwidth are settings of the radio and
not attributes of each packet.
The driver is enabled with ``CONFIG_LPWAN_SX127X`` and registers a character
device, ``/dev/sx127x`` by convention.
Userspace API
=============
Reading and writing
-------------------
``write`` transmits the given bytes with the parameters currently configured.
``read`` returns one received packet, blocking until one arrives; with
``CONFIG_LPWAN_SX127X_RXFIFO_LEN`` the driver keeps a small queue of them, so
a reader that falls behind loses the oldest rather than the newest.
Each packet is preceded by a ``struct sx127x_read_hdr_s``, which carries the
payload length together with the received power and, in LoRa mode, the signal
to noise ratio of that packet.
IOCTL commands
--------------
The basic radio parameters use the common commands of
``nuttx/wireless/ioctl.h``: ``WLIOC_SETRADIOFREQ`` and ``WLIOC_GETRADIOFREQ``
for the frequency in Hz, ``WLIOC_SETTXPOWER`` and ``WLIOC_GETTXPOWER`` for
the output power in dBm. Both may be further limited by the board logic.
The rest is in ``nuttx/wireless/lpwan/sx127x.h``:
* ``SX127XIOC_MODULATIONSET`` and ``SX127XIOC_MODULATIONGET`` choose between
LoRa, FSK and OOK.
* ``SX127XIOC_OPMODESET`` and ``SX127XIOC_OPMODEGET`` move the radio between
sleep, standby, transmit, receive and channel activity detection.
* ``SX127XIOC_SYNCWORDSET`` and ``SX127XIOC_SYNCWORDGET`` change the sync
word, and ``SX127XIOC_PREAMBLESET`` and ``SX127XIOC_PREAMBLEGET`` the
preamble length.
* ``SX127XIOC_RSSIGET`` reads the current RSSI, ``SX127XIOC_CHANSCAN`` scans
a channel and ``SX127XIOC_RANDOMGET`` returns a random number taken from
the noise of the receiver.
Note that the spreading factor, the bandwidth and the coding rate have no
ioctl of their own yet: they come from the configuration below.
Configuration
=============
============================================ ==================================
Option Meaning
============================================ ==================================
``LPWAN_SX127X_RFFREQ_DEFAULT`` Frequency, in Hz, at registration
``LPWAN_SX127X_TXPOWER_DEFAULT`` Output power in dBm
``LPWAN_SX127X_MODULATION_DEFAULT`` 1 for FSK, 2 for OOK, 3 for LoRa
``LPWAN_SX127X_RXSUPPORT`` Build the receive path
``LPWAN_SX127X_TXSUPPORT`` Build the transmit path
``LPWAN_SX127X_LORA`` Build the LoRa modem
``LPWAN_SX127X_FSKOOK`` Build the FSK and OOK modem
``LPWAN_SX127X_LORA_SYNCWORD`` Sync word, see below
``LPWAN_SX127X_LORA_BW_DEFAULT`` Bandwidth, 7 selects 125 kHz
``LPWAN_SX127X_LORA_SF_DEFAULT`` Spreading factor, 6 to 12
``LPWAN_SX127X_CRCON`` Append and check a CRC
``LPWAN_SX127X_RXFIFO_LEN`` Packets buffered by the driver
============================================ ==================================
Talking to another radio
========================
Two settings decide whether two devices hear each other at all, and both are
silent failures when they disagree:
* **Band.** The chip has separate low and high frequency front ends and the
modem has to be told which one is in use. The driver derives that from the
configured frequency, using the high band above 525 MHz.
* **Sync word.** ``CONFIG_LPWAN_SX127X_LORA_SYNCWORD`` defaults to 0x12, the
private network value. A public LoRaWAN network uses 0x34, and a receiver
configured for the other value never even detects the frame.
Beyond those, the spreading factor, the bandwidth and the coding rate have to
match on both sides.
Two boards reach each other with the ``sx127x`` example of
``apps/examples/sx127x_demo``. Start the receiver first, since the example
gives up after the time given with ``-d``::
board A> sx127x -m 0 -f 917200000 -r -d 60
board B> sx127x -m 0 -f 917200000 -t -p 0 -l 32 -d 30
The receiver prints the payload of every packet along with its signal to
noise ratio and its received power. Keep the boards a metre or so apart: at a
few centimetres a transmitter saturates the other receiver and the packets
arrive with a broken CRC.
The same commands work against a gateway. A concentrator running the ``lora``
command of ``apps/wireless/lora_pkt_fwd`` sends a packet with
``lora tx 917200000 7 hello``, which any board listening on that frequency
receives; and a board transmitting as above shows up on the gateway as an
ordinary uplink. See :ref:`lora_gw` for the gateway side.
Board implementation
====================
The driver is registered with ``sx127x_register``, which takes an SPI bus and
a ``struct sx127x_lower_s``. That structure carries what the chip needs from
the board: attaching the DIO0 interrupt, which signals the end of a
transmission or a reception, a ``reset`` hook, and the optional
``opmode_change``, ``freq_select`` and ``pa_select`` hooks used by boards
whose antenna switch or power amplifier path depends on the operating mode,
the frequency or the requested power. See
``nuttx/wireless/lpwan/sx127x.h``.
Boards
======
* :ref:`ST B-L072Z-LRWAN1 <b-l072z-lrwan1>`
* :ref:`Nucleo L073RZ <nucleo-l073rz>`
* :ref:`Nucleo F091RC <nucleo-f091rc>`
* :ref:`nRF52840-DK <nrf52840-dk>`
* :ref:`Heltec WiFi LoRa 32 <heltec_wifi_lora32>`

View file

@ -1,3 +1,5 @@
.. _nrf52840-dk:
===========
nRF52840-DK
===========

View file

@ -1,3 +1,5 @@
.. _nucleo-f091rc:
=================
ST Nucleo F091RC
=================

View file

@ -1,3 +1,5 @@
.. _b-l072z-lrwan1:
=================
ST B-L072Z-LRWAN1
=================

View file

@ -1,3 +1,5 @@
.. _nucleo-l073rz:
================
ST Nucleo L073RZ
================

View file

@ -1,3 +1,5 @@
.. _heltec_wifi_lora32:
======================
Heltec WiFi LoRa 32 V2
======================

View file

@ -79,6 +79,29 @@ config LPWAN_SX127X_LORA_IMPHEADER
range 0 1
default 0
config LPWAN_SX127X_LORA_SYNCWORD
hex "SX127X LORA sync word"
default 0x12
---help---
Sync word written to the modem: 0x12 for a private network, 0x34
for the public LoRaWAN network. A gateway configured for the
public network does not even detect a frame sent with the private
sync word, so this has to match the other end.
config LPWAN_SX127X_LORA_BW_DEFAULT
int "SX127X LORA default bandwidth"
default 0
range 0 9
---help---
Bandwidth used until it is changed: 0 = 7.8 kHz, 1 = 10.4 kHz,
2 = 15.6 kHz, 3 = 20.8 kHz, 4 = 31.2 kHz, 5 = 41.4 kHz,
6 = 62.5 kHz, 7 = 125 kHz, 9 = 250 kHz. LoRaWAN uses 125 kHz.
config LPWAN_SX127X_LORA_SF_DEFAULT
int "SX127X LORA default spreading factor"
default 7
range 6 12
endif # LPWAN_SX127X_LORA
config LPWAN_SX127X_FSKOOK

View file

@ -130,11 +130,11 @@
/* Default LORA bandwidth */
#define SX127X_LRM_BW_DEFAULT LORA_BANDWIDTH_7P8KHZ
#define SX127X_LRM_BW_DEFAULT CONFIG_LPWAN_SX127X_LORA_BW_DEFAULT
/* Default SF for LORA */
#define SX127X_LRM_SF_DEFAULT (7)
#define SX127X_LRM_SF_DEFAULT CONFIG_LPWAN_SX127X_LORA_SF_DEFAULT
/* FSK/OOK RX/TX FIFO size (two separate FIFOs) */
@ -1286,7 +1286,12 @@ errout:
*
****************************************************************************/
/* The stall watchdog is only wired into the FSK and OOK receive path, so a
* configuration with LoRa alone must not compile it.
*/
#if defined(CONFIG_LPWAN_SX127X_RXSUPPORT) && \
defined(CONFIG_LPWAN_SX127X_FSKOOK) && \
CONFIG_LPWAN_SX127X_RX_TIMEOUT > 0
static void sx127x_rx_watchdog(FAR void *arg)
{
@ -1321,7 +1326,7 @@ static void sx127x_rx_watchdog(FAR void *arg)
sx127x_rx_watchdog, dev,
MSEC2TICK(dev->rx_timeout));
}
#endif /* CONFIG_LPWAN_SX127X_RXSUPPORT && CONFIG_LPWAN_SX127X_RX_TIMEOUT > 0 */
#endif /* RXSUPPORT && FSKOOK && RX_TIMEOUT > 0 */
/****************************************************************************
* Name: sx127x_lora_isr0_process
@ -3875,6 +3880,22 @@ static int sx127x_frequency_set(FAR struct sx127x_dev_s *dev, uint32_t freq)
sx127x_writeregbyte(dev, SX127X_CMN_FRFLSB, SX127X_CMN_FRF_LSB(frf));
/* Tell the modem which of the two front ends the frequency belongs to.
* The chip comes up in low frequency mode, and a board wired for the high
* band (868 or 915 MHz) then neither transmits nor receives anything.
*/
if (freq > SX127X_HFBAND_THR)
{
sx127x_modregbyte(dev, SX127X_CMN_OPMODE, 0,
SX127X_CMN_OPMODE_LFMODEON);
}
else
{
sx127x_modregbyte(dev, SX127X_CMN_OPMODE,
SX127X_CMN_OPMODE_LFMODEON, 0);
}
/* Unlock SPI */
sx127x_unlock(dev->spi);

View file

@ -181,7 +181,8 @@
/* FSK/OOK/LORA: RF carrier frequency */
#define SX127X_CMN_FRF_MAX (0xffffff)
#define SX127X_FRF_FROM_FREQ(freq) (freq/SX127X_FSTEP)
#define SX127X_FRF_FROM_FREQ(freq) \
((uint32_t)(((uint64_t)(freq) << SX127X_FSTEP_SHIFT) / SX127X_FXOSC))
#define SX127X_CMN_FRF_MSB(frf) ((frf >> 16) & 0xff)
#define SX127X_CMN_FRF_MID(frf) ((frf >> 8) & 0xff)
#define SX127X_CMN_FRF_LSB(frf) ((frf >> 0) & 0xff)
@ -337,7 +338,8 @@
#define SX127X_FOM_FDEV_MSB_MASK (0x3f)
#define SX127X_FOM_FDEV_MAX (0x3fff)
#define SX127X_FDEV_FROM_FREQ(freq) (freq/SX127X_FSTEP)
#define SX127X_FDEV_FROM_FREQ(freq) \
((uint32_t)(((uint64_t)(freq) << SX127X_FSTEP_SHIFT) / SX127X_FXOSC))
#define SX127X_FOM_FDEV_MSB(v) ((v >> 8) & 0xff)
#define SX127X_FOM_FDEV_LSB(v) ((v >> 0) & 0xff)
@ -790,7 +792,7 @@
/* LORA: LORA Sync Word */
#define SX127X_LRM_SYNCWORD_DEFAULT (0x12)
#define SX127X_LRM_SYNCWORD_DEFAULT CONFIG_LPWAN_SX127X_LORA_SYNCWORD
#define SX127X_LRM_SYNCWORD_LORAWAN (0x34)
/* Lora data rate:
@ -810,9 +812,14 @@
#define SX127X_FXOSC (32000000)
/* FSTEP is FXOSC/(2**19) =~ 61 Hz */
/* One step of the frequency synthesiser is FXOSC/(2**19), about 61.035 Hz.
* It is not an integer, so the conversions below multiply first and divide
* afterwards instead of dividing by a truncated step: rounding it down to
* 61 Hz places a 915 MHz channel more than 500 kHz away from the requested
* frequency, well outside the bandwidth of the channel.
*/
#define SX127X_FSTEP (SX127X_FXOSC/(2<<18))
#define SX127X_FSTEP_SHIFT (19)
/****************************************************************************
* Public Data Types