star-mesh LoRa network
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 factor | SPREADING_FACTOR | |
bandwidth | BW_KHZ | |
operating radio frequency | CF_MHZ | |
gateway or device | GATEWAY | |
transmit power | TX_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.
command | arguments | description |
---|---|---|
? | list commands | |
dl | destID byte0 byte1 etc | send downlink to device (from gateway) |
ls | list downstream devices attached | |
op | dBm | manually 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.
main.h@1:fcd4c56fc56c, 2019-12-03 (annotated)
- 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?
User | Revision | Line number | New 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 |