end node on synchronous star LoRa network.

Dependencies:   SX127x sx12xx_hal TSL2561

radio chip selection

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

This project for use with LoRaWAN_singlechannel_gateway project.

Alternately gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Network description is at gateway project page. Synchronous star network.

Hardware Support

This project supports SX1276 and SX1272, sx126x kit, sx126x shield, and sx128x 2.4GHz. The ST board B-L072Z-LRWAN1 is also supported (TypeABZ module). When B-L072Z-LRWAN1 target is selected, TARGET_DISCO_L072CZ_LRWAN1 is defined by tools, allowing correct radio driver configuration for this platform. Alternately, any mbed board that can use LoRa radio shield board should work, but NUCLEO boards are tested.

End-node Unique ID

DevEUI is created from CPU serial number. AppEUI and AppKey are declared as software constants.

End-node Configuration

Data rate definition LORAMAC_DEFAULT_DATARATE configured in LoRaMac-definitions.h. See gateway project page for configuration of gateway.
LoRaWAN addressing is configured in Comissioning.h; only OTA mode is functional.
Header file board/lora_config.h, selects application layer options (i.e. sensors) to be compiled in.

Serial Interface

Serial port operates at 115200bps.
Application layer single_us915_main.cpp User button triggers uplink (i.e. blue button on nucleo board), or jumper enables continuously sends repeated uplink packets. The MAC layer holds each uplink request until the allocated timeslot.

commandargumentsdescription
?-print available commands
. (period)-print status (DevEUI, DevAddr, etc)
ullength integerset payload length of test uplink packets

sensor demo

Selected grove sensors may be plugged into SX1272 shield.
To enable, edit lora_config.h to define SENSORS.

Sensor connections on SX1272MB2xAS:

D8 D9: buttonRX TX: (unused)A3 A4: Rotary Angle Sensor
D6 D7: RGB LEDSCL SDA: digital light sensorA1 A2: Rotary Angle Sensor

Digital input pin, state reported via uplink: PC8
Digital output pin, controlled via downlink: PC6
PWM out: PB_10

Jumper enables auto-repeated transmit: PC10 and PC12 on NUCLEO board, located on end of morpho headers nearby JP4.

