Synchronous wireless star LoRa network, central device.

Dependencies:   SX127x sx12xx_hal

radio chip selection

Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.


Alternate to this project gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Synchronous Star Network

This project acts as central node for LoRaWAN-like network operating on single radio channel. Intended for use where network infrastructure would never exist due to cost and/or complexity of standard network. This project uses the class-B method of beacon generation to synchronize the end nodes with the gateway. OTA mode must be used. End-node will be allocated an uplink time slot upon joining. End node may transmit uplink at this assigned timeslot, if it desires to do so. This time slot is always referenced to the beacon sent by gateway.

LoRaWAN server is not necessary. All network control is implemented by this project. User can observe network activity on the mbed serial port. Downlinks can be scheduled using command on serial port.

This implementation must not be used on radio channels requiring duty-cycle transmit limiting.

alterations from LoRaWAN specification

This mode of operation uses a single datarate on single radio channel. ADR is not implemented, because datarate cannot be changed. OTA mode must be used. When join-accept is sent by gateway, it will have appended (instead of CFlist) the beacon timing answer to inform of when next beacon occurs, and two timing values: the time slot allocated to this end-node and the periodicity of the network. Periodicity means how often the end-node may again transmit. /media/uploads/dudmuck/class-b-single.png Beacon is sent for purpose of providing timing reference to end-nodes. The beacon payload may contain a broadcast command to end nodes. Time value is not sent in beacon payload. The time at which beacon is sent provides timing reference: every 128 seconds as standard.

Rx2 receive window is not implemented. Only Rx1 is used because a single radio channel is used. Rx1 delay is reduced to 100 milliseconds. Original LoRaWAN has 1000 millisecond Rx1 delay to accommodate internet latency.

LoRaWAN standard class-B beacon requires GPS timing reference. This implementation does not use GPS, instead a hardware timer peripheral generates interrupts to send beacons. Absolute accuracy is not required, only relative crystal drift between gateway and end-nodes is considered.

Timing values are always specified as 30ms per step as in LoRaWAN standard. Each beacon period has 4096 30ms steps per beacon period.

join OTA procedure

The join procedure has changed the join-accept delay to 100 milliseconds (standard is 5 seconds). This allows end-node to hunt for gateway on several channels during joining. When gateway starts, it will scan available channels for the optimal choice based on ambient noise on the channels. End node will retry join request until it finds the gateway. Gateway might change channel during operation if it deems current channel too busy.

configuration of network

End nodes must be provisioned by editing file Comissioning.h. The array motes lists every end node permitted on network. It contains appEui, devEUI and appKey just as specified in standard LoRaWAN. All provisioning is hard-coded; changing provisioning requires reprogramming gateway. When changing number of motes, N_MOTES definition must be changed in lorawan.h.

lorawan.h

#define N_MOTES     8
extern ota_mote_t motes[N_MOTES];   /* from Comissioning.h */

configuring number of end-nodes vs transmit rate

Trade-off can be selected between number of end-nodes on network vs. how often each end-node can transmit.
In this example, where DR_13 is SF7 at 500KHz:

lorawan.cpp

    #elif (LORAMAC_DEFAULT_DATARATE == DR_13)
        #define TX_SLOT_STEPPING        8  //approx 0.25 seconds
        #define PERIODICITY_SLOTS       (TX_SLOT_STEPPING * 6)
    #endif

Here, each end-node is given time of 240ms = 8 * 30ms; accommodating maximum payload length for both uplink and downlink.
6 in this code is the maximum count of end nodes using this gateway. Each end-node can transmit every 1.44 seconds, in this example.
If you wanted to change 6 to 20 end-nodes, each would be able to use network every 4.8 seconds.
Another example: If you wanted to use DR_12 = SF8, you would have approx 2.5 to 3dB more gain compared to SF7, but each end-node must be given double time, resulting in 20 nodes able to use network every 9.6 seconds at DR_12.

network capacity limitation

The number of end-nodes which can be supported is determined by number of SLOT_STEPPING's which can occur during BEACON_PERIOD. Beacon guard is implemented same as standard LoRaWAN, which is 3 seconds prior to beacon start and 2.12 seconds after beacon start, which gives 122.88 seconds for network traffic for each beacon period.

gateway configuration

spreading factor is declared at #define LORAMAC_DEFAULT_DATARATE in lorawan.h, valid rates are DR_8 to DR_13 (sf12 to sf7). In the end-node, the same definition must be configured in LoRaMac-definitions.h. This network operates at this constant datarate for all packets.
Band plan can be selected by defining USE_BAND_* in lorawan.h. 434MHz can be used on SX1276 shield. TypeABZ module and sx1272 only support 800/900MHz channels band.

