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 "main.h"
Wayne Roberts 0:6015834e4279 2
Wayne Roberts 0:6015834e4279 3 #ifdef TARGET_STM32L0
Wayne Roberts 0:6015834e4279 4 #include "stm32l0xx_ll_utils.h"
Wayne Roberts 0:6015834e4279 5 #elif defined(TARGET_STM32L1)
Wayne Roberts 0:6015834e4279 6 #include "stm32l1xx_ll_utils.h"
Wayne Roberts 0:6015834e4279 7 #elif defined(TARGET_STM32L4)
Wayne Roberts 0:6015834e4279 8 #include "stm32l4xx_ll_utils.h"
Wayne Roberts 0:6015834e4279 9 #endif
Wayne Roberts 0:6015834e4279 10
Wayne Roberts 0:6015834e4279 11 #ifdef TARGET_DISCO_L072CZ_LRWAN1
Wayne Roberts 0:6015834e4279 12 DigitalOut rxdbg(PB_8);
Wayne Roberts 0:6015834e4279 13 #elif defined(TARGET_FF_ARDUINO)
Wayne Roberts 0:6015834e4279 14 DigitalOut rxdbg(PC_3);
Wayne Roberts 0:6015834e4279 15 #endif
Wayne Roberts 0:6015834e4279 16
Wayne Roberts 0:6015834e4279 17 RawSerial pc(USBTX, USBRX);
Wayne Roberts 0:6015834e4279 18
Wayne Roberts 0:6015834e4279 19 EventQueue queue(32 * EVENTS_EVENT_SIZE);
Wayne Roberts 0:6015834e4279 20
Wayne Roberts 0:6015834e4279 21 char pcbuf[64]; /* local user terminal */
Wayne Roberts 0:6015834e4279 22 int pcbuf_len;
Wayne Roberts 0:6015834e4279 23
Wayne Roberts 0:6015834e4279 24 unsigned discovery_ans_time_step_us;
Wayne Roberts 0:6015834e4279 25 unsigned discovery_ans_time_total_us;
Wayne Roberts 0:6015834e4279 26
Wayne Roberts 0:6015834e4279 27 uint32_t my_id;
Wayne Roberts 0:6015834e4279 28
Wayne Roberts 0:6015834e4279 29 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 30 const uint8_t hops_from_gateway = 0;
Wayne Roberts 0:6015834e4279 31 #else
Wayne Roberts 0:6015834e4279 32 uint8_t hops_from_gateway;
Wayne Roberts 0:6015834e4279 33 #endif
Wayne Roberts 0:6015834e4279 34
Wayne Roberts 0:6015834e4279 35 volatile flags_t flags;
Wayne Roberts 0:6015834e4279 36
Wayne Roberts 0:6015834e4279 37 static uint8_t attemptCnt;
Wayne Roberts 0:6015834e4279 38 static int req_timeout_id;
Wayne Roberts 0:6015834e4279 39 reqflags_t reqFlags;
Wayne Roberts 0:6015834e4279 40 volatile unsigned channelVacantCount;
Wayne Roberts 0:6015834e4279 41
Wayne Roberts 0:6015834e4279 42 uint8_t txBuf[255];
Wayne Roberts 0:6015834e4279 43 uint8_t txBuf_idx;
Wayne Roberts 0:6015834e4279 44 uint32_t tx_dest_id;
Wayne Roberts 0:6015834e4279 45
Wayne Roberts 0:6015834e4279 46 bool remove_directlyAttached_device(uint32_t id)
Wayne Roberts 0:6015834e4279 47 {
Wayne Roberts 0:6015834e4279 48 bool removed = false;
Wayne Roberts 0:6015834e4279 49 lid_list_t* L;
Wayne Roberts 0:6015834e4279 50 for (L = attachedDevices; L != NULL; L = L->next) {
Wayne Roberts 0:6015834e4279 51 if (L->id == id) {
Wayne Roberts 0:6015834e4279 52 /* remove/clear all child devices also */
Wayne Roberts 0:6015834e4279 53 cid_list_t* children;
Wayne Roberts 0:6015834e4279 54 for (children = L->attachedList; children != NULL; children = children->next)
Wayne Roberts 0:6015834e4279 55 children->id = ID_NONE;
Wayne Roberts 0:6015834e4279 56
Wayne Roberts 0:6015834e4279 57 removed = true;
Wayne Roberts 0:6015834e4279 58 L->id = ID_NONE;
Wayne Roberts 0:6015834e4279 59 }
Wayne Roberts 0:6015834e4279 60 }
Wayne Roberts 0:6015834e4279 61 return removed;
Wayne Roberts 0:6015834e4279 62 }
Wayne Roberts 0:6015834e4279 63
Wayne Roberts 0:6015834e4279 64 void remove_childDevice(uint32_t id, uint32_t* attachedTo)
Wayne Roberts 0:6015834e4279 65 {
Wayne Roberts 0:6015834e4279 66 lid_list_t* L;
Wayne Roberts 0:6015834e4279 67 *attachedTo = ID_NONE;
Wayne Roberts 0:6015834e4279 68 for (L = attachedDevices; L != NULL; L = L->next) {
Wayne Roberts 0:6015834e4279 69 cid_list_t* children;
Wayne Roberts 0:6015834e4279 70 for (children = L->attachedList; children != NULL; children = children->next) {
Wayne Roberts 0:6015834e4279 71 if (children->id == id) {
Wayne Roberts 0:6015834e4279 72 *attachedTo = L->id;
Wayne Roberts 0:6015834e4279 73 children->id = ID_NONE;
Wayne Roberts 0:6015834e4279 74 }
Wayne Roberts 0:6015834e4279 75 }
Wayne Roberts 0:6015834e4279 76 }
Wayne Roberts 0:6015834e4279 77 }
Wayne Roberts 0:6015834e4279 78
Wayne Roberts 0:6015834e4279 79 void setPreambleSize(bool wakesize, uint8_t by)
Wayne Roberts 0:6015834e4279 80 {
Wayne Roberts 0:6015834e4279 81 if (wakesize) {
Wayne Roberts 0:6015834e4279 82 Radio::LoRaPacketConfig(N_PRE_SYMB, false, true, false); // preambleLen, fixLen, crcOn, invIQ
Wayne Roberts 0:6015834e4279 83 if (flags.discoverAnswering) {
Wayne Roberts 0:6015834e4279 84 Mdbg_printf("\e[41mLPDA%02x\e[0m ", by);
Wayne Roberts 0:6015834e4279 85 }
Wayne Roberts 0:6015834e4279 86 } else {
Wayne Roberts 0:6015834e4279 87 Radio::LoRaPacketConfig(8, false, true, false); // preambleLen, fixLen, crcOn, invIQ
Wayne Roberts 0:6015834e4279 88 }
Wayne Roberts 0:6015834e4279 89 }
Wayne Roberts 0:6015834e4279 90
Wayne Roberts 0:6015834e4279 91 static void rxSingle()
Wayne Roberts 0:6015834e4279 92 {
Wayne Roberts 0:6015834e4279 93 if (flags.CallTXRequest) {
Wayne Roberts 0:6015834e4279 94 txBuf_send(true);
Wayne Roberts 0:6015834e4279 95 flags.CallTXRequest = 0;
Wayne Roberts 0:6015834e4279 96 } else if (reqFlags.octet == 0 || flags.deferred_send) {
Wayne Roberts 0:6015834e4279 97 flags.vacantCheck = 1;
Wayne Roberts 0:6015834e4279 98 /* rx single: auto-timeout */
Wayne Roberts 0:6015834e4279 99 Radio::Rx(999);
Wayne Roberts 0:6015834e4279 100 }
Wayne Roberts 0:6015834e4279 101 }
Wayne Roberts 0:6015834e4279 102
Wayne Roberts 0:6015834e4279 103 void start_periodic_rxing(uint8_t by) // definition
Wayne Roberts 0:6015834e4279 104 {
Wayne Roberts 0:6015834e4279 105 setPreambleSize(true, by | 3);
Wayne Roberts 0:6015834e4279 106 Radio::SetLoRaSymbolTimeout(8);
Wayne Roberts 0:6015834e4279 107 queue.call_in(WAKEUP_INTERVAL_MS, rxSingle);
Wayne Roberts 0:6015834e4279 108 }
Wayne Roberts 0:6015834e4279 109
Wayne Roberts 0:6015834e4279 110 const char* const cmdStrs[] = {
Wayne Roberts 0:6015834e4279 111 "unused", /* 0 CMD_UNUSED */
Wayne Roberts 0:6015834e4279 112 "Ans", /* 1 CMD_ANS, */
Wayne Roberts 0:6015834e4279 113 "discoverReq", /* 2 CMD_DISCOVERY_REQ, */
Wayne Roberts 0:6015834e4279 114 "discoverAns", /* 3 CMD_DISCOVERY_ANS, */
Wayne Roberts 0:6015834e4279 115 "attachReq", /* 4 CMD_ATTACH_REQ, */
Wayne Roberts 0:6015834e4279 116 "userPayReqUp", /* 5 CMD_USER_PAYLOAD_UP_REQ, */
Wayne Roberts 0:6015834e4279 117 "userPayReqDn", /* 6 CMD_USER_PAYLOAD_DN_REQ, */
Wayne Roberts 0:6015834e4279 118 "newDev", /* 7 CMD_NEW_DEVICE_ATTACHED_REQ, */
Wayne Roberts 0:6015834e4279 119 "removeDev", /* 8 CMD_REMOVE_DEVICE_REQ, */
Wayne Roberts 0:6015834e4279 120 "downstreamNotResponding", /* 9 CMD_DOWNSTREAM_NOT_RESPONDING, */
Wayne Roberts 0:6015834e4279 121 };
Wayne Roberts 0:6015834e4279 122
Wayne Roberts 0:6015834e4279 123
Wayne Roberts 0:6015834e4279 124 uint8_t tx_len;
Wayne Roberts 0:6015834e4279 125
Wayne Roberts 0:6015834e4279 126 static void _send_(void)
Wayne Roberts 0:6015834e4279 127 {
Wayne Roberts 0:6015834e4279 128 if (flags.sending_req)
Wayne Roberts 0:6015834e4279 129 setPreambleSize(true, 2); // sending request
Wayne Roberts 0:6015834e4279 130
Wayne Roberts 0:6015834e4279 131 Radio::Send(tx_len, 0, 0, 0); /* begin transmission */
Wayne Roberts 0:6015834e4279 132 if (flags.discoverAnswering) {
Wayne Roberts 0:6015834e4279 133 Mdbg_printf("\e[36m%u->txing%u_to:%lx_%s\e[0m", txBuf_idx, tx_len, tx_dest_id, cmdStrs[Radio::radio.tx_buf[9]]);
Wayne Roberts 0:6015834e4279 134 } else {
Wayne Roberts 0:6015834e4279 135 Mdbg_printf("%u->\e[31mtxing\e[0m%u_to:%lx_\e[7m%s\e[0m", txBuf_idx, tx_len, tx_dest_id, cmdStrs[Radio::radio.tx_buf[9]]);
Wayne Roberts 0:6015834e4279 136 }
Wayne Roberts 0:6015834e4279 137 Mdbg_printf(":");
Wayne Roberts 0:6015834e4279 138 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 139 /*{
Wayne Roberts 0:6015834e4279 140 unsigned n;
Wayne Roberts 0:6015834e4279 141 for (n = 0; n < tx_len; n++)
Wayne Roberts 0:6015834e4279 142 pc.printf("%02x ", Radio::radio.tx_buf[n]);
Wayne Roberts 0:6015834e4279 143 }*/
Wayne Roberts 0:6015834e4279 144 #endif /* MESH_DEBUG */
Wayne Roberts 0:6015834e4279 145
Wayne Roberts 0:6015834e4279 146 if (flags.sending_req) {
Wayne Roberts 0:6015834e4279 147 attemptCnt++;
Wayne Roberts 0:6015834e4279 148 flags.getAns = 1;
Wayne Roberts 0:6015834e4279 149 } else
Wayne Roberts 0:6015834e4279 150 flags.getAns = 0;
Wayne Roberts 0:6015834e4279 151
Wayne Roberts 0:6015834e4279 152 channelVacantCount = 0;
Wayne Roberts 0:6015834e4279 153 } // .._send_()
Wayne Roberts 0:6015834e4279 154
Wayne Roberts 0:6015834e4279 155 void txBuf_send(bool sendingReq)
Wayne Roberts 0:6015834e4279 156 {
Wayne Roberts 0:6015834e4279 157 uint16_t crc;
Wayne Roberts 0:6015834e4279 158
Wayne Roberts 0:6015834e4279 159 if (txBuf_idx == 0) {
Wayne Roberts 0:6015834e4279 160 return;
Wayne Roberts 0:6015834e4279 161 }
Wayne Roberts 0:6015834e4279 162
Wayne Roberts 0:6015834e4279 163 Radio::Standby();
Wayne Roberts 0:6015834e4279 164
Wayne Roberts 0:6015834e4279 165 if (sendingReq) {
Wayne Roberts 0:6015834e4279 166 if (attemptCnt > RETRY_LIMIT) {
Wayne Roberts 0:6015834e4279 167 /* give up trying */
Wayne Roberts 0:6015834e4279 168 txBuf_idx = 0;
Wayne Roberts 0:6015834e4279 169 attemptCnt = 0;
Wayne Roberts 0:6015834e4279 170 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 171 reqFlags.octet = 0; // TODO dropping request
Wayne Roberts 0:6015834e4279 172 start_periodic_rxing(0x80); // retry give-up
Wayne Roberts 0:6015834e4279 173 return;
Wayne Roberts 0:6015834e4279 174 #else
Wayne Roberts 0:6015834e4279 175 if (tx_dest_id == attUp.id) {
Wayne Roberts 0:6015834e4279 176 /* find new upstream device */
Wayne Roberts 0:6015834e4279 177 queue.call_in(1000, upstream_init);
Wayne Roberts 0:6015834e4279 178 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ)
Wayne Roberts 0:6015834e4279 179 app_uplink_complete(); // TODO report failure to application layer
Wayne Roberts 0:6015834e4279 180 return;
Wayne Roberts 0:6015834e4279 181 } else {
Wayne Roberts 0:6015834e4279 182 txBuf[txBuf_idx++] = CMD_DOWNSTREAM_NOT_RESPONDING;
Wayne Roberts 0:6015834e4279 183 putu32ToBuf(&txBuf[txBuf_idx], my_id); // ID of reporting device
Wayne Roberts 0:6015834e4279 184 txBuf_idx += 4;
Wayne Roberts 0:6015834e4279 185 putu32ToBuf(&txBuf[txBuf_idx], tx_dest_id); // ID of failed downstream device
Wayne Roberts 0:6015834e4279 186 txBuf_idx += 4;
Wayne Roberts 0:6015834e4279 187 tx_dest_id = attUp.id;
Wayne Roberts 0:6015834e4279 188 reqFlags.bits.currentOp = CMD_DOWNSTREAM_NOT_RESPONDING;
Wayne Roberts 0:6015834e4279 189 }
Wayne Roberts 0:6015834e4279 190 #endif /* !GATEWAY */
Wayne Roberts 0:6015834e4279 191 }
Wayne Roberts 0:6015834e4279 192 } // ..if (sendingReq)
Wayne Roberts 0:6015834e4279 193
Wayne Roberts 0:6015834e4279 194 Radio::radio.tx_buf[0] = hops_from_gateway;
Wayne Roberts 0:6015834e4279 195 putu32ToBuf(&Radio::radio.tx_buf[1], my_id);
Wayne Roberts 0:6015834e4279 196 putu32ToBuf(&Radio::radio.tx_buf[5], tx_dest_id);
Wayne Roberts 0:6015834e4279 197
Wayne Roberts 0:6015834e4279 198 tx_len = txBuf_idx;
Wayne Roberts 0:6015834e4279 199 memcpy(&Radio::radio.tx_buf[9], txBuf, tx_len);
Wayne Roberts 0:6015834e4279 200 tx_len += 9;
Wayne Roberts 0:6015834e4279 201 crc = crc16(Radio::radio.tx_buf, tx_len);
Wayne Roberts 0:6015834e4279 202 putu16ToBuf(&Radio::radio.tx_buf[tx_len], crc);
Wayne Roberts 0:6015834e4279 203 tx_len += 2;
Wayne Roberts 0:6015834e4279 204
Wayne Roberts 0:6015834e4279 205 flags.sending_req = sendingReq;
Wayne Roberts 0:6015834e4279 206 if (sendingReq) {
Wayne Roberts 0:6015834e4279 207 if (channelVacantCount > CHANNEL_VACANT_REQUIRED_COUNT)
Wayne Roberts 0:6015834e4279 208 _send_();
Wayne Roberts 0:6015834e4279 209 else {
Wayne Roberts 0:6015834e4279 210 flags.deferred_send = 1;
Wayne Roberts 0:6015834e4279 211 start_periodic_rxing(0x70); // deferred send
Wayne Roberts 0:6015834e4279 212 }
Wayne Roberts 0:6015834e4279 213 } else
Wayne Roberts 0:6015834e4279 214 _send_();
Wayne Roberts 0:6015834e4279 215
Wayne Roberts 0:6015834e4279 216 } // ..txBuf_send()
Wayne Roberts 0:6015834e4279 217
Wayne Roberts 0:6015834e4279 218 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 219 bool rx_log_disable;
Wayne Roberts 0:6015834e4279 220 char rx_log[768];
Wayne Roberts 0:6015834e4279 221 volatile unsigned rx_log_buf_idx;
Wayne Roberts 0:6015834e4279 222 int _rx_log_printf(const char *format, ...)
Wayne Roberts 0:6015834e4279 223 {
Wayne Roberts 0:6015834e4279 224 va_list aptr;
Wayne Roberts 0:6015834e4279 225 int ret = -1;
Wayne Roberts 0:6015834e4279 226
Wayne Roberts 0:6015834e4279 227 va_start(aptr, format);
Wayne Roberts 0:6015834e4279 228 if (!rx_log_disable) {
Wayne Roberts 0:6015834e4279 229 ret = vsprintf(rx_log + rx_log_buf_idx , format, aptr);
Wayne Roberts 0:6015834e4279 230 rx_log_buf_idx += ret;
Wayne Roberts 0:6015834e4279 231 if (rx_log_buf_idx >= sizeof(rx_log)-1) {
Wayne Roberts 0:6015834e4279 232 pc.printf("\e[31mrx_log_overrun\e[0m ");
Wayne Roberts 0:6015834e4279 233 rx_log_disable = true;
Wayne Roberts 0:6015834e4279 234 }
Wayne Roberts 0:6015834e4279 235 }
Wayne Roberts 0:6015834e4279 236 va_end(aptr);
Wayne Roberts 0:6015834e4279 237
Wayne Roberts 0:6015834e4279 238
Wayne Roberts 0:6015834e4279 239 return ret;
Wayne Roberts 0:6015834e4279 240 }
Wayne Roberts 0:6015834e4279 241
Wayne Roberts 0:6015834e4279 242 void rx_log_print()
Wayne Roberts 0:6015834e4279 243 {
Wayne Roberts 0:6015834e4279 244 rx_log[rx_log_buf_idx+1] = 0;
Wayne Roberts 0:6015834e4279 245 pc.printf(rx_log);
Wayne Roberts 0:6015834e4279 246 rx_log_buf_idx = 0;
Wayne Roberts 0:6015834e4279 247 }
Wayne Roberts 0:6015834e4279 248 #endif /* MESH_DEBUG */
Wayne Roberts 0:6015834e4279 249
Wayne Roberts 0:6015834e4279 250 static void txAns(unsigned sending_id)
Wayne Roberts 0:6015834e4279 251 {
Wayne Roberts 0:6015834e4279 252 unsigned elapsedAlready;
Wayne Roberts 0:6015834e4279 253 int pad_us;
Wayne Roberts 0:6015834e4279 254 txBuf_idx = 0; // previously sent request no longer needed
Wayne Roberts 0:6015834e4279 255 txBuf[txBuf_idx++] = CMD_ANS;
Wayne Roberts 0:6015834e4279 256 txBuf[txBuf_idx++] = reqFlags.bits.txAns;
Wayne Roberts 0:6015834e4279 257 setPreambleSize(false, 6); // sending answer
Wayne Roberts 0:6015834e4279 258 tx_dest_id = sending_id;
Wayne Roberts 0:6015834e4279 259 elapsedAlready = Radio::lpt.read_us() - Radio::irqAt;
Wayne Roberts 0:6015834e4279 260 pad_us = (ANS_PAD_MS * 1000) - elapsedAlready;
Wayne Roberts 0:6015834e4279 261 if (pad_us > 100) {
Wayne Roberts 0:6015834e4279 262 wait_us(pad_us); // short wait time more accurate in busy-loop
Wayne Roberts 0:6015834e4279 263 }
Wayne Roberts 0:6015834e4279 264 txBuf_send(false);
Wayne Roberts 0:6015834e4279 265 if (pad_us <= 0) {
Wayne Roberts 0:6015834e4279 266 pc.printf("\e[41mLATE:%d ", pad_us);
Wayne Roberts 0:6015834e4279 267 pc.printf("\e[0m ");
Wayne Roberts 0:6015834e4279 268 }
Wayne Roberts 0:6015834e4279 269
Wayne Roberts 0:6015834e4279 270 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 271 // printing of rx_log was deferred until this answer sent
Wayne Roberts 0:6015834e4279 272 rx_log_print();
Wayne Roberts 0:6015834e4279 273 #endif /* MESH_DEBUG */
Wayne Roberts 0:6015834e4279 274 }
Wayne Roberts 0:6015834e4279 275
Wayne Roberts 0:6015834e4279 276 uint16_t getu16FromBuf(const uint8_t* in)
Wayne Roberts 0:6015834e4279 277 {
Wayne Roberts 0:6015834e4279 278 uint16_t ret;
Wayne Roberts 0:6015834e4279 279
Wayne Roberts 0:6015834e4279 280 ret = in[1];
Wayne Roberts 0:6015834e4279 281 ret <<= 8;
Wayne Roberts 0:6015834e4279 282 ret |= in[0];
Wayne Roberts 0:6015834e4279 283
Wayne Roberts 0:6015834e4279 284 return ret;
Wayne Roberts 0:6015834e4279 285 }
Wayne Roberts 0:6015834e4279 286
Wayne Roberts 0:6015834e4279 287 void putu16ToBuf(uint8_t* out, uint16_t v)
Wayne Roberts 0:6015834e4279 288 {
Wayne Roberts 0:6015834e4279 289 *out++ = v & 0xff;
Wayne Roberts 0:6015834e4279 290 v >>= 8;
Wayne Roberts 0:6015834e4279 291 *out = v & 0xff;
Wayne Roberts 0:6015834e4279 292 }
Wayne Roberts 0:6015834e4279 293
Wayne Roberts 0:6015834e4279 294 void putu32ToBuf(uint8_t* out, uint32_t v)
Wayne Roberts 0:6015834e4279 295 {
Wayne Roberts 0:6015834e4279 296 /* most significant last */
Wayne Roberts 0:6015834e4279 297 /* least significant first */
Wayne Roberts 0:6015834e4279 298 *out++ = v & 0xff;
Wayne Roberts 0:6015834e4279 299 v >>= 8;
Wayne Roberts 0:6015834e4279 300 *out++ = v & 0xff;
Wayne Roberts 0:6015834e4279 301 v >>= 8;
Wayne Roberts 0:6015834e4279 302 *out++ = v & 0xff;
Wayne Roberts 0:6015834e4279 303 v >>= 8;
Wayne Roberts 0:6015834e4279 304 *out = v & 0xff;
Wayne Roberts 0:6015834e4279 305 }
Wayne Roberts 0:6015834e4279 306
Wayne Roberts 0:6015834e4279 307 uint32_t getu32FromBuf(const uint8_t* in)
Wayne Roberts 0:6015834e4279 308 {
Wayne Roberts 0:6015834e4279 309 uint32_t ret;
Wayne Roberts 0:6015834e4279 310
Wayne Roberts 0:6015834e4279 311 ret = in[3];
Wayne Roberts 0:6015834e4279 312 ret <<= 8;
Wayne Roberts 0:6015834e4279 313 ret |= in[2];
Wayne Roberts 0:6015834e4279 314 ret <<= 8;
Wayne Roberts 0:6015834e4279 315 ret |= in[1];
Wayne Roberts 0:6015834e4279 316 ret <<= 8;
Wayne Roberts 0:6015834e4279 317 ret |= in[0];
Wayne Roberts 0:6015834e4279 318
Wayne Roberts 0:6015834e4279 319 return ret;
Wayne Roberts 0:6015834e4279 320 }
Wayne Roberts 0:6015834e4279 321
Wayne Roberts 0:6015834e4279 322
Wayne Roberts 0:6015834e4279 323
Wayne Roberts 0:6015834e4279 324 struct _fwd_ fwd;
Wayne Roberts 0:6015834e4279 325 struct _nr_ notResponding;
Wayne Roberts 0:6015834e4279 326
Wayne Roberts 0:6015834e4279 327 void txDoneCB()
Wayne Roberts 0:6015834e4279 328 {
Wayne Roberts 0:6015834e4279 329 if (flags.getAns) {
Wayne Roberts 0:6015834e4279 330 unsigned toms;
Wayne Roberts 0:6015834e4279 331 setPreambleSize(false, 5); // getting answer
Wayne Roberts 0:6015834e4279 332 Radio::Rx(0);
Wayne Roberts 0:6015834e4279 333 #ifndef GATEWAY
Wayne Roberts 0:6015834e4279 334 if (reqFlags.bits.currentOp == CMD_DISCOVERY_REQ) {
Wayne Roberts 0:6015834e4279 335 /* discovering: listen for answers from any upstream devices */
Wayne Roberts 0:6015834e4279 336 toms = discovery_ans_time_total_us / 1000;
Wayne Roberts 0:6015834e4279 337 queue.call_in(toms, discovery_rx_end);
Wayne Roberts 0:6015834e4279 338 } else
Wayne Roberts 0:6015834e4279 339 #endif /* !GATEWAY */
Wayne Roberts 0:6015834e4279 340 {
Wayne Roberts 0:6015834e4279 341 unsigned target_us;
Wayne Roberts 0:6015834e4279 342 target_us = Radio::lora_toa_us(ANS_SIZE_BYTE) * 4; // four packet length's worth
Wayne Roberts 0:6015834e4279 343 toms = (ANS_PAD_MS + ANS_PAD_MS) + (target_us / 1000); // microseconds to milliseconds
Wayne Roberts 0:6015834e4279 344 req_timeout_id = queue.call_in(toms, txBuf_send, true);
Wayne Roberts 0:6015834e4279 345 }
Wayne Roberts 0:6015834e4279 346 } else if (reqFlags.bits.txAns != NOT_ANSWERING) { // txDone callback answer-tx-complete
Wayne Roberts 0:6015834e4279 347 /* we just sent answer: restore to idle condition waiting for wakeup packet */
Wayne Roberts 0:6015834e4279 348 reqFlags.bits.txAns = NOT_ANSWERING;
Wayne Roberts 0:6015834e4279 349 txBuf_idx = 0;
Wayne Roberts 0:6015834e4279 350
Wayne Roberts 0:6015834e4279 351 #ifndef GATEWAY
Wayne Roberts 0:6015834e4279 352 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ || reqFlags.bits.currentOp == CMD_USER_PAYLOAD_DN_REQ) {
Wayne Roberts 0:6015834e4279 353 int n;
Wayne Roberts 0:6015834e4279 354 if (fwd.len >= 0) {
Wayne Roberts 0:6015834e4279 355 tx_dest_id = fwd.tx_dest_id;
Wayne Roberts 0:6015834e4279 356 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ) {
Wayne Roberts 0:6015834e4279 357 /* forward upstream */
Wayne Roberts 0:6015834e4279 358 txBuf[txBuf_idx++] = CMD_USER_PAYLOAD_UP_REQ;
Wayne Roberts 0:6015834e4279 359 putu32ToBuf(&txBuf[txBuf_idx], fwd.B_id); // originating_src_id
Wayne Roberts 0:6015834e4279 360 txBuf_idx += 4;
Wayne Roberts 0:6015834e4279 361 } else if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_DN_REQ) {
Wayne Roberts 0:6015834e4279 362 /* forward downstream */
Wayne Roberts 0:6015834e4279 363 txBuf[txBuf_idx++] = CMD_USER_PAYLOAD_DN_REQ;
Wayne Roberts 0:6015834e4279 364 putu32ToBuf(&txBuf[txBuf_idx], fwd.A_id); // final_dest_id
Wayne Roberts 0:6015834e4279 365 txBuf_idx += 4;
Wayne Roberts 0:6015834e4279 366 }
Wayne Roberts 0:6015834e4279 367 txBuf[txBuf_idx++] = fwd.len;
Wayne Roberts 0:6015834e4279 368 for (n = 0; n < fwd.len; n++)
Wayne Roberts 0:6015834e4279 369 txBuf[txBuf_idx++] = fwd.buf[n];
Wayne Roberts 0:6015834e4279 370
Wayne Roberts 0:6015834e4279 371 queue.call_in(500, txBuf_send, true);
Wayne Roberts 0:6015834e4279 372 fwd.len = -1;
Wayne Roberts 0:6015834e4279 373 } // ..if (fwd.len >= 0)
Wayne Roberts 0:6015834e4279 374 else {
Wayne Roberts 0:6015834e4279 375 /* uplink/downlink not forwarding */
Wayne Roberts 0:6015834e4279 376 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ)
Wayne Roberts 0:6015834e4279 377 app_uplink_complete();
Wayne Roberts 0:6015834e4279 378 reqFlags.bits.currentOp = CMD_UNUSED;
Wayne Roberts 0:6015834e4279 379 }
Wayne Roberts 0:6015834e4279 380 }
Wayne Roberts 0:6015834e4279 381
Wayne Roberts 0:6015834e4279 382 if (attUp.id == ID_NONE) {
Wayne Roberts 0:6015834e4279 383 /* disconnected from upstream, rediscover */
Wayne Roberts 0:6015834e4279 384 queue.call_in(1000, upstream_init);
Wayne Roberts 0:6015834e4279 385 } else if (id_newDeviceNotification != ID_NONE) {
Wayne Roberts 0:6015834e4279 386 upstream_new_device_notify();
Wayne Roberts 0:6015834e4279 387 } else if (reqFlags.bits.currentOp == CMD_DOWNSTREAM_NOT_RESPONDING) {
Wayne Roberts 0:6015834e4279 388 tx_dest_id = attUp.id;
Wayne Roberts 0:6015834e4279 389 txBuf[txBuf_idx++] = CMD_DOWNSTREAM_NOT_RESPONDING;
Wayne Roberts 0:6015834e4279 390 putu32ToBuf(&txBuf[txBuf_idx], notResponding.reporting_id);
Wayne Roberts 0:6015834e4279 391 txBuf_idx += 4;
Wayne Roberts 0:6015834e4279 392 putu32ToBuf(&txBuf[txBuf_idx], notResponding.device_not_respoding_id);
Wayne Roberts 0:6015834e4279 393 txBuf_idx += 4;
Wayne Roberts 0:6015834e4279 394 queue.call_in(500, txBuf_send, true);
Wayne Roberts 0:6015834e4279 395 } else
Wayne Roberts 0:6015834e4279 396 #endif /* !GATEWAY */
Wayne Roberts 0:6015834e4279 397 if (downRemove.destID != ID_NONE) {
Wayne Roberts 0:6015834e4279 398 request_remove_device();
Wayne Roberts 0:6015834e4279 399 }
Wayne Roberts 0:6015834e4279 400 start_periodic_rxing(0x60); // reqFlags.bits.txAns != NOT_ANSWERING
Wayne Roberts 0:6015834e4279 401 } else if (reqFlags.bits.currentOp == CMD_DISCOVERY_ANS) {
Wayne Roberts 0:6015834e4279 402 if (flags.firstDiscoverAns) {
Wayne Roberts 0:6015834e4279 403 unsigned rnd = Radio::Random() % N_HALF_DISCOVERY_ANS;
Wayne Roberts 0:6015834e4279 404 queue.call_in((discovery_ans_time_step_us * rnd) / 1000, txBuf_send, false);
Wayne Roberts 0:6015834e4279 405 flags.firstDiscoverAns = 0;
Wayne Roberts 0:6015834e4279 406 } else {
Wayne Roberts 0:6015834e4279 407 reqFlags.bits.currentOp = CMD_UNUSED;
Wayne Roberts 0:6015834e4279 408 txBuf_idx = 0;
Wayne Roberts 0:6015834e4279 409 //start_periodic_rxing(0x50); // 2nd discoverAns
Wayne Roberts 0:6015834e4279 410 }
Wayne Roberts 0:6015834e4279 411 } else {
Wayne Roberts 0:6015834e4279 412 /* ? wtf did we just transmit ? */
Wayne Roberts 0:6015834e4279 413 pc.printf("\e[31mnoTxAns_or_STATE_GET_ANS\e[0m ");
Wayne Roberts 0:6015834e4279 414 }
Wayne Roberts 0:6015834e4279 415 } // ..txDoneCB()
Wayne Roberts 0:6015834e4279 416
Wayne Roberts 0:6015834e4279 417
Wayne Roberts 0:6015834e4279 418 uint16_t crc16( uint8_t *buffer, uint16_t length )
Wayne Roberts 0:6015834e4279 419 {
Wayne Roberts 0:6015834e4279 420 uint16_t i;
Wayne Roberts 0:6015834e4279 421 // The CRC calculation follows CCITT
Wayne Roberts 0:6015834e4279 422 const uint16_t polynom = 0x1021;
Wayne Roberts 0:6015834e4279 423 // CRC initial value
Wayne Roberts 0:6015834e4279 424 uint16_t crc = 0x0000;
Wayne Roberts 0:6015834e4279 425
Wayne Roberts 0:6015834e4279 426 if( buffer == NULL )
Wayne Roberts 0:6015834e4279 427 {
Wayne Roberts 0:6015834e4279 428 return 0;
Wayne Roberts 0:6015834e4279 429 }
Wayne Roberts 0:6015834e4279 430
Wayne Roberts 0:6015834e4279 431 for( i = 0; i < length; ++i )
Wayne Roberts 0:6015834e4279 432 {
Wayne Roberts 0:6015834e4279 433 uint16_t j;
Wayne Roberts 0:6015834e4279 434 crc ^= ( uint16_t ) buffer[i] << 8;
Wayne Roberts 0:6015834e4279 435 for( j = 0; j < 8; ++j )
Wayne Roberts 0:6015834e4279 436 {
Wayne Roberts 0:6015834e4279 437 crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
Wayne Roberts 0:6015834e4279 438 }
Wayne Roberts 0:6015834e4279 439 }
Wayne Roberts 0:6015834e4279 440
Wayne Roberts 0:6015834e4279 441 return crc;
Wayne Roberts 0:6015834e4279 442 }
Wayne Roberts 0:6015834e4279 443
Wayne Roberts 0:6015834e4279 444
Wayne Roberts 0:6015834e4279 445 void rxDoneCB(uint8_t size, float rssi, float snr)
Wayne Roberts 0:6015834e4279 446 {
Wayne Roberts 0:6015834e4279 447 uint8_t rx_buf_idx;
Wayne Roberts 0:6015834e4279 448 uint16_t calc, rxCrc;
Wayne Roberts 0:6015834e4279 449 uint8_t rx_hfg = Radio::radio.rx_buf[0];
Wayne Roberts 0:6015834e4279 450 uint32_t sending_id = getu32FromBuf(&Radio::radio.rx_buf[1]);
Wayne Roberts 0:6015834e4279 451 uint32_t dest_id = getu32FromBuf(&Radio::radio.rx_buf[5]);
Wayne Roberts 0:6015834e4279 452 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 453 upInfo_t up_info;
Wayne Roberts 0:6015834e4279 454 up_info.originating_src_id = ID_NONE;
Wayne Roberts 0:6015834e4279 455 #endif /* GATEWAY */
Wayne Roberts 0:6015834e4279 456
Wayne Roberts 0:6015834e4279 457 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 458 bool print_log_here = true;
Wayne Roberts 0:6015834e4279 459 rx_log_buf_idx = 0;
Wayne Roberts 0:6015834e4279 460 rx_log[0] = 0;
Wayne Roberts 0:6015834e4279 461 rx_log_disable = false;
Wayne Roberts 0:6015834e4279 462 #endif /* MESH_DEBUG */
Wayne Roberts 0:6015834e4279 463
Wayne Roberts 0:6015834e4279 464 if (flags.vacantCheck) {
Wayne Roberts 0:6015834e4279 465 channelVacantCount = 0;
Wayne Roberts 0:6015834e4279 466 flags.vacantCheck = 0;
Wayne Roberts 0:6015834e4279 467 }
Wayne Roberts 0:6015834e4279 468
Wayne Roberts 0:6015834e4279 469 Rx_log_printf("\e[32mrxDone %ubytes %.1fdBm %.1fdB\e[0m ", size, rssi, snr);
Wayne Roberts 0:6015834e4279 470 Rx_log_printf("from:%lx_to_%lx ", sending_id, dest_id);
Wayne Roberts 0:6015834e4279 471
Wayne Roberts 0:6015834e4279 472 if (dest_id != my_id && dest_id != ANY_ID) {
Wayne Roberts 0:6015834e4279 473 #ifndef GATEWAY
Wayne Roberts 0:6015834e4279 474 /* check if upstream device were attached to is re-attaching */
Wayne Roberts 0:6015834e4279 475 upstream_attached_check(sending_id);
Wayne Roberts 0:6015834e4279 476 #endif /* !GATEWAY */
Wayne Roberts 0:6015834e4279 477 if (!flags.discoverAnswering)
Wayne Roberts 0:6015834e4279 478 start_periodic_rxing(0x40); // rxDone notForMe
Wayne Roberts 0:6015834e4279 479 goto done;
Wayne Roberts 0:6015834e4279 480 }
Wayne Roberts 0:6015834e4279 481
Wayne Roberts 0:6015834e4279 482 calc = crc16(Radio::radio.rx_buf, size-2);
Wayne Roberts 0:6015834e4279 483 rxCrc = getu16FromBuf(&Radio::radio.rx_buf[size-2]);
Wayne Roberts 0:6015834e4279 484 if (calc != rxCrc) {
Wayne Roberts 0:6015834e4279 485 Rx_log_printf("%04x != %04x\r\n", calc, rxCrc);
Wayne Roberts 0:6015834e4279 486 if (!flags.discoverAnswering)
Wayne Roberts 0:6015834e4279 487 start_periodic_rxing(0x30); // rxDone crcfail
Wayne Roberts 0:6015834e4279 488 goto done;
Wayne Roberts 0:6015834e4279 489 }
Wayne Roberts 0:6015834e4279 490
Wayne Roberts 0:6015834e4279 491 size -= 2; // take off trailing crc
Wayne Roberts 0:6015834e4279 492 for (rx_buf_idx = 9; rx_buf_idx < size; ) {
Wayne Roberts 0:6015834e4279 493 cmd_e cmd = (cmd_e)Radio::radio.rx_buf[rx_buf_idx++];
Wayne Roberts 0:6015834e4279 494
Wayne Roberts 0:6015834e4279 495 Rx_log_printf(" curOp:%u_", reqFlags.bits.currentOp);
Wayne Roberts 0:6015834e4279 496 Rx_log_printf(" \e[7mRxCmd:%s\e[0m", cmdStrs[cmd]);
Wayne Roberts 0:6015834e4279 497 Rx_log_printf(" ");
Wayne Roberts 0:6015834e4279 498
Wayne Roberts 0:6015834e4279 499 switch (cmd) {
Wayne Roberts 0:6015834e4279 500 ans_e ans;
Wayne Roberts 0:6015834e4279 501 case CMD_ANS:
Wayne Roberts 0:6015834e4279 502 ans = (ans_e)Radio::radio.rx_buf[rx_buf_idx++];
Wayne Roberts 0:6015834e4279 503 /* request as been answered successfully */
Wayne Roberts 0:6015834e4279 504 Rx_log_printf("\e[35mrxAns\e[0m ");
Wayne Roberts 0:6015834e4279 505 if (flags.getAns) {
Wayne Roberts 0:6015834e4279 506 if (ans == ANSWER_OK) {
Wayne Roberts 0:6015834e4279 507 txBuf_idx = 0;
Wayne Roberts 0:6015834e4279 508 queue.cancel(req_timeout_id);
Wayne Roberts 0:6015834e4279 509 attemptCnt = 0;
Wayne Roberts 0:6015834e4279 510 downstream_ans_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
Wayne Roberts 0:6015834e4279 511 #ifndef GATEWAY
Wayne Roberts 0:6015834e4279 512 upstream_ans_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
Wayne Roberts 0:6015834e4279 513 #endif /* !GATEWAY */
Wayne Roberts 0:6015834e4279 514
Wayne Roberts 0:6015834e4279 515 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ) {
Wayne Roberts 0:6015834e4279 516 fwd.len = -1;
Wayne Roberts 0:6015834e4279 517 reqFlags.bits.currentOp = CMD_UNUSED;
Wayne Roberts 0:6015834e4279 518 }
Wayne Roberts 0:6015834e4279 519
Wayne Roberts 0:6015834e4279 520 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_DN_REQ) {
Wayne Roberts 0:6015834e4279 521 fwd.len = -1;
Wayne Roberts 0:6015834e4279 522 reqFlags.bits.currentOp = CMD_UNUSED;
Wayne Roberts 0:6015834e4279 523 }
Wayne Roberts 0:6015834e4279 524 }
Wayne Roberts 0:6015834e4279 525 }
Wayne Roberts 0:6015834e4279 526
Wayne Roberts 0:6015834e4279 527 break;
Wayne Roberts 0:6015834e4279 528 case CMD_UNUSED:
Wayne Roberts 0:6015834e4279 529 break;
Wayne Roberts 0:6015834e4279 530 case CMD_DISCOVERY_REQ:
Wayne Roberts 0:6015834e4279 531 case CMD_ATTACH_REQ:
Wayne Roberts 0:6015834e4279 532 case CMD_NEW_DEVICE_ATTACHED_REQ:
Wayne Roberts 0:6015834e4279 533 case CMD_USER_PAYLOAD_UP_REQ:
Wayne Roberts 0:6015834e4279 534 case CMD_DOWNSTREAM_NOT_RESPONDING:
Wayne Roberts 0:6015834e4279 535 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 536 downstream_req_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd, &up_info);
Wayne Roberts 0:6015834e4279 537 #else
Wayne Roberts 0:6015834e4279 538 downstream_req_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
Wayne Roberts 0:6015834e4279 539 #endif
Wayne Roberts 0:6015834e4279 540 break;
Wayne Roberts 0:6015834e4279 541 case CMD_REMOVE_DEVICE_REQ:
Wayne Roberts 0:6015834e4279 542 case CMD_DISCOVERY_ANS:
Wayne Roberts 0:6015834e4279 543 case CMD_USER_PAYLOAD_DN_REQ:
Wayne Roberts 0:6015834e4279 544 upstream_req_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
Wayne Roberts 0:6015834e4279 545 break;
Wayne Roberts 0:6015834e4279 546 } // ..switch (cmd)
Wayne Roberts 0:6015834e4279 547
Wayne Roberts 0:6015834e4279 548 } // ..for (rx_buf_idx = 9; rx_buf_idx < size; )
Wayne Roberts 0:6015834e4279 549
Wayne Roberts 0:6015834e4279 550 if (reqFlags.bits.currentOp != CMD_DISCOVERY_ANS && reqFlags.bits.currentOp != CMD_DISCOVERY_REQ) {
Wayne Roberts 0:6015834e4279 551 if (reqFlags.bits.txAns != NOT_ANSWERING) {
Wayne Roberts 0:6015834e4279 552 queue.call(txAns, sending_id); // must return from this function prior to transmitting
Wayne Roberts 0:6015834e4279 553 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 554 print_log_here = false;
Wayne Roberts 0:6015834e4279 555 #endif /* MESH_DEBUG */
Wayne Roberts 0:6015834e4279 556 } else {
Wayne Roberts 0:6015834e4279 557 Radio::Sleep();
Wayne Roberts 0:6015834e4279 558 start_periodic_rxing(0x20); // rxDone
Wayne Roberts 0:6015834e4279 559 }
Wayne Roberts 0:6015834e4279 560 }
Wayne Roberts 0:6015834e4279 561
Wayne Roberts 0:6015834e4279 562 done:
Wayne Roberts 0:6015834e4279 563 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 564 if (rx_hfg == 0) {
Wayne Roberts 0:6015834e4279 565 Rx_log_printf("\e[31mrx_hfg:%u\e[0m ", rx_hfg); /* another gateway */
Wayne Roberts 0:6015834e4279 566 } else {
Wayne Roberts 0:6015834e4279 567 Rx_log_printf("rx_hfg:%u ", rx_hfg);
Wayne Roberts 0:6015834e4279 568 }
Wayne Roberts 0:6015834e4279 569 #else
Wayne Roberts 0:6015834e4279 570 Rx_log_printf("rx_hfg:%u ", rx_hfg);
Wayne Roberts 0:6015834e4279 571 /* compare against attached upstream */
Wayne Roberts 0:6015834e4279 572 if (hops_from_gateway != HFG_UNATTACHED)
Wayne Roberts 0:6015834e4279 573 upstream_signal_check(rssi, snr, rx_hfg, sending_id);
Wayne Roberts 0:6015834e4279 574 #endif
Wayne Roberts 0:6015834e4279 575
Wayne Roberts 0:6015834e4279 576 #ifdef MESH_DEBUG
Wayne Roberts 0:6015834e4279 577 if (print_log_here)
Wayne Roberts 0:6015834e4279 578 queue.call_in(10, rx_log_print);
Wayne Roberts 0:6015834e4279 579 // else txAns must be completed first, will be called from txAns
Wayne Roberts 0:6015834e4279 580 #endif /* MESH_DEBUG */
Wayne Roberts 0:6015834e4279 581
Wayne Roberts 0:6015834e4279 582 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 583 if (up_info.originating_src_id != ID_NONE) {
Wayne Roberts 0:6015834e4279 584 /* txAns takes priority over application layer */
Wayne Roberts 0:6015834e4279 585 queue.call(gateway_uplink, up_info.len, up_info.originating_src_id, &Radio::radio.rx_buf[up_info.rxBufIdx]);
Wayne Roberts 0:6015834e4279 586 }
Wayne Roberts 0:6015834e4279 587 #endif /* GATEWAY */
Wayne Roberts 0:6015834e4279 588 } // ..rxDoneCB()
Wayne Roberts 0:6015834e4279 589
Wayne Roberts 0:6015834e4279 590 void txTimeoutCB()
Wayne Roberts 0:6015834e4279 591 {
Wayne Roberts 0:6015834e4279 592 pc.printf("\e[41mTxTimeout\e[0m\r\n");
Wayne Roberts 0:6015834e4279 593 }
Wayne Roberts 0:6015834e4279 594
Wayne Roberts 0:6015834e4279 595 void rxTimeoutCB()
Wayne Roberts 0:6015834e4279 596 {
Wayne Roberts 0:6015834e4279 597 Radio::Sleep();
Wayne Roberts 0:6015834e4279 598 queue.call_in(WAKEUP_INTERVAL_MS, rxSingle);
Wayne Roberts 0:6015834e4279 599
Wayne Roberts 0:6015834e4279 600 if (flags.vacantCheck) {
Wayne Roberts 0:6015834e4279 601 channelVacantCount++;
Wayne Roberts 0:6015834e4279 602 if (flags.deferred_send) {
Wayne Roberts 0:6015834e4279 603 uint8_t vc_thresh = CHANNEL_VACANT_REQUIRED_COUNT;
Wayne Roberts 0:6015834e4279 604 if (attemptCnt > 1) {
Wayne Roberts 0:6015834e4279 605 /* retry backoff */
Wayne Roberts 0:6015834e4279 606 vc_thresh += Radio::Random() % CHANNEL_VACANT_REQUIRED_COUNT;
Wayne Roberts 0:6015834e4279 607 }
Wayne Roberts 0:6015834e4279 608 if (channelVacantCount > vc_thresh) {
Wayne Roberts 0:6015834e4279 609 _send_();
Wayne Roberts 0:6015834e4279 610 flags.deferred_send = 0;
Wayne Roberts 0:6015834e4279 611 }
Wayne Roberts 0:6015834e4279 612 }
Wayne Roberts 0:6015834e4279 613 flags.vacantCheck = 0;
Wayne Roberts 0:6015834e4279 614 }
Wayne Roberts 0:6015834e4279 615 }
Wayne Roberts 0:6015834e4279 616
Wayne Roberts 0:6015834e4279 617 uint32_t find_dest_id(uint32_t reqid)
Wayne Roberts 0:6015834e4279 618 {
Wayne Roberts 0:6015834e4279 619 lid_list_t* L;
Wayne Roberts 0:6015834e4279 620 for (L = attachedDevices; L != NULL; L = L->next) {
Wayne Roberts 0:6015834e4279 621 if (L->id == reqid)
Wayne Roberts 0:6015834e4279 622 return L->id; // is locally attached
Wayne Roberts 0:6015834e4279 623 }
Wayne Roberts 0:6015834e4279 624
Wayne Roberts 0:6015834e4279 625 for (L = attachedDevices; L != NULL; L = L->next) {
Wayne Roberts 0:6015834e4279 626 if (L->attachedList != NULL) {
Wayne Roberts 0:6015834e4279 627 cid_list_t* children;
Wayne Roberts 0:6015834e4279 628 for (children = L->attachedList; children != NULL; children = children->next) {
Wayne Roberts 0:6015834e4279 629 if (children->id == reqid)
Wayne Roberts 0:6015834e4279 630 return L->id;
Wayne Roberts 0:6015834e4279 631 }
Wayne Roberts 0:6015834e4279 632 }
Wayne Roberts 0:6015834e4279 633 }
Wayne Roberts 0:6015834e4279 634 return ID_NONE;
Wayne Roberts 0:6015834e4279 635 }
Wayne Roberts 0:6015834e4279 636
Wayne Roberts 0:6015834e4279 637 void cmd_tx(uint8_t argsAt)
Wayne Roberts 0:6015834e4279 638 {
Wayne Roberts 0:6015834e4279 639 unsigned symbs;
Wayne Roberts 0:6015834e4279 640 if (sscanf(pcbuf+argsAt, "%u", &symbs) == 1) {
Wayne Roberts 0:6015834e4279 641 Radio::LoRaPacketConfig(symbs, false, true, false); // preambleLen, fixLen, crcOn, invIQ
Wayne Roberts 0:6015834e4279 642 pc.printf("txing %u symbols\r\n", symbs);
Wayne Roberts 0:6015834e4279 643 }
Wayne Roberts 0:6015834e4279 644
Wayne Roberts 0:6015834e4279 645 txBuf[txBuf_idx++] = CMD_UNUSED;
Wayne Roberts 0:6015834e4279 646 tx_dest_id = ANY_ID;
Wayne Roberts 0:6015834e4279 647 txBuf_send(false);
Wayne Roberts 0:6015834e4279 648 }
Wayne Roberts 0:6015834e4279 649
Wayne Roberts 0:6015834e4279 650 void cmd_list_devices(uint8_t argsAt)
Wayne Roberts 0:6015834e4279 651 {
Wayne Roberts 0:6015834e4279 652 lid_list_t* L;
Wayne Roberts 0:6015834e4279 653 pc.printf("my_id:%lx hops_from_gateway:%u\r\n", my_id, hops_from_gateway);
Wayne Roberts 0:6015834e4279 654 for (L = attachedDevices; L != NULL; L = L->next) {
Wayne Roberts 0:6015834e4279 655 pc.printf("%lx", L->id);
Wayne Roberts 0:6015834e4279 656 if (L->attachedList != NULL) {
Wayne Roberts 0:6015834e4279 657 cid_list_t* children;
Wayne Roberts 0:6015834e4279 658 pc.printf(": ");
Wayne Roberts 0:6015834e4279 659 for (children = L->attachedList; children != NULL; children = children->next)
Wayne Roberts 0:6015834e4279 660 pc.printf("%lx ", children->id);
Wayne Roberts 0:6015834e4279 661 }
Wayne Roberts 0:6015834e4279 662 pc.printf("\r\n");
Wayne Roberts 0:6015834e4279 663 }
Wayne Roberts 0:6015834e4279 664 }
Wayne Roberts 0:6015834e4279 665
Wayne Roberts 0:6015834e4279 666 void cmd_print_status(uint8_t idx)
Wayne Roberts 0:6015834e4279 667 {
Wayne Roberts 0:6015834e4279 668 radio_print_status();
Wayne Roberts 0:6015834e4279 669
Wayne Roberts 0:6015834e4279 670 pc.printf("my_id:%lx hops_from_gateway:%u\r\n", my_id, hops_from_gateway);
Wayne Roberts 0:6015834e4279 671 pc.printf("ClearChan%u reqFlags:%02x ", channelVacantCount, reqFlags.octet);
Wayne Roberts 0:6015834e4279 672 #ifndef GATEWAY
Wayne Roberts 0:6015834e4279 673 upstream_print_status();
Wayne Roberts 0:6015834e4279 674 #endif /* GATEWAY */
Wayne Roberts 0:6015834e4279 675 pc.printf("\r\n");
Wayne Roberts 0:6015834e4279 676 }
Wayne Roberts 0:6015834e4279 677
Wayne Roberts 0:6015834e4279 678 typedef struct {
Wayne Roberts 0:6015834e4279 679 const char* const cmd;
Wayne Roberts 0:6015834e4279 680 void (*handler)(uint8_t args_at);
Wayne Roberts 0:6015834e4279 681 const char* const arg_descr;
Wayne Roberts 0:6015834e4279 682 const char* const description;
Wayne Roberts 0:6015834e4279 683 } menu_item_t;
Wayne Roberts 0:6015834e4279 684
Wayne Roberts 0:6015834e4279 685 void cmd_help(uint8_t);
Wayne Roberts 0:6015834e4279 686
Wayne Roberts 0:6015834e4279 687 const menu_item_t menu_items[] = {
Wayne Roberts 0:6015834e4279 688 { ".", cmd_print_status, "","print status"},
Wayne Roberts 0:6015834e4279 689 { "op", cmd_op, "%u","get/set tx power"},
Wayne Roberts 0:6015834e4279 690 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 691 { "dl", cmd_downlink, "%x %x...","send downlink <destIDhex> <payload bytes hex>"},
Wayne Roberts 0:6015834e4279 692 #endif /* GATEWAY */
Wayne Roberts 0:6015834e4279 693 { "ls", cmd_list_devices, "%u","list seen downstream devices"},
Wayne Roberts 0:6015834e4279 694 { "tx", cmd_tx, "%u","tx test preamble length"},
Wayne Roberts 0:6015834e4279 695 { "?", cmd_help, "","this list of commands"},
Wayne Roberts 0:6015834e4279 696 { NULL, NULL, NULL, NULL }
Wayne Roberts 0:6015834e4279 697 };
Wayne Roberts 0:6015834e4279 698
Wayne Roberts 0:6015834e4279 699 void cmd_help(uint8_t args_at)
Wayne Roberts 0:6015834e4279 700 {
Wayne Roberts 0:6015834e4279 701 int i;
Wayne Roberts 0:6015834e4279 702
Wayne Roberts 0:6015834e4279 703 for (i = 0; menu_items[i].cmd != NULL ; i++) {
Wayne Roberts 0:6015834e4279 704 printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
Wayne Roberts 0:6015834e4279 705 }
Wayne Roberts 0:6015834e4279 706 }
Wayne Roberts 0:6015834e4279 707
Wayne Roberts 0:6015834e4279 708 void console()
Wayne Roberts 0:6015834e4279 709 {
Wayne Roberts 0:6015834e4279 710 uint8_t i, user_cmd_len;
Wayne Roberts 0:6015834e4279 711
Wayne Roberts 0:6015834e4279 712 if (pcbuf_len == 0)
Wayne Roberts 0:6015834e4279 713 return;
Wayne Roberts 0:6015834e4279 714
Wayne Roberts 0:6015834e4279 715 printf("\r\n");
Wayne Roberts 0:6015834e4279 716
Wayne Roberts 0:6015834e4279 717 /* get end of user-entered command */
Wayne Roberts 0:6015834e4279 718 user_cmd_len = 1; // first character can be any character
Wayne Roberts 0:6015834e4279 719 for (i = 1; i <= pcbuf_len; i++) {
Wayne Roberts 0:6015834e4279 720 if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
Wayne Roberts 0:6015834e4279 721 user_cmd_len = i;
Wayne Roberts 0:6015834e4279 722 break;
Wayne Roberts 0:6015834e4279 723 }
Wayne Roberts 0:6015834e4279 724 }
Wayne Roberts 0:6015834e4279 725
Wayne Roberts 0:6015834e4279 726 for (i = 0; menu_items[i].cmd != NULL ; i++) {
Wayne Roberts 0:6015834e4279 727 int mi_len = strlen(menu_items[i].cmd);
Wayne Roberts 0:6015834e4279 728 if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
Wayne Roberts 0:6015834e4279 729 while (pcbuf[mi_len] == ' ') // skip past spaces
Wayne Roberts 0:6015834e4279 730 mi_len++;
Wayne Roberts 0:6015834e4279 731 menu_items[i].handler(mi_len);
Wayne Roberts 0:6015834e4279 732 break;
Wayne Roberts 0:6015834e4279 733 }
Wayne Roberts 0:6015834e4279 734 }
Wayne Roberts 0:6015834e4279 735
Wayne Roberts 0:6015834e4279 736 pcbuf_len = 0;
Wayne Roberts 0:6015834e4279 737 printf("> ");
Wayne Roberts 0:6015834e4279 738 fflush(stdout);
Wayne Roberts 0:6015834e4279 739 }
Wayne Roberts 0:6015834e4279 740
Wayne Roberts 0:6015834e4279 741 void radio_irq_topHalf()
Wayne Roberts 0:6015834e4279 742 {
Wayne Roberts 0:6015834e4279 743 /* isr context -> main loop context */
Wayne Roberts 0:6015834e4279 744 queue.call(Radio::service);
Wayne Roberts 0:6015834e4279 745 }
Wayne Roberts 0:6015834e4279 746
Wayne Roberts 0:6015834e4279 747 const RadioEvents_t rev = {
Wayne Roberts 0:6015834e4279 748 /* DioPin_top_half */ radio_irq_topHalf,
Wayne Roberts 0:6015834e4279 749 /* TxDone_topHalf */ NULL,
Wayne Roberts 0:6015834e4279 750 /* TxDone_botHalf */ txDoneCB,
Wayne Roberts 0:6015834e4279 751 /* TxTimeout */ txTimeoutCB,
Wayne Roberts 0:6015834e4279 752 /* RxDone */ rxDoneCB,
Wayne Roberts 0:6015834e4279 753 /* RxTimeout */ rxTimeoutCB,
Wayne Roberts 0:6015834e4279 754 /* RxError */ NULL,
Wayne Roberts 0:6015834e4279 755 /* FhssChangeChannel */NULL,
Wayne Roberts 0:6015834e4279 756 /* CadDone */ NULL
Wayne Roberts 0:6015834e4279 757 };
Wayne Roberts 0:6015834e4279 758
Wayne Roberts 0:6015834e4279 759 void rx_callback()
Wayne Roberts 0:6015834e4279 760 {
Wayne Roberts 0:6015834e4279 761 static uint8_t pcbuf_idx = 0;
Wayne Roberts 0:6015834e4279 762 static uint8_t prev_len = 0;
Wayne Roberts 0:6015834e4279 763 char c = pc.getc();
Wayne Roberts 0:6015834e4279 764 if (c == 8) {
Wayne Roberts 0:6015834e4279 765 if (pcbuf_idx > 0) {
Wayne Roberts 0:6015834e4279 766 pc.putc(8);
Wayne Roberts 0:6015834e4279 767 pc.putc(' ');
Wayne Roberts 0:6015834e4279 768 pc.putc(8);
Wayne Roberts 0:6015834e4279 769 pcbuf_idx--;
Wayne Roberts 0:6015834e4279 770 }
Wayne Roberts 0:6015834e4279 771 } else if (c == 3) { // ctrl-C
Wayne Roberts 0:6015834e4279 772 pcbuf_len = -1;
Wayne Roberts 0:6015834e4279 773 } else if (c == '\r') {
Wayne Roberts 0:6015834e4279 774 if (pcbuf_idx == 0) {
Wayne Roberts 0:6015834e4279 775 pcbuf_len = prev_len;
Wayne Roberts 0:6015834e4279 776 } else {
Wayne Roberts 0:6015834e4279 777 pcbuf[pcbuf_idx] = 0; // null terminate
Wayne Roberts 0:6015834e4279 778 prev_len = pcbuf_idx;
Wayne Roberts 0:6015834e4279 779 pcbuf_idx = 0;
Wayne Roberts 0:6015834e4279 780 pcbuf_len = prev_len;
Wayne Roberts 0:6015834e4279 781 }
Wayne Roberts 0:6015834e4279 782 queue.call(console);
Wayne Roberts 0:6015834e4279 783 } else if (pcbuf_idx < sizeof(pcbuf)) {
Wayne Roberts 0:6015834e4279 784 pcbuf[pcbuf_idx++] = c;
Wayne Roberts 0:6015834e4279 785 pc.putc(c);
Wayne Roberts 0:6015834e4279 786 }
Wayne Roberts 0:6015834e4279 787 }
Wayne Roberts 0:6015834e4279 788
Wayne Roberts 0:6015834e4279 789 int main()
Wayne Roberts 0:6015834e4279 790 {
Wayne Roberts 0:6015834e4279 791 pc.baud(115200);
Wayne Roberts 0:6015834e4279 792 pc.printf("\r\nreset\r\n");
Wayne Roberts 0:6015834e4279 793 pc.attach(rx_callback);
Wayne Roberts 0:6015834e4279 794
Wayne Roberts 0:6015834e4279 795 {
Wayne Roberts 0:6015834e4279 796 uint32_t u32;
Wayne Roberts 0:6015834e4279 797 #ifdef TARGET_FAMILY_STM32
Wayne Roberts 0:6015834e4279 798 u32 = LL_GetUID_Word0();
Wayne Roberts 0:6015834e4279 799 u32 <<= 2;
Wayne Roberts 0:6015834e4279 800 u32 ^= LL_GetUID_Word1();
Wayne Roberts 0:6015834e4279 801 u32 ^= LL_GetUID_Word2();
Wayne Roberts 0:6015834e4279 802 #else
Wayne Roberts 0:6015834e4279 803 #error TODO_nSTM32
Wayne Roberts 0:6015834e4279 804 #endif
Wayne Roberts 0:6015834e4279 805 my_id = u32;
Wayne Roberts 0:6015834e4279 806 pc.printf("my_id %lx\r\n", my_id);
Wayne Roberts 0:6015834e4279 807 }
Wayne Roberts 0:6015834e4279 808
Wayne Roberts 0:6015834e4279 809 wait_ms(200); // power stabilization from cold-reset
Wayne Roberts 0:6015834e4279 810 Radio::Init(&rev);
Wayne Roberts 0:6015834e4279 811
Wayne Roberts 0:6015834e4279 812 rxdbg = 0;
Wayne Roberts 0:6015834e4279 813 Radio::Standby();
Wayne Roberts 0:6015834e4279 814 Radio::LoRaModemConfig(BW_KHZ, SPREADING_FACTOR, 1);
Wayne Roberts 0:6015834e4279 815 Radio::SetChannel(CF_MHZ * 1000000);
Wayne Roberts 0:6015834e4279 816 Radio::set_tx_dbm(TX_DBM);
Wayne Roberts 0:6015834e4279 817 #ifdef SX126x_H
Wayne Roberts 0:6015834e4279 818 {
Wayne Roberts 0:6015834e4279 819 status_t status;
Wayne Roberts 0:6015834e4279 820 uint8_t stopOnPreamble = 1;
Wayne Roberts 0:6015834e4279 821
Wayne Roberts 0:6015834e4279 822 Radio::radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);
Wayne Roberts 0:6015834e4279 823 wait_ms(20);
Wayne Roberts 0:6015834e4279 824 Radio::radio.xfer(OPCODE_STOP_TIMER_ON_PREAMBLE, 1, 0, &stopOnPreamble);
Wayne Roberts 0:6015834e4279 825 }
Wayne Roberts 0:6015834e4279 826 #endif /* SX126x_H */
Wayne Roberts 0:6015834e4279 827
Wayne Roberts 0:6015834e4279 828 setPreambleSize(false, 4); //init
Wayne Roberts 0:6015834e4279 829 {
Wayne Roberts 0:6015834e4279 830 unsigned daDur = Radio::lora_toa_us(DISCOVERY_ANS_LENGTH);
Wayne Roberts 0:6015834e4279 831 discovery_ans_time_step_us = daDur + (ANS_PAD_MS * 1000); // + padding for receiver handling
Wayne Roberts 0:6015834e4279 832 discovery_ans_time_total_us = discovery_ans_time_step_us * N_DISCOVERY_ANS;
Wayne Roberts 0:6015834e4279 833 }
Wayne Roberts 0:6015834e4279 834
Wayne Roberts 0:6015834e4279 835 #ifdef SX128x_H
Wayne Roberts 0:6015834e4279 836 /* C preprocess doesnt do floating point */
Wayne Roberts 0:6015834e4279 837 if (N_PRE_SYMB > 255) {
Wayne Roberts 0:6015834e4279 838 pc.printf("\e[41mlong preamble oversized %.1f\e[0m\r\n", N_PRE_SYMB);
Wayne Roberts 0:6015834e4279 839 }
Wayne Roberts 0:6015834e4279 840 #endif /* ..SX128x_H */
Wayne Roberts 0:6015834e4279 841
Wayne Roberts 0:6015834e4279 842 #ifdef GATEWAY
Wayne Roberts 0:6015834e4279 843 start_periodic_rxing(0x10); // gateway startup
Wayne Roberts 0:6015834e4279 844 #else
Wayne Roberts 0:6015834e4279 845 init_attached_upstream();
Wayne Roberts 0:6015834e4279 846 hops_from_gateway = HFG_UNATTACHED;
Wayne Roberts 0:6015834e4279 847 upstream_init();
Wayne Roberts 0:6015834e4279 848 #endif
Wayne Roberts 0:6015834e4279 849
Wayne Roberts 0:6015834e4279 850 app_init();
Wayne Roberts 0:6015834e4279 851 fwd.len = -1;
Wayne Roberts 0:6015834e4279 852
Wayne Roberts 0:6015834e4279 853 /* if (!sleep_manager_can_deep_sleep()) {
Wayne Roberts 0:6015834e4279 854 sleep_manager_unlock_deep_sleep();
Wayne Roberts 0:6015834e4279 855 pc.printf("unLockDeepSleep\r\n");
Wayne Roberts 0:6015834e4279 856 }*/
Wayne Roberts 0:6015834e4279 857
Wayne Roberts 0:6015834e4279 858 queue.dispatch();
Wayne Roberts 0:6015834e4279 859 } // ..main()
Wayne Roberts 0:6015834e4279 860