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 "sx12xx.h"
Wayne Roberts 0:6015834e4279 2 #ifdef SX126x_H
Wayne Roberts 0:6015834e4279 3 #include "main.h"
Wayne Roberts 0:6015834e4279 4
Wayne Roberts 0:6015834e4279 5 uint8_t tx_param_buf[2];
Wayne Roberts 0:6015834e4279 6 uint8_t pa_config_buf[4];
Wayne Roberts 0:6015834e4279 7
Wayne Roberts 0:6015834e4279 8 void tx_dbm_print()
Wayne Roberts 0:6015834e4279 9 {
Wayne Roberts 0:6015834e4279 10 PwrCtrl_t PwrCtrl;
Wayne Roberts 0:6015834e4279 11 PaCtrl1b_t PaCtrl1b;
Wayne Roberts 0:6015834e4279 12 unsigned v = Radio::radio.readReg(REG_ADDR_ANACTRL16, 1);
Wayne Roberts 0:6015834e4279 13
Wayne Roberts 0:6015834e4279 14 if (v & 0x10) {
Wayne Roberts 0:6015834e4279 15 pc.printf("%d", PA_OFF_DBM);
Wayne Roberts 0:6015834e4279 16 return;
Wayne Roberts 0:6015834e4279 17 }
Wayne Roberts 0:6015834e4279 18
Wayne Roberts 0:6015834e4279 19 PwrCtrl.octet = Radio::radio.readReg(REG_ADDR_PWR_CTRL, 1);
Wayne Roberts 0:6015834e4279 20
Wayne Roberts 0:6015834e4279 21 PaCtrl1b.octet = Radio::radio.readReg(REG_ADDR_PA_CTRL1B, 1);
Wayne Roberts 0:6015834e4279 22 pa_config_buf[2] = PaCtrl1b.bits.tx_mode_bat; // deviceSel
Wayne Roberts 0:6015834e4279 23
Wayne Roberts 0:6015834e4279 24 if (PaCtrl1b.bits.tx_mode_bat)
Wayne Roberts 0:6015834e4279 25 pc.printf("%ddBm ", PwrCtrl.bits.tx_pwr - 17);
Wayne Roberts 0:6015834e4279 26 else
Wayne Roberts 0:6015834e4279 27 pc.printf("%ddBm ", PwrCtrl.bits.tx_pwr - 9);
Wayne Roberts 0:6015834e4279 28 }
Wayne Roberts 0:6015834e4279 29
Wayne Roberts 0:6015834e4279 30 bool isRadioRxing()
Wayne Roberts 0:6015834e4279 31 {
Wayne Roberts 0:6015834e4279 32 status_t status;
Wayne Roberts 0:6015834e4279 33 Radio::radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);
Wayne Roberts 0:6015834e4279 34 return status.bits.chipMode == 5;
Wayne Roberts 0:6015834e4279 35 }
Wayne Roberts 0:6015834e4279 36
Wayne Roberts 0:6015834e4279 37 void radio_printOpMode()
Wayne Roberts 0:6015834e4279 38 {
Wayne Roberts 0:6015834e4279 39 status_t status;
Wayne Roberts 0:6015834e4279 40 Radio::radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);
Wayne Roberts 0:6015834e4279 41
Wayne Roberts 0:6015834e4279 42 /* translate opmode_status_strs to opmode_select_strs */
Wayne Roberts 0:6015834e4279 43 switch (status.bits.chipMode) {
Wayne Roberts 0:6015834e4279 44 case 2: pc.printf("STBY_RC"); break;
Wayne Roberts 0:6015834e4279 45 case 3: pc.printf("STBY_XOSC"); break;
Wayne Roberts 0:6015834e4279 46 case 4: pc.printf("FS"); break;
Wayne Roberts 0:6015834e4279 47 case 5: pc.printf("RX"); break;
Wayne Roberts 0:6015834e4279 48 case 6: pc.printf("TX"); break;
Wayne Roberts 0:6015834e4279 49 default: pc.printf("<%d>", status.bits.chipMode); break;
Wayne Roberts 0:6015834e4279 50 }
Wayne Roberts 0:6015834e4279 51 }
Wayne Roberts 0:6015834e4279 52
Wayne Roberts 0:6015834e4279 53 void lora_printHeaderMode()
Wayne Roberts 0:6015834e4279 54 {
Wayne Roberts 0:6015834e4279 55 loraConfig1_t conf1;
Wayne Roberts 0:6015834e4279 56 conf1.octet = Radio::radio.readReg(REG_ADDR_LORA_CONFIG1, 1);
Wayne Roberts 0:6015834e4279 57 if (conf1.bits.implicit_header)
Wayne Roberts 0:6015834e4279 58 pc.printf("implicit ");
Wayne Roberts 0:6015834e4279 59 else
Wayne Roberts 0:6015834e4279 60 pc.printf("explicit ");
Wayne Roberts 0:6015834e4279 61 }
Wayne Roberts 0:6015834e4279 62
Wayne Roberts 0:6015834e4279 63 void radio_print_status()
Wayne Roberts 0:6015834e4279 64 {
Wayne Roberts 0:6015834e4279 65 tx_dbm_print();
Wayne Roberts 0:6015834e4279 66 pc.printf(" %.3fMHz ", Radio::radio.getMHz());
Wayne Roberts 0:6015834e4279 67 radio_printOpMode();
Wayne Roberts 0:6015834e4279 68 pc.printf("\r\n");
Wayne Roberts 0:6015834e4279 69 }
Wayne Roberts 0:6015834e4279 70
Wayne Roberts 0:6015834e4279 71 bool tx_dbm_write(int dbm)
Wayne Roberts 0:6015834e4279 72 {
Wayne Roberts 0:6015834e4279 73 unsigned v = Radio::radio.readReg(REG_ADDR_ANACTRL16, 1);
Wayne Roberts 0:6015834e4279 74
Wayne Roberts 0:6015834e4279 75 if (dbm == PA_OFF_DBM) {
Wayne Roberts 0:6015834e4279 76 /* bench test: prevent overloading receiving station (very low tx power) */
Wayne Roberts 0:6015834e4279 77 v |= 0x10; // pa dac atb tst
Wayne Roberts 0:6015834e4279 78 Radio::radio.writeReg(REG_ADDR_ANACTRL16, v, 1);
Wayne Roberts 0:6015834e4279 79 } else {
Wayne Roberts 0:6015834e4279 80 tx_param_buf[0] = dbm;
Wayne Roberts 0:6015834e4279 81 Radio::radio.xfer(OPCODE_SET_TX_PARAMS, 2, 0, tx_param_buf);
Wayne Roberts 0:6015834e4279 82
Wayne Roberts 0:6015834e4279 83 if (v & 0x10) {
Wayne Roberts 0:6015834e4279 84 v &= ~0x10;
Wayne Roberts 0:6015834e4279 85 Radio::radio.writeReg(REG_ADDR_ANACTRL16, v, 1);
Wayne Roberts 0:6015834e4279 86 }
Wayne Roberts 0:6015834e4279 87 }
Wayne Roberts 0:6015834e4279 88
Wayne Roberts 0:6015834e4279 89 return false;
Wayne Roberts 0:6015834e4279 90 }
Wayne Roberts 0:6015834e4279 91
Wayne Roberts 0:6015834e4279 92 void cmd_op(uint8_t argsAt)
Wayne Roberts 0:6015834e4279 93 {
Wayne Roberts 0:6015834e4279 94 int dbm;
Wayne Roberts 0:6015834e4279 95 if (sscanf(pcbuf+argsAt, "%d", &dbm) == 1) {
Wayne Roberts 0:6015834e4279 96 tx_dbm_write(dbm);
Wayne Roberts 0:6015834e4279 97 }
Wayne Roberts 0:6015834e4279 98
Wayne Roberts 0:6015834e4279 99 tx_dbm_print();
Wayne Roberts 0:6015834e4279 100 }
Wayne Roberts 0:6015834e4279 101
Wayne Roberts 0:6015834e4279 102 #endif /* ..SX126x_H */
Wayne Roberts 0:6015834e4279 103