end-node provisioning

Security permits only matching DevEUI / AppEui to join the network, due to unique AES root key for each end device; in this case the DevEUI must be programmed in advance into gateway. However, if the same AES root key could be used for each end device , then any number of end devices could be added at a later time, without checking DevEUI on the gateway when an end device joins the network. On the gateway, the end device join acceptance is performed in file lorawan.cpp LoRaWan::parse_receive() where MType == MTYPE_JOIN_REQ. A memcmp() is performed on both DevEUI and AppEUI.

If you wish to allow any DevEUI to join, define ANY_DEVEUI at top of lorawan.cpp . In this configuration, all end devices must use same AppEUI and AES key. N_MOTES must be defined to the maximum number of end devices expected. Test end device by commenting BoardGetUniqueId() in end node, and replace DevEui[] with 8 arbitrary bytes to verify gateway permits any DevEUI to join.

RAM usage

For gateway CPU, recommend to consider larger RAM size depending on number of end devices required. ota_motes_t has size of 123 bytes. Each end device has one of these, however if less RAM usage is required for more end-devices, the MAC command queue size may be reduced.

hardware support

The radio driver supports both SX1272 and SX1276, sx126x kit, sx126x radio-only shield, and sx128x 2.4GHz.. The selection of radio chip type is made by your choice of importing radio driver library.



Beacon generation requires low power ticker to be clocked from crystal, not internal RC oscillator.

Gateway Serial Interface

Gateway serial port operates at 115200bps.

commandargumentdescription
list-list joined end nodes
?-list available commands
dl<mote-hex-dev-addr> <hex-payload>send downlink, sent after uplink received
gpo<mote-hex-dev-addr> <0 or 1>set output PC6 pin level on end device
b32bit hex valueset beacon payload to be sent at next beacon
. (period)-print current status
opdBmconfigure TX power of gateway
sbcountskip sending beacons, for testing end node
fhex devAddrprinter filtering, show only single end node
hm-print filtering, hide MAC layer printing
hp-print filtering, hide APP layer printing
sa-print filtering, show all, undo hm and hp

Any received uplink will be printed with DevAddr and decrypted payload.

