star-mesh LoRa network

Dependencies:   sx12xx_hal

start-mesh

radio chip selection

Radio chip driver is not included, because options are available.
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 NAmote72 or Murata discovery, then you must import only sx127x driver.

In this network, devices repeat messages to/from devices out of range of central gateway device. Appropriate for use when slightly larger batteries cost less than extra LoRaWAN gateways. This network uses LoRa transceiver directly and is not LoRaWAN. This network is appropriate for use where extra latency added from store-and-forward is of minimal consequence.

network implementation

Network achieves low-power operation by device waking up at regular intervals to check if LoRa preamble exists. If so, packet is received, otherwise devices sleeps. In this type of operation, trade-off is made between transmitter sending long preamble, and receiver waking up to receive this preamble along with associate message at the end of preamble. This long preamble is only used for request packets: the reply packet will have normal 8-symbol preamble length. This is known as asynchronous low-power operation, permitting an arbitrary number of devices to operate.

Devices start operation on the network by sending a discovery request to any devices that can hear it. Any devices which hears this discovery request will then send a discovery reply at randomized time offset relative to the request. After a pre-established time limit, the discovering device will decide which device to attach to depending on signal quality and how many hops away from the central gateway the device resides.

After device has attached to network, downlinks and uplinks can be sent to/from device. To facilitate downlinks, the devices closer to central gateway will be sent a new-device-notification to inform all devices between the central gateway and the newly attached device, which devices the new device can be reached via.

All devices have two logical interfaces to the network: An upstream interface, and a downstream interface. However, the central gateway device only has a downstream interface, because the "upstream" is only a UART interface to the user handling the user payloads on central gateway.

All devices on network are programmed with same firmware, except for gateway. In main.h #define GATEWAY is commented-out for devices on network, or is defined for central gateway device. Only one central gateway must exist on this network. The unique identifying address of device is derived from CPU unique ID registers, of which 4 byte ID number is used on this network. Using this CPU serial number permits the same binary file to be programmed into any number of devices.

network configuration

Network is configured in main.h

define in main.h
spreading factorSPREADING_FACTOR
bandwidthBW_KHZ
operating radio frequencyCF_MHZ
gateway or deviceGATEWAY
transmit powerTX_DBM

MAC layer timing scales according to LoRa symbol period. When spreading factor and/or bandwidth is changed, all network timing is scaled accordingly by MAC layer.
The transceivers used with the project operate at one datarate. This datarate is fixed, and must be defined at compile time for all devices and gateway.

low power operation

This MAC layer uses mbed eventqueue for scheduling. To enable low power operation, events.use-lowpower-timer-ticker is defined in mbed_app.json. This requires bare-metal operation to have eventqueue use low power timer, permitting deep sleep. LoRa applications such as this do not require RTOS: bare-metal mode is preferred for typical LoRa use.

application layer

User payloads are handled in app_endDevice.cpp. Uplinks are send from application layer by calling uplink(uint8_t *buffer_ptr, uint8_t length) Downlinks are handled in callback function app_downlink()

For gateway, app_gateway.cpp handles user payloads. For downlinks, an example is provided in cmd_downlink() where destination and payload is entered on serial port. All uplinks are handled in callback function gateway_uplink().

Header file app.h contains definitions common to application layer on both network central control and end device.

Note

This page describes how to use the network, for more detailed description of implementation, see details page.

serial terminal user interface

The STDIO UART is used to send and receive user-payload on the gateway, but is also available on end-devices. This serial port is configured at 115200 : 8,N,1.

commandargumentsdescription
?list commands
dldestID byte0 byte1 etcsend downlink to device (from gateway)
lslist downstream devices attached
opdBmmanually change transmit power

For the list of downstream devices, on start each row is printed directly attached device. If devices are attached further downstream, they will be subsequently printed on the same row.

testing / evaluation

Use 3 devices for testing: one gateway and two devices.
Three devices required for checking message repeating (relaying) function. The gateway device must be installed at some distance to prevent both devices from connecting directly to gateway.
Gateway needs to be located far enough away, so signal strength preference overrides hop count from gateway.

