Tools
HOME » RESOURCES » TOOLS » LoRa Basics Station » The LNS Protocol

LoRa Basics Station

The LNS Protocol

Station connecting to an LNS is a two-step process which uses websockets as transport to exchange text records containing JSON-encoded objects.

First, Station queries the LNS for the URI of the actual connection end-point of the LNS. Second, it establishes a data connection with that end-point to receive setup information and henceforth exchange LoRa uplink and downlink frames through that connection. This way an LNS MAY dynamically assign gateways to different data connection end-points in order to, for example, distribute load or favor geographical proximity.

Note: The LNS service assigning data connection end-points can be separate from the LNS and run on servers separate from the LNS connection end-points, which again may be distributed across different servers.

LNS Protocol

Querying the LNS Connection End-Point (Discovery)

By default, the URI of the initial query is constructed from the contents of the file tc.uri by appending the path /router-info (see Section Credentials for other options). This forms a websocket end-point to which the following JSON object is submitted:

{
  "router": ID6 | EUI | integer
}

Station hereby simply declares its identity and asks for directions regarding the LNS connection end-point it SHALL connect to. The identity is a 64 bit EUI provided in the field router either in the form of string encoding the EUI in ID6 or EUI format, or as an integer value representing the EUI.

Depending on the authorization scheme (cf. Section Authentication Modes), the LNS SHALL verify that the declared identity matches the supplied credentials and SHALL answer with the following JSON object, immediately closing the connection afterwards:

{
  "router": ID6,
  "muxs"  : ID6,
  "uri"   : "URI",
  "error" : STRING   // only in case of error
}

The field router SHALL contain the normalized gateway identity, the field muxs SHALL contain the identity of the LNS connection end-point selected by the LNS, and the field uri SHALL contain the address of this connection end-point. Station then connects to this URI to establish a data connection with the LNS.

If the gateway cannot be identified or in case of any other problem, the response object SHALL contain the field error describing the problem. The fields muxs and uri MAY be missing.

Connecting to an LNS

After a successful query of the LNS data connection end-point, Station will connect to the obtained end-point URI using the currently configured credentials. As mentioned before, the established link is a websocket connecting where Station and LNS exchange JSON objects. Each JSON object, whether in uplink (i.e., from Station to LNS) or downlink direction (i.e., from LNS to Station), MUST have a field named msgtype which identifies the type of message and determines the structure of the JSON object.

Right after the websocket connection has been established, Station first sends a version message and the LNS SHALL respond with a router_config message. Afterwards normal operation is started with uplink/downlink messages as described below.

version Message

The first message sent by Station is a version message reporting to the LNS its version information (station, firmware, model), the protocol version (the constant 2 for now), and a list of optional features supported by the gateway:

{
  "msgtype"   : "version"
  "station"   : STRING
  "firmware"  : STRING
  "package"   : STRING
  "model"     : STRING
  "protocol"  : INT
  "features"  : [ STRING, .. ]
}

Currently, the following features are indicated:

  • rmtsh: Station supportes remote-shell access through the websocket link established with the LNS.
  • prod: Station has been built at production level, that is, certain test/debug features MAY be disabled.
  • gps Station has access to a GPS device.

router_config Message

The LNS SHALL respond to a version message with a router_config message to specify a channel plan for the Station and define some basic operation modes:

{
  "msgtype"    : "router_config"
  "NetID"      : [ INT, .. ]
  "JoinEui"    : [ [INT,INT], .. ]  // ranges: beg,end inclusive
  "region"     : STRING             // e.g. "EU863", "US902", ..
  "hwspec"     : STRING
  "freq_range" : [ INT, INT ]       // min, max (hz)
  "DRs"        : [ [INT,INT,INT], .. ]   // sf,bw,dnonly
  "sx1301_conf": [ SX1301CONF, .. ]
  "nocca"      : BOOL
  "nodc"       : BOOL
  "nodwell"    : BOOL
}

The fields NetID and JoinEui are used to filter LoRa frames received by Station. NetID is a list of NetID values that are accepted. Any LoRa data frame carrying a NetID other than the listed ones will be dropped. JoinEui is a list of pairs of integer values encoding ranges of join EUIs. Join request frames will be dropped by Station unless the field JoinEui in the message satisfies the condition BegEui<=JoinEui<=EndEui for at least one pair [BegEui,EndEui].

The fields nocca, nodc, and nodwell are only available in debug builds of Station to disable certain regulatory transmit constraints, namely clear channel assessment as well as duty-cycle and dwell-time limitations.

The field region specifies the region of channel plan and thereby controls certain regulatory behavior such as clear channel assessment as well as duty-cycle and dwell-time limitations. Valid names are compatible with the names of the LoRaWAN Regional Parameters specification, except the end frequency is dropped (e.g., EU863 and US902).