Committer:
dudmuck
Date:
Thu Aug 10 17:56:02 2017 -0700
Revision:
16:67fa19bc6331
Parent:
13:fa2095be01c4
Child:
18:806cf9b7a904
add print filter on dev_addr

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dudmuck 0:2ff18de8d48b 1 #include "sx127x_lora.h"
dudmuck 0:2ff18de8d48b 2 #include "tim.h"
dudmuck 0:2ff18de8d48b 3
dudmuck 13:fa2095be01c4 4 #define USE_BAND_915
dudmuck 13:fa2095be01c4 5 //#define USE_BAND_433
dudmuck 13:fa2095be01c4 6
dudmuck 13:fa2095be01c4 7 #ifdef USE_BAND_915
dudmuck 13:fa2095be01c4 8 /*!
dudmuck 13:fa2095be01c4 9 * LoRaMac datarates definition
dudmuck 13:fa2095be01c4 10 */
dudmuck 13:fa2095be01c4 11 #define DR_8 8 // SF12 - BW500 |
dudmuck 13:fa2095be01c4 12 #define DR_9 9 // SF11 - BW500 |
dudmuck 13:fa2095be01c4 13 #define DR_10 10 // SF10 - BW500 |
dudmuck 13:fa2095be01c4 14 #define DR_11 11 // SF9 - BW500 |
dudmuck 13:fa2095be01c4 15 #define DR_12 12 // SF8 - BW500 +-> Down link
dudmuck 13:fa2095be01c4 16 #define DR_13 13 // SF7 - BW500 |
dudmuck 13:fa2095be01c4 17 #define DR_14 14 // RFU |
dudmuck 13:fa2095be01c4 18 #define DR_15 15 // RFU |
dudmuck 13:fa2095be01c4 19
dudmuck 13:fa2095be01c4 20 //#define LORAMAC_DEFAULT_DATARATE DR_8 // sf12
dudmuck 13:fa2095be01c4 21 //#define LORAMAC_DEFAULT_DATARATE DR_9 // sf11
dudmuck 13:fa2095be01c4 22 //#define LORAMAC_DEFAULT_DATARATE DR_10 // sf10
dudmuck 13:fa2095be01c4 23 //#define LORAMAC_DEFAULT_DATARATE DR_11 // sf9
dudmuck 13:fa2095be01c4 24 //#define LORAMAC_DEFAULT_DATARATE DR_12 // sf8
dudmuck 13:fa2095be01c4 25 #define LORAMAC_DEFAULT_DATARATE DR_13 // sf7
dudmuck 13:fa2095be01c4 26
dudmuck 13:fa2095be01c4 27 #define LORAMAC_FIRST_CHANNEL ( (uint32_t)910.0e6 )
dudmuck 13:fa2095be01c4 28 #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)800e3 )
dudmuck 13:fa2095be01c4 29 #define LORA_MAX_NB_CHANNELS 8
dudmuck 0:2ff18de8d48b 30
dudmuck 13:fa2095be01c4 31 #define BANDWIDTH_KHZ 500
dudmuck 13:fa2095be01c4 32 /* end USE_BAND_915 */
dudmuck 13:fa2095be01c4 33 #elif defined(USE_BAND_433)
dudmuck 13:fa2095be01c4 34
dudmuck 13:fa2095be01c4 35 #define DR_0 0 // SF12 - BW125
dudmuck 13:fa2095be01c4 36 #define DR_1 1 // SF11 - BW125
dudmuck 13:fa2095be01c4 37 #define DR_2 2 // SF10 - BW125
dudmuck 13:fa2095be01c4 38 #define DR_3 3 // SF9 - BW125
dudmuck 13:fa2095be01c4 39 #define DR_4 4 // SF8 - BW125
dudmuck 13:fa2095be01c4 40 #define DR_5 5 // SF7 - BW125
dudmuck 13:fa2095be01c4 41 //#define DR_6 6 // SF7 - BW250
dudmuck 13:fa2095be01c4 42 //#define DR_7 7 // FSK
dudmuck 13:fa2095be01c4 43
dudmuck 13:fa2095be01c4 44 //#define LORAMAC_DEFAULT_DATARATE DR_0 // sf12
dudmuck 13:fa2095be01c4 45 //#define LORAMAC_DEFAULT_DATARATE DR_1 // sf11
dudmuck 13:fa2095be01c4 46 //#define LORAMAC_DEFAULT_DATARATE DR_2 // sf10
dudmuck 13:fa2095be01c4 47 //#define LORAMAC_DEFAULT_DATARATE DR_3 // sf9
dudmuck 13:fa2095be01c4 48 //#define LORAMAC_DEFAULT_DATARATE DR_4 // sf8
dudmuck 13:fa2095be01c4 49 #define LORAMAC_DEFAULT_DATARATE DR_5 // sf7
dudmuck 13:fa2095be01c4 50
dudmuck 13:fa2095be01c4 51 #define LORAMAC_FIRST_CHANNEL ( (uint32_t)433.32e6 )
dudmuck 13:fa2095be01c4 52 #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)200e3 )
dudmuck 13:fa2095be01c4 53 #define LORA_MAX_NB_CHANNELS 7 /* last channel 434.52 */
dudmuck 13:fa2095be01c4 54
dudmuck 13:fa2095be01c4 55 #define BANDWIDTH_KHZ 125
dudmuck 13:fa2095be01c4 56 /* end USE_BAND_433 */
dudmuck 13:fa2095be01c4 57 #endif
dudmuck 0:2ff18de8d48b 58
dudmuck 0:2ff18de8d48b 59 #define DEVADDR_NONE 0xffffffff
dudmuck 0:2ff18de8d48b 60
dudmuck 0:2ff18de8d48b 61 /*!
dudmuck 0:2ff18de8d48b 62 * Sync word for Private LoRa networks
dudmuck 0:2ff18de8d48b 63 */
dudmuck 0:2ff18de8d48b 64 #define LORA_MAC_PRIVATE_SYNCWORD 0x12
dudmuck 0:2ff18de8d48b 65
dudmuck 0:2ff18de8d48b 66 /*!
dudmuck 0:2ff18de8d48b 67 * Sync word for Public LoRa networks
dudmuck 0:2ff18de8d48b 68 */
dudmuck 0:2ff18de8d48b 69 #define LORA_MAC_PUBLIC_SYNCWORD 0x34
dudmuck 0:2ff18de8d48b 70
dudmuck 0:2ff18de8d48b 71 #define BEACON_PERIOD_ms (BEACON_INTERVAL_SECONDS * 1000)
dudmuck 0:2ff18de8d48b 72 #define BEACON_GUARD_ms 1000
dudmuck 0:2ff18de8d48b 73 #define BEACON_SIZE 6
dudmuck 0:2ff18de8d48b 74
dudmuck 0:2ff18de8d48b 75 #define LORA_EUI_LENGTH 8
dudmuck 0:2ff18de8d48b 76 #define LORA_CYPHERKEYBYTES 16
dudmuck 0:2ff18de8d48b 77
dudmuck 0:2ff18de8d48b 78 extern SX127x radio;
dudmuck 0:2ff18de8d48b 79 extern SX127x_lora lora;
dudmuck 0:2ff18de8d48b 80 extern Timer timer;
dudmuck 0:2ff18de8d48b 81 extern EventQueue queue;
dudmuck 0:2ff18de8d48b 82
dudmuck 0:2ff18de8d48b 83 #define MAC_CMD_QUEUE_SIZE 6
dudmuck 0:2ff18de8d48b 84 #define MAC_CMD_SIZE 8
dudmuck 0:2ff18de8d48b 85 typedef struct {
dudmuck 0:2ff18de8d48b 86 uint8_t dev_eui[LORA_EUI_LENGTH];
dudmuck 0:2ff18de8d48b 87 uint8_t app_eui[LORA_EUI_LENGTH];
dudmuck 0:2ff18de8d48b 88 uint8_t app_key[LORA_CYPHERKEYBYTES];
dudmuck 0:2ff18de8d48b 89
dudmuck 0:2ff18de8d48b 90 uint8_t app_session_key[LORA_CYPHERKEYBYTES];
dudmuck 0:2ff18de8d48b 91 uint8_t network_session_key[LORA_CYPHERKEYBYTES];
dudmuck 0:2ff18de8d48b 92 uint32_t dev_addr;
dudmuck 0:2ff18de8d48b 93
dudmuck 0:2ff18de8d48b 94 uint16_t tx_slot_offset;
dudmuck 0:2ff18de8d48b 95
dudmuck 0:2ff18de8d48b 96 uint8_t macCmd_queue[MAC_CMD_QUEUE_SIZE][MAC_CMD_SIZE];
dudmuck 0:2ff18de8d48b 97 uint8_t macCmd_queue_in_idx, macCmd_queue_out_idx;
dudmuck 0:2ff18de8d48b 98
dudmuck 0:2ff18de8d48b 99 uint8_t user_downlink_length;
dudmuck 0:2ff18de8d48b 100 uint16_t FCntDown;
dudmuck 0:2ff18de8d48b 101 } ota_mote_t;
dudmuck 12:9a8c13c4298b 102 #define N_MOTES 8
dudmuck 0:2ff18de8d48b 103 extern ota_mote_t motes[N_MOTES]; /* from Comissioning.h */
dudmuck 0:2ff18de8d48b 104
dudmuck 0:2ff18de8d48b 105
dudmuck 0:2ff18de8d48b 106 class LoRaWan {
dudmuck 0:2ff18de8d48b 107 private:
dudmuck 0:2ff18de8d48b 108 static void SendJoinComplete(uint16_t deviceNonce, uint8_t firstReceiveWindowDataRateoffset, ota_mote_t* mote);
dudmuck 0:2ff18de8d48b 109 static void parse_mac_command(ota_mote_t* mote, uint8_t* rx_cmd_buf, uint8_t rx_cmd_buf_len);
dudmuck 0:2ff18de8d48b 110 static void parse_uplink(ota_mote_t* mote);
dudmuck 0:2ff18de8d48b 111 static void parse_join_req(ota_mote_t* mote);
dudmuck 0:2ff18de8d48b 112 static void classA_downlink(ota_mote_t* mote);
dudmuck 9:a0ce66c18ec0 113
dudmuck 0:2ff18de8d48b 114 public:
dudmuck 0:2ff18de8d48b 115 static int parse_receive(void);
dudmuck 0:2ff18de8d48b 116 static void init(void);
dudmuck 9:a0ce66c18ec0 117 static void print_octets_rev(char const* label, uint8_t const* buf, uint8_t buf_len);
dudmuck 0:2ff18de8d48b 118
dudmuck 0:2ff18de8d48b 119 static volatile uint16_t rx_slot;
dudmuck 0:2ff18de8d48b 120 static uint8_t user_downlink[];
dudmuck 0:2ff18de8d48b 121 static const uint8_t Datarates[];
dudmuck 0:2ff18de8d48b 122 static volatile uint32_t rx_ms;
dudmuck 0:2ff18de8d48b 123 static bool do_downlink;
dudmuck 16:67fa19bc6331 124 static uint32_t dev_addr_filter;
dudmuck 16:67fa19bc6331 125 static void filtered_printf(uint32_t dev_addr, const char* format, ...);
dudmuck 0:2ff18de8d48b 126 };
dudmuck 0:2ff18de8d48b 127
dudmuck 0:2ff18de8d48b 128
dudmuck 0:2ff18de8d48b 129 // from main.cpp:
dudmuck 0:2ff18de8d48b 130 void send_downlink(void);
dudmuck 16:67fa19bc6331 131 void decrypted_uplink(uint32_t dev_addr, uint8_t* buf, uint8_t buflen, uint8_t port);