Committer:
Wayne Roberts
Date:
Tue Dec 03 09:50:00 2019 -0800
Revision:
1:fcd4c56fc56c
Parent:
0:6015834e4279
remove radio driver

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Wayne Roberts 0:6015834e4279 1 #include "radio.h"
Wayne Roberts 0:6015834e4279 2
Wayne Roberts 0:6015834e4279 3
Wayne Roberts 0:6015834e4279 4 //#define GATEWAY
Wayne Roberts 0:6015834e4279 5
Wayne Roberts 0:6015834e4279 6 //#define MESH_DEBUG
Wayne Roberts 0:6015834e4279 7
Wayne Roberts 0:6015834e4279 8 #define DISCOVERY_ANS_LENGTH 13
Wayne Roberts 0:6015834e4279 9 #define ANS_PAD_MS 5
Wayne Roberts 0:6015834e4279 10
Wayne Roberts 0:6015834e4279 11 #define ANS_SIZE_BYTE 9 /* */
Wayne Roberts 0:6015834e4279 12
Wayne Roberts 0:6015834e4279 13 #define RETRY_LIMIT 5 /* maximum count of reqest retries until giving up */
Wayne Roberts 0:6015834e4279 14 /********************************************************/
Wayne Roberts 0:6015834e4279 15
Wayne Roberts 0:6015834e4279 16 #define SPREADING_FACTOR 8
Wayne Roberts 0:6015834e4279 17 #ifdef SX128x_H
Wayne Roberts 0:6015834e4279 18 #define CF_MHZ 2487.0
Wayne Roberts 0:6015834e4279 19 #define BW_KHZ 400
Wayne Roberts 0:6015834e4279 20 #define TX_DBM 12
Wayne Roberts 0:6015834e4279 21 #else
Wayne Roberts 0:6015834e4279 22 #define CF_MHZ 917.6
Wayne Roberts 0:6015834e4279 23 #define BW_KHZ 500
Wayne Roberts 0:6015834e4279 24 #define TX_DBM 17
Wayne Roberts 0:6015834e4279 25 #endif
Wayne Roberts 0:6015834e4279 26
Wayne Roberts 0:6015834e4279 27 #define SP_US ((1<<SPREADING_FACTOR) / (BW_KHZ/1000.0))
Wayne Roberts 0:6015834e4279 28 #define N_PRE_SYMB (((WAKEUP_INTERVAL_MS * 1000) / SP_US) + 8)
Wayne Roberts 0:6015834e4279 29
Wayne Roberts 0:6015834e4279 30 #define N_DISCOVERY_ANS 180 // enough time slots for randomized collision avoidance
Wayne Roberts 0:6015834e4279 31 #define N_HALF_DISCOVERY_ANS (N_DISCOVERY_ANS / 2)
Wayne Roberts 0:6015834e4279 32
Wayne Roberts 0:6015834e4279 33 #define WAKEUP_INTERVAL_MS 1000
Wayne Roberts 0:6015834e4279 34 #define CHANNEL_VACANT_REQUIRED_COUNT 3
Wayne Roberts 0:6015834e4279 35
Wayne Roberts 0:6015834e4279 36 #define ID_NONE 0x00000000
Wayne Roberts 0:6015834e4279 37 #define ANY_ID 0xffffffff
Wayne Roberts 0:6015834e4279 38
Wayne Roberts 0:6015834e4279 39 typedef enum {
Wayne Roberts 0:6015834e4279 40 /* 0 */ CMD_UNUSED = 0,
Wayne Roberts 0:6015834e4279 41 /* 1 */ CMD_ANS,
Wayne Roberts 0:6015834e4279 42 /* 2 */ CMD_DISCOVERY_REQ,
Wayne Roberts 0:6015834e4279 43 /* 3 */ CMD_DISCOVERY_ANS,
Wayne Roberts 0:6015834e4279 44 /* 4 */ CMD_ATTACH_REQ,
Wayne Roberts 0:6015834e4279 45 /* 5 */ CMD_USER_PAYLOAD_UP_REQ,
Wayne Roberts 0:6015834e4279 46 /* 6 */ CMD_USER_PAYLOAD_DN_REQ,
Wayne Roberts 0:6015834e4279 47 /* 7 */ CMD_NEW_DEVICE_ATTACHED_REQ,
Wayne Roberts 0:6015834e4279 48 /* 8 */ CMD_REMOVE_DEVICE_REQ,
Wayne Roberts 0:6015834e4279 49 /* 9 */ CMD_DOWNSTREAM_NOT_RESPONDING,
Wayne Roberts 0:6015834e4279 50 } cmd_e;
Wayne Roberts 0:6015834e4279 51
Wayne Roberts 0:6015834e4279 52 typedef enum {
Wayne Roberts 0:6015834e4279 53 /* 0 */ NOT_ANSWERING = 0,
Wayne Roberts 0:6015834e4279 54 /* 1 */ ANSWER_OK,
Wayne Roberts 0:6015834e4279 55 /* 2 */ ANSWER_BUSY,
Wayne Roberts 0:6015834e4279 56 /* 3 */ ANSWER_UNATTACHED,
Wayne Roberts 0:6015834e4279 57 } ans_e;
Wayne Roberts 0:6015834e4279 58
Wayne Roberts 0:6015834e4279 59 typedef union {
Wayne Roberts 0:6015834e4279 60 struct {
Wayne Roberts 0:6015834e4279 61 uint8_t currentOp : 5; // 0,1,2,3,4
Wayne Roberts 0:6015834e4279 62 uint8_t txAns : 3; // 5,6,7
Wayne Roberts 0:6015834e4279 63 } bits;
Wayne Roberts 0:6015834e4279 64 uint16_t octet;
Wayne Roberts 0:6015834e4279 65 } reqflags_t;
Wayne Roberts 0:6015834e4279 66 extern reqflags_t reqFlags;
Wayne Roberts 0:6015834e4279 67
Wayne Roberts 0:6015834e4279 68 typedef struct {
Wayne Roberts 0:6015834e4279 69 uint8_t discoverAnswering : 1; // 0
Wayne Roberts 0:6015834e4279 70 uint8_t sending_req : 1; // 1
Wayne Roberts 0:6015834e4279 71 uint8_t firstDiscoverAns : 1; // 2
Wayne Roberts 0:6015834e4279 72 uint8_t CallTXRequest : 1; // 3
Wayne Roberts 0:6015834e4279 73 uint8_t unused : 1; // 4
Wayne Roberts 0:6015834e4279 74 uint8_t getAns : 1; // 5
Wayne Roberts 0:6015834e4279 75 uint8_t vacantCheck : 1; // 6
Wayne Roberts 0:6015834e4279 76 uint8_t deferred_send : 1; // 7
Wayne Roberts 0:6015834e4279 77 } flags_t;
Wayne Roberts 0:6015834e4279 78
Wayne Roberts 0:6015834e4279 79 extern volatile flags_t flags;
Wayne Roberts 0:6015834e4279 80
Wayne Roberts 0:6015834e4279 81 typedef struct local lid_list_t;
Wayne Roberts 0:6015834e4279 82 typedef struct children cid_list_t;
Wayne Roberts 0:6015834e4279 83
Wayne Roberts 0:6015834e4279 84 struct children {
Wayne Roberts 0:6015834e4279 85 uint32_t id;
Wayne Roberts 0:6015834e4279 86 children* next;
Wayne Roberts 0:6015834e4279 87 };
Wayne Roberts 0:6015834e4279 88
Wayne Roberts 0:6015834e4279 89 struct local {
Wayne Roberts 0:6015834e4279 90 uint32_t id; // device directly attached
Wayne Roberts 0:6015834e4279 91 children* attachedList; // devices attached to id
Wayne Roberts 0:6015834e4279 92 local* next;
Wayne Roberts 0:6015834e4279 93 };
Wayne Roberts 0:6015834e4279 94
Wayne Roberts 0:6015834e4279 95 struct _fwd_ {
Wayne Roberts 0:6015834e4279 96 uint8_t buf[247];
Wayne Roberts 0:6015834e4279 97 int len;
Wayne Roberts 0:6015834e4279 98 uint32_t A_id;
Wayne Roberts 0:6015834e4279 99 uint32_t B_id;
Wayne Roberts 0:6015834e4279 100 uint32_t tx_dest_id; // pkt destination
Wayne Roberts 0:6015834e4279 101 };
Wayne Roberts 0:6015834e4279 102 extern struct _fwd_ fwd;
Wayne Roberts 0:6015834e4279 103
Wayne Roberts 0:6015834e4279 104 struct _nr_ {
Wayne Roberts 0:6015834e4279 105 uint32_t reporting_id;
Wayne Roberts 0:6015834e4279 106 uint32_t device_not_respoding_id;
Wayne Roberts 0:6015834e4279 107 };
Wayne Roberts 0:6015834e4279 108 extern struct _nr_ notResponding;
Wayne Roberts 0:6015834e4279 109
Wayne Roberts 0:6015834e4279 110 /* from main.cpp: */
Wayne Roberts 0:6015834e4279 111 #define HFG_UNATTACHED 0xff
Wayne Roberts 0:6015834e4279 112 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 113 extern const uint8_t hops_from_gateway;
Wayne Roberts 0:6015834e4279 114 #else
Wayne Roberts 0:6015834e4279 115 extern uint8_t hops_from_gateway;
Wayne Roberts 0:6015834e4279 116 #endif
Wayne Roberts 0:6015834e4279 117 extern EventQueue queue;
Wayne Roberts 0:6015834e4279 118 extern RawSerial pc;
Wayne Roberts 0:6015834e4279 119 extern char pcbuf[64]; /* local user terminal */
Wayne Roberts 0:6015834e4279 120 extern uint32_t my_id;
Wayne Roberts 0:6015834e4279 121 extern unsigned discovery_ans_time_step_us;
Wayne Roberts 0:6015834e4279 122 extern unsigned discovery_ans_time_total_us;
Wayne Roberts 0:6015834e4279 123 void setPreambleSize(bool wakesize, uint8_t by);
Wayne Roberts 0:6015834e4279 124 uint32_t getu32FromBuf(const uint8_t* in);
Wayne Roberts 0:6015834e4279 125 void putu32ToBuf(uint8_t* out, uint32_t v);
Wayne Roberts 0:6015834e4279 126 void putu16ToBuf(uint8_t* out, uint16_t v);
Wayne Roberts 0:6015834e4279 127 uint16_t getu16FromBuf(const uint8_t* in);
Wayne Roberts 0:6015834e4279 128 uint16_t crc16( uint8_t *buffer, uint16_t length );
Wayne Roberts 0:6015834e4279 129 void txBuf_send(bool sendingReq);
Wayne Roberts 0:6015834e4279 130 bool remove_directlyAttached_device(uint32_t id);
Wayne Roberts 0:6015834e4279 131 void remove_childDevice(uint32_t id, uint32_t* attachedTo);
Wayne Roberts 0:6015834e4279 132 uint32_t find_dest_id(uint32_t reqid);
Wayne Roberts 0:6015834e4279 133 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 134 int _rx_log_printf(const char *format, ...);
Wayne Roberts 0:6015834e4279 135 #endif /* MESH_DEBUG */
Wayne Roberts 0:6015834e4279 136 extern uint32_t tx_dest_id;
Wayne Roberts 0:6015834e4279 137 extern uint8_t txBuf[];
Wayne Roberts 0:6015834e4279 138 extern uint8_t txBuf_idx;
Wayne Roberts 0:6015834e4279 139 extern uint16_t dbg_plCur;
Wayne Roberts 0:6015834e4279 140 extern uint8_t dbg_plSetBy;
Wayne Roberts 0:6015834e4279 141 extern const char* const cmdStrs[];
Wayne Roberts 0:6015834e4279 142 void start_periodic_rxing(uint8_t by);
Wayne Roberts 0:6015834e4279 143
Wayne Roberts 0:6015834e4279 144 /* radio specific */
Wayne Roberts 0:6015834e4279 145 void radio_print_status(void);
Wayne Roberts 0:6015834e4279 146 void cmd_op(uint8_t);
Wayne Roberts 0:6015834e4279 147 void radio_printOpMode(void);
Wayne Roberts 0:6015834e4279 148 bool isRadioRxing(void);
Wayne Roberts 0:6015834e4279 149
Wayne Roberts 0:6015834e4279 150 #ifndef GATEWAY
Wayne Roberts 0:6015834e4279 151 /* upstream interface */
Wayne Roberts 0:6015834e4279 152 extern uint32_t id_newDeviceNotification;
Wayne Roberts 0:6015834e4279 153 typedef struct {
Wayne Roberts 0:6015834e4279 154 uint32_t id, cnt;
Wayne Roberts 0:6015834e4279 155 int preference;
Wayne Roberts 0:6015834e4279 156 uint8_t hfg;
Wayne Roberts 0:6015834e4279 157 } upstream_t;
Wayne Roberts 0:6015834e4279 158 extern upstream_t attUp;
Wayne Roberts 0:6015834e4279 159 void upstream_init(void);
Wayne Roberts 0:6015834e4279 160 void discovery_rx_end(void);
Wayne Roberts 0:6015834e4279 161 void upstream_print_status(void);
Wayne Roberts 0:6015834e4279 162 int uplink(const uint8_t* userPayload, uint8_t userPayloadSize);
Wayne Roberts 0:6015834e4279 163 void upstream_new_device_notify(void);
Wayne Roberts 0:6015834e4279 164 void upstream_ans_rxDoneCB(float rssi, float snr, uint8_t* idx, uint32_t, uint8_t);
Wayne Roberts 0:6015834e4279 165 #endif /* !GATEWAY */
Wayne Roberts 0:6015834e4279 166 void upstream_req_rxDoneCB(float rssi, float snr, uint8_t* idx, uint32_t, uint8_t);
Wayne Roberts 0:6015834e4279 167 void upstream_signal_check(float rssi, float snr, uint8_t rx_hfg, uint32_t rx_id);
Wayne Roberts 0:6015834e4279 168 void init_attached_upstream(void);
Wayne Roberts 0:6015834e4279 169 void upstream_attached_check(uint32_t);
Wayne Roberts 0:6015834e4279 170
Wayne Roberts 0:6015834e4279 171 /* downstream interface */
Wayne Roberts 0:6015834e4279 172 struct remove {
Wayne Roberts 0:6015834e4279 173 uint32_t destID;
Wayne Roberts 0:6015834e4279 174 uint32_t removeID;
Wayne Roberts 0:6015834e4279 175 };
Wayne Roberts 0:6015834e4279 176 extern struct remove downRemove;
Wayne Roberts 0:6015834e4279 177 extern lid_list_t* attachedDevices;
Wayne Roberts 0:6015834e4279 178 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 179 typedef struct {
Wayne Roberts 0:6015834e4279 180 uint8_t len;
Wayne Roberts 0:6015834e4279 181 uint32_t originating_src_id;
Wayne Roberts 0:6015834e4279 182 uint8_t rxBufIdx;
Wayne Roberts 0:6015834e4279 183 } upInfo_t;
Wayne Roberts 0:6015834e4279 184 void downstream_req_rxDoneCB(float rssi, float snr, uint8_t* idx, uint32_t, uint8_t, upInfo_t*);
Wayne Roberts 0:6015834e4279 185 #else
Wayne Roberts 0:6015834e4279 186 void downstream_req_rxDoneCB(float rssi, float snr, uint8_t* idx, uint32_t, uint8_t);
Wayne Roberts 0:6015834e4279 187 #endif
Wayne Roberts 0:6015834e4279 188 void downstream_ans_rxDoneCB(float rssi, float snr, uint8_t* idx, uint32_t, uint8_t);
Wayne Roberts 0:6015834e4279 189 void request_remove_device(void);
Wayne Roberts 0:6015834e4279 190
Wayne Roberts 0:6015834e4279 191
Wayne Roberts 0:6015834e4279 192 void cmd_downlink(uint8_t argsAt);
Wayne Roberts 0:6015834e4279 193
Wayne Roberts 0:6015834e4279 194 /* application layer: */
Wayne Roberts 0:6015834e4279 195 void app_init(void);
Wayne Roberts 0:6015834e4279 196 void gateway_uplink(uint8_t len, uint32_t, const uint8_t* payload); /* uplink handler */
Wayne Roberts 0:6015834e4279 197 void app_downlink(uint8_t len, const uint8_t* payload); /* downlink handler */
Wayne Roberts 0:6015834e4279 198 void app_uplink_complete(void);
Wayne Roberts 0:6015834e4279 199
Wayne Roberts 0:6015834e4279 200 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 201 #define Mdbg_printf(fmt, ...) pc.printf((fmt), ##__VA_ARGS__)
Wayne Roberts 0:6015834e4279 202 #define mdbg_putc(x) pc.putc(x)
Wayne Roberts 0:6015834e4279 203 #define Rx_log_printf(fmt, ...) _rx_log_printf((fmt), ##__VA_ARGS__)
Wayne Roberts 0:6015834e4279 204 #else
Wayne Roberts 0:6015834e4279 205 #define Mdbg_printf(fmt, ...)
Wayne Roberts 0:6015834e4279 206 #define mdbg_putc(x)
Wayne Roberts 0:6015834e4279 207 #define Rx_log_printf(fmt, ...)
Wayne Roberts 0:6015834e4279 208 #endif
Wayne Roberts 0:6015834e4279 209