The field hwspec describes the what concentrator hardware is needed to operate the channel plan delivered in the router_config message. The assigned string MUST have the following form: sx1301/**N** where N denotes the number of SX1301 concentrator chips required to operate the channel plan. Station will check this requirement against its hardware capabilities.

The field freq_range defines the lower and upper boundaries of the available spectrum for the region set. Station will NOT allow downlink transmissions outside of this frequency range.

The field DRs defines the available data rates of the channel plan. It MUST be an array of 16 entries with each entry being an array of three (3) integers encoding the spreading factor SF, the bandwidth BW, and a DNONLY flag. SF MUST be in the range 7..12 for LoRa, or 0 for FSK. BW MUST be one of the numbers 125, 250, 500, and BW is ignored for FSK. DNONLY MUST be 1 if the data rate is valid for downlink frames only, and 0 otherwise.

The field sx1301_conf defines how the channel plan maps to the individual SX1301 chips. Its value is an array of SX1301CONF objects. The number of array elements MUST be in accordance with the value of the field hwspec.

The layout of a SX1301CONF object looks like this:

{
  "radio_0": { .. } // same structure as radio_1
  "radio_1": {
    "enable": BOOL,
    "freq"  : INT
  },
  "chan_FSK": {
    "enable": BOOL
  },
  "chan_Lora_std": {
    "enable": BOOL,
    "radio": 0|1,
    "if": INT,
    "bandwidth": INT,
    "spread_factor": INT
  },
  "chan_multiSF_0": { .. }  // _0 .. _7 all have the same structure
  ..
  "chan_multiSF_7": {
    "enable": BOOL,
    "radio": 0|1,
    "if": INT
  }
}

Uplink Messages

Uplink messages can be grouped into two categories: (1) Those reflecting reception of LoRaWAN radio frames, and (2) Management messages to interact with the LNS. Both share a common set of fields described in the next section, followed by sections describing the indiviual message types encoding radio and management messages, respectively.

Radio Meta-Data

The following fields are part of all messages that encode LoRaWAN radio frames. These fields reflect the meta data and timing information accompanying any radio frame:

{
  ...
  "DR"    : INT,
  "Freq"  : INT,
  "upinfo": {
    "rctx"    : INT64,
    "xtime"   : INT64,
    "gpstime" : INT64,
    "rssi"    : FLOAT,
    "snr"     : FLOAT
  }
}

The field DR is the data rate the frame was received on; possible values are intergers in the range 0..15. The field Freq encodes the receive frequency in Hz. Possible value of DR and Freq depend on the current channel plan.

The object upinfo contains some context required by Station to process a subsequent matching downlink frame. The fields rctx and xtime are passed along through the LNS and MUST be added to matching Class A downlink messages.

The field gpstime is the receive timetamp of the frame expressed in microseconds since GPS epoch. If Station does not have access to a PPS signal synchronized to GPS time, the value of this field is 0.

LoRaWAN Join Requests

Join request frames are parsed and the individual parts are sent back in fields as shown below. Messages of this type also carry fields discussed in the Section Radio Meta-Data.

{
  "msgtype" : "jreq",
  "MHdr"    : UINT,
  "JoinEui" : EUI,
  "DevEui"  : EUI,
  "DevNonce": UINT,
  "MIC"     : INT32,
  ..
}

Note: The field MIC is represented as a 32 bit signed integer.

LoRaWAN Data Frames

Data frames are parsed and the individual parts are sent back in fields as shown below. Messages of this type also carry fields discussed in the Section Radio Meta-Data.

{
  "msgtype"   : "updf",
  "MHdr"      : UINT,
  "DevAddr"   : INT32,
  "FCtrl"     : UINT,
  "FCnt",     : UINT,
  "FOpts"     : "HEX",
  "FPort"     : INT(-1..255),
  "FRMPayload": "HEX",
  "MIC"       : INT32,
  ..
}

If the frame does not contain a port, the value of the field FPort is -1. If the frame does not carry options or data, the value of the fields FOpts or FRMPayload will be an empty string, respectively. Otherwise those fields contain strings of hexadecimal characters.

Note: The fields DevAddr and MIC are represented as 32 bit signed integers.

LoRaWAN Proprietary Frames

Frames marked as proprietary are forwarded in the following form, whereby the entire frame payload is passed along uninterpreted. Messages of this type also carry fields discussed in the Section Radio Meta-Data.

{
  "msgtype"   : "propdf",
  "FRMPayload": "HEX",
  ..
}

Transmit Confirmation

Station acknowledges transmit requests from the LNS with a message of type dntxed. This message is only sent when a frame has been put on air. There is no feedback to the LNS if a frame could not be sent (e.g., because it was too late, there was a conflict with ongoing transmit, or the gateway’s duty cycle was exhausted). These conditions are summarized in health status messages and do NOT trigger an individual response.

{
  "msgtype"  : "dntxed",
  "diid"     : INT64,
  "DevEui"   : "EUI",
  "rctx"     : INT64,
  "xtime"    : INT64,
  "txtime"   : FLOAT,
  "gpstime"  : INT64
}

The field diid field is a copy of the field diid of the dnmsg message. It identifies a particular device interaction and is used by the LNS to reference some interal state. It is passed along unchanged by Station.

The field rctx specifies the antenna used for transmit and the field xtime is the exact internal time when the frame was put on air.

Downlink Messages

This section covers all message types that are used by the LNS to transmit LoRaWAN data frames.

Single frame downlinks are either a response to a Class A/C uplink frame, or are unsolicited downlink frames for a Class B/C interaction. Multiframe downlinks are used to schedule a sequence of multicast frames.

Single Frame Downlink

Station supports downlinks of Class A, B, and C. For all three classes, the same message type is being used but different fields need to be present.

A Class A downlink frame is a response to a previously sent uplink. The layout for Class A REQUIRES the following fields:

{
  "msgtype"  : "dnmsg",
  "DevEui"   : "EUI",
  "dC"       : 0,          // class A
  "diid"     : INT64,
  "pdu"      : "HEX",
  "RxDelay"  : INT(0..15),
  "RX1DR"    : INT(0..15),
  "RX1Freq"  : INT,
  "RX2DR"    : INT(0..15),
  "RX2Freq"  : INT,
  "priority" : INT(0..255),
  "xtime"    : INT64,
  "rctx"     : INT64
}

The field diid is a number issued by the LNS to identify a particular device interaction. It is passed along by Station to dntxed messages. The field pdu is the radio frame as it will be put on air.

Station will choose either RX1 or RX2 transmission parameters, whereby RX1 is preferred. If the frame arrives too late or the transmission spot is blocked, it will try RX2. To force a decision, the LNS can omit either the RX1 or the RX2 parameters.

The field priority is used when conflicting transmission requests are encountered. The LNS can give frames that carry an acknowledgement or some important MAC commands higher priority. If two frames are in conflict, the one with the higher priority value is preferred.

The fields xtime and rctx contain values that originate from the corresponding uplink message. Those values must be passed along unchanged by the LNS. They are used by Station to manage proper transmit timing and to handle antenna associations.

A Class C downlink frame which answers an uplink and aim at RX1 looks almost identical to Class A dnmsg messages. The only difference is the field dC, which declares a Class C interaction. If the transmission cannot make the RX1 opportunity, Station will switch over to RX2 parameters and choose a convenient time to send the frame. There is a maximum time Station will postpone transmission before the frame is dropped.

{
  "msgtype" : "dnmsg",
  "DevEui"  : "EUI",
  "dC" : 2,         // class C
  "diid" : INT64,
  "pdu"      : "HEX",
  "RxDelay"  : INT(0..15),
  "RX1DR"    : INT(0..15),
  "RX1Freq"  : INT,
  "RX2DR"    : INT(0..15),
  "RX2Freq"  : INT,
  "priority" : INT(0..255),
  "xtime"    : INT64,
  "rctx"     : INT64
}

A Class C downlink frame not answering an uplink REQUIRES the following fields, whereby the same transmission strategy as described above applies. The transmission is delayed until Station finds a convenient time slot. If there is no such opportunity, the frame will be dropped after some configurable time; the default is one (1) sec.

The field rctx is OPTIONAL but MAY be used to select an antenna preference. The LNS MAY copy this field from a previous Class A uplink and guide Station to use the same antenna which received that Class A uplink.

{
  "msgtype"  : "dnmsg",
  "DevEui"   : "EUI",
  "dC"       : 2,          // class C
  "diid"     : INT64,
  "pdu"      : "HEX",
  "RX2DR"    : INT(0..15),
  "RX2Freq"  : INT,
  "priority" : INT(0..255),
  "rctx"     : INT64
}

A Class B downlink frame REQUIRES the following fields:

{
  "msgtype"  : "dnmsg",
  "DevEui"   : "EUI",
  "dC"       : 1,           // class B
  "diid"     : INT64,
  "pdu"      : "HEX",
  "DR"       : INT(0..15),
  "Freq"     : INT,
  "priority" : INT(0..255),
  "gpstime"  : INT64,
  "rctx"     : INT64
}

The transmission of a Class B frame REQUIRES the LNS to provide a GPS-aligned time. The value is expressed in microseconds since the GPS epoch.

The field rctx is OPTIONAL but MAY be used to select an antenna preference. The LNS MAY copy this field from a previous Class A uplink and guide Station to use the same antenna which received that Class A uplink.

Multicast Schedule

Messages of type dnsched can be use to schedule a set of multicast messages in one go. Each of the message elements needs to specify a GPS time at which it shall be sent. This type of transmission does not generate dntxed responses from Station.

{
  "msgtype"  : "dnsched",
  "schedule" : [
    {
      "pdu"      : "HEX",
      "DR"       : INT(0..15),
      "Freq"     : INT,
      "priority" : INT(0..255),
      "gpstime"  : INT64,
      "rctx"     : INT64
    },
    ...
  ]
}

The field rctx is OPTIONAL but MAY be used to select an antenna preference. LNS MAY copy this field from a previous Class A uplink and guide Station to use the same antenna which received that Class A uplink.

Monitoring Round-trip Times

The message exchange between Station and LNS allows for monitoring round-trip times by piggybacking certain fields into normal message exchanges. The LNS MAY add a field MuxTime to every downlink sent to Station:

{
  "msgtype" : ".."
  "MuxTime" : FLOAT    // downlink
  ..
}

The field MuxTime contains a float value representing a UTC timestamp with fractional seconds and marks the time when this message was sent by the LNS. Station will respond by adding the field RefTime:

{
  "msgtype" : ".."
  "RefTime" : FLOAT  // uplink
  ..
}

The field RefTime is calculated from the last received MuxTime adjusted by the time interval on the router between the arrival of MuxTime and the sending of RefTime. Since downlink traffic is less frequent than uplink messages, it is likely that a MuxTime is reused to construct multiple RefTime fields. This means that round-trip measurements are composed of the latency of the last downlink message and the most recent uplink message.

Time Synchronization

There are two modes of operation for time synchronization: (1) With access to a PPS signal, and (2) Independent of a PPS signal.

Synchronizing PPS to GPS Time

If Station has access to a PPS signal aligned to GPS seconds, it will infer the sychronization to GPS time by running a protocol with the LNS. To infer the correct GPS time label for a given PPS pulse, Station keeps sending timesync upstream messages to the LNS which should be promptly answered with a downtream timesync message.

Format of an upstream message:

{
  "msgtype"  : "timesync"
  "txtime"   : INT64,
}

The downstream response SHOULD be sent by the LNS immediately after reception of the corresponding upstream timesync. The LNS SHALL insert the field gpstime field and pass through the field txtime received from Station, whereby txtime is some unspecified time local to Station.

The field gpstime marks the instant of time when the LNS processes the timesync message. Its value is the time in microseconds since GPS epoch.

{
  "msgtype"  : "timesync"
  "txtime"   : INT64,
  "gpstime"  : INT64
}

Transferring GPS Time

The LNS SHALL periodically send the following message to Station to transfer GPS time.

{
  "msgtype"  : "timesync"
  "xtime"    : INT64,
  "gpstime"  : INT64
}

The field xtime is particular to Station and the GPS time is inferred from overlapping upstream frames arriving at the LNS.

Remote Command

Station supports two mechanisms for running remote commands on the gateway in the following format:

{
  "msgtype"  : "runcmd"
  "command"  : STRING,
  "arguments": [ STRING, ... ]
}

The field arguments is OPTIONAL or MAY be an empty array. If present and not empty, these arguments are passed along with the command.

The field command contains the actual command to execute. Based on its content, Station behaves as follows:

  • If the executable flag is set, run the command with the specified set of arguments.
  • If it is a file but not executable, treat it as a file containing a shell script. Pass it together with the arguments to the system local shell for interpretation.
  • If none of the above is true, assume the command value represents shell statements. Run the local shell and pass the value as statements together with the arguments for execution.

Remote Shell

Station supports a remote shell session tunneling through the websocket to the LNS. The session is initiated by a command as shown below. After the session is established, the input data towards the shell and the output data generated by the shell and its commands are tunneled through the websocket as binary records. The first byte of the binary record is the session index since Station can maintain multiple sessions. The remaining bytes of a binary record are the input/output data. The encoding is transparent to the websocket transport.

To query, initiate, or stop a session, Tthe LNS SHALL send the following message:

{
  "msgtype"  : "rmtsh",
  "user"     : STRING,
  "term"     : STRING,
  "start"    : INT,
  "stop"     : INT
}

If the fields start and stop are absent, the current session state is queried. Otherwise only one of the two fields SHALL be present indicating a session start or stop, respectively.

The value of the field term is the value of the TERM environment variable. It is set before the local shell is being started.

The field user describes the user who is operating the session. For Station this is just informational context.

Station will respond with the following message describing the current state of all sessions:

{
  "msgtype"  : "rmtsh",
  "rmtsh"    : [
    {
      "user"     : STRING,
      "started"  : BOOL,
      "age"      : INT,
      "pid"      : INT
    },
    ..
  ]
}

The field age is the time in seconds since the input/output action on this remote shell session.

The field pid is the local process identifier of the started shell.

The field started indicates that the shell process is actually running.