Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ LPWAN
.. toctree::
:maxdepth: 1

lora_gw.rst
sx126x.rst
sx127x.rst

128 changes: 128 additions & 0 deletions Documentation/components/drivers/character/wireless/lpwan/lora_gw.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
.. _lora_gw:

================================
LoRa gateway (concentrator) API
================================

A LoRa gateway does not listen to one channel at a time as an end device
does: a concentrator demodulates several channels at once, on two radio
front-ends, and every packet arrives with its own frequency, spreading factor
and coding rate. The commands that configure a radio, ``WLIOC_SETRADIOFREQ``
and the ``WLIOC_LORA_x`` family, therefore have nothing to act upon on such a
device, and what it needs instead is a channel plan, a way to be started and
stopped, and the counter its timestamps are taken from.

This is the device independent interface those drivers implement, declared in
``nuttx/wireless/lpwan/lora_gw.h``. An application drives a gateway through it
and never names a chip.

Character device
================

A driver of this class registers a character device, ``/dev/lora0`` by
convention, on which:

* ``read`` returns whole multiples of ``struct lora_gw_rxpkt_s``, oldest
first, as many as fit in the buffer. It blocks until at least one packet is
available unless the file was opened with ``O_NONBLOCK``, and fails with
``EINVAL`` if the buffer cannot hold one whole packet.

* ``write`` takes exactly one ``struct lora_gw_txpkt_s``. The packet carries
its own frequency, power, modulation and, for a LoRaWAN downlink,
``invert_pol``. It is sent immediately with ``LORA_GW_TX_IMMEDIATE`` or at a
concentrator timestamp with ``LORA_GW_TX_TIMESTAMPED``.

Both structures follow the layout of the userspace HAL that Semtech publishes
for this family of chips, which is what gateway software is written against
on other systems, so that such an application ports by replacing its
``lgw_receive`` with ``read`` and its ``lgw_send`` with ``write``. Two things
deliberately differ:

.. list-table::
:header-rows: 1

* - Field
- Unit
* - ``rssi_dbm10``, ``snr_db10``
- Tenths of a dBm or dB, as integers, so that no floating point is
needed in a driver
* - ``datarate``
- The spreading factor as a plain number, 7 to 12, not a bit mask
* - ``bandwidth``
- ``LORA_GW_BW_125K``, ``_250K`` or ``_500K``
* - ``coderate``
- ``enum wlioc_lora_cr_e``, shared with the end device drivers
* - ``status``
- ``LORA_GW_STAT_CRC_OK``, ``_CRC_BAD`` or ``_NO_CRC``

A packet reported as ``LORA_GW_STAT_CRC_BAD`` must never be forwarded as if
it were valid: those are mostly correlator false triggers.

IOCTL commands
==============

See ``nuttx/wireless/ioctl.h`` : ``WLIOC_GW_x``.

* ``WLIOC_GW_START`` resets the chip, loads the firmware of its internal
MCUs, calibrates it and starts receiving on the selected channel plan.
``WLIOC_GW_STOP`` stops it and ``WLIOC_GW_RESET`` does both in sequence.

* ``WLIOC_GW_SETREGION`` selects a channel plan by name, for example
``"AU915"``, while the concentrator is stopped. ``WLIOC_GW_GETREGION``
takes a ``struct lora_gw_regionreq_s``: an ``index`` of -1 describes the
active plan, and 0 upwards enumerates the supported ones until ``ENODEV``.
The description that comes back lists the centre frequency of each radio
and the frequency, radio and type of every channel.

* ``WLIOC_GW_GETSTATUS`` fills a ``struct lora_gw_status_s`` with the state
of the concentrator and its counters, including the packets dropped
because their CRC failed.

* ``WLIOC_GW_GETTRIGCNT`` reads the internal counter of the concentrator, in
microseconds. This is the time base a LoRaWAN network server schedules
downlinks against.

Whether the units of the packet should instead follow the ones of the end
device commands, that is, bandwidth in Hz and levels scaled by a hundred as
in ``struct wlioc_rx_hdr_s``, is a question for the common LoRa API rather
than for one driver, and is left as it is until that API materialises.

SX1301 driver
=============

The Semtech SX1301 is the baseband processor of a LoRaWAN gateway: eight
multi-SF demodulators, one LoRa standard demodulator and one FSK
demodulator, driven by two SX125x radio front-ends that are reached through
an SPI bridge inside the SX1301 itself. It is the first implementation of the
interface above and is enabled with ``CONFIG_LPWAN_SX1301``.

Options
-------

* ``CONFIG_LPWAN_SX1301_SPIFREQ`` is the SPI clock, up to 10 MHz.

* ``CONFIG_LPWAN_SX1301_DEFAULT_REGION`` is the channel plan selected when
the driver is registered: one of AU915, AU915-1, US915, US915-1, EU868,
AS923, KR920 or IN866. AU915 and US915 default to the second sub-band,
which is what The Things Network and the Brazilian deployments use.

* ``CONFIG_LPWAN_SX1301_PRIVATE_NETWORK`` switches the frame synchronisation
word from the public LoRaWAN one to the private one.

* ``CONFIG_LPWAN_SX1301_RXBADCRC`` and ``CONFIG_LPWAN_SX1301_RXNOCRC``
deliver the packets that a gateway normally drops, which is useful when
bringing a shield up against an unknown transmitter.

Board implementation
--------------------

The driver is registered with ``sx1301_register``, which takes the device
path, an SPI bus and a ``struct sx1301_lower_s``. That structure carries the
two things the chip needs from the board: a ``reset`` hook driving its reset
line, and an optional ``band_select`` hook for the shields whose front-end
filters are switched between 868 and 915 MHz by a pair of GPIOs. See
``nuttx/wireless/lpwan/sx1301.h``.

A worked example, with the wiring of an LRWAN_GS_HF1 shield, the expected
boot output and a gateway forwarding to a public network, is in the
:ref:`Nucleo F746ZG <nucleo-f746zg>` page.
132 changes: 132 additions & 0 deletions Documentation/components/drivers/character/wireless/lpwan/sx127x.rst
Original file line number Diff line number Diff line change
@@ -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>`
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _nrf52840-dk:

===========
nRF52840-DK
===========
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _nucleo-f091rc:

=================
ST Nucleo F091RC
=================
Expand Down
Loading
Loading