Committer:
dudmuck
Date:
Tue Aug 08 15:16:37 2017 -0700
Revision:
23:a862b5601663
Parent:
21:500ff43d8424
Child:
25:fed9d5b77183
sx1276 driver: support PaSelect on TYPE_ABZ module

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dudmuck 0:8f0d0ae0a077 1 /*
dudmuck 0:8f0d0ae0a077 2 / _____) _ | |
dudmuck 0:8f0d0ae0a077 3 ( (____ _____ ____ _| |_ _____ ____| |__
dudmuck 0:8f0d0ae0a077 4 \____ \| ___ | (_ _) ___ |/ ___) _ \
dudmuck 0:8f0d0ae0a077 5 _____) ) ____| | | || |_| ____( (___| | | |
dudmuck 0:8f0d0ae0a077 6 (______/|_____)_|_|_| \__)_____)\____)_| |_|
dudmuck 0:8f0d0ae0a077 7 (C)2013 Semtech
dudmuck 0:8f0d0ae0a077 8 ___ _____ _ ___ _ _____ ___ ___ ___ ___
dudmuck 0:8f0d0ae0a077 9 / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
dudmuck 0:8f0d0ae0a077 10 \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
dudmuck 0:8f0d0ae0a077 11 |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
dudmuck 0:8f0d0ae0a077 12 embedded.connectivity.solutions===============
dudmuck 0:8f0d0ae0a077 13
dudmuck 0:8f0d0ae0a077 14 Description: LoRa MAC layer implementation
dudmuck 0:8f0d0ae0a077 15
dudmuck 0:8f0d0ae0a077 16 License: Revised BSD License, see LICENSE.TXT file include in the project
dudmuck 0:8f0d0ae0a077 17
dudmuck 0:8f0d0ae0a077 18 Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE )
dudmuck 0:8f0d0ae0a077 19 */
dudmuck 0:8f0d0ae0a077 20 #include <math.h>
dudmuck 0:8f0d0ae0a077 21 #include "board.h"
dudmuck 0:8f0d0ae0a077 22
dudmuck 0:8f0d0ae0a077 23 #include "LoRaMacCrypto.h"
dudmuck 0:8f0d0ae0a077 24 #include "LoRaMac.h"
dudmuck 0:8f0d0ae0a077 25 #include "LoRaMacTest.h"
dudmuck 0:8f0d0ae0a077 26
dudmuck 16:915815632c1f 27 #define TX_DEBUG
dudmuck 16:915815632c1f 28
dudmuck 0:8f0d0ae0a077 29 #define PING_SLOT_RESOLUTION_us 30000
dudmuck 0:8f0d0ae0a077 30
dudmuck 0:8f0d0ae0a077 31 /*!
dudmuck 0:8f0d0ae0a077 32 * Maximum PHY layer payload size
dudmuck 0:8f0d0ae0a077 33 */
dudmuck 0:8f0d0ae0a077 34 #define LORAMAC_PHY_MAXPAYLOAD 255
dudmuck 0:8f0d0ae0a077 35
dudmuck 0:8f0d0ae0a077 36 /*!
dudmuck 0:8f0d0ae0a077 37 * Maximum MAC commands buffer size
dudmuck 0:8f0d0ae0a077 38 */
dudmuck 0:8f0d0ae0a077 39 #define LORA_MAC_COMMAND_MAX_LENGTH 15
dudmuck 0:8f0d0ae0a077 40
dudmuck 0:8f0d0ae0a077 41 /*!
dudmuck 0:8f0d0ae0a077 42 * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength
dudmuck 0:8f0d0ae0a077 43 * in RxWindowSetup function.
dudmuck 0:8f0d0ae0a077 44 * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD
dudmuck 0:8f0d0ae0a077 45 */
dudmuck 0:8f0d0ae0a077 46 #define LORA_MAC_FRMPAYLOAD_OVERHEAD 13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4)
dudmuck 0:8f0d0ae0a077 47
dudmuck 0:8f0d0ae0a077 48 /*!
dudmuck 0:8f0d0ae0a077 49 * LoRaMac duty cycle for the back-off procedure during the first hour.
dudmuck 0:8f0d0ae0a077 50 */
dudmuck 0:8f0d0ae0a077 51 #define BACKOFF_DC_1_HOUR 100
dudmuck 0:8f0d0ae0a077 52
dudmuck 0:8f0d0ae0a077 53 /*!
dudmuck 0:8f0d0ae0a077 54 * LoRaMac duty cycle for the back-off procedure during the next 10 hours.
dudmuck 0:8f0d0ae0a077 55 */
dudmuck 0:8f0d0ae0a077 56 #define BACKOFF_DC_10_HOURS 1000
dudmuck 0:8f0d0ae0a077 57
dudmuck 0:8f0d0ae0a077 58 /*!
dudmuck 0:8f0d0ae0a077 59 * LoRaMac duty cycle for the back-off procedure during the next 24 hours.
dudmuck 0:8f0d0ae0a077 60 */
dudmuck 0:8f0d0ae0a077 61 #define BACKOFF_DC_24_HOURS 10000
dudmuck 0:8f0d0ae0a077 62
dudmuck 20:42839629a5dc 63 #define DIO0_LAG_us 1000
dudmuck 20:42839629a5dc 64
dudmuck 0:8f0d0ae0a077 65 /*!
dudmuck 0:8f0d0ae0a077 66 * Device IEEE EUI
dudmuck 0:8f0d0ae0a077 67 */
dudmuck 0:8f0d0ae0a077 68 static uint8_t *LoRaMacDevEui;
dudmuck 0:8f0d0ae0a077 69
dudmuck 0:8f0d0ae0a077 70 /*!
dudmuck 0:8f0d0ae0a077 71 * Application IEEE EUI
dudmuck 0:8f0d0ae0a077 72 */
dudmuck 0:8f0d0ae0a077 73 static uint8_t *LoRaMacAppEui;
dudmuck 0:8f0d0ae0a077 74
dudmuck 0:8f0d0ae0a077 75 /*!
dudmuck 0:8f0d0ae0a077 76 * AES encryption/decryption cipher application key
dudmuck 0:8f0d0ae0a077 77 */
dudmuck 0:8f0d0ae0a077 78 static uint8_t *LoRaMacAppKey;
dudmuck 0:8f0d0ae0a077 79
dudmuck 0:8f0d0ae0a077 80 /*!
dudmuck 0:8f0d0ae0a077 81 * AES encryption/decryption cipher network session key
dudmuck 0:8f0d0ae0a077 82 */
dudmuck 0:8f0d0ae0a077 83 static uint8_t LoRaMacNwkSKey[] =
dudmuck 0:8f0d0ae0a077 84 {
dudmuck 0:8f0d0ae0a077 85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
dudmuck 0:8f0d0ae0a077 86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
dudmuck 0:8f0d0ae0a077 87 };
dudmuck 0:8f0d0ae0a077 88
dudmuck 0:8f0d0ae0a077 89 /*!
dudmuck 0:8f0d0ae0a077 90 * AES encryption/decryption cipher application session key
dudmuck 0:8f0d0ae0a077 91 */
dudmuck 0:8f0d0ae0a077 92 static uint8_t LoRaMacAppSKey[] =
dudmuck 0:8f0d0ae0a077 93 {
dudmuck 0:8f0d0ae0a077 94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
dudmuck 0:8f0d0ae0a077 95 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
dudmuck 0:8f0d0ae0a077 96 };
dudmuck 0:8f0d0ae0a077 97
dudmuck 0:8f0d0ae0a077 98 /*!
dudmuck 0:8f0d0ae0a077 99 * Device nonce is a random value extracted by issuing a sequence of RSSI
dudmuck 0:8f0d0ae0a077 100 * measurements
dudmuck 0:8f0d0ae0a077 101 */
dudmuck 0:8f0d0ae0a077 102 static uint16_t LoRaMacDevNonce;
dudmuck 0:8f0d0ae0a077 103
dudmuck 0:8f0d0ae0a077 104 /*!
dudmuck 0:8f0d0ae0a077 105 * Network ID ( 3 bytes )
dudmuck 0:8f0d0ae0a077 106 */
dudmuck 0:8f0d0ae0a077 107 static uint32_t LoRaMacNetID;
dudmuck 0:8f0d0ae0a077 108
dudmuck 0:8f0d0ae0a077 109 /*!
dudmuck 0:8f0d0ae0a077 110 * Mote Address
dudmuck 0:8f0d0ae0a077 111 */
dudmuck 0:8f0d0ae0a077 112 static uint32_t LoRaMacDevAddr;
dudmuck 0:8f0d0ae0a077 113
dudmuck 0:8f0d0ae0a077 114 /*!
dudmuck 0:8f0d0ae0a077 115 * Multicast channels linked list
dudmuck 0:8f0d0ae0a077 116 */
dudmuck 0:8f0d0ae0a077 117 static MulticastParams_t *MulticastChannels = NULL;
dudmuck 0:8f0d0ae0a077 118
dudmuck 0:8f0d0ae0a077 119 /*!
dudmuck 0:8f0d0ae0a077 120 * Actual device class
dudmuck 0:8f0d0ae0a077 121 */
dudmuck 0:8f0d0ae0a077 122 static DeviceClass_t LoRaMacDeviceClass;
dudmuck 0:8f0d0ae0a077 123
dudmuck 0:8f0d0ae0a077 124 /*!
dudmuck 0:8f0d0ae0a077 125 * Indicates if the node is connected to a private or public network
dudmuck 0:8f0d0ae0a077 126 */
dudmuck 0:8f0d0ae0a077 127 static bool PublicNetwork;
dudmuck 0:8f0d0ae0a077 128
dudmuck 0:8f0d0ae0a077 129 /*!
dudmuck 0:8f0d0ae0a077 130 * Buffer containing the data to be sent or received.
dudmuck 0:8f0d0ae0a077 131 */
dudmuck 0:8f0d0ae0a077 132 static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD];
dudmuck 0:8f0d0ae0a077 133
dudmuck 0:8f0d0ae0a077 134 /*!
dudmuck 0:8f0d0ae0a077 135 * Length of packet in LoRaMacBuffer
dudmuck 0:8f0d0ae0a077 136 */
dudmuck 0:8f0d0ae0a077 137 static uint16_t LoRaMacBufferPktLen = 0;
dudmuck 0:8f0d0ae0a077 138
dudmuck 0:8f0d0ae0a077 139 /*!
dudmuck 0:8f0d0ae0a077 140 * Length of the payload in LoRaMacBuffer
dudmuck 0:8f0d0ae0a077 141 */
dudmuck 0:8f0d0ae0a077 142 static uint8_t LoRaMacTxPayloadLen = 0;
dudmuck 0:8f0d0ae0a077 143
dudmuck 0:8f0d0ae0a077 144 /*!
dudmuck 0:8f0d0ae0a077 145 * Buffer containing the upper layer data.
dudmuck 0:8f0d0ae0a077 146 */
dudmuck 0:8f0d0ae0a077 147 static uint8_t LoRaMacRxPayload[LORAMAC_PHY_MAXPAYLOAD];
dudmuck 0:8f0d0ae0a077 148
dudmuck 0:8f0d0ae0a077 149 /*!
dudmuck 0:8f0d0ae0a077 150 * LoRaMAC frame counter. Each time a packet is sent the counter is incremented.
dudmuck 0:8f0d0ae0a077 151 * Only the 16 LSB bits are sent
dudmuck 0:8f0d0ae0a077 152 */
dudmuck 0:8f0d0ae0a077 153 static uint32_t UpLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 154
dudmuck 0:8f0d0ae0a077 155 /*!
dudmuck 0:8f0d0ae0a077 156 * LoRaMAC frame counter. Each time a packet is received the counter is incremented.
dudmuck 0:8f0d0ae0a077 157 * Only the 16 LSB bits are received
dudmuck 0:8f0d0ae0a077 158 */
dudmuck 0:8f0d0ae0a077 159 static uint32_t DownLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 160
dudmuck 0:8f0d0ae0a077 161 /*!
dudmuck 0:8f0d0ae0a077 162 * IsPacketCounterFixed enables the MIC field tests by fixing the
dudmuck 0:8f0d0ae0a077 163 * UpLinkCounter value
dudmuck 0:8f0d0ae0a077 164 */
dudmuck 16:915815632c1f 165 //static bool IsUpLinkCounterFixed = false;
dudmuck 0:8f0d0ae0a077 166
dudmuck 0:8f0d0ae0a077 167 /*!
dudmuck 0:8f0d0ae0a077 168 * Used for test purposes. Disables the opening of the reception windows.
dudmuck 0:8f0d0ae0a077 169 */
dudmuck 0:8f0d0ae0a077 170 static bool IsRxWindowsEnabled = true;
dudmuck 0:8f0d0ae0a077 171 LowPowerTimeout rx_timeout;
dudmuck 0:8f0d0ae0a077 172
dudmuck 0:8f0d0ae0a077 173 /*!
dudmuck 0:8f0d0ae0a077 174 * Indicates if the MAC layer has already joined a network.
dudmuck 0:8f0d0ae0a077 175 */
dudmuck 0:8f0d0ae0a077 176 static bool IsLoRaMacNetworkJoined = false;
dudmuck 0:8f0d0ae0a077 177
dudmuck 0:8f0d0ae0a077 178 /*!
dudmuck 0:8f0d0ae0a077 179 * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates
dudmuck 0:8f0d0ae0a077 180 * if the nodes needs to manage the server acknowledgement.
dudmuck 0:8f0d0ae0a077 181 */
dudmuck 0:8f0d0ae0a077 182 static bool NodeAckRequested = false;
dudmuck 0:8f0d0ae0a077 183
dudmuck 0:8f0d0ae0a077 184 /*!
dudmuck 0:8f0d0ae0a077 185 * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates
dudmuck 0:8f0d0ae0a077 186 * if the ACK bit must be set for the next transmission
dudmuck 0:8f0d0ae0a077 187 */
dudmuck 0:8f0d0ae0a077 188 static bool SrvAckRequested = false;
dudmuck 0:8f0d0ae0a077 189
dudmuck 0:8f0d0ae0a077 190 /*!
dudmuck 0:8f0d0ae0a077 191 * Indicates if the MAC layer wants to send MAC commands
dudmuck 0:8f0d0ae0a077 192 */
dudmuck 0:8f0d0ae0a077 193 static bool MacCommandsInNextTx = false;
dudmuck 0:8f0d0ae0a077 194
dudmuck 0:8f0d0ae0a077 195 /*!
dudmuck 0:8f0d0ae0a077 196 * Contains the current MacCommandsBuffer index
dudmuck 0:8f0d0ae0a077 197 */
dudmuck 0:8f0d0ae0a077 198 static uint8_t MacCommandsBufferIndex = 0;
dudmuck 0:8f0d0ae0a077 199
dudmuck 0:8f0d0ae0a077 200 /*!
dudmuck 0:8f0d0ae0a077 201 * Contains the current MacCommandsBuffer index for MAC commands to repeat
dudmuck 0:8f0d0ae0a077 202 */
dudmuck 0:8f0d0ae0a077 203 static uint8_t MacCommandsBufferToRepeatIndex = 0;
dudmuck 0:8f0d0ae0a077 204
dudmuck 0:8f0d0ae0a077 205 /*!
dudmuck 0:8f0d0ae0a077 206 * Buffer containing the MAC layer commands
dudmuck 0:8f0d0ae0a077 207 */
dudmuck 0:8f0d0ae0a077 208 static uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH];
dudmuck 0:8f0d0ae0a077 209
dudmuck 0:8f0d0ae0a077 210 /*!
dudmuck 0:8f0d0ae0a077 211 * Buffer containing the MAC layer commands which must be repeated
dudmuck 0:8f0d0ae0a077 212 */
dudmuck 0:8f0d0ae0a077 213 static uint8_t MacCommandsBufferToRepeat[LORA_MAC_COMMAND_MAX_LENGTH];
dudmuck 0:8f0d0ae0a077 214
dudmuck 18:9ac71c0eb70d 215 #define BEACON_SIZE 6 /* bytes */
dudmuck 18:9ac71c0eb70d 216 #define BEACON_CHANNEL_DR LORAMAC_DEFAULT_DATARATE
dudmuck 18:9ac71c0eb70d 217
dudmuck 18:9ac71c0eb70d 218
dudmuck 0:8f0d0ae0a077 219 #if defined( USE_BAND_915_SINGLE )
dudmuck 18:9ac71c0eb70d 220 /*!
dudmuck 18:9ac71c0eb70d 221 * Data rates table definition
dudmuck 18:9ac71c0eb70d 222 */
dudmuck 18:9ac71c0eb70d 223 const uint8_t Datarates[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
dudmuck 18:9ac71c0eb70d 224
dudmuck 18:9ac71c0eb70d 225 /*!
dudmuck 18:9ac71c0eb70d 226 * Bandwidths table definition in Hz
dudmuck 18:9ac71c0eb70d 227 */
dudmuck 18:9ac71c0eb70d 228 const uint32_t Bandwidths[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
dudmuck 18:9ac71c0eb70d 229
dudmuck 18:9ac71c0eb70d 230 /*!
dudmuck 18:9ac71c0eb70d 231 * LoRaMac bands
dudmuck 18:9ac71c0eb70d 232 */
dudmuck 18:9ac71c0eb70d 233 /*static Band_t Bands[LORA_MAX_NB_BANDS] =
dudmuck 18:9ac71c0eb70d 234 {
dudmuck 18:9ac71c0eb70d 235 BAND0,
dudmuck 18:9ac71c0eb70d 236 };*/
dudmuck 18:9ac71c0eb70d 237
dudmuck 18:9ac71c0eb70d 238 /*!
dudmuck 18:9ac71c0eb70d 239 * Tx output powers table definition
dudmuck 18:9ac71c0eb70d 240 */
dudmuck 23:a862b5601663 241 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
dudmuck 23:a862b5601663 242 const int8_t TxPowers[] = { 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0 };
dudmuck 18:9ac71c0eb70d 243 #define LORAMAC_FIRST_CHANNEL ( (uint32_t)910.0e6 )
dudmuck 18:9ac71c0eb70d 244 #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)800e3 )
dudmuck 18:9ac71c0eb70d 245 #define LORA_MAX_NB_CHANNELS 8
dudmuck 18:9ac71c0eb70d 246
dudmuck 18:9ac71c0eb70d 247 #define LORA_BANDWIDTH 2 /* 2=500KHz */
dudmuck 18:9ac71c0eb70d 248
dudmuck 18:9ac71c0eb70d 249 /* measured beacon duration (all at bw500, 6 byte fixed payload length)
dudmuck 18:9ac71c0eb70d 250 * latency assigned for correct rx-before-tx measurement */
dudmuck 18:9ac71c0eb70d 251 #if (LORAMAC_DEFAULT_DATARATE == DR_8)
dudmuck 18:9ac71c0eb70d 252 #define BEACON_RXDONE_LATENCY_us 6000
dudmuck 18:9ac71c0eb70d 253 #define BEACON_TOA_us 209000
dudmuck 18:9ac71c0eb70d 254 #elif (LORAMAC_DEFAULT_DATARATE == DR_9)
dudmuck 18:9ac71c0eb70d 255 #define BEACON_RXDONE_LATENCY_us 3500
dudmuck 18:9ac71c0eb70d 256 #define BEACON_TOA_us 105000
dudmuck 18:9ac71c0eb70d 257 #elif (LORAMAC_DEFAULT_DATARATE == DR_10)
dudmuck 18:9ac71c0eb70d 258 #define BEACON_RXDONE_LATENCY_us 1460
dudmuck 18:9ac71c0eb70d 259 #define BEACON_TOA_us 52800
dudmuck 18:9ac71c0eb70d 260 #elif (LORAMAC_DEFAULT_DATARATE == DR_11)
dudmuck 18:9ac71c0eb70d 261 #define BEACON_RXDONE_LATENCY_us 2000
dudmuck 18:9ac71c0eb70d 262 #define BEACON_TOA_us 26000
dudmuck 18:9ac71c0eb70d 263 #elif (LORAMAC_DEFAULT_DATARATE == DR_12)
dudmuck 18:9ac71c0eb70d 264 #define BEACON_RXDONE_LATENCY_us 1500
dudmuck 18:9ac71c0eb70d 265 #define BEACON_TOA_us 12800
dudmuck 18:9ac71c0eb70d 266 #elif (LORAMAC_DEFAULT_DATARATE == DR_13)
dudmuck 18:9ac71c0eb70d 267 #define BEACON_RXDONE_LATENCY_us 1300
dudmuck 18:9ac71c0eb70d 268 #define BEACON_TOA_us 6560
dudmuck 18:9ac71c0eb70d 269 #else
dudmuck 18:9ac71c0eb70d 270 #error datarate
dudmuck 18:9ac71c0eb70d 271 #endif
dudmuck 18:9ac71c0eb70d 272
dudmuck 18:9ac71c0eb70d 273 /* end us915 */
dudmuck 18:9ac71c0eb70d 274 #elif defined(USE_BAND_433) && defined(ENABLE_SX1276)
dudmuck 18:9ac71c0eb70d 275 const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 };
dudmuck 18:9ac71c0eb70d 276 const uint32_t Bandwidths[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
dudmuck 18:9ac71c0eb70d 277 const int8_t TxPowers[] = { 10, 7, 4, 1, -2, -5 };
dudmuck 18:9ac71c0eb70d 278 #define LORA_BANDWIDTH 0 /* 0=125KHz */
dudmuck 18:9ac71c0eb70d 279
dudmuck 18:9ac71c0eb70d 280 #define LORAMAC_FIRST_CHANNEL ( (uint32_t)433.32e6 )
dudmuck 18:9ac71c0eb70d 281 #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)200e3 )
dudmuck 18:9ac71c0eb70d 282 #define LORA_MAX_NB_CHANNELS 7
dudmuck 18:9ac71c0eb70d 283
dudmuck 18:9ac71c0eb70d 284 #if (LORAMAC_DEFAULT_DATARATE == DR_0)
dudmuck 18:9ac71c0eb70d 285 #define BEACON_RXDONE_LATENCY_us 27500
dudmuck 18:9ac71c0eb70d 286 #define BEACON_TOA_us 828100
dudmuck 18:9ac71c0eb70d 287 #elif (LORAMAC_DEFAULT_DATARATE == DR_1)
dudmuck 18:9ac71c0eb70d 288 #define BEACON_RXDONE_LATENCY_us 12500
dudmuck 18:9ac71c0eb70d 289 #define BEACON_TOA_us 414400
dudmuck 18:9ac71c0eb70d 290 #elif (LORAMAC_DEFAULT_DATARATE == DR_2)
dudmuck 18:9ac71c0eb70d 291 #define BEACON_RXDONE_LATENCY_us 6000
dudmuck 18:9ac71c0eb70d 292 #define BEACON_TOA_us 207500
dudmuck 18:9ac71c0eb70d 293 #elif (LORAMAC_DEFAULT_DATARATE == DR_3)
dudmuck 18:9ac71c0eb70d 294 #define BEACON_RXDONE_LATENCY_us 3840
dudmuck 18:9ac71c0eb70d 295 #define BEACON_TOA_us 103000
dudmuck 18:9ac71c0eb70d 296 #elif (LORAMAC_DEFAULT_DATARATE == DR_4)
dudmuck 18:9ac71c0eb70d 297 #define BEACON_RXDONE_LATENCY_us 2800
dudmuck 18:9ac71c0eb70d 298 #define BEACON_TOA_us 51000
dudmuck 18:9ac71c0eb70d 299 #elif (LORAMAC_DEFAULT_DATARATE == DR_5)
dudmuck 18:9ac71c0eb70d 300 #define BEACON_RXDONE_LATENCY_us 1400
dudmuck 18:9ac71c0eb70d 301 #define BEACON_TOA_us 25800
dudmuck 18:9ac71c0eb70d 302 #else
dudmuck 18:9ac71c0eb70d 303 #error datarate
dudmuck 18:9ac71c0eb70d 304 #endif
dudmuck 18:9ac71c0eb70d 305
dudmuck 18:9ac71c0eb70d 306 /* end USE_BAND_433 */
dudmuck 18:9ac71c0eb70d 307 #else
dudmuck 18:9ac71c0eb70d 308 #error "Please define a frequency band in the compiler options."
dudmuck 0:8f0d0ae0a077 309 #endif
dudmuck 0:8f0d0ae0a077 310
dudmuck 18:9ac71c0eb70d 311 static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS]; // populated in init
dudmuck 18:9ac71c0eb70d 312
dudmuck 16:915815632c1f 313 #define BEACON_GUARD_us 2000000 // pre-beacon start
dudmuck 0:8f0d0ae0a077 314 #define BEACON_RESERVED_us 2120000 // post-beacon start
dudmuck 0:8f0d0ae0a077 315
dudmuck 0:8f0d0ae0a077 316 #define BEACON_MIN_SYMBOL_TIMEOUT 8
dudmuck 0:8f0d0ae0a077 317
dudmuck 0:8f0d0ae0a077 318 LowPowerTimer lp_timer;
dudmuck 0:8f0d0ae0a077 319
dudmuck 13:18de9ee3a461 320 #ifdef DEBUG_GWTX_JUMPER
dudmuck 13:18de9ee3a461 321 InterruptIn gwtx_pin(DEBUG_GWTX_JUMPER);
dudmuck 13:18de9ee3a461 322 unsigned int gwtx_rise_us;
dudmuck 13:18de9ee3a461 323
dudmuck 13:18de9ee3a461 324 void gwtx_pin_callback()
dudmuck 13:18de9ee3a461 325 {
dudmuck 13:18de9ee3a461 326 gwtx_rise_us = lp_timer.read_us();
dudmuck 13:18de9ee3a461 327 }
dudmuck 13:18de9ee3a461 328 #endif
dudmuck 13:18de9ee3a461 329
dudmuck 0:8f0d0ae0a077 330 /*!
dudmuck 0:8f0d0ae0a077 331 * LoRaMac parameters
dudmuck 0:8f0d0ae0a077 332 */
dudmuck 0:8f0d0ae0a077 333 LoRaMacParams_t LoRaMacParams;
dudmuck 0:8f0d0ae0a077 334
dudmuck 0:8f0d0ae0a077 335 /*!
dudmuck 0:8f0d0ae0a077 336 * LoRaMac default parameters
dudmuck 0:8f0d0ae0a077 337 */
dudmuck 0:8f0d0ae0a077 338 LoRaMacParams_t LoRaMacParamsDefaults;
dudmuck 0:8f0d0ae0a077 339
dudmuck 0:8f0d0ae0a077 340 /*!
dudmuck 0:8f0d0ae0a077 341 * Uplink messages repetitions counter
dudmuck 0:8f0d0ae0a077 342 */
dudmuck 0:8f0d0ae0a077 343 static uint8_t ChannelsNbRepCounter = 0;
dudmuck 0:8f0d0ae0a077 344
dudmuck 0:8f0d0ae0a077 345 static uint32_t AggregatedLastTxDoneTime_us;
dudmuck 0:8f0d0ae0a077 346
dudmuck 0:8f0d0ae0a077 347 /*!
dudmuck 0:8f0d0ae0a077 348 * Current channel index
dudmuck 0:8f0d0ae0a077 349 */
dudmuck 0:8f0d0ae0a077 350 static uint8_t Channel;
dudmuck 0:8f0d0ae0a077 351
dudmuck 0:8f0d0ae0a077 352 /*!
dudmuck 0:8f0d0ae0a077 353 * Stores the time at LoRaMac initialization.
dudmuck 0:8f0d0ae0a077 354 *
dudmuck 0:8f0d0ae0a077 355 * \remark Used for the BACKOFF_DC computation.
dudmuck 0:8f0d0ae0a077 356 */
dudmuck 0:8f0d0ae0a077 357 //static TimerTime_t LoRaMacInitializationTime = 0;
dudmuck 0:8f0d0ae0a077 358
dudmuck 0:8f0d0ae0a077 359 /*!
dudmuck 0:8f0d0ae0a077 360 * LoRaMac upper layer event functions
dudmuck 0:8f0d0ae0a077 361 */
dudmuck 0:8f0d0ae0a077 362 static LoRaMacPrimitives_t *LoRaMacPrimitives;
dudmuck 0:8f0d0ae0a077 363
dudmuck 0:8f0d0ae0a077 364 /*!
dudmuck 0:8f0d0ae0a077 365 * LoRaMac upper layer callback functions
dudmuck 0:8f0d0ae0a077 366 */
dudmuck 0:8f0d0ae0a077 367 static LoRaMacCallback_t *LoRaMacCallbacks;
dudmuck 0:8f0d0ae0a077 368
dudmuck 0:8f0d0ae0a077 369 /*!
dudmuck 0:8f0d0ae0a077 370 * Radio events function pointer
dudmuck 0:8f0d0ae0a077 371 */
dudmuck 0:8f0d0ae0a077 372 static RadioEvents_t RadioEvents;
dudmuck 0:8f0d0ae0a077 373
dudmuck 0:8f0d0ae0a077 374 /*!
dudmuck 0:8f0d0ae0a077 375 * LoRaMac duty cycle delayed Tx timer
dudmuck 0:8f0d0ae0a077 376 */
dudmuck 0:8f0d0ae0a077 377 LowPowerTimeout tx_timeout;
dudmuck 0:8f0d0ae0a077 378
dudmuck 0:8f0d0ae0a077 379
dudmuck 0:8f0d0ae0a077 380 /*!
dudmuck 0:8f0d0ae0a077 381 * LoRaMac reception windows delay
dudmuck 0:8f0d0ae0a077 382 * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME
dudmuck 0:8f0d0ae0a077 383 * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME
dudmuck 0:8f0d0ae0a077 384 */
dudmuck 0:8f0d0ae0a077 385 static uint32_t RxWindowDelay_us;
dudmuck 0:8f0d0ae0a077 386
dudmuck 0:8f0d0ae0a077 387 typedef enum {
dudmuck 0:8f0d0ae0a077 388 BEACON_STATE_NONE = 0,
dudmuck 0:8f0d0ae0a077 389 BEACON_STATE_FIRST_ACQ,
dudmuck 0:8f0d0ae0a077 390 BEACON_STATE_ACQ_ERROR,
dudmuck 21:500ff43d8424 391 BEACON_STATE_LOCKED,
dudmuck 0:8f0d0ae0a077 392 } beacon_state_e;
dudmuck 0:8f0d0ae0a077 393
dudmuck 16:915815632c1f 394 static struct beacon_struct {
dudmuck 0:8f0d0ae0a077 395 int rx_precession_us; // positive: rxing before tx start, negative: rxing after tx start
dudmuck 0:8f0d0ae0a077 396 unsigned int RxBeaconSetupAt_us;
dudmuck 0:8f0d0ae0a077 397 unsigned int LastBeaconRx_us; // updated only at beacon reception
dudmuck 13:18de9ee3a461 398 int last_BeaconRxTimerError_us;
dudmuck 13:18de9ee3a461 399 int known_working_BeaconRxTimerError_us;
dudmuck 0:8f0d0ae0a077 400
dudmuck 0:8f0d0ae0a077 401 float symbol_period_secs;
dudmuck 0:8f0d0ae0a077 402
dudmuck 0:8f0d0ae0a077 403 uint8_t Precess_symbols; // how many symbols we want to start receiver before expected transmitter
dudmuck 16:915815632c1f 404 uint16_t SymbolTimeout;
dudmuck 0:8f0d0ae0a077 405 float SymbolTimeout_sec;
dudmuck 0:8f0d0ae0a077 406 uint8_t num_missed;
dudmuck 0:8f0d0ae0a077 407
dudmuck 0:8f0d0ae0a077 408 beacon_state_e state;
dudmuck 0:8f0d0ae0a077 409
dudmuck 0:8f0d0ae0a077 410 uint16_t tx_slot_offset;
dudmuck 0:8f0d0ae0a077 411 uint16_t periodicity_slots;
dudmuck 0:8f0d0ae0a077 412
dudmuck 16:915815632c1f 413 LowPowerTimeout timeout_rx;
dudmuck 16:915815632c1f 414 LowPowerTimeout timeout_guard;
dudmuck 16:915815632c1f 415 bool guard;
dudmuck 17:3215f12051f9 416 unsigned guard_at;
dudmuck 0:8f0d0ae0a077 417 } BeaconCtx;
dudmuck 16:915815632c1f 418 bool expecting_beacon;
dudmuck 0:8f0d0ae0a077 419
dudmuck 0:8f0d0ae0a077 420 /*!
dudmuck 0:8f0d0ae0a077 421 * Rx window parameters
dudmuck 0:8f0d0ae0a077 422 */
dudmuck 0:8f0d0ae0a077 423 typedef struct
dudmuck 0:8f0d0ae0a077 424 {
dudmuck 0:8f0d0ae0a077 425 int8_t Datarate;
dudmuck 0:8f0d0ae0a077 426 uint8_t Bandwidth;
dudmuck 0:8f0d0ae0a077 427 uint32_t RxWindowTimeout;
dudmuck 0:8f0d0ae0a077 428 int32_t RxOffset;
dudmuck 0:8f0d0ae0a077 429 }RxConfigParams_t;
dudmuck 0:8f0d0ae0a077 430
dudmuck 0:8f0d0ae0a077 431 /*!
dudmuck 0:8f0d0ae0a077 432 * Rx windows params
dudmuck 0:8f0d0ae0a077 433 */
dudmuck 0:8f0d0ae0a077 434 static RxConfigParams_t RxWindowsParam;
dudmuck 0:8f0d0ae0a077 435
dudmuck 16:915815632c1f 436
dudmuck 0:8f0d0ae0a077 437 /*!
dudmuck 0:8f0d0ae0a077 438 * Acknowledge timeout timer. Used for packet retransmissions.
dudmuck 0:8f0d0ae0a077 439 */
dudmuck 0:8f0d0ae0a077 440 static LowPowerTimeout AckTimeoutTimer;
dudmuck 0:8f0d0ae0a077 441
dudmuck 0:8f0d0ae0a077 442 uint32_t TxTimeOnAir = 0;
dudmuck 0:8f0d0ae0a077 443
dudmuck 0:8f0d0ae0a077 444 /*!
dudmuck 0:8f0d0ae0a077 445 * Number of trials for the Join Request
dudmuck 0:8f0d0ae0a077 446 */
dudmuck 0:8f0d0ae0a077 447 static uint8_t JoinRequestTrials;
dudmuck 0:8f0d0ae0a077 448
dudmuck 0:8f0d0ae0a077 449 /*!
dudmuck 0:8f0d0ae0a077 450 * Maximum number of trials for the Join Request
dudmuck 0:8f0d0ae0a077 451 */
dudmuck 0:8f0d0ae0a077 452 static uint8_t MaxJoinRequestTrials;
dudmuck 0:8f0d0ae0a077 453
dudmuck 0:8f0d0ae0a077 454 /*!
dudmuck 0:8f0d0ae0a077 455 * Structure to hold an MCPS indication data.
dudmuck 0:8f0d0ae0a077 456 */
dudmuck 0:8f0d0ae0a077 457 static McpsIndication_t McpsIndication;
dudmuck 0:8f0d0ae0a077 458
dudmuck 0:8f0d0ae0a077 459 /*!
dudmuck 0:8f0d0ae0a077 460 * Structure to hold MCPS confirm data.
dudmuck 0:8f0d0ae0a077 461 */
dudmuck 0:8f0d0ae0a077 462 static McpsConfirm_t McpsConfirm;
dudmuck 0:8f0d0ae0a077 463
dudmuck 0:8f0d0ae0a077 464 /*!
dudmuck 0:8f0d0ae0a077 465 * Structure to hold MLME confirm data.
dudmuck 0:8f0d0ae0a077 466 */
dudmuck 0:8f0d0ae0a077 467 static MlmeConfirm_t MlmeConfirm;
dudmuck 0:8f0d0ae0a077 468
dudmuck 0:8f0d0ae0a077 469 /*!
dudmuck 0:8f0d0ae0a077 470 * Structure to hold MLME indication data.
dudmuck 0:8f0d0ae0a077 471 */
dudmuck 0:8f0d0ae0a077 472 static MlmeIndication_t MlmeIndication;
dudmuck 0:8f0d0ae0a077 473
dudmuck 0:8f0d0ae0a077 474 /*!
dudmuck 0:8f0d0ae0a077 475 * LoRaMac tx/rx operation state
dudmuck 0:8f0d0ae0a077 476 */
dudmuck 0:8f0d0ae0a077 477 LoRaMacFlags_t LoRaMacFlags;
dudmuck 20:42839629a5dc 478 static volatile struct {
dudmuck 20:42839629a5dc 479 uint8_t rx_win : 1;
dudmuck 20:42839629a5dc 480 uint8_t join_send : 1;
dudmuck 20:42839629a5dc 481 uint8_t beacon_setup : 1;
dudmuck 20:42839629a5dc 482 uint8_t send : 1;
dudmuck 20:42839629a5dc 483 } flags;
dudmuck 0:8f0d0ae0a077 484
dudmuck 0:8f0d0ae0a077 485 /*!
dudmuck 0:8f0d0ae0a077 486 * \brief Function to be executed on Radio Tx Done event
dudmuck 0:8f0d0ae0a077 487 */
dudmuck 0:8f0d0ae0a077 488 static void OnRadioTxDone( unsigned int tx_done_us );
dudmuck 0:8f0d0ae0a077 489 unsigned int TxDone_us;
dudmuck 0:8f0d0ae0a077 490
dudmuck 0:8f0d0ae0a077 491 /*!
dudmuck 0:8f0d0ae0a077 492 * \brief This function prepares the MAC to abort the execution of function
dudmuck 0:8f0d0ae0a077 493 * OnRadioRxDone in case of a reception error.
dudmuck 0:8f0d0ae0a077 494 */
dudmuck 0:8f0d0ae0a077 495 static void PrepareRxDoneAbort( void );
dudmuck 0:8f0d0ae0a077 496
dudmuck 0:8f0d0ae0a077 497 /*!
dudmuck 0:8f0d0ae0a077 498 * \brief Function to be executed on Radio Rx Done event
dudmuck 0:8f0d0ae0a077 499 */
dudmuck 0:8f0d0ae0a077 500 static void OnRadioRxDone(unsigned rx_us, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
dudmuck 0:8f0d0ae0a077 501
dudmuck 0:8f0d0ae0a077 502 /*!
dudmuck 0:8f0d0ae0a077 503 * \brief Function executed on Radio Tx Timeout event
dudmuck 0:8f0d0ae0a077 504 */
dudmuck 0:8f0d0ae0a077 505 static void OnRadioTxTimeout( void );
dudmuck 0:8f0d0ae0a077 506
dudmuck 0:8f0d0ae0a077 507 /*!
dudmuck 0:8f0d0ae0a077 508 * \brief Function executed on Radio Rx error event
dudmuck 0:8f0d0ae0a077 509 */
dudmuck 0:8f0d0ae0a077 510 static void OnRadioRxError( void );
dudmuck 0:8f0d0ae0a077 511
dudmuck 0:8f0d0ae0a077 512 /*!
dudmuck 0:8f0d0ae0a077 513 * \brief Function executed on Radio Rx Timeout event
dudmuck 0:8f0d0ae0a077 514 */
dudmuck 0:8f0d0ae0a077 515 static void OnRadioRxTimeout( void );
dudmuck 0:8f0d0ae0a077 516
dudmuck 0:8f0d0ae0a077 517 static void OnRxWindowTimerEvent( void );
dudmuck 0:8f0d0ae0a077 518
dudmuck 0:8f0d0ae0a077 519 /*!
dudmuck 0:8f0d0ae0a077 520 * \brief Function executed on AckTimeout timer event
dudmuck 0:8f0d0ae0a077 521 */
dudmuck 0:8f0d0ae0a077 522 static void OnAckTimeoutTimerEvent( void );
dudmuck 0:8f0d0ae0a077 523
dudmuck 0:8f0d0ae0a077 524
dudmuck 0:8f0d0ae0a077 525 /*!
dudmuck 0:8f0d0ae0a077 526 * \brief Initializes and opens the reception window
dudmuck 0:8f0d0ae0a077 527 *
dudmuck 0:8f0d0ae0a077 528 * \param [IN] freq window channel frequency
dudmuck 0:8f0d0ae0a077 529 * \param [IN] datarate window channel datarate
dudmuck 0:8f0d0ae0a077 530 * \param [IN] bandwidth window channel bandwidth
dudmuck 0:8f0d0ae0a077 531 * \param [IN] timeout window channel timeout
dudmuck 0:8f0d0ae0a077 532 *
dudmuck 0:8f0d0ae0a077 533 * \retval status Operation status [true: Success, false: Fail]
dudmuck 0:8f0d0ae0a077 534 */
dudmuck 0:8f0d0ae0a077 535 static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous );
dudmuck 0:8f0d0ae0a077 536
dudmuck 0:8f0d0ae0a077 537 /*!
dudmuck 0:8f0d0ae0a077 538 * \brief Adds a new MAC command to be sent.
dudmuck 0:8f0d0ae0a077 539 *
dudmuck 0:8f0d0ae0a077 540 * \Remark MAC layer internal function
dudmuck 0:8f0d0ae0a077 541 *
dudmuck 0:8f0d0ae0a077 542 * \param [in] cmd MAC command to be added
dudmuck 0:8f0d0ae0a077 543 * [MOTE_MAC_LINK_CHECK_REQ,
dudmuck 0:8f0d0ae0a077 544 * MOTE_MAC_LINK_ADR_ANS,
dudmuck 0:8f0d0ae0a077 545 * MOTE_MAC_DUTY_CYCLE_ANS,
dudmuck 0:8f0d0ae0a077 546 * MOTE_MAC_RX2_PARAM_SET_ANS,
dudmuck 0:8f0d0ae0a077 547 * MOTE_MAC_DEV_STATUS_ANS
dudmuck 0:8f0d0ae0a077 548 * MOTE_MAC_NEW_CHANNEL_ANS]
dudmuck 0:8f0d0ae0a077 549 * \param [in] p1 1st parameter ( optional depends on the command )
dudmuck 0:8f0d0ae0a077 550 * \param [in] p2 2nd parameter ( optional depends on the command )
dudmuck 0:8f0d0ae0a077 551 *
dudmuck 0:8f0d0ae0a077 552 * \retval status Function status [0: OK, 1: Unknown command, 2: Buffer full]
dudmuck 0:8f0d0ae0a077 553 */
dudmuck 0:8f0d0ae0a077 554 static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 );
dudmuck 0:8f0d0ae0a077 555
dudmuck 0:8f0d0ae0a077 556 /*!
dudmuck 0:8f0d0ae0a077 557 * \brief Parses the MAC commands which must be repeated.
dudmuck 0:8f0d0ae0a077 558 *
dudmuck 0:8f0d0ae0a077 559 * \Remark MAC layer internal function
dudmuck 0:8f0d0ae0a077 560 *
dudmuck 0:8f0d0ae0a077 561 * \param [IN] cmdBufIn Buffer which stores the MAC commands to send
dudmuck 0:8f0d0ae0a077 562 * \param [IN] length Length of the input buffer to parse
dudmuck 0:8f0d0ae0a077 563 * \param [OUT] cmdBufOut Buffer which stores the MAC commands which must be
dudmuck 0:8f0d0ae0a077 564 * repeated.
dudmuck 0:8f0d0ae0a077 565 *
dudmuck 0:8f0d0ae0a077 566 * \retval Size of the MAC commands to repeat.
dudmuck 0:8f0d0ae0a077 567 */
dudmuck 0:8f0d0ae0a077 568 static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut );
dudmuck 0:8f0d0ae0a077 569
dudmuck 0:8f0d0ae0a077 570 /*!
dudmuck 0:8f0d0ae0a077 571 * \brief Verifies, if a value is in a given range.
dudmuck 0:8f0d0ae0a077 572 *
dudmuck 0:8f0d0ae0a077 573 * \param value Value to verify, if it is in range
dudmuck 0:8f0d0ae0a077 574 *
dudmuck 0:8f0d0ae0a077 575 * \param min Minimum possible value
dudmuck 0:8f0d0ae0a077 576 *
dudmuck 0:8f0d0ae0a077 577 * \param max Maximum possible value
dudmuck 0:8f0d0ae0a077 578 *
dudmuck 0:8f0d0ae0a077 579 * \retval Returns the maximum valid tx power
dudmuck 0:8f0d0ae0a077 580 */
dudmuck 0:8f0d0ae0a077 581 static bool ValueInRange( int8_t value, int8_t min, int8_t max );
dudmuck 0:8f0d0ae0a077 582
dudmuck 0:8f0d0ae0a077 583
dudmuck 0:8f0d0ae0a077 584 /*!
dudmuck 0:8f0d0ae0a077 585 * \brief Decodes MAC commands in the fOpts field and in the payload
dudmuck 0:8f0d0ae0a077 586 */
dudmuck 0:8f0d0ae0a077 587 static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr );
dudmuck 0:8f0d0ae0a077 588
dudmuck 0:8f0d0ae0a077 589 /*!
dudmuck 0:8f0d0ae0a077 590 * \brief LoRaMAC layer generic send frame
dudmuck 0:8f0d0ae0a077 591 *
dudmuck 0:8f0d0ae0a077 592 * \param [IN] macHdr MAC header field
dudmuck 0:8f0d0ae0a077 593 * \param [IN] fPort MAC payload port
dudmuck 0:8f0d0ae0a077 594 * \param [IN] fBuffer MAC data buffer to be sent
dudmuck 0:8f0d0ae0a077 595 * \param [IN] fBufferSize MAC data buffer size
dudmuck 0:8f0d0ae0a077 596 * \retval status Status of the operation.
dudmuck 0:8f0d0ae0a077 597 */
dudmuck 0:8f0d0ae0a077 598 LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize );
dudmuck 0:8f0d0ae0a077 599
dudmuck 0:8f0d0ae0a077 600 /*!
dudmuck 0:8f0d0ae0a077 601 * \brief LoRaMAC layer frame buffer initialization
dudmuck 0:8f0d0ae0a077 602 *
dudmuck 0:8f0d0ae0a077 603 * \param [IN] macHdr MAC header field
dudmuck 0:8f0d0ae0a077 604 * \param [IN] fCtrl MAC frame control field
dudmuck 0:8f0d0ae0a077 605 * \param [IN] fOpts MAC commands buffer
dudmuck 0:8f0d0ae0a077 606 * \param [IN] fPort MAC payload port
dudmuck 0:8f0d0ae0a077 607 * \param [IN] fBuffer MAC data buffer to be sent
dudmuck 0:8f0d0ae0a077 608 * \param [IN] fBufferSize MAC data buffer size
dudmuck 0:8f0d0ae0a077 609 * \retval status Status of the operation.
dudmuck 0:8f0d0ae0a077 610 */
dudmuck 0:8f0d0ae0a077 611 LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize );
dudmuck 0:8f0d0ae0a077 612
dudmuck 0:8f0d0ae0a077 613 /*
dudmuck 0:8f0d0ae0a077 614 * \brief Schedules the frame according to the duty cycle
dudmuck 0:8f0d0ae0a077 615 *
dudmuck 0:8f0d0ae0a077 616 * \retval Status of the operation
dudmuck 0:8f0d0ae0a077 617 */
dudmuck 0:8f0d0ae0a077 618 static LoRaMacStatus_t ScheduleTx( void );
dudmuck 0:8f0d0ae0a077 619
dudmuck 0:8f0d0ae0a077 620
dudmuck 0:8f0d0ae0a077 621 /*!
dudmuck 0:8f0d0ae0a077 622 * \brief LoRaMAC layer prepared frame buffer transmission with channel specification
dudmuck 0:8f0d0ae0a077 623 *
dudmuck 0:8f0d0ae0a077 624 * \remark PrepareFrame must be called at least once before calling this
dudmuck 0:8f0d0ae0a077 625 * function.
dudmuck 0:8f0d0ae0a077 626 *
dudmuck 0:8f0d0ae0a077 627 * \param [IN] channel Channel parameters
dudmuck 0:8f0d0ae0a077 628 * \retval status Status of the operation.
dudmuck 0:8f0d0ae0a077 629 */
dudmuck 0:8f0d0ae0a077 630 LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel );
dudmuck 0:8f0d0ae0a077 631
dudmuck 0:8f0d0ae0a077 632 /*!
dudmuck 0:8f0d0ae0a077 633 * \brief Sets the radio in continuous transmission mode
dudmuck 0:8f0d0ae0a077 634 *
dudmuck 0:8f0d0ae0a077 635 * \remark Uses the radio parameters set on the previous transmission.
dudmuck 0:8f0d0ae0a077 636 *
dudmuck 0:8f0d0ae0a077 637 * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode
dudmuck 0:8f0d0ae0a077 638 * \retval status Status of the operation.
dudmuck 0:8f0d0ae0a077 639 */
dudmuck 0:8f0d0ae0a077 640 LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout );
dudmuck 0:8f0d0ae0a077 641
dudmuck 0:8f0d0ae0a077 642 /*!
dudmuck 0:8f0d0ae0a077 643 * \brief Sets the radio in continuous transmission mode
dudmuck 0:8f0d0ae0a077 644 *
dudmuck 0:8f0d0ae0a077 645 * \remark Uses the radio parameters set on the previous transmission.
dudmuck 0:8f0d0ae0a077 646 *
dudmuck 0:8f0d0ae0a077 647 * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode
dudmuck 0:8f0d0ae0a077 648 * \param [IN] frequency RF frequency to be set.
dudmuck 0:8f0d0ae0a077 649 * \param [IN] power RF ouptput power to be set.
dudmuck 0:8f0d0ae0a077 650 * \retval status Status of the operation.
dudmuck 0:8f0d0ae0a077 651 */
dudmuck 0:8f0d0ae0a077 652 LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power );
dudmuck 0:8f0d0ae0a077 653
dudmuck 0:8f0d0ae0a077 654 /*!
dudmuck 0:8f0d0ae0a077 655 * \brief Resets MAC specific parameters to default
dudmuck 0:8f0d0ae0a077 656 */
dudmuck 0:8f0d0ae0a077 657 static void ResetMacParameters( void );
dudmuck 0:8f0d0ae0a077 658
dudmuck 0:8f0d0ae0a077 659 void
dudmuck 0:8f0d0ae0a077 660 loramac_print_status()
dudmuck 0:8f0d0ae0a077 661 {
dudmuck 16:915815632c1f 662 isr_printf("DR%u=sf%u guard:%d\r\n",
dudmuck 16:915815632c1f 663 LoRaMacParams.ChannelsDatarate_fixed,
dudmuck 16:915815632c1f 664 Datarates[LoRaMacParams.ChannelsDatarate_fixed],
dudmuck 16:915815632c1f 665 BeaconCtx.guard
dudmuck 16:915815632c1f 666 );
dudmuck 0:8f0d0ae0a077 667 }
dudmuck 0:8f0d0ae0a077 668
dudmuck 0:8f0d0ae0a077 669 /*
dudmuck 0:8f0d0ae0a077 670 * Rx window precise timing
dudmuck 0:8f0d0ae0a077 671 *
dudmuck 0:8f0d0ae0a077 672 * For more details please consult the following document, chapter 3.1.2.
dudmuck 0:8f0d0ae0a077 673 * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf
dudmuck 0:8f0d0ae0a077 674 * or
dudmuck 0:8f0d0ae0a077 675 * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf
dudmuck 0:8f0d0ae0a077 676 *
dudmuck 0:8f0d0ae0a077 677 * Downlink start: T = Tx + 1s (+/- 20 us)
dudmuck 0:8f0d0ae0a077 678 * |
dudmuck 0:8f0d0ae0a077 679 * TRxEarly | TRxLate
dudmuck 0:8f0d0ae0a077 680 * | | |
dudmuck 0:8f0d0ae0a077 681 * | | +---+---+---+---+---+---+---+---+
dudmuck 0:8f0d0ae0a077 682 * | | | Latest Rx window |
dudmuck 0:8f0d0ae0a077 683 * | | +---+---+---+---+---+---+---+---+
dudmuck 0:8f0d0ae0a077 684 * | | |
dudmuck 0:8f0d0ae0a077 685 * +---+---+---+---+---+---+---+---+
dudmuck 0:8f0d0ae0a077 686 * | Earliest Rx window |
dudmuck 0:8f0d0ae0a077 687 * +---+---+---+---+---+---+---+---+
dudmuck 0:8f0d0ae0a077 688 * |
dudmuck 0:8f0d0ae0a077 689 * +---+---+---+---+---+---+---+---+
dudmuck 0:8f0d0ae0a077 690 *Downlink preamble 8 symbols | | | | | | | | |
dudmuck 0:8f0d0ae0a077 691 * +---+---+---+---+---+---+---+---+
dudmuck 0:8f0d0ae0a077 692 *
dudmuck 0:8f0d0ae0a077 693 * Worst case Rx window timings
dudmuck 0:8f0d0ae0a077 694 *
dudmuck 0:8f0d0ae0a077 695 * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME
dudmuck 0:8f0d0ae0a077 696 * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME
dudmuck 0:8f0d0ae0a077 697 *
dudmuck 0:8f0d0ae0a077 698 * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
dudmuck 0:8f0d0ae0a077 699 *
dudmuck 0:8f0d0ae0a077 700 * RxOffset = ( TRxLate + TRxEarly ) / 2
dudmuck 0:8f0d0ae0a077 701 *
dudmuck 0:8f0d0ae0a077 702 * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
dudmuck 0:8f0d0ae0a077 703 * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME
dudmuck 0:8f0d0ae0a077 704 *
dudmuck 0:8f0d0ae0a077 705 * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol
dudmuck 0:8f0d0ae0a077 706 */
dudmuck 0:8f0d0ae0a077 707 /*!
dudmuck 0:8f0d0ae0a077 708 * Computes the Rx window parameters.
dudmuck 0:8f0d0ae0a077 709 *
dudmuck 0:8f0d0ae0a077 710 * \param [IN] datarate Rx window datarate to be used
dudmuck 0:8f0d0ae0a077 711 * \param [IN] rxError Maximum timing error of the receiver. in milliseconds
dudmuck 0:8f0d0ae0a077 712 * The receiver will turn on in a [-rxError : +rxError] ms
dudmuck 0:8f0d0ae0a077 713 * interval around RxOffset
dudmuck 0:8f0d0ae0a077 714 *
dudmuck 0:8f0d0ae0a077 715 * \retval rxConfigParams Returns a RxConfigParams_t structure.
dudmuck 0:8f0d0ae0a077 716 */
dudmuck 0:8f0d0ae0a077 717 static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError );
dudmuck 0:8f0d0ae0a077 718
dudmuck 0:8f0d0ae0a077 719 static void OnRadioTxDone( unsigned int tx_done_us )
dudmuck 0:8f0d0ae0a077 720 {
dudmuck 0:8f0d0ae0a077 721 // Setup timers
dudmuck 16:915815632c1f 722 if (IsRxWindowsEnabled)
dudmuck 0:8f0d0ae0a077 723 {
dudmuck 0:8f0d0ae0a077 724 rx_timeout.attach_us(&OnRxWindowTimerEvent, RxWindowDelay_us);
dudmuck 16:915815632c1f 725 if (NodeAckRequested)
dudmuck 0:8f0d0ae0a077 726 {
dudmuck 0:8f0d0ae0a077 727 AckTimeoutTimer.attach_us(&OnAckTimeoutTimerEvent, (RxWindowDelay_us/1000) + ACK_TIMEOUT_us + randr(-ACK_TIMEOUT_RND_us, ACK_TIMEOUT_RND_us));
dudmuck 0:8f0d0ae0a077 728 }
dudmuck 0:8f0d0ae0a077 729 }
dudmuck 0:8f0d0ae0a077 730 else
dudmuck 0:8f0d0ae0a077 731 {
dudmuck 0:8f0d0ae0a077 732 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 0:8f0d0ae0a077 733 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT;
dudmuck 0:8f0d0ae0a077 734
dudmuck 0:8f0d0ae0a077 735 if( LoRaMacFlags.Value == 0 )
dudmuck 0:8f0d0ae0a077 736 {
dudmuck 0:8f0d0ae0a077 737 LoRaMacFlags.Bits.McpsReq = 1;
dudmuck 0:8f0d0ae0a077 738 }
dudmuck 0:8f0d0ae0a077 739 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 0:8f0d0ae0a077 740 }
dudmuck 16:915815632c1f 741 Radio.Sleep( );
dudmuck 16:915815632c1f 742 TxDone_us = tx_done_us;
dudmuck 0:8f0d0ae0a077 743
dudmuck 0:8f0d0ae0a077 744 // Update Aggregated last tx done time
dudmuck 0:8f0d0ae0a077 745 AggregatedLastTxDoneTime_us = tx_done_us;
dudmuck 0:8f0d0ae0a077 746 // Update Backoff
dudmuck 0:8f0d0ae0a077 747
dudmuck 16:915815632c1f 748 if (!NodeAckRequested)
dudmuck 0:8f0d0ae0a077 749 {
dudmuck 0:8f0d0ae0a077 750 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 0:8f0d0ae0a077 751 ChannelsNbRepCounter++;
dudmuck 0:8f0d0ae0a077 752 }
dudmuck 10:00997daeb0c0 753
dudmuck 10:00997daeb0c0 754 MlmeIndication.MlmeIndication = MLME_TXDONE;
dudmuck 10:00997daeb0c0 755 MlmeIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 16:915815632c1f 756 LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
dudmuck 16:915815632c1f 757 }
dudmuck 16:915815632c1f 758
dudmuck 16:915815632c1f 759
dudmuck 16:915815632c1f 760 static void application_callbacks()
dudmuck 16:915815632c1f 761 {
dudmuck 16:915815632c1f 762 if (LoRaMacFlags.Bits.McpsInd) {
dudmuck 16:915815632c1f 763 LoRaMacFlags.Bits.McpsInd = 0;
dudmuck 16:915815632c1f 764 LoRaMacPrimitives->MacMcpsIndication( &McpsIndication );
dudmuck 16:915815632c1f 765 }
dudmuck 16:915815632c1f 766
dudmuck 16:915815632c1f 767 if (LoRaMacFlags.Bits.McpsReq) {
dudmuck 16:915815632c1f 768 LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm );
dudmuck 16:915815632c1f 769 LoRaMacFlags.Bits.McpsReq = 0;
dudmuck 16:915815632c1f 770 }
dudmuck 0:8f0d0ae0a077 771 }
dudmuck 0:8f0d0ae0a077 772
dudmuck 0:8f0d0ae0a077 773 static void PrepareRxDoneAbort( void )
dudmuck 0:8f0d0ae0a077 774 {
dudmuck 16:915815632c1f 775 if (NodeAckRequested)
dudmuck 0:8f0d0ae0a077 776 {
dudmuck 0:8f0d0ae0a077 777 OnAckTimeoutTimerEvent( );
dudmuck 0:8f0d0ae0a077 778 }
dudmuck 0:8f0d0ae0a077 779
dudmuck 0:8f0d0ae0a077 780 LoRaMacFlags.Bits.McpsInd = 1;
dudmuck 0:8f0d0ae0a077 781 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 16:915815632c1f 782
dudmuck 16:915815632c1f 783 application_callbacks();
dudmuck 0:8f0d0ae0a077 784 }
dudmuck 0:8f0d0ae0a077 785
dudmuck 20:42839629a5dc 786 void send_bh()
dudmuck 16:915815632c1f 787 {
dudmuck 5:c108560af4c3 788 Radio.SetTxConfig(
dudmuck 5:c108560af4c3 789 /* RadioModems_t modem */ MODEM_LORA,
dudmuck 5:c108560af4c3 790 /* int8_t power */ TxPowers[LoRaMacParams.ChannelsTxPower],
dudmuck 5:c108560af4c3 791 /* uint32_t fdev */ 0,
dudmuck 18:9ac71c0eb70d 792 /* uint32_t bandwidth */ LORA_BANDWIDTH,
dudmuck 5:c108560af4c3 793 /* uint32_t datarate */ Datarates[LoRaMacParams.ChannelsDatarate_fixed],
dudmuck 5:c108560af4c3 794 /* uint8_t coderate */ 1,
dudmuck 5:c108560af4c3 795 /* uint16_t preambleLen */ 8,
dudmuck 5:c108560af4c3 796 /* bool fixLen */ false,
dudmuck 5:c108560af4c3 797 /* bool crcOn */ true,
dudmuck 5:c108560af4c3 798 /* bool freqHopOn */ 0,
dudmuck 5:c108560af4c3 799 /* uint8_t hopPeriod */ 0,
dudmuck 5:c108560af4c3 800 /* bool iqInverted */ false,
dudmuck 5:c108560af4c3 801 /* uint32_t timeout */ 3e3
dudmuck 5:c108560af4c3 802 );
dudmuck 16:915815632c1f 803 Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen );
dudmuck 16:915815632c1f 804
dudmuck 20:42839629a5dc 805 }
dudmuck 20:42839629a5dc 806
dudmuck 20:42839629a5dc 807 void send_callback()
dudmuck 20:42839629a5dc 808 {
dudmuck 20:42839629a5dc 809 if (BeaconCtx.guard) {
dudmuck 20:42839629a5dc 810 if (LoRaMacFlags.Bits.pending_tx) {
dudmuck 20:42839629a5dc 811 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_INCOMPLETE;
dudmuck 20:42839629a5dc 812 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_INCOMPLETE;
dudmuck 20:42839629a5dc 813 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 20:42839629a5dc 814 LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm );
dudmuck 20:42839629a5dc 815 }
dudmuck 20:42839629a5dc 816 return;
dudmuck 20:42839629a5dc 817 }
dudmuck 20:42839629a5dc 818
dudmuck 20:42839629a5dc 819 tx_timeout.attach_us(&send_callback, BeaconCtx.periodicity_slots * 30000);
dudmuck 20:42839629a5dc 820
dudmuck 20:42839629a5dc 821 if (!LoRaMacFlags.Bits.pending_tx)
dudmuck 20:42839629a5dc 822 return;
dudmuck 20:42839629a5dc 823
dudmuck 20:42839629a5dc 824 flags.send = 1;
dudmuck 20:42839629a5dc 825
dudmuck 16:915815632c1f 826 LoRaMacFlags.Bits.pending_tx = 0;
dudmuck 0:8f0d0ae0a077 827 }
dudmuck 0:8f0d0ae0a077 828
dudmuck 0:8f0d0ae0a077 829 void OnRxBeaconSetup()
dudmuck 0:8f0d0ae0a077 830 {
dudmuck 20:42839629a5dc 831 flags.beacon_setup = 1;
dudmuck 20:42839629a5dc 832 }
dudmuck 20:42839629a5dc 833
dudmuck 20:42839629a5dc 834 void OnRxBeaconSetupBH()
dudmuck 20:42839629a5dc 835 {
dudmuck 16:915815632c1f 836 unsigned int prevRxBeaconSetupAt_us;
dudmuck 16:915815632c1f 837 unsigned now_us = lp_timer.read_us();
dudmuck 20:42839629a5dc 838
dudmuck 16:915815632c1f 839 prevRxBeaconSetupAt_us = BeaconCtx.RxBeaconSetupAt_us;
dudmuck 16:915815632c1f 840 BeaconCtx.RxBeaconSetupAt_us = now_us;
dudmuck 10:00997daeb0c0 841 expecting_beacon = true;
dudmuck 10:00997daeb0c0 842
dudmuck 0:8f0d0ae0a077 843 Radio.SetRxConfig(
dudmuck 0:8f0d0ae0a077 844 /* RadioModems_t */ MODEM_LORA,
dudmuck 18:9ac71c0eb70d 845 /* uint32_t bandwidth */ LORA_BANDWIDTH,
dudmuck 0:8f0d0ae0a077 846 /* uint32_t datarate */ Datarates[BEACON_CHANNEL_DR],
dudmuck 0:8f0d0ae0a077 847 /* uint8_t coderate */ 1,
dudmuck 0:8f0d0ae0a077 848 /* uint32_t bandwidthAfc */ 0,
dudmuck 0:8f0d0ae0a077 849 /* uint16_t preambleLen */ 10,
dudmuck 0:8f0d0ae0a077 850 /* uint16_t symbTimeout */ BeaconCtx.SymbolTimeout,
dudmuck 0:8f0d0ae0a077 851 /* bool fixLen */ true,
dudmuck 0:8f0d0ae0a077 852 /* uint8_t payloadLen */ BEACON_SIZE,
dudmuck 0:8f0d0ae0a077 853 /* bool crcOn */ false,
dudmuck 0:8f0d0ae0a077 854 /* bool freqHopOn */ 0,
dudmuck 0:8f0d0ae0a077 855 /* uint8_t hopPeriod */ 0,
dudmuck 0:8f0d0ae0a077 856 /* bool iqInverted */ false,
dudmuck 0:8f0d0ae0a077 857 /* bool rxContinuous */false
dudmuck 0:8f0d0ae0a077 858 );
dudmuck 0:8f0d0ae0a077 859
dudmuck 0:8f0d0ae0a077 860 Radio.Rx(2000);
dudmuck 16:915815632c1f 861
dudmuck 16:915815632c1f 862 unsigned int us_since_last_beacon_start = BeaconCtx.RxBeaconSetupAt_us - prevRxBeaconSetupAt_us;
dudmuck 16:915815632c1f 863 isr_printf("OnRxBeaconSetup() %u since-last:%u\r\n", BeaconCtx.SymbolTimeout, us_since_last_beacon_start);
dudmuck 16:915815632c1f 864 }
dudmuck 16:915815632c1f 865
dudmuck 16:915815632c1f 866 void guard_callback()
dudmuck 16:915815632c1f 867 {
dudmuck 16:915815632c1f 868 unsigned now_us = lp_timer.read_us();
dudmuck 21:500ff43d8424 869 unsigned since_last = now_us - BeaconCtx.guard_at;
dudmuck 21:500ff43d8424 870
dudmuck 23:a862b5601663 871 if (BeaconCtx.state != BEACON_STATE_FIRST_ACQ && since_last < (BEACON_INTERVAL_us - 1000000)) {
dudmuck 21:500ff43d8424 872 isr_printf("guard-fail %u\r\n", since_last);
dudmuck 21:500ff43d8424 873 BeaconCtx.timeout_rx.detach();
dudmuck 21:500ff43d8424 874 BeaconCtx.state = BEACON_STATE_NONE;
dudmuck 21:500ff43d8424 875 IsLoRaMacNetworkJoined = false;
dudmuck 23:a862b5601663 876
dudmuck 23:a862b5601663 877 /*McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_STOP;
dudmuck 23:a862b5601663 878 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_STOP;
dudmuck 23:a862b5601663 879 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 23:a862b5601663 880 LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm );*/
dudmuck 21:500ff43d8424 881 return;
dudmuck 21:500ff43d8424 882 }
dudmuck 16:915815632c1f 883
dudmuck 16:915815632c1f 884 /* occurs BEACON_GUARD_us prior to OnRxBeaconSetup() */
dudmuck 16:915815632c1f 885 BeaconCtx.guard = true;
dudmuck 21:500ff43d8424 886
dudmuck 21:500ff43d8424 887 isr_printf("guard since-last:%u\r\n", since_last);
dudmuck 17:3215f12051f9 888
dudmuck 17:3215f12051f9 889 BeaconCtx.guard_at = now_us;
dudmuck 0:8f0d0ae0a077 890 }
dudmuck 0:8f0d0ae0a077 891
dudmuck 0:8f0d0ae0a077 892 static void set_beacon_symbol_timeout(float secs)
dudmuck 0:8f0d0ae0a077 893 {
dudmuck 16:915815632c1f 894 isr_printf("symTo:%.1f ", secs);
dudmuck 0:8f0d0ae0a077 895 BeaconCtx.SymbolTimeout = secs / BeaconCtx.symbol_period_secs;
dudmuck 0:8f0d0ae0a077 896 if (BeaconCtx.SymbolTimeout < (BEACON_MIN_SYMBOL_TIMEOUT+BeaconCtx.Precess_symbols)) {
dudmuck 8:ab2f9a8d2eaa 897 BeaconCtx.SymbolTimeout = BEACON_MIN_SYMBOL_TIMEOUT+BeaconCtx.Precess_symbols;
dudmuck 0:8f0d0ae0a077 898 }
dudmuck 8:ab2f9a8d2eaa 899 BeaconCtx.SymbolTimeout_sec = BeaconCtx.SymbolTimeout * BeaconCtx.symbol_period_secs;
dudmuck 16:915815632c1f 900 isr_printf("%u\r\n", BeaconCtx.SymbolTimeout);
dudmuck 0:8f0d0ae0a077 901 }
dudmuck 0:8f0d0ae0a077 902
dudmuck 0:8f0d0ae0a077 903 static uint16_t beacon_crc( uint8_t *buffer, uint16_t length )
dudmuck 0:8f0d0ae0a077 904 {
dudmuck 0:8f0d0ae0a077 905 // The CRC calculation follows CCITT
dudmuck 0:8f0d0ae0a077 906 const uint16_t polynom = 0x1021;
dudmuck 0:8f0d0ae0a077 907 // CRC initial value
dudmuck 0:8f0d0ae0a077 908 uint16_t crc = 0x0000;
dudmuck 0:8f0d0ae0a077 909
dudmuck 0:8f0d0ae0a077 910 if( buffer == NULL )
dudmuck 0:8f0d0ae0a077 911 {
dudmuck 0:8f0d0ae0a077 912 return 0;
dudmuck 0:8f0d0ae0a077 913 }
dudmuck 0:8f0d0ae0a077 914
dudmuck 0:8f0d0ae0a077 915 for( uint16_t i = 0; i < length; ++i )
dudmuck 0:8f0d0ae0a077 916 {
dudmuck 0:8f0d0ae0a077 917 crc ^= ( uint16_t ) buffer[i] << 8;
dudmuck 0:8f0d0ae0a077 918 for( uint16_t j = 0; j < 8; ++j )
dudmuck 0:8f0d0ae0a077 919 {
dudmuck 0:8f0d0ae0a077 920 crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
dudmuck 0:8f0d0ae0a077 921 }
dudmuck 0:8f0d0ae0a077 922 }
dudmuck 0:8f0d0ae0a077 923
dudmuck 0:8f0d0ae0a077 924 return crc;
dudmuck 0:8f0d0ae0a077 925 }
dudmuck 0:8f0d0ae0a077 926
dudmuck 0:8f0d0ae0a077 927 /* low power timer needs larger value due to poor resolution */
dudmuck 0:8f0d0ae0a077 928 #define TARGET_PRECESSION_US 3000
dudmuck 0:8f0d0ae0a077 929 #define BEACON_RX_TIMEOUT_LOCKED 0.008
dudmuck 0:8f0d0ae0a077 930
dudmuck 0:8f0d0ae0a077 931 void rx_beacon(unsigned int rx_us, uint8_t* payload, uint16_t size)
dudmuck 0:8f0d0ae0a077 932 {
dudmuck 0:8f0d0ae0a077 933 static bool compensate_precession = false;
dudmuck 0:8f0d0ae0a077 934 int32_t compensation = 0;
dudmuck 0:8f0d0ae0a077 935 unsigned ThisBeaconRx_us = rx_us - (BEACON_TOA_us + BEACON_RXDONE_LATENCY_us);
dudmuck 0:8f0d0ae0a077 936
dudmuck 16:915815632c1f 937 BeaconCtx.guard = false;
dudmuck 0:8f0d0ae0a077 938 BeaconCtx.rx_precession_us = ThisBeaconRx_us - BeaconCtx.RxBeaconSetupAt_us;
dudmuck 0:8f0d0ae0a077 939 if (BeaconCtx.state != BEACON_STATE_FIRST_ACQ) {
dudmuck 12:ed33c53afcaf 940 unsigned int us_since_last = ThisBeaconRx_us - BeaconCtx.LastBeaconRx_us;
dudmuck 12:ed33c53afcaf 941 unsigned int intervals_since_last = us_since_last / BEACON_INTERVAL_us;
dudmuck 13:18de9ee3a461 942 BeaconCtx.known_working_BeaconRxTimerError_us = BeaconCtx.last_BeaconRxTimerError_us;
dudmuck 0:8f0d0ae0a077 943 /* get average of error history */
dudmuck 13:18de9ee3a461 944 BeaconCtx.last_BeaconRxTimerError_us = (ThisBeaconRx_us - BeaconCtx.LastBeaconRx_us) % BEACON_INTERVAL_us;
dudmuck 0:8f0d0ae0a077 945 /* BeaconRxTimerError: positive means our clock is fast
dudmuck 0:8f0d0ae0a077 946 * negative means our clock is slow */
dudmuck 13:18de9ee3a461 947 if (BeaconCtx.last_BeaconRxTimerError_us > (BEACON_INTERVAL_us/2))
dudmuck 13:18de9ee3a461 948 BeaconCtx.last_BeaconRxTimerError_us -= BEACON_INTERVAL_us; // negative value representing slow crystal
dudmuck 0:8f0d0ae0a077 949
dudmuck 0:8f0d0ae0a077 950 if (intervals_since_last > 1) {
dudmuck 0:8f0d0ae0a077 951 /* timer error is measured over more than one beacon period */
dudmuck 13:18de9ee3a461 952 BeaconCtx.last_BeaconRxTimerError_us /= intervals_since_last;
dudmuck 0:8f0d0ae0a077 953 }
dudmuck 0:8f0d0ae0a077 954
dudmuck 0:8f0d0ae0a077 955 if (BeaconCtx.state == BEACON_STATE_ACQ_ERROR) {
dudmuck 1:53c30224eda8 956 isr_printf("-->LOCKED ");
dudmuck 21:500ff43d8424 957 BeaconCtx.state = BEACON_STATE_LOCKED;
dudmuck 0:8f0d0ae0a077 958 compensate_precession = true;
dudmuck 0:8f0d0ae0a077 959 set_beacon_symbol_timeout(BEACON_RX_TIMEOUT_LOCKED);
dudmuck 0:8f0d0ae0a077 960 }
dudmuck 0:8f0d0ae0a077 961 } else {
dudmuck 0:8f0d0ae0a077 962 /* ignore precession at first acquisition because it has slot resolution added */
dudmuck 1:53c30224eda8 963 isr_printf("-->ACQ_ERROR ");
dudmuck 0:8f0d0ae0a077 964 // next beacon will give us our crystal error
dudmuck 0:8f0d0ae0a077 965 BeaconCtx.state = BEACON_STATE_ACQ_ERROR;
dudmuck 0:8f0d0ae0a077 966 }
dudmuck 0:8f0d0ae0a077 967
dudmuck 13:18de9ee3a461 968 #ifdef DEBUG_GWTX_JUMPER
dudmuck 13:18de9ee3a461 969 isr_printf("rx-before-gwtx:%d ", gwtx_rise_us - BeaconCtx.RxBeaconSetupAt_us);
dudmuck 13:18de9ee3a461 970 #endif /* DEBUG_GWTX_JUMPER */
dudmuck 13:18de9ee3a461 971 isr_printf("err%d=%u-%u ", BeaconCtx.last_BeaconRxTimerError_us, ThisBeaconRx_us, BeaconCtx.LastBeaconRx_us);
dudmuck 1:53c30224eda8 972 isr_printf(" rx-before-tx:%d ", BeaconCtx.rx_precession_us);
dudmuck 0:8f0d0ae0a077 973 BeaconCtx.LastBeaconRx_us = ThisBeaconRx_us;
dudmuck 0:8f0d0ae0a077 974
dudmuck 21:500ff43d8424 975 if (BeaconCtx.state == BEACON_STATE_LOCKED) {
dudmuck 0:8f0d0ae0a077 976 if (compensate_precession) {
dudmuck 13:18de9ee3a461 977 compensation = BeaconCtx.rx_precession_us - TARGET_PRECESSION_US + BeaconCtx.last_BeaconRxTimerError_us;
dudmuck 1:53c30224eda8 978 isr_printf(" comp%ld", compensation);
dudmuck 0:8f0d0ae0a077 979 }
dudmuck 0:8f0d0ae0a077 980 }
dudmuck 0:8f0d0ae0a077 981
dudmuck 16:915815632c1f 982 unsigned int next_beacon_expected_us = BEACON_INTERVAL_us + compensation;
dudmuck 0:8f0d0ae0a077 983 unsigned now_us = lp_timer.read_us();
dudmuck 0:8f0d0ae0a077 984 unsigned us_since_rx_setup = now_us - BeaconCtx.RxBeaconSetupAt_us;
dudmuck 16:915815632c1f 985 unsigned timeout_us = next_beacon_expected_us - us_since_rx_setup;
dudmuck 16:915815632c1f 986 BeaconCtx.timeout_rx.attach_us(&OnRxBeaconSetup, timeout_us);
dudmuck 16:915815632c1f 987 BeaconCtx.timeout_guard.attach_us(&guard_callback, timeout_us - BEACON_GUARD_us);
dudmuck 16:915815632c1f 988
dudmuck 16:915815632c1f 989 /***************************************/
dudmuck 16:915815632c1f 990 unsigned us_since_beacon_start = now_us - ThisBeaconRx_us;
dudmuck 16:915815632c1f 991 timeout_us = BEACON_RESERVED_us + (BeaconCtx.tx_slot_offset * 30000) - us_since_beacon_start;
dudmuck 16:915815632c1f 992 tx_timeout.attach_us(&send_callback, timeout_us);
dudmuck 16:915815632c1f 993 /***************************************/
dudmuck 16:915815632c1f 994 isr_printf(" nextTx:%u\r\n", timeout_us);
dudmuck 0:8f0d0ae0a077 995
dudmuck 0:8f0d0ae0a077 996 if (BeaconCtx.num_missed > 0) {
dudmuck 0:8f0d0ae0a077 997 /* restore rx symbol timeout */
dudmuck 0:8f0d0ae0a077 998 set_beacon_symbol_timeout(BEACON_RX_TIMEOUT_LOCKED);
dudmuck 0:8f0d0ae0a077 999 }
dudmuck 0:8f0d0ae0a077 1000
dudmuck 0:8f0d0ae0a077 1001 BeaconCtx.num_missed = 0;
dudmuck 0:8f0d0ae0a077 1002
dudmuck 0:8f0d0ae0a077 1003 MlmeIndication.MlmeIndication = MLME_BEACON;
dudmuck 0:8f0d0ae0a077 1004 MlmeIndication.Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED;
dudmuck 0:8f0d0ae0a077 1005 LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
dudmuck 0:8f0d0ae0a077 1006
dudmuck 0:8f0d0ae0a077 1007 /* check beacon payload */
dudmuck 0:8f0d0ae0a077 1008 uint16_t calc_crc = beacon_crc(payload, 4);
dudmuck 0:8f0d0ae0a077 1009 uint16_t rx_crc = payload[4];
dudmuck 0:8f0d0ae0a077 1010 rx_crc |= payload[5] << 8;
dudmuck 0:8f0d0ae0a077 1011 if (rx_crc == calc_crc) {
dudmuck 0:8f0d0ae0a077 1012 unsigned int rx = payload[0];
dudmuck 0:8f0d0ae0a077 1013 rx |= payload[1] << 8;
dudmuck 0:8f0d0ae0a077 1014 rx |= payload[2] << 16;
dudmuck 0:8f0d0ae0a077 1015 rx |= payload[3] << 24;
dudmuck 10:00997daeb0c0 1016 if (rx != 0) {
dudmuck 10:00997daeb0c0 1017 //isr_printf("beacon payload:%08x\r\n", rx);
dudmuck 10:00997daeb0c0 1018 LoRaMacRxPayload[0] = payload[0];
dudmuck 10:00997daeb0c0 1019 LoRaMacRxPayload[1] = payload[1];
dudmuck 10:00997daeb0c0 1020 LoRaMacRxPayload[2] = payload[2];
dudmuck 10:00997daeb0c0 1021 LoRaMacRxPayload[3] = payload[3];
dudmuck 10:00997daeb0c0 1022 McpsIndication.McpsIndication = MCPS_MULTICAST;
dudmuck 10:00997daeb0c0 1023 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 10:00997daeb0c0 1024 McpsIndication.Buffer = LoRaMacRxPayload;
dudmuck 10:00997daeb0c0 1025 McpsIndication.BufferSize = 4;
dudmuck 10:00997daeb0c0 1026 McpsIndication.RxData = true;
dudmuck 16:915815632c1f 1027 LoRaMacPrimitives->MacMcpsIndication( &McpsIndication );
dudmuck 10:00997daeb0c0 1028 }
dudmuck 0:8f0d0ae0a077 1029 } else
dudmuck 1:53c30224eda8 1030 isr_printf("calc_crc:%04x rx_crc:%04x\r\n", calc_crc, rx_crc);
dudmuck 0:8f0d0ae0a077 1031 }
dudmuck 0:8f0d0ae0a077 1032
dudmuck 0:8f0d0ae0a077 1033 float get_symbol_period(uint8_t bw, uint8_t sf)
dudmuck 0:8f0d0ae0a077 1034 {
dudmuck 0:8f0d0ae0a077 1035 //bw:, // 0=125kHz, 1=250kHz, 2=500KHz
dudmuck 0:8f0d0ae0a077 1036 float hz;
dudmuck 0:8f0d0ae0a077 1037 switch( bw )
dudmuck 0:8f0d0ae0a077 1038 {
dudmuck 0:8f0d0ae0a077 1039 //case 0: // 7.8 kHz
dudmuck 0:8f0d0ae0a077 1040 // bw = 78e2;
dudmuck 0:8f0d0ae0a077 1041 // break;
dudmuck 0:8f0d0ae0a077 1042 //case 1: // 10.4 kHz
dudmuck 0:8f0d0ae0a077 1043 // bw = 104e2;
dudmuck 0:8f0d0ae0a077 1044 // break;
dudmuck 0:8f0d0ae0a077 1045 //case 2: // 15.6 kHz
dudmuck 0:8f0d0ae0a077 1046 // bw = 156e2;
dudmuck 0:8f0d0ae0a077 1047 // break;
dudmuck 0:8f0d0ae0a077 1048 //case 3: // 20.8 kHz
dudmuck 0:8f0d0ae0a077 1049 // bw = 208e2;
dudmuck 0:8f0d0ae0a077 1050 // break;
dudmuck 0:8f0d0ae0a077 1051 //case 4: // 31.2 kHz
dudmuck 0:8f0d0ae0a077 1052 // bw = 312e2;
dudmuck 0:8f0d0ae0a077 1053 // break;
dudmuck 0:8f0d0ae0a077 1054 //case 5: // 41.4 kHz
dudmuck 0:8f0d0ae0a077 1055 // bw = 414e2;
dudmuck 0:8f0d0ae0a077 1056 // break;
dudmuck 0:8f0d0ae0a077 1057 //case 6: // 62.5 kHz
dudmuck 0:8f0d0ae0a077 1058 // bw = 625e2;
dudmuck 0:8f0d0ae0a077 1059 // break;
dudmuck 0:8f0d0ae0a077 1060 case 0: // 125 kHz
dudmuck 0:8f0d0ae0a077 1061 hz = 125e3;
dudmuck 0:8f0d0ae0a077 1062 break;
dudmuck 0:8f0d0ae0a077 1063 case 1: // 250 kHz
dudmuck 0:8f0d0ae0a077 1064 hz = 250e3;
dudmuck 0:8f0d0ae0a077 1065 break;
dudmuck 0:8f0d0ae0a077 1066 case 2: // 500 kHz
dudmuck 0:8f0d0ae0a077 1067 hz = 500e3;
dudmuck 0:8f0d0ae0a077 1068 break;
dudmuck 0:8f0d0ae0a077 1069 default:
dudmuck 0:8f0d0ae0a077 1070 return 0;
dudmuck 0:8f0d0ae0a077 1071 }
dudmuck 0:8f0d0ae0a077 1072
dudmuck 0:8f0d0ae0a077 1073 // return symbol period in seconds
dudmuck 0:8f0d0ae0a077 1074 return (1 << sf) / hz;
dudmuck 0:8f0d0ae0a077 1075 }
dudmuck 0:8f0d0ae0a077 1076
dudmuck 0:8f0d0ae0a077 1077 static uint32_t GetRxBandwidth( int8_t datarate )
dudmuck 0:8f0d0ae0a077 1078 {
dudmuck 0:8f0d0ae0a077 1079 return 2; /* always 500KHz */
dudmuck 0:8f0d0ae0a077 1080 }
dudmuck 0:8f0d0ae0a077 1081
dudmuck 0:8f0d0ae0a077 1082 static void OnRadioRxDone(unsigned rx_us, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
dudmuck 0:8f0d0ae0a077 1083 {
dudmuck 0:8f0d0ae0a077 1084 LoRaMacHeader_t macHdr;
dudmuck 0:8f0d0ae0a077 1085 LoRaMacFrameCtrl_t fCtrl;
dudmuck 0:8f0d0ae0a077 1086 bool skipIndication = false;
dudmuck 0:8f0d0ae0a077 1087
dudmuck 0:8f0d0ae0a077 1088 uint8_t pktHeaderLen = 0;
dudmuck 0:8f0d0ae0a077 1089 uint32_t address = 0;
dudmuck 0:8f0d0ae0a077 1090 uint8_t appPayloadStartIndex = 0;
dudmuck 0:8f0d0ae0a077 1091 uint8_t port = 0xFF;
dudmuck 0:8f0d0ae0a077 1092 uint8_t frameLen = 0;
dudmuck 0:8f0d0ae0a077 1093 uint32_t mic = 0;
dudmuck 0:8f0d0ae0a077 1094 uint32_t micRx = 0;
dudmuck 0:8f0d0ae0a077 1095
dudmuck 0:8f0d0ae0a077 1096 uint16_t sequenceCounter = 0;
dudmuck 0:8f0d0ae0a077 1097 uint16_t sequenceCounterPrev = 0;
dudmuck 0:8f0d0ae0a077 1098 uint16_t sequenceCounterDiff = 0;
dudmuck 0:8f0d0ae0a077 1099 uint32_t downLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 1100
dudmuck 0:8f0d0ae0a077 1101 MulticastParams_t *curMulticastParams = NULL;
dudmuck 0:8f0d0ae0a077 1102 uint8_t *nwkSKey = LoRaMacNwkSKey;
dudmuck 0:8f0d0ae0a077 1103 uint8_t *appSKey = LoRaMacAppSKey;
dudmuck 0:8f0d0ae0a077 1104
dudmuck 0:8f0d0ae0a077 1105 uint8_t multicast = 0;
dudmuck 0:8f0d0ae0a077 1106
dudmuck 0:8f0d0ae0a077 1107 bool isMicOk = false;
dudmuck 0:8f0d0ae0a077 1108
dudmuck 0:8f0d0ae0a077 1109 McpsConfirm.AckReceived = false;
dudmuck 0:8f0d0ae0a077 1110 McpsIndication.Rssi = rssi;
dudmuck 0:8f0d0ae0a077 1111 McpsIndication.Snr = snr;
dudmuck 0:8f0d0ae0a077 1112 McpsIndication.Port = 0;
dudmuck 0:8f0d0ae0a077 1113 McpsIndication.Multicast = 0;
dudmuck 0:8f0d0ae0a077 1114 McpsIndication.FramePending = 0;
dudmuck 0:8f0d0ae0a077 1115 McpsIndication.Buffer = NULL;
dudmuck 0:8f0d0ae0a077 1116 McpsIndication.BufferSize = 0;
dudmuck 0:8f0d0ae0a077 1117 McpsIndication.RxData = false;
dudmuck 0:8f0d0ae0a077 1118 McpsIndication.AckReceived = false;
dudmuck 0:8f0d0ae0a077 1119 McpsIndication.DownLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 1120 McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
dudmuck 0:8f0d0ae0a077 1121
dudmuck 0:8f0d0ae0a077 1122 Radio.Sleep( );
dudmuck 0:8f0d0ae0a077 1123
dudmuck 0:8f0d0ae0a077 1124 if (expecting_beacon) {
dudmuck 0:8f0d0ae0a077 1125 rx_beacon(rx_us, payload, size);
dudmuck 0:8f0d0ae0a077 1126 expecting_beacon = false;
dudmuck 0:8f0d0ae0a077 1127 return;
dudmuck 0:8f0d0ae0a077 1128 }
dudmuck 0:8f0d0ae0a077 1129
dudmuck 0:8f0d0ae0a077 1130 macHdr.Value = payload[pktHeaderLen++];
dudmuck 0:8f0d0ae0a077 1131
dudmuck 0:8f0d0ae0a077 1132 switch( macHdr.Bits.MType )
dudmuck 0:8f0d0ae0a077 1133 {
dudmuck 0:8f0d0ae0a077 1134 case FRAME_TYPE_JOIN_ACCEPT:
dudmuck 16:915815632c1f 1135 if (IsLoRaMacNetworkJoined)
dudmuck 0:8f0d0ae0a077 1136 {
dudmuck 2:f2d9aa163652 1137 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_JOIN_ACCEPT;
dudmuck 0:8f0d0ae0a077 1138 PrepareRxDoneAbort( );
dudmuck 0:8f0d0ae0a077 1139 return;
dudmuck 0:8f0d0ae0a077 1140 }
dudmuck 0:8f0d0ae0a077 1141 LoRaMacJoinDecrypt( payload + 1, size - 1, LoRaMacAppKey, LoRaMacRxPayload + 1 );
dudmuck 0:8f0d0ae0a077 1142
dudmuck 0:8f0d0ae0a077 1143 LoRaMacRxPayload[0] = macHdr.Value;
dudmuck 0:8f0d0ae0a077 1144
dudmuck 0:8f0d0ae0a077 1145 LoRaMacJoinComputeMic( LoRaMacRxPayload, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic );
dudmuck 0:8f0d0ae0a077 1146
dudmuck 0:8f0d0ae0a077 1147 micRx |= ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN];
dudmuck 0:8f0d0ae0a077 1148 micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 1] << 8 );
dudmuck 0:8f0d0ae0a077 1149 micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 );
dudmuck 0:8f0d0ae0a077 1150 micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 );
dudmuck 0:8f0d0ae0a077 1151
dudmuck 0:8f0d0ae0a077 1152 if( micRx == mic )
dudmuck 0:8f0d0ae0a077 1153 {
dudmuck 0:8f0d0ae0a077 1154 LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
dudmuck 0:8f0d0ae0a077 1155
dudmuck 0:8f0d0ae0a077 1156 LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4];
dudmuck 0:8f0d0ae0a077 1157 LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 );
dudmuck 0:8f0d0ae0a077 1158 LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 );
dudmuck 0:8f0d0ae0a077 1159
dudmuck 0:8f0d0ae0a077 1160 LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7];
dudmuck 0:8f0d0ae0a077 1161 LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 );
dudmuck 0:8f0d0ae0a077 1162 LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 );
dudmuck 0:8f0d0ae0a077 1163 LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 );
dudmuck 0:8f0d0ae0a077 1164
dudmuck 0:8f0d0ae0a077 1165 // DLSettings
dudmuck 0:8f0d0ae0a077 1166 LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07;
dudmuck 0:8f0d0ae0a077 1167
dudmuck 0:8f0d0ae0a077 1168 LoRaMacParams.ReceiveDelay_us = ( LoRaMacRxPayload[12] & 0x0F );
dudmuck 0:8f0d0ae0a077 1169 if( LoRaMacParams.ReceiveDelay_us == 0 )
dudmuck 0:8f0d0ae0a077 1170 LoRaMacParams.ReceiveDelay_us = RECEIVE_DELAY_us;
dudmuck 0:8f0d0ae0a077 1171 else
dudmuck 0:8f0d0ae0a077 1172 LoRaMacParams.ReceiveDelay_us *= 10;
dudmuck 0:8f0d0ae0a077 1173
dudmuck 0:8f0d0ae0a077 1174 uint16_t beaconTimingDelay = LoRaMacRxPayload[13] & 0xff;
dudmuck 0:8f0d0ae0a077 1175 beaconTimingDelay |= LoRaMacRxPayload[14] << 8;
dudmuck 0:8f0d0ae0a077 1176
dudmuck 1:53c30224eda8 1177 isr_printf("%lx slots:%x (rxdelay %lu)", LoRaMacDevAddr, beaconTimingDelay, LoRaMacParams.ReceiveDelay_us);
dudmuck 0:8f0d0ae0a077 1178 unsigned int now_us = lp_timer.read_us();
dudmuck 0:8f0d0ae0a077 1179 unsigned int us_since_TxDone = now_us - TxDone_us;
dudmuck 16:915815632c1f 1180 unsigned us_to_beacon = ( PING_SLOT_RESOLUTION_us * beaconTimingDelay );
dudmuck 16:915815632c1f 1181 unsigned timeout_us = us_to_beacon - us_since_TxDone;
dudmuck 16:915815632c1f 1182 BeaconCtx.timeout_rx.attach_us(&OnRxBeaconSetup, timeout_us);
dudmuck 16:915815632c1f 1183 BeaconCtx.timeout_guard.attach_us(&guard_callback, timeout_us - BEACON_GUARD_us);
dudmuck 16:915815632c1f 1184
dudmuck 16:915815632c1f 1185 BeaconCtx.tx_slot_offset = LoRaMacRxPayload[15];
dudmuck 16:915815632c1f 1186 BeaconCtx.tx_slot_offset |= LoRaMacRxPayload[16] << 8;
dudmuck 16:915815632c1f 1187 BeaconCtx.periodicity_slots = LoRaMacRxPayload[17];
dudmuck 16:915815632c1f 1188 BeaconCtx.periodicity_slots |= LoRaMacRxPayload[18] << 8;
dudmuck 16:915815632c1f 1189
dudmuck 16:915815632c1f 1190 BeaconCtx.LastBeaconRx_us = (us_to_beacon + TxDone_us) - BEACON_INTERVAL_us;
dudmuck 16:915815632c1f 1191 unsigned us_since_beacon_start = now_us - BeaconCtx.LastBeaconRx_us;
dudmuck 16:915815632c1f 1192 int now_slot = (us_since_beacon_start - BEACON_RESERVED_us) / 30000;
dudmuck 16:915815632c1f 1193 int use_slot = BeaconCtx.tx_slot_offset;
dudmuck 16:915815632c1f 1194 while (use_slot < now_slot)
dudmuck 16:915815632c1f 1195 use_slot += BeaconCtx.periodicity_slots;
dudmuck 16:915815632c1f 1196 int slots_now_to_next = use_slot - now_slot;
dudmuck 16:915815632c1f 1197 timeout_us = slots_now_to_next * 30000;
dudmuck 16:915815632c1f 1198 tx_timeout.attach_us(&send_callback, timeout_us);
dudmuck 16:915815632c1f 1199
dudmuck 16:915815632c1f 1200 isr_printf(" now_slot:%d use_slot:%d timeout_us:%u\r\n", now_slot, use_slot, timeout_us);
dudmuck 16:915815632c1f 1201 isr_printf("us_to_beacon:%u, since_txDone:%u\r\n", us_to_beacon, us_since_TxDone);
dudmuck 0:8f0d0ae0a077 1202 BeaconCtx.state = BEACON_STATE_FIRST_ACQ;
dudmuck 16:915815632c1f 1203 BeaconCtx.guard = false;
dudmuck 0:8f0d0ae0a077 1204 BeaconCtx.num_missed = 0;
dudmuck 0:8f0d0ae0a077 1205 BeaconCtx.rx_precession_us = 0;
dudmuck 13:18de9ee3a461 1206 BeaconCtx.last_BeaconRxTimerError_us = -PPM_100_BEACON_INTERVAL;
dudmuck 13:18de9ee3a461 1207 BeaconCtx.known_working_BeaconRxTimerError_us = -PPM_100_BEACON_INTERVAL;
dudmuck 0:8f0d0ae0a077 1208 BeaconCtx.symbol_period_secs = get_symbol_period(GetRxBandwidth(LORAMAC_DEFAULT_DATARATE), Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:8f0d0ae0a077 1209 // N-ms: slot resolution + minimum for preamble detector + 100ppm fast crystal rxing 12ms early
dudmuck 0:8f0d0ae0a077 1210 BeaconCtx.Precess_symbols = TARGET_PRECESSION_US * (BeaconCtx.symbol_period_secs);
dudmuck 8:ab2f9a8d2eaa 1211 BeaconCtx.SymbolTimeout_sec = 0.1 + (BEACON_MIN_SYMBOL_TIMEOUT * BeaconCtx.symbol_period_secs);
dudmuck 0:8f0d0ae0a077 1212 BeaconCtx.SymbolTimeout = BeaconCtx.SymbolTimeout_sec / BeaconCtx.symbol_period_secs;
dudmuck 0:8f0d0ae0a077 1213
dudmuck 0:8f0d0ae0a077 1214 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1215 IsLoRaMacNetworkJoined = true;
dudmuck 0:8f0d0ae0a077 1216 LoRaMacParams.ChannelsDatarate_fixed = LoRaMacParamsDefaults.ChannelsDatarate_fixed;
dudmuck 0:8f0d0ae0a077 1217 }
dudmuck 0:8f0d0ae0a077 1218 else
dudmuck 0:8f0d0ae0a077 1219 {
dudmuck 0:8f0d0ae0a077 1220 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
dudmuck 1:53c30224eda8 1221 isr_printf("join-mic-fail\r\n");
dudmuck 0:8f0d0ae0a077 1222 JoinRequestTrials = MaxJoinRequestTrials; // stop trying
dudmuck 0:8f0d0ae0a077 1223 }
dudmuck 16:915815632c1f 1224 LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
dudmuck 16:915815632c1f 1225 LoRaMacFlags.Bits.MlmeReq = 0; // MacMlmeConfirm() called
dudmuck 0:8f0d0ae0a077 1226 break;
dudmuck 0:8f0d0ae0a077 1227 case FRAME_TYPE_DATA_CONFIRMED_DOWN:
dudmuck 0:8f0d0ae0a077 1228 case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
dudmuck 0:8f0d0ae0a077 1229 {
dudmuck 0:8f0d0ae0a077 1230 address = payload[pktHeaderLen++];
dudmuck 0:8f0d0ae0a077 1231 address |= ( (uint32_t)payload[pktHeaderLen++] << 8 );
dudmuck 0:8f0d0ae0a077 1232 address |= ( (uint32_t)payload[pktHeaderLen++] << 16 );
dudmuck 0:8f0d0ae0a077 1233 address |= ( (uint32_t)payload[pktHeaderLen++] << 24 );
dudmuck 0:8f0d0ae0a077 1234
dudmuck 0:8f0d0ae0a077 1235 if( address != LoRaMacDevAddr )
dudmuck 0:8f0d0ae0a077 1236 {
dudmuck 0:8f0d0ae0a077 1237 curMulticastParams = MulticastChannels;
dudmuck 0:8f0d0ae0a077 1238 while( curMulticastParams != NULL )
dudmuck 0:8f0d0ae0a077 1239 {
dudmuck 0:8f0d0ae0a077 1240 if( address == curMulticastParams->Address )
dudmuck 0:8f0d0ae0a077 1241 {
dudmuck 0:8f0d0ae0a077 1242 multicast = 1;
dudmuck 0:8f0d0ae0a077 1243 nwkSKey = curMulticastParams->NwkSKey;
dudmuck 0:8f0d0ae0a077 1244 appSKey = curMulticastParams->AppSKey;
dudmuck 0:8f0d0ae0a077 1245 downLinkCounter = curMulticastParams->DownLinkCounter;
dudmuck 0:8f0d0ae0a077 1246 break;
dudmuck 0:8f0d0ae0a077 1247 }
dudmuck 0:8f0d0ae0a077 1248 curMulticastParams = curMulticastParams->Next;
dudmuck 0:8f0d0ae0a077 1249 }
dudmuck 0:8f0d0ae0a077 1250 if( multicast == 0 )
dudmuck 0:8f0d0ae0a077 1251 {
dudmuck 0:8f0d0ae0a077 1252 // We are not the destination of this frame.
dudmuck 0:8f0d0ae0a077 1253 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL;
dudmuck 0:8f0d0ae0a077 1254 PrepareRxDoneAbort( );
dudmuck 0:8f0d0ae0a077 1255 return;
dudmuck 0:8f0d0ae0a077 1256 }
dudmuck 0:8f0d0ae0a077 1257 }
dudmuck 0:8f0d0ae0a077 1258 else
dudmuck 0:8f0d0ae0a077 1259 {
dudmuck 0:8f0d0ae0a077 1260 multicast = 0;
dudmuck 0:8f0d0ae0a077 1261 nwkSKey = LoRaMacNwkSKey;
dudmuck 0:8f0d0ae0a077 1262 appSKey = LoRaMacAppSKey;
dudmuck 0:8f0d0ae0a077 1263 downLinkCounter = DownLinkCounter;
dudmuck 0:8f0d0ae0a077 1264 }
dudmuck 0:8f0d0ae0a077 1265
dudmuck 0:8f0d0ae0a077 1266 fCtrl.Value = payload[pktHeaderLen++];
dudmuck 0:8f0d0ae0a077 1267
dudmuck 0:8f0d0ae0a077 1268 sequenceCounter = ( uint16_t )payload[pktHeaderLen++];
dudmuck 0:8f0d0ae0a077 1269 sequenceCounter |= ( uint16_t )payload[pktHeaderLen++] << 8;
dudmuck 0:8f0d0ae0a077 1270
dudmuck 0:8f0d0ae0a077 1271 appPayloadStartIndex = 8 + fCtrl.Bits.FOptsLen;
dudmuck 0:8f0d0ae0a077 1272
dudmuck 0:8f0d0ae0a077 1273 micRx |= ( uint32_t )payload[size - LORAMAC_MFR_LEN];
dudmuck 0:8f0d0ae0a077 1274 micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 1] << 8 );
dudmuck 0:8f0d0ae0a077 1275 micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 2] << 16 );
dudmuck 0:8f0d0ae0a077 1276 micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 3] << 24 );
dudmuck 0:8f0d0ae0a077 1277
dudmuck 0:8f0d0ae0a077 1278 sequenceCounterPrev = ( uint16_t )downLinkCounter;
dudmuck 0:8f0d0ae0a077 1279 sequenceCounterDiff = ( sequenceCounter - sequenceCounterPrev );
dudmuck 0:8f0d0ae0a077 1280
dudmuck 0:8f0d0ae0a077 1281 if( sequenceCounterDiff < ( 1 << 15 ) )
dudmuck 0:8f0d0ae0a077 1282 {
dudmuck 0:8f0d0ae0a077 1283 downLinkCounter += sequenceCounterDiff;
dudmuck 0:8f0d0ae0a077 1284 LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic );
dudmuck 0:8f0d0ae0a077 1285 if( micRx == mic )
dudmuck 0:8f0d0ae0a077 1286 {
dudmuck 0:8f0d0ae0a077 1287 isMicOk = true;
dudmuck 0:8f0d0ae0a077 1288 }
dudmuck 0:8f0d0ae0a077 1289 }
dudmuck 0:8f0d0ae0a077 1290 else
dudmuck 0:8f0d0ae0a077 1291 {
dudmuck 0:8f0d0ae0a077 1292 // check for sequence roll-over
dudmuck 0:8f0d0ae0a077 1293 uint32_t downLinkCounterTmp = downLinkCounter + 0x10000 + ( int16_t )sequenceCounterDiff;
dudmuck 0:8f0d0ae0a077 1294 LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounterTmp, &mic );
dudmuck 0:8f0d0ae0a077 1295 if( micRx == mic )
dudmuck 0:8f0d0ae0a077 1296 {
dudmuck 0:8f0d0ae0a077 1297 isMicOk = true;
dudmuck 0:8f0d0ae0a077 1298 downLinkCounter = downLinkCounterTmp;
dudmuck 0:8f0d0ae0a077 1299 }
dudmuck 0:8f0d0ae0a077 1300 }
dudmuck 0:8f0d0ae0a077 1301
dudmuck 0:8f0d0ae0a077 1302 // Check for a the maximum allowed counter difference
dudmuck 0:8f0d0ae0a077 1303 if( sequenceCounterDiff >= MAX_FCNT_GAP )
dudmuck 0:8f0d0ae0a077 1304 {
dudmuck 0:8f0d0ae0a077 1305 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS;
dudmuck 0:8f0d0ae0a077 1306 McpsIndication.DownLinkCounter = downLinkCounter;
dudmuck 0:8f0d0ae0a077 1307 PrepareRxDoneAbort( );
dudmuck 0:8f0d0ae0a077 1308 return;
dudmuck 0:8f0d0ae0a077 1309 }
dudmuck 0:8f0d0ae0a077 1310
dudmuck 0:8f0d0ae0a077 1311 if( isMicOk == true )
dudmuck 0:8f0d0ae0a077 1312 {
dudmuck 0:8f0d0ae0a077 1313 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1314 McpsIndication.Multicast = multicast;
dudmuck 0:8f0d0ae0a077 1315 McpsIndication.FramePending = fCtrl.Bits.FPending;
dudmuck 0:8f0d0ae0a077 1316 McpsIndication.Buffer = NULL;
dudmuck 0:8f0d0ae0a077 1317 McpsIndication.BufferSize = 0;
dudmuck 0:8f0d0ae0a077 1318 McpsIndication.DownLinkCounter = downLinkCounter;
dudmuck 0:8f0d0ae0a077 1319
dudmuck 0:8f0d0ae0a077 1320 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1321
dudmuck 0:8f0d0ae0a077 1322 MacCommandsBufferToRepeatIndex = 0;
dudmuck 0:8f0d0ae0a077 1323
dudmuck 0:8f0d0ae0a077 1324 // Update 32 bits downlink counter
dudmuck 0:8f0d0ae0a077 1325 if( multicast == 1 )
dudmuck 0:8f0d0ae0a077 1326 {
dudmuck 0:8f0d0ae0a077 1327 McpsIndication.McpsIndication = MCPS_MULTICAST;
dudmuck 0:8f0d0ae0a077 1328
dudmuck 0:8f0d0ae0a077 1329 if( ( curMulticastParams->DownLinkCounter == downLinkCounter ) &&
dudmuck 0:8f0d0ae0a077 1330 ( curMulticastParams->DownLinkCounter != 0 ) )
dudmuck 0:8f0d0ae0a077 1331 {
dudmuck 0:8f0d0ae0a077 1332 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
dudmuck 0:8f0d0ae0a077 1333 McpsIndication.DownLinkCounter = downLinkCounter;
dudmuck 0:8f0d0ae0a077 1334 PrepareRxDoneAbort( );
dudmuck 0:8f0d0ae0a077 1335 return;
dudmuck 0:8f0d0ae0a077 1336 }
dudmuck 0:8f0d0ae0a077 1337 curMulticastParams->DownLinkCounter = downLinkCounter;
dudmuck 0:8f0d0ae0a077 1338 }
dudmuck 0:8f0d0ae0a077 1339 else
dudmuck 0:8f0d0ae0a077 1340 {
dudmuck 0:8f0d0ae0a077 1341 if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN )
dudmuck 0:8f0d0ae0a077 1342 {
dudmuck 0:8f0d0ae0a077 1343 SrvAckRequested = true;
dudmuck 0:8f0d0ae0a077 1344 McpsIndication.McpsIndication = MCPS_CONFIRMED;
dudmuck 0:8f0d0ae0a077 1345
dudmuck 0:8f0d0ae0a077 1346 if( ( DownLinkCounter == downLinkCounter ) &&
dudmuck 0:8f0d0ae0a077 1347 ( DownLinkCounter != 0 ) )
dudmuck 0:8f0d0ae0a077 1348 {
dudmuck 0:8f0d0ae0a077 1349 // Duplicated confirmed downlink. Skip indication.
dudmuck 0:8f0d0ae0a077 1350 // In this case, the MAC layer shall accept the MAC commands
dudmuck 0:8f0d0ae0a077 1351 // which are included in the downlink retransmission.
dudmuck 0:8f0d0ae0a077 1352 // It should not provide the same frame to the application
dudmuck 0:8f0d0ae0a077 1353 // layer again.
dudmuck 0:8f0d0ae0a077 1354 skipIndication = true;
dudmuck 0:8f0d0ae0a077 1355 }
dudmuck 0:8f0d0ae0a077 1356 }
dudmuck 0:8f0d0ae0a077 1357 else
dudmuck 0:8f0d0ae0a077 1358 {
dudmuck 0:8f0d0ae0a077 1359 SrvAckRequested = false;
dudmuck 0:8f0d0ae0a077 1360 McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
dudmuck 0:8f0d0ae0a077 1361
dudmuck 0:8f0d0ae0a077 1362 if( ( DownLinkCounter == downLinkCounter ) &&
dudmuck 0:8f0d0ae0a077 1363 ( DownLinkCounter != 0 ) )
dudmuck 0:8f0d0ae0a077 1364 {
dudmuck 0:8f0d0ae0a077 1365 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
dudmuck 0:8f0d0ae0a077 1366 McpsIndication.DownLinkCounter = downLinkCounter;
dudmuck 0:8f0d0ae0a077 1367 PrepareRxDoneAbort( );
dudmuck 0:8f0d0ae0a077 1368 return;
dudmuck 0:8f0d0ae0a077 1369 }
dudmuck 0:8f0d0ae0a077 1370 }
dudmuck 0:8f0d0ae0a077 1371 DownLinkCounter = downLinkCounter;
dudmuck 0:8f0d0ae0a077 1372 }
dudmuck 0:8f0d0ae0a077 1373
dudmuck 0:8f0d0ae0a077 1374 // This must be done before parsing the payload and the MAC commands.
dudmuck 0:8f0d0ae0a077 1375 // We need to reset the MacCommandsBufferIndex here, since we need
dudmuck 0:8f0d0ae0a077 1376 // to take retransmissions and repititions into account. Error cases
dudmuck 0:8f0d0ae0a077 1377 // will be handled in function OnMacStateCheckTimerEvent.
dudmuck 0:8f0d0ae0a077 1378 if( McpsConfirm.McpsRequest == MCPS_CONFIRMED )
dudmuck 0:8f0d0ae0a077 1379 {
dudmuck 0:8f0d0ae0a077 1380 if( fCtrl.Bits.Ack == 1 )
dudmuck 0:8f0d0ae0a077 1381 {// Reset MacCommandsBufferIndex when we have received an ACK.
dudmuck 0:8f0d0ae0a077 1382 MacCommandsBufferIndex = 0;
dudmuck 0:8f0d0ae0a077 1383 }
dudmuck 0:8f0d0ae0a077 1384 }
dudmuck 0:8f0d0ae0a077 1385 else
dudmuck 0:8f0d0ae0a077 1386 {// Reset the variable if we have received any valid frame.
dudmuck 0:8f0d0ae0a077 1387 MacCommandsBufferIndex = 0;
dudmuck 0:8f0d0ae0a077 1388 }
dudmuck 0:8f0d0ae0a077 1389
dudmuck 0:8f0d0ae0a077 1390 // Process payload and MAC commands
dudmuck 0:8f0d0ae0a077 1391 if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 )
dudmuck 0:8f0d0ae0a077 1392 {
dudmuck 0:8f0d0ae0a077 1393 port = payload[appPayloadStartIndex++];
dudmuck 0:8f0d0ae0a077 1394 frameLen = ( size - 4 ) - appPayloadStartIndex;
dudmuck 0:8f0d0ae0a077 1395
dudmuck 0:8f0d0ae0a077 1396 McpsIndication.Port = port;
dudmuck 0:8f0d0ae0a077 1397
dudmuck 0:8f0d0ae0a077 1398 if( port == 0 )
dudmuck 0:8f0d0ae0a077 1399 {
dudmuck 0:8f0d0ae0a077 1400 // Only allow frames which do not have fOpts
dudmuck 0:8f0d0ae0a077 1401 if( fCtrl.Bits.FOptsLen == 0 )
dudmuck 0:8f0d0ae0a077 1402 {
dudmuck 0:8f0d0ae0a077 1403 LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
dudmuck 0:8f0d0ae0a077 1404 frameLen,
dudmuck 0:8f0d0ae0a077 1405 nwkSKey,
dudmuck 0:8f0d0ae0a077 1406 address,
dudmuck 0:8f0d0ae0a077 1407 DOWN_LINK,
dudmuck 0:8f0d0ae0a077 1408 downLinkCounter,
dudmuck 0:8f0d0ae0a077 1409 LoRaMacRxPayload );
dudmuck 0:8f0d0ae0a077 1410
dudmuck 0:8f0d0ae0a077 1411 // Decode frame payload MAC commands
dudmuck 0:8f0d0ae0a077 1412 ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr );
dudmuck 0:8f0d0ae0a077 1413 }
dudmuck 0:8f0d0ae0a077 1414 else
dudmuck 0:8f0d0ae0a077 1415 {
dudmuck 0:8f0d0ae0a077 1416 skipIndication = true;
dudmuck 0:8f0d0ae0a077 1417 }
dudmuck 0:8f0d0ae0a077 1418 }
dudmuck 0:8f0d0ae0a077 1419 else
dudmuck 0:8f0d0ae0a077 1420 {
dudmuck 0:8f0d0ae0a077 1421 if( fCtrl.Bits.FOptsLen > 0 )
dudmuck 0:8f0d0ae0a077 1422 {
dudmuck 0:8f0d0ae0a077 1423 // Decode Options field MAC commands. Omit the fPort.
dudmuck 0:8f0d0ae0a077 1424 ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr );
dudmuck 0:8f0d0ae0a077 1425 }
dudmuck 0:8f0d0ae0a077 1426
dudmuck 0:8f0d0ae0a077 1427 LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
dudmuck 0:8f0d0ae0a077 1428 frameLen,
dudmuck 0:8f0d0ae0a077 1429 appSKey,
dudmuck 0:8f0d0ae0a077 1430 address,
dudmuck 0:8f0d0ae0a077 1431 DOWN_LINK,
dudmuck 0:8f0d0ae0a077 1432 downLinkCounter,
dudmuck 0:8f0d0ae0a077 1433 LoRaMacRxPayload );
dudmuck 0:8f0d0ae0a077 1434
dudmuck 0:8f0d0ae0a077 1435 if( skipIndication == false )
dudmuck 0:8f0d0ae0a077 1436 {
dudmuck 0:8f0d0ae0a077 1437 McpsIndication.Buffer = LoRaMacRxPayload;
dudmuck 0:8f0d0ae0a077 1438 McpsIndication.BufferSize = frameLen;
dudmuck 0:8f0d0ae0a077 1439 McpsIndication.RxData = true;
dudmuck 0:8f0d0ae0a077 1440 }
dudmuck 0:8f0d0ae0a077 1441 }
dudmuck 0:8f0d0ae0a077 1442 }
dudmuck 0:8f0d0ae0a077 1443 else
dudmuck 0:8f0d0ae0a077 1444 {
dudmuck 0:8f0d0ae0a077 1445 if( fCtrl.Bits.FOptsLen > 0 )
dudmuck 0:8f0d0ae0a077 1446 {
dudmuck 0:8f0d0ae0a077 1447 // Decode Options field MAC commands
dudmuck 0:8f0d0ae0a077 1448 ProcessMacCommands( payload, 8, appPayloadStartIndex, snr );
dudmuck 0:8f0d0ae0a077 1449 }
dudmuck 0:8f0d0ae0a077 1450 }
dudmuck 0:8f0d0ae0a077 1451
dudmuck 0:8f0d0ae0a077 1452 if( skipIndication == false )
dudmuck 0:8f0d0ae0a077 1453 {
dudmuck 0:8f0d0ae0a077 1454 // Check if the frame is an acknowledgement
dudmuck 0:8f0d0ae0a077 1455 if( fCtrl.Bits.Ack == 1 )
dudmuck 0:8f0d0ae0a077 1456 {
dudmuck 0:8f0d0ae0a077 1457 McpsConfirm.AckReceived = true;
dudmuck 0:8f0d0ae0a077 1458 McpsIndication.AckReceived = true;
dudmuck 0:8f0d0ae0a077 1459
dudmuck 0:8f0d0ae0a077 1460 // Stop the AckTimeout timer as no more retransmissions
dudmuck 0:8f0d0ae0a077 1461 // are needed.
dudmuck 0:8f0d0ae0a077 1462 }
dudmuck 0:8f0d0ae0a077 1463 else
dudmuck 0:8f0d0ae0a077 1464 {
dudmuck 0:8f0d0ae0a077 1465 McpsConfirm.AckReceived = false;
dudmuck 0:8f0d0ae0a077 1466 }
dudmuck 0:8f0d0ae0a077 1467 }
dudmuck 0:8f0d0ae0a077 1468 // Provide always an indication, skip the callback to the user application,
dudmuck 0:8f0d0ae0a077 1469 // in case of a confirmed downlink retransmission.
dudmuck 0:8f0d0ae0a077 1470 LoRaMacFlags.Bits.McpsInd = 1;
dudmuck 0:8f0d0ae0a077 1471 LoRaMacFlags.Bits.McpsIndSkip = skipIndication;
dudmuck 0:8f0d0ae0a077 1472 }
dudmuck 0:8f0d0ae0a077 1473 else
dudmuck 0:8f0d0ae0a077 1474 {
dudmuck 0:8f0d0ae0a077 1475 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
dudmuck 5:c108560af4c3 1476 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
dudmuck 0:8f0d0ae0a077 1477 PrepareRxDoneAbort( );
dudmuck 0:8f0d0ae0a077 1478 return;
dudmuck 0:8f0d0ae0a077 1479 }
dudmuck 0:8f0d0ae0a077 1480 }
dudmuck 0:8f0d0ae0a077 1481 break;
dudmuck 0:8f0d0ae0a077 1482 case FRAME_TYPE_PROPRIETARY:
dudmuck 0:8f0d0ae0a077 1483 {
dudmuck 0:8f0d0ae0a077 1484 memcpy1( LoRaMacRxPayload, &payload[pktHeaderLen], size );
dudmuck 0:8f0d0ae0a077 1485
dudmuck 0:8f0d0ae0a077 1486 McpsIndication.McpsIndication = MCPS_PROPRIETARY;
dudmuck 0:8f0d0ae0a077 1487 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1488 McpsIndication.Buffer = LoRaMacRxPayload;
dudmuck 0:8f0d0ae0a077 1489 McpsIndication.BufferSize = size - pktHeaderLen;
dudmuck 0:8f0d0ae0a077 1490
dudmuck 0:8f0d0ae0a077 1491 LoRaMacFlags.Bits.McpsInd = 1;
dudmuck 0:8f0d0ae0a077 1492 break;
dudmuck 0:8f0d0ae0a077 1493 }
dudmuck 0:8f0d0ae0a077 1494 default:
dudmuck 2:f2d9aa163652 1495 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_RX_MTYPE;
dudmuck 5:c108560af4c3 1496 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_RX_MTYPE;
dudmuck 20:42839629a5dc 1497 isr_printf("%d:macHdr:%02x(%d,%d) ", size, macHdr.Value, rssi, snr);
dudmuck 0:8f0d0ae0a077 1498 PrepareRxDoneAbort( );
dudmuck 0:8f0d0ae0a077 1499 break;
dudmuck 0:8f0d0ae0a077 1500 }
dudmuck 0:8f0d0ae0a077 1501 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 0:8f0d0ae0a077 1502
dudmuck 16:915815632c1f 1503 application_callbacks();
dudmuck 16:915815632c1f 1504
dudmuck 1:53c30224eda8 1505 } // ..OnRadioRxDone();
dudmuck 0:8f0d0ae0a077 1506
dudmuck 16:915815632c1f 1507
dudmuck 16:915815632c1f 1508
dudmuck 0:8f0d0ae0a077 1509 static void OnRadioTxTimeout( void )
dudmuck 0:8f0d0ae0a077 1510 {
dudmuck 0:8f0d0ae0a077 1511 Radio.Sleep( );
dudmuck 0:8f0d0ae0a077 1512
dudmuck 0:8f0d0ae0a077 1513 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
dudmuck 0:8f0d0ae0a077 1514 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
dudmuck 0:8f0d0ae0a077 1515 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 0:8f0d0ae0a077 1516 }
dudmuck 0:8f0d0ae0a077 1517
dudmuck 0:8f0d0ae0a077 1518 static void OnRadioRxError( void )
dudmuck 0:8f0d0ae0a077 1519 {
dudmuck 0:8f0d0ae0a077 1520 Radio.Sleep( );
dudmuck 0:8f0d0ae0a077 1521
dudmuck 16:915815632c1f 1522 if (NodeAckRequested)
dudmuck 0:8f0d0ae0a077 1523 {
dudmuck 0:8f0d0ae0a077 1524 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
dudmuck 0:8f0d0ae0a077 1525 }
dudmuck 0:8f0d0ae0a077 1526 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
dudmuck 0:8f0d0ae0a077 1527
dudmuck 0:8f0d0ae0a077 1528 if ((lp_timer.read_us() - AggregatedLastTxDoneTime_us) >= RxWindowDelay_us )
dudmuck 0:8f0d0ae0a077 1529 {
dudmuck 0:8f0d0ae0a077 1530 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 0:8f0d0ae0a077 1531 }
dudmuck 0:8f0d0ae0a077 1532 }
dudmuck 0:8f0d0ae0a077 1533
dudmuck 16:915815632c1f 1534 static void
dudmuck 16:915815632c1f 1535 join_send()
dudmuck 16:915815632c1f 1536 {
dudmuck 20:42839629a5dc 1537 flags.join_send = 1;
dudmuck 20:42839629a5dc 1538 }
dudmuck 20:42839629a5dc 1539
dudmuck 20:42839629a5dc 1540 static void
dudmuck 20:42839629a5dc 1541 join_send_bh()
dudmuck 20:42839629a5dc 1542 {
dudmuck 16:915815632c1f 1543 if (JoinRequestTrials < MaxJoinRequestTrials) {
dudmuck 16:915815632c1f 1544 LoRaMacHeader_t macHdr;
dudmuck 16:915815632c1f 1545 LoRaMacFrameCtrl_t fCtrl;
dudmuck 16:915815632c1f 1546
dudmuck 16:915815632c1f 1547 if (++Channel == LORA_MAX_NB_CHANNELS)
dudmuck 16:915815632c1f 1548 Channel = 0;
dudmuck 16:915815632c1f 1549 isr_printf("<join-ch%u>", Channel);
dudmuck 16:915815632c1f 1550
dudmuck 16:915815632c1f 1551 macHdr.Value = 0;
dudmuck 16:915815632c1f 1552 macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
dudmuck 16:915815632c1f 1553
dudmuck 16:915815632c1f 1554 fCtrl.Value = 0;
dudmuck 16:915815632c1f 1555 fCtrl.Bits.Adr = 0;
dudmuck 16:915815632c1f 1556
dudmuck 16:915815632c1f 1557 /* In case of join request retransmissions, the stack must prepare
dudmuck 16:915815632c1f 1558 * the frame again, because the network server keeps track of the random
dudmuck 16:915815632c1f 1559 * LoRaMacDevNonce values to prevent reply attacks. */
dudmuck 16:915815632c1f 1560 PrepareFrame( &macHdr, &fCtrl, 0, NULL, 0 );
dudmuck 16:915815632c1f 1561
dudmuck 16:915815632c1f 1562 ScheduleTx();
dudmuck 16:915815632c1f 1563 } else {
dudmuck 16:915815632c1f 1564 MlmeConfirm.MlmeRequest = MLME_JOIN;
dudmuck 16:915815632c1f 1565 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
dudmuck 16:915815632c1f 1566 LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
dudmuck 16:915815632c1f 1567 }
dudmuck 16:915815632c1f 1568 }
dudmuck 16:915815632c1f 1569
dudmuck 0:8f0d0ae0a077 1570 static void OnRadioRxTimeout( void )
dudmuck 0:8f0d0ae0a077 1571 {
dudmuck 16:915815632c1f 1572 static unsigned LastBeaconRx_us;
dudmuck 16:915815632c1f 1573
dudmuck 0:8f0d0ae0a077 1574 Radio.Sleep( );
dudmuck 0:8f0d0ae0a077 1575
dudmuck 0:8f0d0ae0a077 1576 if (expecting_beacon) {
dudmuck 16:915815632c1f 1577 unsigned next_beacon_expected_us = BEACON_INTERVAL_us;
dudmuck 0:8f0d0ae0a077 1578 if (BeaconCtx.state == BEACON_STATE_FIRST_ACQ) {
dudmuck 16:915815632c1f 1579 next_beacon_expected_us -= 1000000;
dudmuck 16:915815632c1f 1580 set_beacon_symbol_timeout(1.000);
dudmuck 0:8f0d0ae0a077 1581 } else {
dudmuck 16:915815632c1f 1582 next_beacon_expected_us += BeaconCtx.known_working_BeaconRxTimerError_us;
dudmuck 0:8f0d0ae0a077 1583 // for measurement resolution and temperature drift while missing beacons:
dudmuck 16:915815632c1f 1584 next_beacon_expected_us -= 10000;
dudmuck 16:915815632c1f 1585 set_beacon_symbol_timeout(BeaconCtx.SymbolTimeout_sec + 0.025);
dudmuck 0:8f0d0ae0a077 1586 }
dudmuck 16:915815632c1f 1587 unsigned now_us = lp_timer.read_us();
dudmuck 0:8f0d0ae0a077 1588 unsigned us_since_rx_setup = now_us - BeaconCtx.RxBeaconSetupAt_us;
dudmuck 16:915815632c1f 1589 unsigned b_timeout_us = next_beacon_expected_us - us_since_rx_setup;
dudmuck 16:915815632c1f 1590 BeaconCtx.timeout_rx.attach_us(&OnRxBeaconSetup, b_timeout_us);
dudmuck 16:915815632c1f 1591 BeaconCtx.timeout_guard.attach_us(&guard_callback, b_timeout_us - BEACON_GUARD_us);
dudmuck 16:915815632c1f 1592
dudmuck 16:915815632c1f 1593 if (BeaconCtx.num_missed == 0) /* first missed beacon, init our local LastBeaconRx_us */
dudmuck 16:915815632c1f 1594 LastBeaconRx_us = BeaconCtx.LastBeaconRx_us;
dudmuck 16:915815632c1f 1595
dudmuck 16:915815632c1f 1596 BeaconCtx.num_missed++;
dudmuck 16:915815632c1f 1597 /***************************************/
dudmuck 16:915815632c1f 1598 unsigned fake_ThisBeaconRx_us = LastBeaconRx_us + BEACON_INTERVAL_us + BeaconCtx.known_working_BeaconRxTimerError_us;
dudmuck 16:915815632c1f 1599 unsigned us_since_beacon_start = now_us - fake_ThisBeaconRx_us;
dudmuck 16:915815632c1f 1600 unsigned timeout_us = BEACON_RESERVED_us + (BeaconCtx.tx_slot_offset * 30000) - us_since_beacon_start;
dudmuck 16:915815632c1f 1601 tx_timeout.attach_us(&send_callback, timeout_us);
dudmuck 16:915815632c1f 1602 isr_printf("fake %u\r\n", fake_ThisBeaconRx_us - LastBeaconRx_us);
dudmuck 16:915815632c1f 1603 LastBeaconRx_us = fake_ThisBeaconRx_us; // update our local for next missed beacon
dudmuck 16:915815632c1f 1604 /***************************************/
dudmuck 13:18de9ee3a461 1605
dudmuck 13:18de9ee3a461 1606 #ifdef DEBUG_GWTX_JUMPER
dudmuck 13:18de9ee3a461 1607 isr_printf("rx-before-gwtx:%d ", gwtx_rise_us - BeaconCtx.RxBeaconSetupAt_us);
dudmuck 16:915815632c1f 1608 #endif /* DEBUG_GWTX_JUMPER */
dudmuck 16:915815632c1f 1609
dudmuck 21:500ff43d8424 1610 isr_printf("beacon-rx-timeout %u %u next in %uus (rxing for %u)", BeaconCtx.num_missed, BeaconCtx.SymbolTimeout, b_timeout_us, us_since_rx_setup);
dudmuck 21:500ff43d8424 1611 isr_printf(" %u,nextTx:%u\r\n", us_since_beacon_start, timeout_us);
dudmuck 0:8f0d0ae0a077 1612
dudmuck 0:8f0d0ae0a077 1613 MlmeIndication.MlmeIndication = MLME_BEACON;
dudmuck 0:8f0d0ae0a077 1614 MlmeIndication.Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOST;
dudmuck 0:8f0d0ae0a077 1615 LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
dudmuck 0:8f0d0ae0a077 1616
dudmuck 16:915815632c1f 1617 BeaconCtx.guard = false;
dudmuck 0:8f0d0ae0a077 1618 expecting_beacon = false;
dudmuck 16:915815632c1f 1619 } else {
dudmuck 16:915815632c1f 1620 if (LoRaMacFlags.Bits.MlmeReq && ( MlmeConfirm.MlmeRequest == MLME_JOIN ))
dudmuck 16:915815632c1f 1621 tx_timeout.attach_us(&join_send, (JoinRequestTrials*20000) + randr(0, 70000));
dudmuck 0:8f0d0ae0a077 1622 }
dudmuck 0:8f0d0ae0a077 1623
dudmuck 16:915815632c1f 1624 if (NodeAckRequested)
dudmuck 0:8f0d0ae0a077 1625 {
dudmuck 0:8f0d0ae0a077 1626 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT;
dudmuck 0:8f0d0ae0a077 1627 }
dudmuck 0:8f0d0ae0a077 1628 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT;
dudmuck 0:8f0d0ae0a077 1629 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 16:915815632c1f 1630
dudmuck 16:915815632c1f 1631 application_callbacks();
dudmuck 1:53c30224eda8 1632 } // ..OnRadioRxTimeout();
dudmuck 0:8f0d0ae0a077 1633
dudmuck 20:42839629a5dc 1634 static void OnRxWindowTimerEventBH()
dudmuck 0:8f0d0ae0a077 1635 {
dudmuck 0:8f0d0ae0a077 1636 Radio.Standby( );
dudmuck 8:ab2f9a8d2eaa 1637 if (expecting_beacon) {
dudmuck 8:ab2f9a8d2eaa 1638 isr_printf("rxwin-during-beacon\r\n");
dudmuck 16:915815632c1f 1639 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
dudmuck 16:915815632c1f 1640 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
dudmuck 16:915815632c1f 1641 LoRaMacFlags.Bits.MacDone = 1;
dudmuck 16:915815632c1f 1642 LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm );
dudmuck 8:ab2f9a8d2eaa 1643 } else {
dudmuck 8:ab2f9a8d2eaa 1644 RxWindowSetup( LORAMAC_FIRST_CHANNEL + ( Channel * LORAMAC_STEPWIDTH_CHANNEL), RxWindowsParam.Datarate, RxWindowsParam.Bandwidth, RxWindowsParam.RxWindowTimeout, false );
dudmuck 8:ab2f9a8d2eaa 1645 }
dudmuck 0:8f0d0ae0a077 1646 }
dudmuck 0:8f0d0ae0a077 1647
dudmuck 20:42839629a5dc 1648 static void OnRxWindowTimerEvent( void )
dudmuck 20:42839629a5dc 1649 {
dudmuck 20:42839629a5dc 1650 flags.rx_win = 1;
dudmuck 20:42839629a5dc 1651 }
dudmuck 20:42839629a5dc 1652
dudmuck 0:8f0d0ae0a077 1653 static void OnAckTimeoutTimerEvent( void )
dudmuck 0:8f0d0ae0a077 1654 {
dudmuck 0:8f0d0ae0a077 1655 }
dudmuck 0:8f0d0ae0a077 1656
dudmuck 0:8f0d0ae0a077 1657 static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous )
dudmuck 0:8f0d0ae0a077 1658 {
dudmuck 0:8f0d0ae0a077 1659 uint8_t downlinkDatarate = Datarates[datarate];
dudmuck 0:8f0d0ae0a077 1660 RadioModems_t modem;
dudmuck 0:8f0d0ae0a077 1661
dudmuck 0:8f0d0ae0a077 1662 if( Radio.GetStatus( ) == RF_IDLE )
dudmuck 0:8f0d0ae0a077 1663 {
dudmuck 0:8f0d0ae0a077 1664 Radio.SetChannel( freq );
dudmuck 0:8f0d0ae0a077 1665
dudmuck 0:8f0d0ae0a077 1666 // Store downlink datarate
dudmuck 0:8f0d0ae0a077 1667 McpsIndication.RxDatarate = ( uint8_t ) datarate;
dudmuck 0:8f0d0ae0a077 1668
dudmuck 0:8f0d0ae0a077 1669 modem = MODEM_LORA;
dudmuck 0:8f0d0ae0a077 1670 Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous );
dudmuck 0:8f0d0ae0a077 1671 Radio.SetMaxPayloadLength( MODEM_LORA, 255 );
dudmuck 0:8f0d0ae0a077 1672
dudmuck 0:8f0d0ae0a077 1673 if( rxContinuous == false )
dudmuck 0:8f0d0ae0a077 1674 {
dudmuck 0:8f0d0ae0a077 1675 Radio.Rx( LoRaMacParams.MaxRxWindow );
dudmuck 0:8f0d0ae0a077 1676 }
dudmuck 0:8f0d0ae0a077 1677 else
dudmuck 0:8f0d0ae0a077 1678 {
dudmuck 0:8f0d0ae0a077 1679 Radio.Rx( 0 ); // Continuous mode
dudmuck 0:8f0d0ae0a077 1680 }
dudmuck 0:8f0d0ae0a077 1681 return true;
dudmuck 0:8f0d0ae0a077 1682 }
dudmuck 0:8f0d0ae0a077 1683 return false;
dudmuck 0:8f0d0ae0a077 1684 }
dudmuck 0:8f0d0ae0a077 1685
dudmuck 0:8f0d0ae0a077 1686 static bool ValueInRange( int8_t value, int8_t min, int8_t max )
dudmuck 0:8f0d0ae0a077 1687 {
dudmuck 0:8f0d0ae0a077 1688 if( ( value >= min ) && ( value <= max ) )
dudmuck 0:8f0d0ae0a077 1689 {
dudmuck 0:8f0d0ae0a077 1690 return true;
dudmuck 0:8f0d0ae0a077 1691 }
dudmuck 0:8f0d0ae0a077 1692 return false;
dudmuck 0:8f0d0ae0a077 1693 }
dudmuck 0:8f0d0ae0a077 1694
dudmuck 0:8f0d0ae0a077 1695 static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 )
dudmuck 0:8f0d0ae0a077 1696 {
dudmuck 0:8f0d0ae0a077 1697 LoRaMacStatus_t status = LORAMAC_STATUS_BUSY;
dudmuck 0:8f0d0ae0a077 1698 // The maximum buffer length must take MAC commands to re-send into account.
dudmuck 0:8f0d0ae0a077 1699 uint8_t bufLen = LORA_MAC_COMMAND_MAX_LENGTH - MacCommandsBufferToRepeatIndex;
dudmuck 0:8f0d0ae0a077 1700
dudmuck 0:8f0d0ae0a077 1701 switch( cmd )
dudmuck 0:8f0d0ae0a077 1702 {
dudmuck 0:8f0d0ae0a077 1703 case MOTE_MAC_LINK_CHECK_REQ:
dudmuck 0:8f0d0ae0a077 1704 if( MacCommandsBufferIndex < bufLen )
dudmuck 0:8f0d0ae0a077 1705 {
dudmuck 0:8f0d0ae0a077 1706 MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
dudmuck 0:8f0d0ae0a077 1707 // No payload for this command
dudmuck 0:8f0d0ae0a077 1708 status = LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1709 }
dudmuck 0:8f0d0ae0a077 1710 break;
dudmuck 0:8f0d0ae0a077 1711 case MOTE_MAC_RX_PARAM_SETUP_ANS:
dudmuck 0:8f0d0ae0a077 1712 if( MacCommandsBufferIndex < ( bufLen - 1 ) )
dudmuck 0:8f0d0ae0a077 1713 {
dudmuck 0:8f0d0ae0a077 1714 MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
dudmuck 0:8f0d0ae0a077 1715 // Status: Datarate ACK, Channel ACK
dudmuck 0:8f0d0ae0a077 1716 MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
dudmuck 0:8f0d0ae0a077 1717 status = LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1718 }
dudmuck 0:8f0d0ae0a077 1719 break;
dudmuck 0:8f0d0ae0a077 1720 case MOTE_MAC_DEV_STATUS_ANS:
dudmuck 0:8f0d0ae0a077 1721 if( MacCommandsBufferIndex < ( bufLen - 2 ) )
dudmuck 0:8f0d0ae0a077 1722 {
dudmuck 0:8f0d0ae0a077 1723 MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
dudmuck 0:8f0d0ae0a077 1724 // 1st byte Battery
dudmuck 0:8f0d0ae0a077 1725 // 2nd byte Margin
dudmuck 0:8f0d0ae0a077 1726 MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
dudmuck 0:8f0d0ae0a077 1727 MacCommandsBuffer[MacCommandsBufferIndex++] = p2;
dudmuck 0:8f0d0ae0a077 1728 status = LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1729 }
dudmuck 0:8f0d0ae0a077 1730 break;
dudmuck 0:8f0d0ae0a077 1731 case MOTE_MAC_RX_TIMING_SETUP_ANS:
dudmuck 0:8f0d0ae0a077 1732 if( MacCommandsBufferIndex < bufLen )
dudmuck 0:8f0d0ae0a077 1733 {
dudmuck 0:8f0d0ae0a077 1734 MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
dudmuck 0:8f0d0ae0a077 1735 // No payload for this answer
dudmuck 0:8f0d0ae0a077 1736 status = LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1737 }
dudmuck 0:8f0d0ae0a077 1738 break;
dudmuck 0:8f0d0ae0a077 1739 default:
dudmuck 0:8f0d0ae0a077 1740 return LORAMAC_STATUS_SERVICE_UNKNOWN;
dudmuck 0:8f0d0ae0a077 1741 }
dudmuck 0:8f0d0ae0a077 1742 if( status == LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 1743 {
dudmuck 0:8f0d0ae0a077 1744 MacCommandsInNextTx = true;
dudmuck 0:8f0d0ae0a077 1745 }
dudmuck 0:8f0d0ae0a077 1746 return status;
dudmuck 0:8f0d0ae0a077 1747 }
dudmuck 0:8f0d0ae0a077 1748
dudmuck 0:8f0d0ae0a077 1749 static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut )
dudmuck 0:8f0d0ae0a077 1750 {
dudmuck 0:8f0d0ae0a077 1751 uint8_t i = 0;
dudmuck 0:8f0d0ae0a077 1752 uint8_t cmdCount = 0;
dudmuck 0:8f0d0ae0a077 1753
dudmuck 0:8f0d0ae0a077 1754 if( ( cmdBufIn == NULL ) || ( cmdBufOut == NULL ) )
dudmuck 0:8f0d0ae0a077 1755 {
dudmuck 0:8f0d0ae0a077 1756 return 0;
dudmuck 0:8f0d0ae0a077 1757 }
dudmuck 0:8f0d0ae0a077 1758
dudmuck 0:8f0d0ae0a077 1759 for( i = 0; i < length; i++ )
dudmuck 0:8f0d0ae0a077 1760 {
dudmuck 0:8f0d0ae0a077 1761 switch( cmdBufIn[i] )
dudmuck 0:8f0d0ae0a077 1762 {
dudmuck 0:8f0d0ae0a077 1763 // STICKY
dudmuck 0:8f0d0ae0a077 1764 case MOTE_MAC_RX_PARAM_SETUP_ANS:
dudmuck 0:8f0d0ae0a077 1765 {
dudmuck 0:8f0d0ae0a077 1766 cmdBufOut[cmdCount++] = cmdBufIn[i++];
dudmuck 0:8f0d0ae0a077 1767 cmdBufOut[cmdCount++] = cmdBufIn[i];
dudmuck 0:8f0d0ae0a077 1768 break;
dudmuck 0:8f0d0ae0a077 1769 }
dudmuck 0:8f0d0ae0a077 1770 case MOTE_MAC_RX_TIMING_SETUP_ANS:
dudmuck 0:8f0d0ae0a077 1771 {
dudmuck 0:8f0d0ae0a077 1772 cmdBufOut[cmdCount++] = cmdBufIn[i];
dudmuck 0:8f0d0ae0a077 1773 break;
dudmuck 0:8f0d0ae0a077 1774 }
dudmuck 0:8f0d0ae0a077 1775 // NON-STICKY
dudmuck 0:8f0d0ae0a077 1776 case MOTE_MAC_DEV_STATUS_ANS:
dudmuck 0:8f0d0ae0a077 1777 { // 2 bytes payload
dudmuck 0:8f0d0ae0a077 1778 i += 2;
dudmuck 0:8f0d0ae0a077 1779 break;
dudmuck 0:8f0d0ae0a077 1780 }
dudmuck 0:8f0d0ae0a077 1781 case MOTE_MAC_LINK_CHECK_REQ:
dudmuck 0:8f0d0ae0a077 1782 { // 0 byte payload
dudmuck 0:8f0d0ae0a077 1783 break;
dudmuck 0:8f0d0ae0a077 1784 }
dudmuck 0:8f0d0ae0a077 1785 default:
dudmuck 0:8f0d0ae0a077 1786 break;
dudmuck 0:8f0d0ae0a077 1787 }
dudmuck 0:8f0d0ae0a077 1788 }
dudmuck 0:8f0d0ae0a077 1789
dudmuck 0:8f0d0ae0a077 1790 return cmdCount;
dudmuck 0:8f0d0ae0a077 1791 }
dudmuck 0:8f0d0ae0a077 1792
dudmuck 0:8f0d0ae0a077 1793 static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr )
dudmuck 0:8f0d0ae0a077 1794 {
dudmuck 0:8f0d0ae0a077 1795 while( macIndex < commandsSize )
dudmuck 0:8f0d0ae0a077 1796 {
dudmuck 0:8f0d0ae0a077 1797 // Decode Frame MAC commands
dudmuck 0:8f0d0ae0a077 1798 switch( payload[macIndex++] )
dudmuck 0:8f0d0ae0a077 1799 {
dudmuck 0:8f0d0ae0a077 1800 case SRV_MAC_LINK_CHECK_ANS:
dudmuck 0:8f0d0ae0a077 1801 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
dudmuck 0:8f0d0ae0a077 1802 MlmeConfirm.DemodMargin = payload[macIndex++];
dudmuck 0:8f0d0ae0a077 1803 MlmeConfirm.NbGateways = payload[macIndex++];
dudmuck 0:8f0d0ae0a077 1804 break;
dudmuck 0:8f0d0ae0a077 1805 case SRV_MAC_DEV_STATUS_REQ:
dudmuck 0:8f0d0ae0a077 1806 {
dudmuck 0:8f0d0ae0a077 1807 uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE;
dudmuck 0:8f0d0ae0a077 1808 if( ( LoRaMacCallbacks != NULL ) && ( LoRaMacCallbacks->GetBatteryLevel != NULL ) )
dudmuck 0:8f0d0ae0a077 1809 {
dudmuck 0:8f0d0ae0a077 1810 batteryLevel = LoRaMacCallbacks->GetBatteryLevel( );
dudmuck 0:8f0d0ae0a077 1811 }
dudmuck 0:8f0d0ae0a077 1812 AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr );
dudmuck 0:8f0d0ae0a077 1813 break;
dudmuck 0:8f0d0ae0a077 1814 }
dudmuck 0:8f0d0ae0a077 1815 case SRV_MAC_RX_TIMING_SETUP_REQ:
dudmuck 0:8f0d0ae0a077 1816 {
dudmuck 0:8f0d0ae0a077 1817 uint8_t delay = payload[macIndex++] & 0x0F;
dudmuck 0:8f0d0ae0a077 1818
dudmuck 0:8f0d0ae0a077 1819 if( delay == 0 )
dudmuck 0:8f0d0ae0a077 1820 {
dudmuck 0:8f0d0ae0a077 1821 delay++;
dudmuck 0:8f0d0ae0a077 1822 }
dudmuck 0:8f0d0ae0a077 1823 LoRaMacParams.ReceiveDelay_us = delay * 1e6;
dudmuck 0:8f0d0ae0a077 1824 AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 );
dudmuck 0:8f0d0ae0a077 1825 }
dudmuck 0:8f0d0ae0a077 1826 break;
dudmuck 0:8f0d0ae0a077 1827 default:
dudmuck 0:8f0d0ae0a077 1828 // Unknown command. ABORT MAC commands processing
dudmuck 0:8f0d0ae0a077 1829 return;
dudmuck 0:8f0d0ae0a077 1830 }
dudmuck 0:8f0d0ae0a077 1831 }
dudmuck 0:8f0d0ae0a077 1832 }
dudmuck 0:8f0d0ae0a077 1833
dudmuck 0:8f0d0ae0a077 1834 LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
dudmuck 0:8f0d0ae0a077 1835 {
dudmuck 0:8f0d0ae0a077 1836 LoRaMacFrameCtrl_t fCtrl;
dudmuck 0:8f0d0ae0a077 1837 LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 1838
dudmuck 0:8f0d0ae0a077 1839 fCtrl.Value = 0;
dudmuck 0:8f0d0ae0a077 1840 fCtrl.Bits.FOptsLen = 0;
dudmuck 0:8f0d0ae0a077 1841 fCtrl.Bits.FPending = 0;
dudmuck 0:8f0d0ae0a077 1842 fCtrl.Bits.Ack = false;
dudmuck 0:8f0d0ae0a077 1843 fCtrl.Bits.AdrAckReq = false;
dudmuck 0:8f0d0ae0a077 1844 fCtrl.Bits.Adr = false;
dudmuck 0:8f0d0ae0a077 1845
dudmuck 0:8f0d0ae0a077 1846 // Prepare the frame
dudmuck 0:8f0d0ae0a077 1847 status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize );
dudmuck 0:8f0d0ae0a077 1848
dudmuck 0:8f0d0ae0a077 1849 // Validate status
dudmuck 0:8f0d0ae0a077 1850 if( status != LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 1851 {
dudmuck 0:8f0d0ae0a077 1852 return status;
dudmuck 0:8f0d0ae0a077 1853 }
dudmuck 0:8f0d0ae0a077 1854
dudmuck 0:8f0d0ae0a077 1855 // Reset confirm parameters
dudmuck 0:8f0d0ae0a077 1856 McpsConfirm.AckReceived = false;
dudmuck 0:8f0d0ae0a077 1857 McpsConfirm.UpLinkCounter = UpLinkCounter;
dudmuck 0:8f0d0ae0a077 1858
dudmuck 16:915815632c1f 1859 return ScheduleTx();
dudmuck 0:8f0d0ae0a077 1860 }
dudmuck 0:8f0d0ae0a077 1861
dudmuck 0:8f0d0ae0a077 1862 static LoRaMacStatus_t ScheduleTx( void )
dudmuck 0:8f0d0ae0a077 1863 {
dudmuck 0:8f0d0ae0a077 1864 // Compute Rx1 windows parameters
dudmuck 16:915815632c1f 1865 if (!IsLoRaMacNetworkJoined)
dudmuck 0:8f0d0ae0a077 1866 {
dudmuck 0:8f0d0ae0a077 1867 RxWindowDelay_us = LoRaMacParams.JoinAcceptDelay_us + RxWindowsParam.RxOffset; // dont care
dudmuck 0:8f0d0ae0a077 1868 }
dudmuck 0:8f0d0ae0a077 1869 else
dudmuck 0:8f0d0ae0a077 1870 {
dudmuck 0:8f0d0ae0a077 1871 RxWindowDelay_us = LoRaMacParams.ReceiveDelay_us + RxWindowsParam.RxOffset;
dudmuck 0:8f0d0ae0a077 1872 }
dudmuck 20:42839629a5dc 1873 RxWindowDelay_us -= DIO0_LAG_us;
dudmuck 0:8f0d0ae0a077 1874
dudmuck 0:8f0d0ae0a077 1875 // Schedule transmission of frame
dudmuck 0:8f0d0ae0a077 1876
dudmuck 0:8f0d0ae0a077 1877 // Try to send now
dudmuck 0:8f0d0ae0a077 1878 return SendFrameOnChannel( Channels[Channel] );
dudmuck 0:8f0d0ae0a077 1879 }
dudmuck 0:8f0d0ae0a077 1880
dudmuck 0:8f0d0ae0a077 1881 static void ResetMacParameters( void )
dudmuck 0:8f0d0ae0a077 1882 {
dudmuck 0:8f0d0ae0a077 1883 IsLoRaMacNetworkJoined = false;
dudmuck 0:8f0d0ae0a077 1884
dudmuck 0:8f0d0ae0a077 1885 // Counters
dudmuck 0:8f0d0ae0a077 1886 UpLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 1887 DownLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 1888
dudmuck 0:8f0d0ae0a077 1889 ChannelsNbRepCounter = 0;
dudmuck 0:8f0d0ae0a077 1890
dudmuck 0:8f0d0ae0a077 1891 MacCommandsBufferIndex = 0;
dudmuck 0:8f0d0ae0a077 1892 MacCommandsBufferToRepeatIndex = 0;
dudmuck 0:8f0d0ae0a077 1893
dudmuck 0:8f0d0ae0a077 1894 IsRxWindowsEnabled = true;
dudmuck 0:8f0d0ae0a077 1895
dudmuck 0:8f0d0ae0a077 1896 LoRaMacParams.ChannelsTxPower = LoRaMacParamsDefaults.ChannelsTxPower;
dudmuck 0:8f0d0ae0a077 1897 LoRaMacParams.ChannelsDatarate_fixed = LoRaMacParamsDefaults.ChannelsDatarate_fixed;
dudmuck 0:8f0d0ae0a077 1898
dudmuck 0:8f0d0ae0a077 1899 LoRaMacParams.Rx1DrOffset = LoRaMacParamsDefaults.Rx1DrOffset;
dudmuck 0:8f0d0ae0a077 1900
dudmuck 0:8f0d0ae0a077 1901 NodeAckRequested = false;
dudmuck 0:8f0d0ae0a077 1902 SrvAckRequested = false;
dudmuck 0:8f0d0ae0a077 1903 MacCommandsInNextTx = false;
dudmuck 0:8f0d0ae0a077 1904
dudmuck 0:8f0d0ae0a077 1905 // Reset Multicast downlink counters
dudmuck 0:8f0d0ae0a077 1906 MulticastParams_t *cur = MulticastChannels;
dudmuck 0:8f0d0ae0a077 1907 while( cur != NULL )
dudmuck 0:8f0d0ae0a077 1908 {
dudmuck 0:8f0d0ae0a077 1909 cur->DownLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 1910 cur = cur->Next;
dudmuck 0:8f0d0ae0a077 1911 }
dudmuck 0:8f0d0ae0a077 1912
dudmuck 0:8f0d0ae0a077 1913 }
dudmuck 0:8f0d0ae0a077 1914
dudmuck 0:8f0d0ae0a077 1915 LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
dudmuck 0:8f0d0ae0a077 1916 {
dudmuck 0:8f0d0ae0a077 1917 uint16_t i;
dudmuck 0:8f0d0ae0a077 1918 uint8_t pktHeaderLen = 0;
dudmuck 0:8f0d0ae0a077 1919 uint32_t mic = 0;
dudmuck 0:8f0d0ae0a077 1920 const void* payload = fBuffer;
dudmuck 0:8f0d0ae0a077 1921 uint8_t framePort = fPort;
dudmuck 0:8f0d0ae0a077 1922
dudmuck 0:8f0d0ae0a077 1923 LoRaMacBufferPktLen = 0;
dudmuck 0:8f0d0ae0a077 1924
dudmuck 0:8f0d0ae0a077 1925 NodeAckRequested = false;
dudmuck 0:8f0d0ae0a077 1926
dudmuck 0:8f0d0ae0a077 1927 if( fBuffer == NULL )
dudmuck 0:8f0d0ae0a077 1928 {
dudmuck 0:8f0d0ae0a077 1929 fBufferSize = 0;
dudmuck 0:8f0d0ae0a077 1930 }
dudmuck 0:8f0d0ae0a077 1931
dudmuck 0:8f0d0ae0a077 1932 LoRaMacTxPayloadLen = fBufferSize;
dudmuck 0:8f0d0ae0a077 1933
dudmuck 0:8f0d0ae0a077 1934 LoRaMacBuffer[pktHeaderLen++] = macHdr->Value;
dudmuck 0:8f0d0ae0a077 1935
dudmuck 0:8f0d0ae0a077 1936 switch( macHdr->Bits.MType )
dudmuck 0:8f0d0ae0a077 1937 {
dudmuck 0:8f0d0ae0a077 1938 case FRAME_TYPE_JOIN_REQ:
dudmuck 0:8f0d0ae0a077 1939 LoRaMacBufferPktLen = pktHeaderLen;
dudmuck 0:8f0d0ae0a077 1940
dudmuck 0:8f0d0ae0a077 1941 memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacAppEui, 8 );
dudmuck 0:8f0d0ae0a077 1942 LoRaMacBufferPktLen += 8;
dudmuck 0:8f0d0ae0a077 1943 memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacDevEui, 8 );
dudmuck 0:8f0d0ae0a077 1944 LoRaMacBufferPktLen += 8;
dudmuck 0:8f0d0ae0a077 1945
dudmuck 0:8f0d0ae0a077 1946 LoRaMacDevNonce = Radio.Random( );
dudmuck 0:8f0d0ae0a077 1947
dudmuck 0:8f0d0ae0a077 1948 LoRaMacBuffer[LoRaMacBufferPktLen++] = LoRaMacDevNonce & 0xFF;
dudmuck 0:8f0d0ae0a077 1949 LoRaMacBuffer[LoRaMacBufferPktLen++] = ( LoRaMacDevNonce >> 8 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1950
dudmuck 0:8f0d0ae0a077 1951 LoRaMacJoinComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen & 0xFF, LoRaMacAppKey, &mic );
dudmuck 0:8f0d0ae0a077 1952
dudmuck 0:8f0d0ae0a077 1953 LoRaMacBuffer[LoRaMacBufferPktLen++] = mic & 0xFF;
dudmuck 0:8f0d0ae0a077 1954 LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 8 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1955 LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 16 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1956 LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 24 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1957
dudmuck 0:8f0d0ae0a077 1958 break;
dudmuck 0:8f0d0ae0a077 1959 case FRAME_TYPE_DATA_CONFIRMED_UP:
dudmuck 0:8f0d0ae0a077 1960 NodeAckRequested = true;
dudmuck 0:8f0d0ae0a077 1961 //Intentional fallthrough
dudmuck 0:8f0d0ae0a077 1962 case FRAME_TYPE_DATA_UNCONFIRMED_UP:
dudmuck 16:915815632c1f 1963 if (!IsLoRaMacNetworkJoined)
dudmuck 0:8f0d0ae0a077 1964 {
dudmuck 0:8f0d0ae0a077 1965 return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet
dudmuck 0:8f0d0ae0a077 1966 }
dudmuck 0:8f0d0ae0a077 1967
dudmuck 0:8f0d0ae0a077 1968 fCtrl->Bits.AdrAckReq = 0;
dudmuck 0:8f0d0ae0a077 1969
dudmuck 0:8f0d0ae0a077 1970 if( SrvAckRequested == true )
dudmuck 0:8f0d0ae0a077 1971 {
dudmuck 0:8f0d0ae0a077 1972 SrvAckRequested = false;
dudmuck 0:8f0d0ae0a077 1973 fCtrl->Bits.Ack = 1;
dudmuck 0:8f0d0ae0a077 1974 }
dudmuck 0:8f0d0ae0a077 1975
dudmuck 0:8f0d0ae0a077 1976 LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1977 LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1978 LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1979 LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1980
dudmuck 0:8f0d0ae0a077 1981 LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value;
dudmuck 0:8f0d0ae0a077 1982
dudmuck 0:8f0d0ae0a077 1983 LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF;
dudmuck 0:8f0d0ae0a077 1984 LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 1985
dudmuck 0:8f0d0ae0a077 1986 // Copy the MAC commands which must be re-send into the MAC command buffer
dudmuck 0:8f0d0ae0a077 1987 memcpy1( &MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex );
dudmuck 0:8f0d0ae0a077 1988 MacCommandsBufferIndex += MacCommandsBufferToRepeatIndex;
dudmuck 0:8f0d0ae0a077 1989
dudmuck 0:8f0d0ae0a077 1990 if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) )
dudmuck 0:8f0d0ae0a077 1991 {
dudmuck 0:8f0d0ae0a077 1992 if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) )
dudmuck 0:8f0d0ae0a077 1993 {
dudmuck 0:8f0d0ae0a077 1994 fCtrl->Bits.FOptsLen += MacCommandsBufferIndex;
dudmuck 0:8f0d0ae0a077 1995
dudmuck 0:8f0d0ae0a077 1996 // Update FCtrl field with new value of OptionsLength
dudmuck 0:8f0d0ae0a077 1997 LoRaMacBuffer[0x05] = fCtrl->Value;
dudmuck 0:8f0d0ae0a077 1998 for( i = 0; i < MacCommandsBufferIndex; i++ )
dudmuck 0:8f0d0ae0a077 1999 {
dudmuck 0:8f0d0ae0a077 2000 LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i];
dudmuck 0:8f0d0ae0a077 2001 }
dudmuck 0:8f0d0ae0a077 2002 }
dudmuck 0:8f0d0ae0a077 2003 }
dudmuck 0:8f0d0ae0a077 2004 else
dudmuck 0:8f0d0ae0a077 2005 {
dudmuck 0:8f0d0ae0a077 2006 if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) )
dudmuck 0:8f0d0ae0a077 2007 {
dudmuck 0:8f0d0ae0a077 2008 LoRaMacTxPayloadLen = MacCommandsBufferIndex;
dudmuck 0:8f0d0ae0a077 2009 payload = MacCommandsBuffer;
dudmuck 0:8f0d0ae0a077 2010 framePort = 0;
dudmuck 0:8f0d0ae0a077 2011 }
dudmuck 0:8f0d0ae0a077 2012 }
dudmuck 0:8f0d0ae0a077 2013 MacCommandsInNextTx = false;
dudmuck 0:8f0d0ae0a077 2014 // Store MAC commands which must be re-send in case the device does not receive a downlink anymore
dudmuck 0:8f0d0ae0a077 2015 MacCommandsBufferToRepeatIndex = ParseMacCommandsToRepeat( MacCommandsBuffer, MacCommandsBufferIndex, MacCommandsBufferToRepeat );
dudmuck 0:8f0d0ae0a077 2016 if( MacCommandsBufferToRepeatIndex > 0 )
dudmuck 0:8f0d0ae0a077 2017 {
dudmuck 0:8f0d0ae0a077 2018 MacCommandsInNextTx = true;
dudmuck 0:8f0d0ae0a077 2019 }
dudmuck 0:8f0d0ae0a077 2020
dudmuck 0:8f0d0ae0a077 2021 if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) )
dudmuck 0:8f0d0ae0a077 2022 {
dudmuck 0:8f0d0ae0a077 2023 LoRaMacBuffer[pktHeaderLen++] = framePort;
dudmuck 0:8f0d0ae0a077 2024
dudmuck 0:8f0d0ae0a077 2025 if( framePort == 0 )
dudmuck 0:8f0d0ae0a077 2026 {
dudmuck 0:8f0d0ae0a077 2027 LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] );
dudmuck 0:8f0d0ae0a077 2028 }
dudmuck 0:8f0d0ae0a077 2029 else
dudmuck 0:8f0d0ae0a077 2030 {
dudmuck 0:8f0d0ae0a077 2031 LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] );
dudmuck 0:8f0d0ae0a077 2032 }
dudmuck 0:8f0d0ae0a077 2033 }
dudmuck 0:8f0d0ae0a077 2034 LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen;
dudmuck 0:8f0d0ae0a077 2035
dudmuck 0:8f0d0ae0a077 2036 LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic );
dudmuck 0:8f0d0ae0a077 2037
dudmuck 0:8f0d0ae0a077 2038 LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF;
dudmuck 0:8f0d0ae0a077 2039 LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 2040 LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 2041 LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF;
dudmuck 0:8f0d0ae0a077 2042
dudmuck 0:8f0d0ae0a077 2043 LoRaMacBufferPktLen += LORAMAC_MFR_LEN;
dudmuck 0:8f0d0ae0a077 2044
dudmuck 0:8f0d0ae0a077 2045 break;
dudmuck 0:8f0d0ae0a077 2046 case FRAME_TYPE_PROPRIETARY:
dudmuck 0:8f0d0ae0a077 2047 if( ( fBuffer != NULL ) && ( LoRaMacTxPayloadLen > 0 ) )
dudmuck 0:8f0d0ae0a077 2048 {
dudmuck 0:8f0d0ae0a077 2049 memcpy1( LoRaMacBuffer + pktHeaderLen, ( uint8_t* ) fBuffer, LoRaMacTxPayloadLen );
dudmuck 0:8f0d0ae0a077 2050 LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen;
dudmuck 0:8f0d0ae0a077 2051 }
dudmuck 0:8f0d0ae0a077 2052 break;
dudmuck 0:8f0d0ae0a077 2053 default:
dudmuck 0:8f0d0ae0a077 2054 return LORAMAC_STATUS_SERVICE_UNKNOWN;
dudmuck 0:8f0d0ae0a077 2055 }
dudmuck 0:8f0d0ae0a077 2056
dudmuck 0:8f0d0ae0a077 2057 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2058 }
dudmuck 0:8f0d0ae0a077 2059
dudmuck 0:8f0d0ae0a077 2060 LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel )
dudmuck 0:8f0d0ae0a077 2061 {
dudmuck 0:8f0d0ae0a077 2062 int8_t datarate = Datarates[LoRaMacParams.ChannelsDatarate_fixed];
dudmuck 0:8f0d0ae0a077 2063 int8_t txPowerIndex = 0;
dudmuck 0:8f0d0ae0a077 2064 int8_t txPower = 0;
dudmuck 0:8f0d0ae0a077 2065
dudmuck 16:915815632c1f 2066 if (BeaconCtx.guard) {
dudmuck 17:3215f12051f9 2067 unsigned now_us = lp_timer.read_us();
dudmuck 17:3215f12051f9 2068 if (now_us - BeaconCtx.guard_at > 10000000) {
dudmuck 17:3215f12051f9 2069 isr_printf("beacon fault\r\n");
dudmuck 17:3215f12051f9 2070 IsLoRaMacNetworkJoined = false;
dudmuck 17:3215f12051f9 2071 BeaconCtx.timeout_rx.detach();
dudmuck 17:3215f12051f9 2072 BeaconCtx.timeout_guard.detach();
dudmuck 17:3215f12051f9 2073 BeaconCtx.state = BEACON_STATE_NONE;
dudmuck 17:3215f12051f9 2074 BeaconCtx.guard = false;
dudmuck 17:3215f12051f9 2075 return LORAMAC_STATUS_NO_NETWORK_JOINED;
dudmuck 17:3215f12051f9 2076 }
dudmuck 0:8f0d0ae0a077 2077 return LORAMAC_STATUS_BUSY;
dudmuck 0:8f0d0ae0a077 2078 }
dudmuck 0:8f0d0ae0a077 2079
dudmuck 0:8f0d0ae0a077 2080 txPowerIndex = LoRaMacParams.ChannelsTxPower;
dudmuck 0:8f0d0ae0a077 2081 txPower = TxPowers[txPowerIndex];
dudmuck 0:8f0d0ae0a077 2082
dudmuck 2:f2d9aa163652 2083 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_SEND;
dudmuck 2:f2d9aa163652 2084 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_SEND;
dudmuck 0:8f0d0ae0a077 2085 McpsConfirm.TxPower = txPowerIndex;
dudmuck 0:8f0d0ae0a077 2086 McpsConfirm.UpLinkFrequency = channel.Frequency;
dudmuck 0:8f0d0ae0a077 2087
dudmuck 0:8f0d0ae0a077 2088 Radio.SetChannel( channel.Frequency );
dudmuck 0:8f0d0ae0a077 2089
dudmuck 18:9ac71c0eb70d 2090 #if defined( USE_BAND_915_SINGLE )
dudmuck 0:8f0d0ae0a077 2091 if( LoRaMacParams.ChannelsDatarate_fixed >= DR_8 )
dudmuck 0:8f0d0ae0a077 2092 { // High speed LoRa channel BW500 kHz
dudmuck 18:9ac71c0eb70d 2093 Radio.SetTxConfig( MODEM_LORA, txPower, 0, 2, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
dudmuck 0:8f0d0ae0a077 2094 TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
dudmuck 0:8f0d0ae0a077 2095 } else
dudmuck 0:8f0d0ae0a077 2096 return LORAMAC_STATUS_DATARATE_INVALID;
dudmuck 18:9ac71c0eb70d 2097 #elif defined(USE_BAND_433) && defined (ENABLE_SX1276)
dudmuck 18:9ac71c0eb70d 2098 if( LoRaMacParams.ChannelsDatarate_fixed == DR_7 )
dudmuck 18:9ac71c0eb70d 2099 { // High Speed FSK channel
dudmuck 18:9ac71c0eb70d 2100 //Radio.SetMaxPayloadLength( MODEM_FSK, LoRaMacBufferPktLen );
dudmuck 18:9ac71c0eb70d 2101 Radio.SetTxConfig( MODEM_FSK, txPower, 25e3, 0, datarate * 1e3, 0, 5, false, true, 0, 0, false, 3e3 );
dudmuck 18:9ac71c0eb70d 2102 TxTimeOnAir = Radio.TimeOnAir( MODEM_FSK, LoRaMacBufferPktLen );
dudmuck 18:9ac71c0eb70d 2103
dudmuck 18:9ac71c0eb70d 2104 }
dudmuck 18:9ac71c0eb70d 2105 else if( LoRaMacParams.ChannelsDatarate_fixed == DR_6 )
dudmuck 18:9ac71c0eb70d 2106 { // High speed LoRa channel
dudmuck 18:9ac71c0eb70d 2107 //Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
dudmuck 18:9ac71c0eb70d 2108 Radio.SetTxConfig( MODEM_LORA, txPower, 0, 1, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
dudmuck 18:9ac71c0eb70d 2109 TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
dudmuck 18:9ac71c0eb70d 2110 }
dudmuck 18:9ac71c0eb70d 2111 else
dudmuck 18:9ac71c0eb70d 2112 { // Normal LoRa channel
dudmuck 18:9ac71c0eb70d 2113 //Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
dudmuck 18:9ac71c0eb70d 2114 Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
dudmuck 18:9ac71c0eb70d 2115 TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
dudmuck 18:9ac71c0eb70d 2116 }
dudmuck 18:9ac71c0eb70d 2117 #endif
dudmuck 0:8f0d0ae0a077 2118
dudmuck 0:8f0d0ae0a077 2119 // Store the time on air
dudmuck 0:8f0d0ae0a077 2120 McpsConfirm.TxTimeOnAir = TxTimeOnAir;
dudmuck 0:8f0d0ae0a077 2121 MlmeConfirm.TxTimeOnAir = TxTimeOnAir;
dudmuck 0:8f0d0ae0a077 2122
dudmuck 0:8f0d0ae0a077 2123
dudmuck 16:915815632c1f 2124 if (!IsLoRaMacNetworkJoined)
dudmuck 0:8f0d0ae0a077 2125 {
dudmuck 0:8f0d0ae0a077 2126 JoinRequestTrials++;
dudmuck 7:e238827f0e47 2127 isr_printf("join %luhz try%u DR%u\r\n", channel.Frequency, JoinRequestTrials, LoRaMacParams.ChannelsDatarate_fixed);
dudmuck 0:8f0d0ae0a077 2128 }
dudmuck 0:8f0d0ae0a077 2129
dudmuck 0:8f0d0ae0a077 2130 /* anything not join request is sent at permitted time slot */
dudmuck 0:8f0d0ae0a077 2131 LoRaMacHeader_t* macHdr = (LoRaMacHeader_t*)&LoRaMacBuffer[0];
dudmuck 0:8f0d0ae0a077 2132 if (macHdr->Bits.MType == FRAME_TYPE_JOIN_REQ) {
dudmuck 0:8f0d0ae0a077 2133 // Send now
dudmuck 0:8f0d0ae0a077 2134 Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen );
dudmuck 16:915815632c1f 2135 }
dudmuck 16:915815632c1f 2136 else {
dudmuck 16:915815632c1f 2137 LoRaMacFlags.Bits.pending_tx = 1;
dudmuck 16:915815632c1f 2138 }
dudmuck 0:8f0d0ae0a077 2139
dudmuck 0:8f0d0ae0a077 2140 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2141 }
dudmuck 0:8f0d0ae0a077 2142
dudmuck 0:8f0d0ae0a077 2143 LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout )
dudmuck 0:8f0d0ae0a077 2144 {
dudmuck 0:8f0d0ae0a077 2145 int8_t txPowerIndex = 0;
dudmuck 0:8f0d0ae0a077 2146 int8_t txPower = 0;
dudmuck 0:8f0d0ae0a077 2147
dudmuck 0:8f0d0ae0a077 2148 txPowerIndex = LoRaMacParams.ChannelsTxPower;
dudmuck 0:8f0d0ae0a077 2149 txPower = TxPowers[txPowerIndex];
dudmuck 0:8f0d0ae0a077 2150 Radio.SetTxContinuousWave( Channels[Channel].Frequency, txPower, timeout );
dudmuck 0:8f0d0ae0a077 2151
dudmuck 0:8f0d0ae0a077 2152 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2153 }
dudmuck 0:8f0d0ae0a077 2154
dudmuck 0:8f0d0ae0a077 2155 LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power )
dudmuck 0:8f0d0ae0a077 2156 {
dudmuck 0:8f0d0ae0a077 2157 Radio.SetTxContinuousWave( frequency, power, timeout );
dudmuck 0:8f0d0ae0a077 2158
dudmuck 0:8f0d0ae0a077 2159 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2160 }
dudmuck 0:8f0d0ae0a077 2161
dudmuck 0:8f0d0ae0a077 2162 void seconds()
dudmuck 0:8f0d0ae0a077 2163 {
dudmuck 1:53c30224eda8 2164 isr_printf("second\r\n");
dudmuck 0:8f0d0ae0a077 2165 }
dudmuck 0:8f0d0ae0a077 2166
dudmuck 0:8f0d0ae0a077 2167 LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks )
dudmuck 0:8f0d0ae0a077 2168 {
dudmuck 0:8f0d0ae0a077 2169 if( primitives == NULL )
dudmuck 0:8f0d0ae0a077 2170 {
dudmuck 0:8f0d0ae0a077 2171 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2172 }
dudmuck 0:8f0d0ae0a077 2173
dudmuck 0:8f0d0ae0a077 2174 if( ( primitives->MacMcpsConfirm == NULL ) ||
dudmuck 0:8f0d0ae0a077 2175 ( primitives->MacMcpsIndication == NULL ) ||
dudmuck 0:8f0d0ae0a077 2176 ( primitives->MacMlmeConfirm == NULL ) )
dudmuck 0:8f0d0ae0a077 2177 {
dudmuck 0:8f0d0ae0a077 2178 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2179 }
dudmuck 0:8f0d0ae0a077 2180
dudmuck 0:8f0d0ae0a077 2181 LoRaMacPrimitives = primitives;
dudmuck 0:8f0d0ae0a077 2182 LoRaMacCallbacks = callbacks;
dudmuck 0:8f0d0ae0a077 2183
dudmuck 0:8f0d0ae0a077 2184 LoRaMacFlags.Value = 0;
dudmuck 0:8f0d0ae0a077 2185
dudmuck 0:8f0d0ae0a077 2186 LoRaMacDeviceClass = CLASS_A;
dudmuck 0:8f0d0ae0a077 2187
dudmuck 0:8f0d0ae0a077 2188 JoinRequestTrials = 0;
dudmuck 0:8f0d0ae0a077 2189 MaxJoinRequestTrials = 255;
dudmuck 0:8f0d0ae0a077 2190
dudmuck 0:8f0d0ae0a077 2191
dudmuck 0:8f0d0ae0a077 2192 // Reset to defaults
dudmuck 0:8f0d0ae0a077 2193 LoRaMacParamsDefaults.ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER;
dudmuck 0:8f0d0ae0a077 2194 LoRaMacParamsDefaults.ChannelsDatarate_fixed = LORAMAC_DEFAULT_DATARATE;
dudmuck 0:8f0d0ae0a077 2195
dudmuck 0:8f0d0ae0a077 2196 LoRaMacParamsDefaults.SystemMaxRxError = 10;
dudmuck 0:8f0d0ae0a077 2197 LoRaMacParamsDefaults.MinRxSymbols = 6; // TODO XXX increase
dudmuck 0:8f0d0ae0a077 2198 LoRaMacParamsDefaults.MaxRxWindow = MAX_RX_WINDOW;
dudmuck 0:8f0d0ae0a077 2199
dudmuck 0:8f0d0ae0a077 2200 LoRaMacParamsDefaults.ReceiveDelay_us = RECEIVE_DELAY_us;
dudmuck 0:8f0d0ae0a077 2201 LoRaMacParamsDefaults.JoinAcceptDelay_us = JOIN_ACCEPT_DELAY_us;
dudmuck 0:8f0d0ae0a077 2202
dudmuck 0:8f0d0ae0a077 2203 LoRaMacParamsDefaults.ChannelsNbRep = 1;
dudmuck 0:8f0d0ae0a077 2204 LoRaMacParamsDefaults.Rx1DrOffset = 0;
dudmuck 0:8f0d0ae0a077 2205
dudmuck 0:8f0d0ae0a077 2206 for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS; i++ )
dudmuck 0:8f0d0ae0a077 2207 {
dudmuck 0:8f0d0ae0a077 2208 Channels[i].Frequency = LORAMAC_FIRST_CHANNEL + (i * LORAMAC_STEPWIDTH_CHANNEL);
dudmuck 18:9ac71c0eb70d 2209 Channels[i].DrRange.Value = (LORAMAC_MAX_DATARATE << 4) | LORAMAC_MIN_DATARATE;
dudmuck 0:8f0d0ae0a077 2210 Channels[i].Band = 0;
dudmuck 0:8f0d0ae0a077 2211 }
dudmuck 0:8f0d0ae0a077 2212
dudmuck 0:8f0d0ae0a077 2213 // Init parameters which are not set in function ResetMacParameters
dudmuck 0:8f0d0ae0a077 2214 LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError;
dudmuck 0:8f0d0ae0a077 2215 LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols;
dudmuck 0:8f0d0ae0a077 2216 LoRaMacParams.MaxRxWindow = LoRaMacParamsDefaults.MaxRxWindow;
dudmuck 0:8f0d0ae0a077 2217 LoRaMacParams.ReceiveDelay_us = LoRaMacParamsDefaults.ReceiveDelay_us;
dudmuck 0:8f0d0ae0a077 2218 LoRaMacParams.JoinAcceptDelay_us = LoRaMacParamsDefaults.JoinAcceptDelay_us;
dudmuck 0:8f0d0ae0a077 2219 LoRaMacParams.ChannelsNbRep = LoRaMacParamsDefaults.ChannelsNbRep;
dudmuck 0:8f0d0ae0a077 2220
dudmuck 0:8f0d0ae0a077 2221 ResetMacParameters( );
dudmuck 0:8f0d0ae0a077 2222
dudmuck 0:8f0d0ae0a077 2223 // Initialize Radio driver
dudmuck 0:8f0d0ae0a077 2224 RadioEvents.TxDone = OnRadioTxDone;
dudmuck 0:8f0d0ae0a077 2225 RadioEvents.RxDone = OnRadioRxDone;
dudmuck 0:8f0d0ae0a077 2226 RadioEvents.RxError = OnRadioRxError;
dudmuck 0:8f0d0ae0a077 2227 RadioEvents.TxTimeout = OnRadioTxTimeout;
dudmuck 0:8f0d0ae0a077 2228 RadioEvents.RxTimeout = OnRadioRxTimeout;
dudmuck 0:8f0d0ae0a077 2229 Radio.Init( &RadioEvents );
dudmuck 0:8f0d0ae0a077 2230
dudmuck 0:8f0d0ae0a077 2231 // Random seed initialization
dudmuck 0:8f0d0ae0a077 2232 srand1( Radio.Random( ) );
dudmuck 0:8f0d0ae0a077 2233
dudmuck 0:8f0d0ae0a077 2234 PublicNetwork = true;
dudmuck 0:8f0d0ae0a077 2235 Radio.SetPublicNetwork( PublicNetwork );
dudmuck 0:8f0d0ae0a077 2236 Radio.Sleep( );
dudmuck 0:8f0d0ae0a077 2237
dudmuck 0:8f0d0ae0a077 2238 lp_timer.start();
dudmuck 0:8f0d0ae0a077 2239
dudmuck 0:8f0d0ae0a077 2240 RxWindowsParam = ComputeRxWindowParameters(LORAMAC_DEFAULT_DATARATE, LoRaMacParams.SystemMaxRxError);
dudmuck 13:18de9ee3a461 2241
dudmuck 13:18de9ee3a461 2242 #ifdef DEBUG_GWTX_JUMPER
dudmuck 13:18de9ee3a461 2243 gwtx_pin.rise(&gwtx_pin_callback);
dudmuck 13:18de9ee3a461 2244 #endif
dudmuck 0:8f0d0ae0a077 2245
dudmuck 0:8f0d0ae0a077 2246 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2247 }
dudmuck 0:8f0d0ae0a077 2248
dudmuck 0:8f0d0ae0a077 2249 LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
dudmuck 0:8f0d0ae0a077 2250 {
dudmuck 0:8f0d0ae0a077 2251 uint8_t fOptLen = MacCommandsBufferIndex + MacCommandsBufferToRepeatIndex;
dudmuck 0:8f0d0ae0a077 2252
dudmuck 0:8f0d0ae0a077 2253 if( txInfo == NULL )
dudmuck 0:8f0d0ae0a077 2254 {
dudmuck 0:8f0d0ae0a077 2255 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2256 }
dudmuck 0:8f0d0ae0a077 2257
dudmuck 0:8f0d0ae0a077 2258 txInfo->CurrentPayloadSize = 255;
dudmuck 0:8f0d0ae0a077 2259
dudmuck 0:8f0d0ae0a077 2260 if( txInfo->CurrentPayloadSize >= fOptLen )
dudmuck 0:8f0d0ae0a077 2261 {
dudmuck 0:8f0d0ae0a077 2262 txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize - fOptLen;
dudmuck 0:8f0d0ae0a077 2263 }
dudmuck 0:8f0d0ae0a077 2264 else
dudmuck 0:8f0d0ae0a077 2265 {
dudmuck 0:8f0d0ae0a077 2266 return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR;
dudmuck 0:8f0d0ae0a077 2267 }
dudmuck 0:8f0d0ae0a077 2268
dudmuck 0:8f0d0ae0a077 2269 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2270 }
dudmuck 0:8f0d0ae0a077 2271
dudmuck 0:8f0d0ae0a077 2272 LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet )
dudmuck 0:8f0d0ae0a077 2273 {
dudmuck 0:8f0d0ae0a077 2274 LoRaMacStatus_t status = LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2275
dudmuck 0:8f0d0ae0a077 2276 if( mibGet == NULL )
dudmuck 0:8f0d0ae0a077 2277 {
dudmuck 0:8f0d0ae0a077 2278 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2279 }
dudmuck 0:8f0d0ae0a077 2280
dudmuck 0:8f0d0ae0a077 2281 switch( mibGet->Type )
dudmuck 0:8f0d0ae0a077 2282 {
dudmuck 0:8f0d0ae0a077 2283 case MIB_DEVICE_CLASS:
dudmuck 0:8f0d0ae0a077 2284 {
dudmuck 0:8f0d0ae0a077 2285 mibGet->Param.Class = LoRaMacDeviceClass;
dudmuck 0:8f0d0ae0a077 2286 break;
dudmuck 0:8f0d0ae0a077 2287 }
dudmuck 0:8f0d0ae0a077 2288 case MIB_NETWORK_JOINED:
dudmuck 0:8f0d0ae0a077 2289 {
dudmuck 0:8f0d0ae0a077 2290 mibGet->Param.IsNetworkJoined = IsLoRaMacNetworkJoined;
dudmuck 0:8f0d0ae0a077 2291 break;
dudmuck 0:8f0d0ae0a077 2292 }
dudmuck 0:8f0d0ae0a077 2293 case MIB_NET_ID:
dudmuck 0:8f0d0ae0a077 2294 {
dudmuck 0:8f0d0ae0a077 2295 mibGet->Param.NetID = LoRaMacNetID;
dudmuck 0:8f0d0ae0a077 2296 break;
dudmuck 0:8f0d0ae0a077 2297 }
dudmuck 0:8f0d0ae0a077 2298 case MIB_DEV_ADDR:
dudmuck 0:8f0d0ae0a077 2299 {
dudmuck 0:8f0d0ae0a077 2300 mibGet->Param.DevAddr = LoRaMacDevAddr;
dudmuck 0:8f0d0ae0a077 2301 break;
dudmuck 0:8f0d0ae0a077 2302 }
dudmuck 0:8f0d0ae0a077 2303 case MIB_NWK_SKEY:
dudmuck 0:8f0d0ae0a077 2304 {
dudmuck 0:8f0d0ae0a077 2305 mibGet->Param.NwkSKey = LoRaMacNwkSKey;
dudmuck 0:8f0d0ae0a077 2306 break;
dudmuck 0:8f0d0ae0a077 2307 }
dudmuck 0:8f0d0ae0a077 2308 case MIB_APP_SKEY:
dudmuck 0:8f0d0ae0a077 2309 {
dudmuck 0:8f0d0ae0a077 2310 mibGet->Param.AppSKey = LoRaMacAppSKey;
dudmuck 0:8f0d0ae0a077 2311 break;
dudmuck 0:8f0d0ae0a077 2312 }
dudmuck 0:8f0d0ae0a077 2313 case MIB_PUBLIC_NETWORK:
dudmuck 0:8f0d0ae0a077 2314 {
dudmuck 0:8f0d0ae0a077 2315 mibGet->Param.EnablePublicNetwork = PublicNetwork;
dudmuck 0:8f0d0ae0a077 2316 break;
dudmuck 0:8f0d0ae0a077 2317 }
dudmuck 0:8f0d0ae0a077 2318 case MIB_CHANNELS_NB_REP:
dudmuck 0:8f0d0ae0a077 2319 {
dudmuck 0:8f0d0ae0a077 2320 mibGet->Param.ChannelNbRep = LoRaMacParams.ChannelsNbRep;
dudmuck 0:8f0d0ae0a077 2321 break;
dudmuck 0:8f0d0ae0a077 2322 }
dudmuck 0:8f0d0ae0a077 2323 case MIB_MAX_RX_WINDOW_DURATION:
dudmuck 0:8f0d0ae0a077 2324 {
dudmuck 0:8f0d0ae0a077 2325 mibGet->Param.MaxRxWindow = LoRaMacParams.MaxRxWindow;
dudmuck 0:8f0d0ae0a077 2326 break;
dudmuck 0:8f0d0ae0a077 2327 }
dudmuck 0:8f0d0ae0a077 2328 case MIB_CHANNELS_DEFAULT_TX_POWER:
dudmuck 0:8f0d0ae0a077 2329 {
dudmuck 0:8f0d0ae0a077 2330 mibGet->Param.ChannelsDefaultTxPower = LoRaMacParamsDefaults.ChannelsTxPower;
dudmuck 0:8f0d0ae0a077 2331 break;
dudmuck 0:8f0d0ae0a077 2332 }
dudmuck 0:8f0d0ae0a077 2333 case MIB_CHANNELS_TX_POWER:
dudmuck 0:8f0d0ae0a077 2334 {
dudmuck 0:8f0d0ae0a077 2335 mibGet->Param.ChannelsTxPower = LoRaMacParams.ChannelsTxPower;
dudmuck 0:8f0d0ae0a077 2336 break;
dudmuck 0:8f0d0ae0a077 2337 }
dudmuck 0:8f0d0ae0a077 2338 case MIB_UPLINK_COUNTER:
dudmuck 0:8f0d0ae0a077 2339 {
dudmuck 0:8f0d0ae0a077 2340 mibGet->Param.UpLinkCounter = UpLinkCounter;
dudmuck 0:8f0d0ae0a077 2341 break;
dudmuck 0:8f0d0ae0a077 2342 }
dudmuck 0:8f0d0ae0a077 2343 case MIB_DOWNLINK_COUNTER:
dudmuck 0:8f0d0ae0a077 2344 {
dudmuck 0:8f0d0ae0a077 2345 mibGet->Param.DownLinkCounter = DownLinkCounter;
dudmuck 0:8f0d0ae0a077 2346 break;
dudmuck 0:8f0d0ae0a077 2347 }
dudmuck 0:8f0d0ae0a077 2348 case MIB_MULTICAST_CHANNEL:
dudmuck 0:8f0d0ae0a077 2349 {
dudmuck 0:8f0d0ae0a077 2350 mibGet->Param.MulticastList = MulticastChannels;
dudmuck 0:8f0d0ae0a077 2351 break;
dudmuck 0:8f0d0ae0a077 2352 }
dudmuck 0:8f0d0ae0a077 2353 case MIB_SYSTEM_MAX_RX_ERROR:
dudmuck 0:8f0d0ae0a077 2354 {
dudmuck 0:8f0d0ae0a077 2355 mibGet->Param.SystemMaxRxError = LoRaMacParams.SystemMaxRxError;
dudmuck 0:8f0d0ae0a077 2356 break;
dudmuck 0:8f0d0ae0a077 2357 }
dudmuck 0:8f0d0ae0a077 2358 case MIB_MIN_RX_SYMBOLS:
dudmuck 0:8f0d0ae0a077 2359 {
dudmuck 0:8f0d0ae0a077 2360 mibGet->Param.MinRxSymbols = LoRaMacParams.MinRxSymbols;
dudmuck 0:8f0d0ae0a077 2361 break;
dudmuck 0:8f0d0ae0a077 2362 }
dudmuck 0:8f0d0ae0a077 2363 default:
dudmuck 0:8f0d0ae0a077 2364 status = LORAMAC_STATUS_SERVICE_UNKNOWN;
dudmuck 0:8f0d0ae0a077 2365 break;
dudmuck 0:8f0d0ae0a077 2366 }
dudmuck 0:8f0d0ae0a077 2367
dudmuck 0:8f0d0ae0a077 2368 return status;
dudmuck 0:8f0d0ae0a077 2369 }
dudmuck 0:8f0d0ae0a077 2370
dudmuck 0:8f0d0ae0a077 2371 LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet )
dudmuck 0:8f0d0ae0a077 2372 {
dudmuck 0:8f0d0ae0a077 2373 LoRaMacStatus_t status = LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2374
dudmuck 0:8f0d0ae0a077 2375 if( mibSet == NULL )
dudmuck 0:8f0d0ae0a077 2376 {
dudmuck 0:8f0d0ae0a077 2377 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2378 }
dudmuck 0:8f0d0ae0a077 2379
dudmuck 0:8f0d0ae0a077 2380 switch( mibSet->Type )
dudmuck 0:8f0d0ae0a077 2381 {
dudmuck 0:8f0d0ae0a077 2382 case MIB_DEVICE_CLASS:
dudmuck 0:8f0d0ae0a077 2383 {
dudmuck 0:8f0d0ae0a077 2384 LoRaMacDeviceClass = mibSet->Param.Class;
dudmuck 0:8f0d0ae0a077 2385 switch( LoRaMacDeviceClass )
dudmuck 0:8f0d0ae0a077 2386 {
dudmuck 0:8f0d0ae0a077 2387 case CLASS_A:
dudmuck 0:8f0d0ae0a077 2388 {
dudmuck 0:8f0d0ae0a077 2389 // Set the radio into sleep to setup a defined state
dudmuck 0:8f0d0ae0a077 2390 Radio.Sleep( );
dudmuck 0:8f0d0ae0a077 2391 break;
dudmuck 0:8f0d0ae0a077 2392 }
dudmuck 0:8f0d0ae0a077 2393 case CLASS_B:
dudmuck 0:8f0d0ae0a077 2394 {
dudmuck 0:8f0d0ae0a077 2395 break;
dudmuck 0:8f0d0ae0a077 2396 }
dudmuck 0:8f0d0ae0a077 2397 case CLASS_D:
dudmuck 1:53c30224eda8 2398 isr_printf("TODO MIB_DEVICE_CLASS:D\r\n");
dudmuck 0:8f0d0ae0a077 2399 break;
dudmuck 0:8f0d0ae0a077 2400 }
dudmuck 0:8f0d0ae0a077 2401 break;
dudmuck 0:8f0d0ae0a077 2402 }
dudmuck 0:8f0d0ae0a077 2403 case MIB_NETWORK_JOINED:
dudmuck 0:8f0d0ae0a077 2404 {
dudmuck 0:8f0d0ae0a077 2405 IsLoRaMacNetworkJoined = mibSet->Param.IsNetworkJoined;
dudmuck 16:915815632c1f 2406 if (!IsLoRaMacNetworkJoined) {
dudmuck 16:915815632c1f 2407 BeaconCtx.timeout_rx.detach();
dudmuck 16:915815632c1f 2408 BeaconCtx.timeout_guard.detach();
dudmuck 16:915815632c1f 2409 BeaconCtx.state = BEACON_STATE_NONE;
dudmuck 16:915815632c1f 2410 }
dudmuck 0:8f0d0ae0a077 2411 break;
dudmuck 0:8f0d0ae0a077 2412 }
dudmuck 0:8f0d0ae0a077 2413 case MIB_NET_ID:
dudmuck 0:8f0d0ae0a077 2414 {
dudmuck 0:8f0d0ae0a077 2415 LoRaMacNetID = mibSet->Param.NetID;
dudmuck 0:8f0d0ae0a077 2416 break;
dudmuck 0:8f0d0ae0a077 2417 }
dudmuck 0:8f0d0ae0a077 2418 case MIB_DEV_ADDR:
dudmuck 0:8f0d0ae0a077 2419 {
dudmuck 0:8f0d0ae0a077 2420 LoRaMacDevAddr = mibSet->Param.DevAddr;
dudmuck 0:8f0d0ae0a077 2421 break;
dudmuck 0:8f0d0ae0a077 2422 }
dudmuck 0:8f0d0ae0a077 2423 case MIB_NWK_SKEY:
dudmuck 0:8f0d0ae0a077 2424 {
dudmuck 0:8f0d0ae0a077 2425 if( mibSet->Param.NwkSKey != NULL )
dudmuck 0:8f0d0ae0a077 2426 {
dudmuck 0:8f0d0ae0a077 2427 memcpy1( LoRaMacNwkSKey, mibSet->Param.NwkSKey,
dudmuck 0:8f0d0ae0a077 2428 sizeof( LoRaMacNwkSKey ) );
dudmuck 0:8f0d0ae0a077 2429 }
dudmuck 0:8f0d0ae0a077 2430 else
dudmuck 0:8f0d0ae0a077 2431 {
dudmuck 0:8f0d0ae0a077 2432 status = LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2433 }
dudmuck 0:8f0d0ae0a077 2434 break;
dudmuck 0:8f0d0ae0a077 2435 }
dudmuck 0:8f0d0ae0a077 2436 case MIB_APP_SKEY:
dudmuck 0:8f0d0ae0a077 2437 {
dudmuck 0:8f0d0ae0a077 2438 if( mibSet->Param.AppSKey != NULL )
dudmuck 0:8f0d0ae0a077 2439 {
dudmuck 0:8f0d0ae0a077 2440 memcpy1( LoRaMacAppSKey, mibSet->Param.AppSKey,
dudmuck 0:8f0d0ae0a077 2441 sizeof( LoRaMacAppSKey ) );
dudmuck 0:8f0d0ae0a077 2442 }
dudmuck 0:8f0d0ae0a077 2443 else
dudmuck 0:8f0d0ae0a077 2444 {
dudmuck 0:8f0d0ae0a077 2445 status = LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2446 }
dudmuck 0:8f0d0ae0a077 2447 break;
dudmuck 0:8f0d0ae0a077 2448 }
dudmuck 0:8f0d0ae0a077 2449 case MIB_PUBLIC_NETWORK:
dudmuck 0:8f0d0ae0a077 2450 {
dudmuck 0:8f0d0ae0a077 2451 PublicNetwork = mibSet->Param.EnablePublicNetwork;
dudmuck 0:8f0d0ae0a077 2452 Radio.SetPublicNetwork( PublicNetwork );
dudmuck 0:8f0d0ae0a077 2453 break;
dudmuck 0:8f0d0ae0a077 2454 }
dudmuck 0:8f0d0ae0a077 2455 case MIB_CHANNELS_NB_REP:
dudmuck 0:8f0d0ae0a077 2456 {
dudmuck 0:8f0d0ae0a077 2457 if( ( mibSet->Param.ChannelNbRep >= 1 ) &&
dudmuck 0:8f0d0ae0a077 2458 ( mibSet->Param.ChannelNbRep <= 15 ) )
dudmuck 0:8f0d0ae0a077 2459 {
dudmuck 0:8f0d0ae0a077 2460 LoRaMacParams.ChannelsNbRep = mibSet->Param.ChannelNbRep;
dudmuck 0:8f0d0ae0a077 2461 }
dudmuck 0:8f0d0ae0a077 2462 else
dudmuck 0:8f0d0ae0a077 2463 {
dudmuck 0:8f0d0ae0a077 2464 status = LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2465 }
dudmuck 0:8f0d0ae0a077 2466 break;
dudmuck 0:8f0d0ae0a077 2467 }
dudmuck 0:8f0d0ae0a077 2468 case MIB_MAX_RX_WINDOW_DURATION:
dudmuck 0:8f0d0ae0a077 2469 {
dudmuck 0:8f0d0ae0a077 2470 LoRaMacParams.MaxRxWindow = mibSet->Param.MaxRxWindow;
dudmuck 0:8f0d0ae0a077 2471 break;
dudmuck 0:8f0d0ae0a077 2472 }
dudmuck 0:8f0d0ae0a077 2473 case MIB_CHANNELS_DEFAULT_TX_POWER:
dudmuck 0:8f0d0ae0a077 2474 {
dudmuck 0:8f0d0ae0a077 2475 if( ValueInRange( mibSet->Param.ChannelsDefaultTxPower,
dudmuck 0:8f0d0ae0a077 2476 LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) )
dudmuck 0:8f0d0ae0a077 2477 {
dudmuck 0:8f0d0ae0a077 2478 LoRaMacParamsDefaults.ChannelsTxPower = mibSet->Param.ChannelsDefaultTxPower;
dudmuck 0:8f0d0ae0a077 2479 }
dudmuck 0:8f0d0ae0a077 2480 else
dudmuck 0:8f0d0ae0a077 2481 {
dudmuck 0:8f0d0ae0a077 2482 status = LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2483 }
dudmuck 0:8f0d0ae0a077 2484 break;
dudmuck 0:8f0d0ae0a077 2485 }
dudmuck 0:8f0d0ae0a077 2486 case MIB_CHANNELS_TX_POWER:
dudmuck 0:8f0d0ae0a077 2487 {
dudmuck 0:8f0d0ae0a077 2488 if( ValueInRange( mibSet->Param.ChannelsTxPower,
dudmuck 0:8f0d0ae0a077 2489 LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) )
dudmuck 0:8f0d0ae0a077 2490 {
dudmuck 0:8f0d0ae0a077 2491 LoRaMacParams.ChannelsTxPower = mibSet->Param.ChannelsTxPower;
dudmuck 0:8f0d0ae0a077 2492 }
dudmuck 0:8f0d0ae0a077 2493 else
dudmuck 0:8f0d0ae0a077 2494 {
dudmuck 0:8f0d0ae0a077 2495 status = LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2496 }
dudmuck 0:8f0d0ae0a077 2497 break;
dudmuck 0:8f0d0ae0a077 2498 }
dudmuck 0:8f0d0ae0a077 2499 case MIB_UPLINK_COUNTER:
dudmuck 0:8f0d0ae0a077 2500 {
dudmuck 0:8f0d0ae0a077 2501 UpLinkCounter = mibSet->Param.UpLinkCounter;
dudmuck 0:8f0d0ae0a077 2502 break;
dudmuck 0:8f0d0ae0a077 2503 }
dudmuck 0:8f0d0ae0a077 2504 case MIB_DOWNLINK_COUNTER:
dudmuck 0:8f0d0ae0a077 2505 {
dudmuck 0:8f0d0ae0a077 2506 DownLinkCounter = mibSet->Param.DownLinkCounter;
dudmuck 0:8f0d0ae0a077 2507 break;
dudmuck 0:8f0d0ae0a077 2508 }
dudmuck 0:8f0d0ae0a077 2509 case MIB_SYSTEM_MAX_RX_ERROR:
dudmuck 0:8f0d0ae0a077 2510 {
dudmuck 0:8f0d0ae0a077 2511 LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError;
dudmuck 0:8f0d0ae0a077 2512 break;
dudmuck 0:8f0d0ae0a077 2513 }
dudmuck 0:8f0d0ae0a077 2514 case MIB_MIN_RX_SYMBOLS:
dudmuck 0:8f0d0ae0a077 2515 {
dudmuck 0:8f0d0ae0a077 2516 LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols;
dudmuck 0:8f0d0ae0a077 2517 break;
dudmuck 0:8f0d0ae0a077 2518 }
dudmuck 0:8f0d0ae0a077 2519 default:
dudmuck 0:8f0d0ae0a077 2520 status = LORAMAC_STATUS_SERVICE_UNKNOWN;
dudmuck 0:8f0d0ae0a077 2521 break;
dudmuck 0:8f0d0ae0a077 2522 }
dudmuck 0:8f0d0ae0a077 2523
dudmuck 0:8f0d0ae0a077 2524 return status;
dudmuck 0:8f0d0ae0a077 2525 }
dudmuck 0:8f0d0ae0a077 2526
dudmuck 0:8f0d0ae0a077 2527 LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam )
dudmuck 0:8f0d0ae0a077 2528 {
dudmuck 0:8f0d0ae0a077 2529 if( channelParam == NULL )
dudmuck 0:8f0d0ae0a077 2530 {
dudmuck 0:8f0d0ae0a077 2531 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2532 }
dudmuck 0:8f0d0ae0a077 2533
dudmuck 0:8f0d0ae0a077 2534 // Reset downlink counter
dudmuck 0:8f0d0ae0a077 2535 channelParam->DownLinkCounter = 0;
dudmuck 0:8f0d0ae0a077 2536
dudmuck 0:8f0d0ae0a077 2537 if( MulticastChannels == NULL )
dudmuck 0:8f0d0ae0a077 2538 {
dudmuck 0:8f0d0ae0a077 2539 // New node is the fist element
dudmuck 0:8f0d0ae0a077 2540 MulticastChannels = channelParam;
dudmuck 0:8f0d0ae0a077 2541 }
dudmuck 0:8f0d0ae0a077 2542 else
dudmuck 0:8f0d0ae0a077 2543 {
dudmuck 0:8f0d0ae0a077 2544 MulticastParams_t *cur = MulticastChannels;
dudmuck 0:8f0d0ae0a077 2545
dudmuck 0:8f0d0ae0a077 2546 // Search the last node in the list
dudmuck 0:8f0d0ae0a077 2547 while( cur->Next != NULL )
dudmuck 0:8f0d0ae0a077 2548 {
dudmuck 0:8f0d0ae0a077 2549 cur = cur->Next;
dudmuck 0:8f0d0ae0a077 2550 }
dudmuck 0:8f0d0ae0a077 2551 // This function always finds the last node
dudmuck 0:8f0d0ae0a077 2552 cur->Next = channelParam;
dudmuck 0:8f0d0ae0a077 2553 }
dudmuck 0:8f0d0ae0a077 2554
dudmuck 0:8f0d0ae0a077 2555 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2556 }
dudmuck 0:8f0d0ae0a077 2557
dudmuck 0:8f0d0ae0a077 2558 LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam )
dudmuck 0:8f0d0ae0a077 2559 {
dudmuck 0:8f0d0ae0a077 2560 if( channelParam == NULL )
dudmuck 0:8f0d0ae0a077 2561 {
dudmuck 0:8f0d0ae0a077 2562 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2563 }
dudmuck 0:8f0d0ae0a077 2564
dudmuck 0:8f0d0ae0a077 2565 if( MulticastChannels != NULL )
dudmuck 0:8f0d0ae0a077 2566 {
dudmuck 0:8f0d0ae0a077 2567 if( MulticastChannels == channelParam )
dudmuck 0:8f0d0ae0a077 2568 {
dudmuck 0:8f0d0ae0a077 2569 // First element
dudmuck 0:8f0d0ae0a077 2570 MulticastChannels = channelParam->Next;
dudmuck 0:8f0d0ae0a077 2571 }
dudmuck 0:8f0d0ae0a077 2572 else
dudmuck 0:8f0d0ae0a077 2573 {
dudmuck 0:8f0d0ae0a077 2574 MulticastParams_t *cur = MulticastChannels;
dudmuck 0:8f0d0ae0a077 2575
dudmuck 0:8f0d0ae0a077 2576 // Search the node in the list
dudmuck 0:8f0d0ae0a077 2577 while( cur->Next && cur->Next != channelParam )
dudmuck 0:8f0d0ae0a077 2578 {
dudmuck 0:8f0d0ae0a077 2579 cur = cur->Next;
dudmuck 0:8f0d0ae0a077 2580 }
dudmuck 0:8f0d0ae0a077 2581 // If we found the node, remove it
dudmuck 0:8f0d0ae0a077 2582 if( cur->Next )
dudmuck 0:8f0d0ae0a077 2583 {
dudmuck 0:8f0d0ae0a077 2584 cur->Next = channelParam->Next;
dudmuck 0:8f0d0ae0a077 2585 }
dudmuck 0:8f0d0ae0a077 2586 }
dudmuck 0:8f0d0ae0a077 2587 channelParam->Next = NULL;
dudmuck 0:8f0d0ae0a077 2588 }
dudmuck 0:8f0d0ae0a077 2589
dudmuck 0:8f0d0ae0a077 2590 return LORAMAC_STATUS_OK;
dudmuck 0:8f0d0ae0a077 2591 }
dudmuck 0:8f0d0ae0a077 2592
dudmuck 0:8f0d0ae0a077 2593 LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest )
dudmuck 0:8f0d0ae0a077 2594 {
dudmuck 0:8f0d0ae0a077 2595 LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
dudmuck 0:8f0d0ae0a077 2596 LoRaMacHeader_t macHdr;
dudmuck 0:8f0d0ae0a077 2597
dudmuck 0:8f0d0ae0a077 2598 if( mlmeRequest == NULL )
dudmuck 0:8f0d0ae0a077 2599 {
dudmuck 0:8f0d0ae0a077 2600 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2601 }
dudmuck 0:8f0d0ae0a077 2602
dudmuck 0:8f0d0ae0a077 2603 memset1( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) );
dudmuck 0:8f0d0ae0a077 2604
dudmuck 2:f2d9aa163652 2605 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_MLMEREQ;
dudmuck 0:8f0d0ae0a077 2606
dudmuck 0:8f0d0ae0a077 2607 switch( mlmeRequest->Type )
dudmuck 0:8f0d0ae0a077 2608 {
dudmuck 0:8f0d0ae0a077 2609 case MLME_JOIN:
dudmuck 0:8f0d0ae0a077 2610 {
dudmuck 0:8f0d0ae0a077 2611 if( ( mlmeRequest->Req.Join.DevEui == NULL ) ||
dudmuck 0:8f0d0ae0a077 2612 ( mlmeRequest->Req.Join.AppEui == NULL ) ||
dudmuck 0:8f0d0ae0a077 2613 ( mlmeRequest->Req.Join.AppKey == NULL ) ||
dudmuck 0:8f0d0ae0a077 2614 ( mlmeRequest->Req.Join.NbTrials == 0 ) )
dudmuck 0:8f0d0ae0a077 2615 {
dudmuck 0:8f0d0ae0a077 2616 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2617 }
dudmuck 0:8f0d0ae0a077 2618
dudmuck 0:8f0d0ae0a077 2619 // Enables at least the usage of all datarates.
dudmuck 0:8f0d0ae0a077 2620 if( mlmeRequest->Req.Join.NbTrials < 48 )
dudmuck 0:8f0d0ae0a077 2621 {
dudmuck 0:8f0d0ae0a077 2622 mlmeRequest->Req.Join.NbTrials = 48;
dudmuck 0:8f0d0ae0a077 2623 }
dudmuck 0:8f0d0ae0a077 2624
dudmuck 0:8f0d0ae0a077 2625 LoRaMacFlags.Bits.MlmeReq = 1;
dudmuck 0:8f0d0ae0a077 2626 MlmeConfirm.MlmeRequest = mlmeRequest->Type;
dudmuck 0:8f0d0ae0a077 2627
dudmuck 0:8f0d0ae0a077 2628 LoRaMacDevEui = mlmeRequest->Req.Join.DevEui;
dudmuck 0:8f0d0ae0a077 2629 LoRaMacAppEui = mlmeRequest->Req.Join.AppEui;
dudmuck 0:8f0d0ae0a077 2630 LoRaMacAppKey = mlmeRequest->Req.Join.AppKey;
dudmuck 0:8f0d0ae0a077 2631 MaxJoinRequestTrials = mlmeRequest->Req.Join.NbTrials;
dudmuck 0:8f0d0ae0a077 2632
dudmuck 0:8f0d0ae0a077 2633 // Reset variable JoinRequestTrials
dudmuck 0:8f0d0ae0a077 2634 JoinRequestTrials = 0;
dudmuck 0:8f0d0ae0a077 2635
dudmuck 0:8f0d0ae0a077 2636 // Setup header information
dudmuck 0:8f0d0ae0a077 2637 macHdr.Value = 0;
dudmuck 0:8f0d0ae0a077 2638 macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
dudmuck 0:8f0d0ae0a077 2639
dudmuck 0:8f0d0ae0a077 2640 ResetMacParameters( );
dudmuck 16:915815632c1f 2641 expecting_beacon = false;
dudmuck 16:915815632c1f 2642 BeaconCtx.state = BEACON_STATE_NONE;
dudmuck 0:8f0d0ae0a077 2643
dudmuck 0:8f0d0ae0a077 2644 Channel = 0; // start with first channel
dudmuck 1:53c30224eda8 2645 isr_printf("<ch0>");
dudmuck 1:53c30224eda8 2646 isr_printf("mlme-join-send ch%u\r\n", Channel);
dudmuck 0:8f0d0ae0a077 2647 status = Send( &macHdr, 0, NULL, 0 );
dudmuck 0:8f0d0ae0a077 2648 break;
dudmuck 0:8f0d0ae0a077 2649 }
dudmuck 0:8f0d0ae0a077 2650 case MLME_LINK_CHECK:
dudmuck 0:8f0d0ae0a077 2651 {
dudmuck 0:8f0d0ae0a077 2652 LoRaMacFlags.Bits.MlmeReq = 1;
dudmuck 0:8f0d0ae0a077 2653 // LoRaMac will send this command piggy-pack
dudmuck 0:8f0d0ae0a077 2654 MlmeConfirm.MlmeRequest = mlmeRequest->Type;
dudmuck 0:8f0d0ae0a077 2655
dudmuck 0:8f0d0ae0a077 2656 status = AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 );
dudmuck 0:8f0d0ae0a077 2657 break;
dudmuck 0:8f0d0ae0a077 2658 }
dudmuck 0:8f0d0ae0a077 2659 case MLME_TXCW:
dudmuck 0:8f0d0ae0a077 2660 {
dudmuck 0:8f0d0ae0a077 2661 MlmeConfirm.MlmeRequest = mlmeRequest->Type;
dudmuck 0:8f0d0ae0a077 2662 LoRaMacFlags.Bits.MlmeReq = 1;
dudmuck 0:8f0d0ae0a077 2663 status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout );
dudmuck 0:8f0d0ae0a077 2664 break;
dudmuck 0:8f0d0ae0a077 2665 }
dudmuck 0:8f0d0ae0a077 2666 case MLME_TXCW_1:
dudmuck 0:8f0d0ae0a077 2667 {
dudmuck 0:8f0d0ae0a077 2668 MlmeConfirm.MlmeRequest = mlmeRequest->Type;
dudmuck 0:8f0d0ae0a077 2669 LoRaMacFlags.Bits.MlmeReq = 1;
dudmuck 0:8f0d0ae0a077 2670 status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
dudmuck 0:8f0d0ae0a077 2671 break;
dudmuck 0:8f0d0ae0a077 2672 }
dudmuck 0:8f0d0ae0a077 2673 default:
dudmuck 0:8f0d0ae0a077 2674 break;
dudmuck 0:8f0d0ae0a077 2675 }
dudmuck 0:8f0d0ae0a077 2676
dudmuck 0:8f0d0ae0a077 2677 if( status != LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 2678 {
dudmuck 0:8f0d0ae0a077 2679 NodeAckRequested = false;
dudmuck 0:8f0d0ae0a077 2680 LoRaMacFlags.Bits.MlmeReq = 0;
dudmuck 0:8f0d0ae0a077 2681 }
dudmuck 0:8f0d0ae0a077 2682
dudmuck 0:8f0d0ae0a077 2683 return status;
dudmuck 0:8f0d0ae0a077 2684 }
dudmuck 0:8f0d0ae0a077 2685
dudmuck 0:8f0d0ae0a077 2686 LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest )
dudmuck 0:8f0d0ae0a077 2687 {
dudmuck 0:8f0d0ae0a077 2688 LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
dudmuck 0:8f0d0ae0a077 2689 LoRaMacHeader_t macHdr;
dudmuck 0:8f0d0ae0a077 2690 uint8_t fPort = 0;
dudmuck 0:8f0d0ae0a077 2691 void *fBuffer;
dudmuck 0:8f0d0ae0a077 2692 uint16_t fBufferSize;
dudmuck 0:8f0d0ae0a077 2693 bool readyToSend = false;
dudmuck 0:8f0d0ae0a077 2694
dudmuck 0:8f0d0ae0a077 2695 if( mcpsRequest == NULL )
dudmuck 0:8f0d0ae0a077 2696 {
dudmuck 0:8f0d0ae0a077 2697 return LORAMAC_STATUS_PARAMETER_INVALID;
dudmuck 0:8f0d0ae0a077 2698 }
dudmuck 0:8f0d0ae0a077 2699
dudmuck 0:8f0d0ae0a077 2700 macHdr.Value = 0;
dudmuck 0:8f0d0ae0a077 2701 memset1 ( ( uint8_t* ) &McpsConfirm, 0, sizeof( McpsConfirm ) );
dudmuck 2:f2d9aa163652 2702 McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_MCPSREQ;
dudmuck 0:8f0d0ae0a077 2703
dudmuck 0:8f0d0ae0a077 2704 switch( mcpsRequest->Type )
dudmuck 0:8f0d0ae0a077 2705 {
dudmuck 0:8f0d0ae0a077 2706 case MCPS_UNCONFIRMED:
dudmuck 0:8f0d0ae0a077 2707 {
dudmuck 0:8f0d0ae0a077 2708 readyToSend = true;
dudmuck 0:8f0d0ae0a077 2709
dudmuck 0:8f0d0ae0a077 2710 macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP;
dudmuck 0:8f0d0ae0a077 2711 fPort = mcpsRequest->Req.Unconfirmed.fPort;
dudmuck 0:8f0d0ae0a077 2712 fBuffer = mcpsRequest->Req.Unconfirmed.fBuffer;
dudmuck 0:8f0d0ae0a077 2713 fBufferSize = mcpsRequest->Req.Unconfirmed.fBufferSize;
dudmuck 0:8f0d0ae0a077 2714 break;
dudmuck 0:8f0d0ae0a077 2715 }
dudmuck 0:8f0d0ae0a077 2716 case MCPS_CONFIRMED:
dudmuck 0:8f0d0ae0a077 2717 {
dudmuck 0:8f0d0ae0a077 2718 readyToSend = true;
dudmuck 0:8f0d0ae0a077 2719
dudmuck 0:8f0d0ae0a077 2720 macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
dudmuck 0:8f0d0ae0a077 2721 fPort = mcpsRequest->Req.Confirmed.fPort;
dudmuck 0:8f0d0ae0a077 2722 fBuffer = mcpsRequest->Req.Confirmed.fBuffer;
dudmuck 0:8f0d0ae0a077 2723 fBufferSize = mcpsRequest->Req.Confirmed.fBufferSize;
dudmuck 0:8f0d0ae0a077 2724 break;
dudmuck 0:8f0d0ae0a077 2725 }
dudmuck 0:8f0d0ae0a077 2726 case MCPS_PROPRIETARY:
dudmuck 0:8f0d0ae0a077 2727 {
dudmuck 0:8f0d0ae0a077 2728 readyToSend = true;
dudmuck 0:8f0d0ae0a077 2729
dudmuck 0:8f0d0ae0a077 2730 macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY;
dudmuck 0:8f0d0ae0a077 2731 fBuffer = mcpsRequest->Req.Proprietary.fBuffer;
dudmuck 0:8f0d0ae0a077 2732 fBufferSize = mcpsRequest->Req.Proprietary.fBufferSize;
dudmuck 0:8f0d0ae0a077 2733 break;
dudmuck 0:8f0d0ae0a077 2734 }
dudmuck 0:8f0d0ae0a077 2735 default:
dudmuck 0:8f0d0ae0a077 2736 break;
dudmuck 0:8f0d0ae0a077 2737 }
dudmuck 0:8f0d0ae0a077 2738
dudmuck 0:8f0d0ae0a077 2739 if( readyToSend == true )
dudmuck 0:8f0d0ae0a077 2740 {
dudmuck 0:8f0d0ae0a077 2741 status = Send( &macHdr, fPort, fBuffer, fBufferSize );
dudmuck 0:8f0d0ae0a077 2742 if( status == LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 2743 {
dudmuck 0:8f0d0ae0a077 2744 McpsConfirm.McpsRequest = mcpsRequest->Type;
dudmuck 0:8f0d0ae0a077 2745 LoRaMacFlags.Bits.McpsReq = 1;
dudmuck 0:8f0d0ae0a077 2746 }
dudmuck 0:8f0d0ae0a077 2747 else
dudmuck 0:8f0d0ae0a077 2748 {
dudmuck 0:8f0d0ae0a077 2749 NodeAckRequested = false;
dudmuck 0:8f0d0ae0a077 2750 }
dudmuck 0:8f0d0ae0a077 2751 }
dudmuck 0:8f0d0ae0a077 2752
dudmuck 0:8f0d0ae0a077 2753 return status;
dudmuck 0:8f0d0ae0a077 2754 }
dudmuck 0:8f0d0ae0a077 2755
dudmuck 0:8f0d0ae0a077 2756 void LoRaMacTestRxWindowsOn( bool enable )
dudmuck 0:8f0d0ae0a077 2757 {
dudmuck 0:8f0d0ae0a077 2758 IsRxWindowsEnabled = enable;
dudmuck 0:8f0d0ae0a077 2759 }
dudmuck 0:8f0d0ae0a077 2760
dudmuck 0:8f0d0ae0a077 2761 void LoRaMacTestSetMic( uint16_t txPacketCounter )
dudmuck 0:8f0d0ae0a077 2762 {
dudmuck 0:8f0d0ae0a077 2763 UpLinkCounter = txPacketCounter;
dudmuck 16:915815632c1f 2764 //IsUpLinkCounterFixed = true;
dudmuck 0:8f0d0ae0a077 2765 }
dudmuck 0:8f0d0ae0a077 2766
dudmuck 0:8f0d0ae0a077 2767 void LoRaMacTestSetChannel( uint8_t channel )
dudmuck 0:8f0d0ae0a077 2768 {
dudmuck 1:53c30224eda8 2769 isr_printf("set-testch%u\r\n", channel);
dudmuck 0:8f0d0ae0a077 2770 Channel = channel;
dudmuck 0:8f0d0ae0a077 2771 }
dudmuck 0:8f0d0ae0a077 2772
dudmuck 0:8f0d0ae0a077 2773
dudmuck 0:8f0d0ae0a077 2774 static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError )
dudmuck 0:8f0d0ae0a077 2775 {
dudmuck 0:8f0d0ae0a077 2776 RxConfigParams_t rxConfigParams = { 0, 0, 0, 0 };
dudmuck 0:8f0d0ae0a077 2777 double tSymbol = 0.0;
dudmuck 0:8f0d0ae0a077 2778
dudmuck 0:8f0d0ae0a077 2779 rxConfigParams.Datarate = datarate;
dudmuck 0:8f0d0ae0a077 2780 switch( Bandwidths[datarate] )
dudmuck 0:8f0d0ae0a077 2781 {
dudmuck 0:8f0d0ae0a077 2782 default:
dudmuck 0:8f0d0ae0a077 2783 case 125000:
dudmuck 0:8f0d0ae0a077 2784 rxConfigParams.Bandwidth = 0;
dudmuck 0:8f0d0ae0a077 2785 break;
dudmuck 0:8f0d0ae0a077 2786 case 250000:
dudmuck 0:8f0d0ae0a077 2787 rxConfigParams.Bandwidth = 1;
dudmuck 0:8f0d0ae0a077 2788 break;
dudmuck 0:8f0d0ae0a077 2789 case 500000:
dudmuck 0:8f0d0ae0a077 2790 rxConfigParams.Bandwidth = 2;
dudmuck 0:8f0d0ae0a077 2791 break;
dudmuck 0:8f0d0ae0a077 2792 }
dudmuck 0:8f0d0ae0a077 2793
dudmuck 0:8f0d0ae0a077 2794 #if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
dudmuck 0:8f0d0ae0a077 2795 if( datarate == DR_7 )
dudmuck 0:8f0d0ae0a077 2796 { // FSK
dudmuck 0:8f0d0ae0a077 2797 tSymbol = ( 1.0 / ( double )Datarates[datarate] ) * 8.0; // 1 symbol equals 1 byte
dudmuck 0:8f0d0ae0a077 2798 }
dudmuck 0:8f0d0ae0a077 2799 else
dudmuck 0:8f0d0ae0a077 2800 #endif
dudmuck 0:8f0d0ae0a077 2801 { // LoRa
dudmuck 0:8f0d0ae0a077 2802 tSymbol = ( ( double )( 1 << Datarates[datarate] ) / ( double )Bandwidths[datarate] ) * 1e3;
dudmuck 0:8f0d0ae0a077 2803 }
dudmuck 0:8f0d0ae0a077 2804
dudmuck 0:8f0d0ae0a077 2805 rxConfigParams.RxWindowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * LoRaMacParams.MinRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), LoRaMacParams.MinRxSymbols ); // Computed number of symbols
dudmuck 0:8f0d0ae0a077 2806
dudmuck 0:8f0d0ae0a077 2807 rxConfigParams.RxOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( rxConfigParams.RxWindowTimeout * tSymbol ) / 2.0 ) - RADIO_WAKEUP_TIME );
dudmuck 0:8f0d0ae0a077 2808
dudmuck 0:8f0d0ae0a077 2809 return rxConfigParams;
dudmuck 0:8f0d0ae0a077 2810 }
dudmuck 0:8f0d0ae0a077 2811
dudmuck 20:42839629a5dc 2812 void LoRaMacBottomHalf()
dudmuck 20:42839629a5dc 2813 {
dudmuck 20:42839629a5dc 2814 if (flags.rx_win) {
dudmuck 20:42839629a5dc 2815 OnRxWindowTimerEventBH();
dudmuck 20:42839629a5dc 2816 flags.rx_win = 0;
dudmuck 20:42839629a5dc 2817 }
dudmuck 20:42839629a5dc 2818 if (flags.join_send) {
dudmuck 20:42839629a5dc 2819 join_send_bh();
dudmuck 20:42839629a5dc 2820 flags.join_send = 0;
dudmuck 20:42839629a5dc 2821 }
dudmuck 20:42839629a5dc 2822 if (flags.beacon_setup) {
dudmuck 20:42839629a5dc 2823 OnRxBeaconSetupBH();
dudmuck 20:42839629a5dc 2824 flags.beacon_setup = 0;
dudmuck 20:42839629a5dc 2825 }
dudmuck 20:42839629a5dc 2826 if (flags.send) {
dudmuck 20:42839629a5dc 2827 send_bh();
dudmuck 20:42839629a5dc 2828 flags.send = 0;
dudmuck 20:42839629a5dc 2829 }
dudmuck 20:42839629a5dc 2830 Radio.BottomHalf();
dudmuck 20:42839629a5dc 2831 }