Synchronous wireless star LoRa network, central device.

Dependencies:   SX127x sx12xx_hal

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.


Alternate to this project gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Synchronous Star Network

This project acts as central node for LoRaWAN-like network operating on single radio channel. Intended for use where network infrastructure would never exist due to cost and/or complexity of standard network. This project uses the class-B method of beacon generation to synchronize the end nodes with the gateway. OTA mode must be used. End-node will be allocated an uplink time slot upon joining. End node may transmit uplink at this assigned timeslot, if it desires to do so. This time slot is always referenced to the beacon sent by gateway.

LoRaWAN server is not necessary. All network control is implemented by this project. User can observe network activity on the mbed serial port. Downlinks can be scheduled using command on serial port.

This implementation must not be used on radio channels requiring duty-cycle transmit limiting.

alterations from LoRaWAN specification

This mode of operation uses a single datarate on single radio channel. ADR is not implemented, because datarate cannot be changed. OTA mode must be used. When join-accept is sent by gateway, it will have appended (instead of CFlist) the beacon timing answer to inform of when next beacon occurs, and two timing values: the time slot allocated to this end-node and the periodicity of the network. Periodicity means how often the end-node may again transmit. /media/uploads/dudmuck/class-b-single.png Beacon is sent for purpose of providing timing reference to end-nodes. The beacon payload may contain a broadcast command to end nodes. Time value is not sent in beacon payload. The time at which beacon is sent provides timing reference: every 128 seconds as standard.

Rx2 receive window is not implemented. Only Rx1 is used because a single radio channel is used. Rx1 delay is reduced to 100 milliseconds. Original LoRaWAN has 1000 millisecond Rx1 delay to accommodate internet latency.

LoRaWAN standard class-B beacon requires GPS timing reference. This implementation does not use GPS, instead a hardware timer peripheral generates interrupts to send beacons. Absolute accuracy is not required, only relative crystal drift between gateway and end-nodes is considered.

Timing values are always specified as 30ms per step as in LoRaWAN standard. Each beacon period has 4096 30ms steps per beacon period.

join OTA procedure

The join procedure has changed the join-accept delay to 100 milliseconds (standard is 5 seconds). This allows end-node to hunt for gateway on several channels during joining. When gateway starts, it will scan available channels for the optimal choice based on ambient noise on the channels. End node will retry join request until it finds the gateway. Gateway might change channel during operation if it deems current channel too busy.

configuration of network

End nodes must be provisioned by editing file Comissioning.h. The array motes lists every end node permitted on network. It contains appEui, devEUI and appKey just as specified in standard LoRaWAN. All provisioning is hard-coded; changing provisioning requires reprogramming gateway. When changing number of motes, N_MOTES definition must be changed in lorawan.h.

lorawan.h

#define N_MOTES     8
extern ota_mote_t motes[N_MOTES];   /* from Comissioning.h */

configuring number of end-nodes vs transmit rate

Trade-off can be selected between number of end-nodes on network vs. how often each end-node can transmit.
In this example, where DR_13 is SF7 at 500KHz:

lorawan.cpp

    #elif (LORAMAC_DEFAULT_DATARATE == DR_13)
        #define TX_SLOT_STEPPING        8  //approx 0.25 seconds
        #define PERIODICITY_SLOTS       (TX_SLOT_STEPPING * 6)
    #endif

Here, each end-node is given time of 240ms = 8 * 30ms; accommodating maximum payload length for both uplink and downlink.
6 in this code is the maximum count of end nodes using this gateway. Each end-node can transmit every 1.44 seconds, in this example.
If you wanted to change 6 to 20 end-nodes, each would be able to use network every 4.8 seconds.
Another example: If you wanted to use DR_12 = SF8, you would have approx 2.5 to 3dB more gain compared to SF7, but each end-node must be given double time, resulting in 20 nodes able to use network every 9.6 seconds at DR_12.

network capacity limitation

The number of end-nodes which can be supported is determined by number of SLOT_STEPPING's which can occur during BEACON_PERIOD. Beacon guard is implemented same as standard LoRaWAN, which is 3 seconds prior to beacon start and 2.12 seconds after beacon start, which gives 122.88 seconds for network traffic for each beacon period.

gateway configuration

spreading factor is declared at #define LORAMAC_DEFAULT_DATARATE in lorawan.h, valid rates are DR_8 to DR_13 (sf12 to sf7). In the end-node, the same definition must be configured in LoRaMac-definitions.h. This network operates at this constant datarate for all packets.
Band plan can be selected by defining USE_BAND_* in lorawan.h. 434MHz can be used on SX1276 shield. TypeABZ module and sx1272 only support 800/900MHz channels band.

end-node provisioning

Security permits only matching DevEUI / AppEui to join the network, due to unique AES root key for each end device; in this case the DevEUI must be programmed in advance into gateway. However, if the same AES root key could be used for each end device , then any number of end devices could be added at a later time, without checking DevEUI on the gateway when an end device joins the network. On the gateway, the end device join acceptance is performed in file lorawan.cpp LoRaWan::parse_receive() where MType == MTYPE_JOIN_REQ. A memcmp() is performed on both DevEUI and AppEUI.

If you wish to allow any DevEUI to join, define ANY_DEVEUI at top of lorawan.cpp . In this configuration, all end devices must use same AppEUI and AES key. N_MOTES must be defined to the maximum number of end devices expected. Test end device by commenting BoardGetUniqueId() in end node, and replace DevEui[] with 8 arbitrary bytes to verify gateway permits any DevEUI to join.

RAM usage

For gateway CPU, recommend to consider larger RAM size depending on number of end devices required. ota_motes_t has size of 123 bytes. Each end device has one of these, however if less RAM usage is required for more end-devices, the MAC command queue size may be reduced.

hardware support

The radio driver supports both SX1272 and SX1276, sx126x kit, sx126x radio-only shield, and sx128x 2.4GHz.. The selection of radio chip type is made by your choice of importing radio driver library.



Beacon generation requires low power ticker to be clocked from crystal, not internal RC oscillator.

Gateway Serial Interface

Gateway serial port operates at 115200bps.

commandargumentdescription
list-list joined end nodes
?-list available commands
dl<mote-hex-dev-addr> <hex-payload>send downlink, sent after uplink received
gpo<mote-hex-dev-addr> <0 or 1>set output PC6 pin level on end device
b32bit hex valueset beacon payload to be sent at next beacon
. (period)-print current status
opdBmconfigure TX power of gateway
sbcountskip sending beacons, for testing end node
fhex devAddrprinter filtering, show only single end node
hm-print filtering, hide MAC layer printing
hp-print filtering, hide APP layer printing
sa-print filtering, show all, undo hm and hp

Any received uplink will be printed with DevAddr and decrypted payload.

Committer:
dudmuck
Date:
Thu May 18 16:46:30 2017 -0700
Revision:
0:2ff18de8d48b
Child:
8:307f7faeb594
initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dudmuck 0:2ff18de8d48b 1 #define MBEDTLS_CMAC_C
dudmuck 0:2ff18de8d48b 2 #include <stdint.h>
dudmuck 0:2ff18de8d48b 3 #include "lorawan.h"
dudmuck 0:2ff18de8d48b 4 #include "Commissioning.h"
dudmuck 0:2ff18de8d48b 5
dudmuck 0:2ff18de8d48b 6 #include "gladman_aes.h"
dudmuck 0:2ff18de8d48b 7 #include "gladman_cmac.h"
dudmuck 0:2ff18de8d48b 8
dudmuck 0:2ff18de8d48b 9 #define RECEIVE_DELAY_ms 100
dudmuck 0:2ff18de8d48b 10
dudmuck 0:2ff18de8d48b 11 #define LORA_FRAMEMICBYTES 4
dudmuck 0:2ff18de8d48b 12 #define LORA_ENCRYPTIONBLOCKBYTES 16
dudmuck 0:2ff18de8d48b 13 #define LORA_AUTHENTICATIONBLOCKBYTES 16
dudmuck 0:2ff18de8d48b 14 #define LORA_MAXFRAMELENGTH 235
dudmuck 0:2ff18de8d48b 15 #define LORA_MACHEADERLENGTH 1
dudmuck 0:2ff18de8d48b 16 #define LORA_MINDATAHEADERLENGTH 7
dudmuck 0:2ff18de8d48b 17 #define LORA_PORTLENGTH 1
dudmuck 0:2ff18de8d48b 18 #define LORA_MAXDATABYTES (LORA_MAXFRAMELENGTH - (LORA_MACHEADERLENGTH + LORA_MINDATAHEADERLENGTH + LORA_PORTLENGTH + LORA_FRAMEMICBYTES)) //excluding port
dudmuck 0:2ff18de8d48b 19 #define LORA_NETWORKADDRESSBITS 25
dudmuck 0:2ff18de8d48b 20
dudmuck 0:2ff18de8d48b 21
dudmuck 0:2ff18de8d48b 22 #define DEFAULT_DOWNLINK_PORT 2
dudmuck 0:2ff18de8d48b 23
dudmuck 0:2ff18de8d48b 24 const uint32_t network_id = 0x24;
dudmuck 0:2ff18de8d48b 25 uint32_t networkAddress = 0; // bits 24..0 of DevAddr, for join accept
dudmuck 0:2ff18de8d48b 26 uint16_t next_available_tx_slot = 0;
dudmuck 0:2ff18de8d48b 27
dudmuck 0:2ff18de8d48b 28 uint8_t LoRaWan::user_downlink[128];
dudmuck 0:2ff18de8d48b 29 volatile uint16_t LoRaWan::rx_slot;
dudmuck 0:2ff18de8d48b 30 volatile uint32_t LoRaWan::rx_ms; // captured timer milliseconds at RxDone
dudmuck 0:2ff18de8d48b 31 bool LoRaWan::do_downlink;
dudmuck 0:2ff18de8d48b 32
dudmuck 0:2ff18de8d48b 33 /*
dudmuck 0:2ff18de8d48b 34 * TX_SLOT_STEPPING: time for each mote
dudmuck 0:2ff18de8d48b 35 * PERIODICITY_SLOTS: slot at which first mote can again transmit
dudmuck 0:2ff18de8d48b 36 */
dudmuck 0:2ff18de8d48b 37 #if (LORAMAC_DEFAULT_DATARATE == DR_8)
dudmuck 0:2ff18de8d48b 38 #define TX_SLOT_STEPPING 267 //approx 8 seconds
dudmuck 0:2ff18de8d48b 39 #define PERIODICITY_SLOTS (TX_SLOT_STEPPING * 15)
dudmuck 0:2ff18de8d48b 40 #elif (LORAMAC_DEFAULT_DATARATE == DR_9)
dudmuck 0:2ff18de8d48b 41 #define TX_SLOT_STEPPING 133 //approx 4 seconds
dudmuck 0:2ff18de8d48b 42 #define PERIODICITY_SLOTS (TX_SLOT_STEPPING * 24)
dudmuck 0:2ff18de8d48b 43 #elif (LORAMAC_DEFAULT_DATARATE == DR_10)
dudmuck 0:2ff18de8d48b 44 #define TX_SLOT_STEPPING 67 //approx 2 seconds
dudmuck 0:2ff18de8d48b 45 #define PERIODICITY_SLOTS (TX_SLOT_STEPPING * 24)
dudmuck 0:2ff18de8d48b 46 #elif (LORAMAC_DEFAULT_DATARATE == DR_11)
dudmuck 0:2ff18de8d48b 47 #define TX_SLOT_STEPPING 33 //approx 1.0 seconds
dudmuck 0:2ff18de8d48b 48 #define PERIODICITY_SLOTS (TX_SLOT_STEPPING * 24)
dudmuck 0:2ff18de8d48b 49 #elif (LORAMAC_DEFAULT_DATARATE == DR_12)
dudmuck 0:2ff18de8d48b 50 #define TX_SLOT_STEPPING 16 //approx 0.5 seconds
dudmuck 0:2ff18de8d48b 51 #define PERIODICITY_SLOTS (TX_SLOT_STEPPING * 24)
dudmuck 0:2ff18de8d48b 52 #elif (LORAMAC_DEFAULT_DATARATE == DR_13)
dudmuck 0:2ff18de8d48b 53 #define TX_SLOT_STEPPING 8 //approx 0.25 seconds
dudmuck 0:2ff18de8d48b 54 #define PERIODICITY_SLOTS (TX_SLOT_STEPPING * 24)
dudmuck 0:2ff18de8d48b 55 #endif
dudmuck 0:2ff18de8d48b 56
dudmuck 0:2ff18de8d48b 57 #if (PERIODICITY_SLOTS > 4095)
dudmuck 0:2ff18de8d48b 58 #error "PERIODICITY_SLOTS too large"
dudmuck 0:2ff18de8d48b 59 #endif
dudmuck 0:2ff18de8d48b 60
dudmuck 0:2ff18de8d48b 61
dudmuck 0:2ff18de8d48b 62 typedef enum {
dudmuck 0:2ff18de8d48b 63 MTYPE_JOIN_REQ = 0,
dudmuck 0:2ff18de8d48b 64 MTYPE_JOIN_ACC,//1
dudmuck 0:2ff18de8d48b 65 MTYPE_UNCONF_UP,//2
dudmuck 0:2ff18de8d48b 66 MTYPE_UNCONF_DN,//3
dudmuck 0:2ff18de8d48b 67 MTYPE_CONF_UP,//4
dudmuck 0:2ff18de8d48b 68 MTYPE_CONF_DN,//5
dudmuck 0:2ff18de8d48b 69 MTYPE_RFU,//6
dudmuck 0:2ff18de8d48b 70 MTYPE_P,//7
dudmuck 0:2ff18de8d48b 71 } mtype_e;
dudmuck 0:2ff18de8d48b 72
dudmuck 0:2ff18de8d48b 73 typedef union {
dudmuck 0:2ff18de8d48b 74 struct {
dudmuck 0:2ff18de8d48b 75 uint8_t major : 2; // 0 1
dudmuck 0:2ff18de8d48b 76 uint8_t rfu : 3; // 2 3 4
dudmuck 0:2ff18de8d48b 77 uint8_t MType : 3; // 5 6 7
dudmuck 0:2ff18de8d48b 78 } bits;
dudmuck 0:2ff18de8d48b 79 uint8_t octet;
dudmuck 0:2ff18de8d48b 80 } mhdr_t;
dudmuck 0:2ff18de8d48b 81
dudmuck 0:2ff18de8d48b 82 typedef union {
dudmuck 0:2ff18de8d48b 83 struct {
dudmuck 0:2ff18de8d48b 84 uint8_t FOptsLen : 4; // 0 1 2 3
dudmuck 0:2ff18de8d48b 85 uint8_t FPending : 1; // 4
dudmuck 0:2ff18de8d48b 86 uint8_t ACK : 1; // 5
dudmuck 0:2ff18de8d48b 87 uint8_t ADCACKReq : 1; // 6
dudmuck 0:2ff18de8d48b 88 uint8_t ADR : 1; // 7
dudmuck 0:2ff18de8d48b 89 } dlBits; // downlink (gwtx)
dudmuck 0:2ff18de8d48b 90 struct {
dudmuck 0:2ff18de8d48b 91 uint8_t FOptsLen : 4; // 0 1 2 3
dudmuck 0:2ff18de8d48b 92 uint8_t classB : 1; // 4 unused in classA
dudmuck 0:2ff18de8d48b 93 uint8_t ACK : 1; // 5
dudmuck 0:2ff18de8d48b 94 uint8_t ADCACKReq : 1; // 6
dudmuck 0:2ff18de8d48b 95 uint8_t ADR : 1; // 7
dudmuck 0:2ff18de8d48b 96 } ulBits; // uplink (gwrx)
dudmuck 0:2ff18de8d48b 97 uint8_t octet;
dudmuck 0:2ff18de8d48b 98 } FCtrl_t;
dudmuck 0:2ff18de8d48b 99
dudmuck 0:2ff18de8d48b 100 typedef struct {
dudmuck 0:2ff18de8d48b 101 uint32_t DevAddr;
dudmuck 0:2ff18de8d48b 102 FCtrl_t FCtrl;
dudmuck 0:2ff18de8d48b 103 uint16_t FCnt;
dudmuck 0:2ff18de8d48b 104 } __attribute__((packed)) fhdr_t;
dudmuck 0:2ff18de8d48b 105
dudmuck 0:2ff18de8d48b 106
dudmuck 0:2ff18de8d48b 107 typedef struct {
dudmuck 0:2ff18de8d48b 108 mhdr_t mhdr;
dudmuck 0:2ff18de8d48b 109 uint8_t AppEUI[LORA_EUI_LENGTH];
dudmuck 0:2ff18de8d48b 110 uint8_t DevEUI[LORA_EUI_LENGTH];
dudmuck 0:2ff18de8d48b 111 uint16_t DevNonce;
dudmuck 0:2ff18de8d48b 112 } __attribute__((packed)) join_req_t;
dudmuck 0:2ff18de8d48b 113
dudmuck 0:2ff18de8d48b 114 typedef enum eLoRaMacMoteCmd
dudmuck 0:2ff18de8d48b 115 {
dudmuck 0:2ff18de8d48b 116 /*!
dudmuck 0:2ff18de8d48b 117 * LinkCheckReq
dudmuck 0:2ff18de8d48b 118 */
dudmuck 0:2ff18de8d48b 119 MOTE_MAC_LINK_CHECK_REQ = 0x02,
dudmuck 0:2ff18de8d48b 120 /*!
dudmuck 0:2ff18de8d48b 121 * LinkADRAns
dudmuck 0:2ff18de8d48b 122 */
dudmuck 0:2ff18de8d48b 123 //MOTE_MAC_LINK_ADR_ANS = 0x03,
dudmuck 0:2ff18de8d48b 124 /*!
dudmuck 0:2ff18de8d48b 125 * DutyCycleAns
dudmuck 0:2ff18de8d48b 126 */
dudmuck 0:2ff18de8d48b 127 //MOTE_MAC_DUTY_CYCLE_ANS = 0x04,
dudmuck 0:2ff18de8d48b 128 /*!
dudmuck 0:2ff18de8d48b 129 * RXParamSetupAns
dudmuck 0:2ff18de8d48b 130 */
dudmuck 0:2ff18de8d48b 131 MOTE_MAC_RX_PARAM_SETUP_ANS = 0x05,
dudmuck 0:2ff18de8d48b 132 /*!
dudmuck 0:2ff18de8d48b 133 * DevStatusAns
dudmuck 0:2ff18de8d48b 134 */
dudmuck 0:2ff18de8d48b 135 MOTE_MAC_DEV_STATUS_ANS = 0x06,
dudmuck 0:2ff18de8d48b 136 /*!
dudmuck 0:2ff18de8d48b 137 * NewChannelAns
dudmuck 0:2ff18de8d48b 138 */
dudmuck 0:2ff18de8d48b 139 MOTE_MAC_NEW_CHANNEL_ANS = 0x07,
dudmuck 0:2ff18de8d48b 140 /*!
dudmuck 0:2ff18de8d48b 141 * RXTimingSetupAns
dudmuck 0:2ff18de8d48b 142 */
dudmuck 0:2ff18de8d48b 143 MOTE_MAC_RX_TIMING_SETUP_ANS = 0x08,
dudmuck 0:2ff18de8d48b 144 /*!
dudmuck 0:2ff18de8d48b 145 * PingSlotInfoReq
dudmuck 0:2ff18de8d48b 146 */
dudmuck 0:2ff18de8d48b 147 MOTE_MAC_PING_SLOT_INFO_REQ = 0x10,
dudmuck 0:2ff18de8d48b 148 /*!
dudmuck 0:2ff18de8d48b 149 * PingSlotFreqAns
dudmuck 0:2ff18de8d48b 150 */
dudmuck 0:2ff18de8d48b 151 MOTE_MAC_PING_SLOT_FREQ_ANS = 0x11,
dudmuck 0:2ff18de8d48b 152 /*!
dudmuck 0:2ff18de8d48b 153 * BeaconTimingReq
dudmuck 0:2ff18de8d48b 154 */
dudmuck 0:2ff18de8d48b 155 MOTE_MAC_BEACON_TIMING_REQ = 0x12,
dudmuck 0:2ff18de8d48b 156 /*!
dudmuck 0:2ff18de8d48b 157 * BeaconFreqAns
dudmuck 0:2ff18de8d48b 158 */
dudmuck 0:2ff18de8d48b 159 MOTE_MAC_BEACON_FREQ_ANS = 0x13,
dudmuck 0:2ff18de8d48b 160 }LoRaMacMoteCmd_t;
dudmuck 0:2ff18de8d48b 161
dudmuck 0:2ff18de8d48b 162 typedef enum eLoRaMacSrvCmd
dudmuck 0:2ff18de8d48b 163 {
dudmuck 0:2ff18de8d48b 164 /*!
dudmuck 0:2ff18de8d48b 165 * LinkCheckAns
dudmuck 0:2ff18de8d48b 166 */
dudmuck 0:2ff18de8d48b 167 SRV_MAC_LINK_CHECK_ANS = 0x02,
dudmuck 0:2ff18de8d48b 168 /*!
dudmuck 0:2ff18de8d48b 169 * LinkADRReq
dudmuck 0:2ff18de8d48b 170 */
dudmuck 0:2ff18de8d48b 171 //SRV_MAC_LINK_ADR_REQ = 0x03,
dudmuck 0:2ff18de8d48b 172 /*!
dudmuck 0:2ff18de8d48b 173 * DutyCycleReq
dudmuck 0:2ff18de8d48b 174 */
dudmuck 0:2ff18de8d48b 175 //SRV_MAC_DUTY_CYCLE_REQ = 0x04,
dudmuck 0:2ff18de8d48b 176 /*!
dudmuck 0:2ff18de8d48b 177 * RXParamSetupReq
dudmuck 0:2ff18de8d48b 178 */
dudmuck 0:2ff18de8d48b 179 SRV_MAC_RX_PARAM_SETUP_REQ = 0x05,
dudmuck 0:2ff18de8d48b 180 /*!
dudmuck 0:2ff18de8d48b 181 * DevStatusReq
dudmuck 0:2ff18de8d48b 182 */
dudmuck 0:2ff18de8d48b 183 SRV_MAC_DEV_STATUS_REQ = 0x06,
dudmuck 0:2ff18de8d48b 184 /*!
dudmuck 0:2ff18de8d48b 185 * NewChannelReq
dudmuck 0:2ff18de8d48b 186 */
dudmuck 0:2ff18de8d48b 187 SRV_MAC_NEW_CHANNEL_REQ = 0x07,
dudmuck 0:2ff18de8d48b 188 /*!
dudmuck 0:2ff18de8d48b 189 * RXTimingSetupReq
dudmuck 0:2ff18de8d48b 190 */
dudmuck 0:2ff18de8d48b 191 SRV_MAC_RX_TIMING_SETUP_REQ = 0x08,
dudmuck 0:2ff18de8d48b 192 /*!
dudmuck 0:2ff18de8d48b 193 * PingSlotInfoAns
dudmuck 0:2ff18de8d48b 194 */
dudmuck 0:2ff18de8d48b 195 SRV_MAC_PING_SLOT_INFO_ANS = 0x10,
dudmuck 0:2ff18de8d48b 196 /*!
dudmuck 0:2ff18de8d48b 197 * PingSlotChannelReq
dudmuck 0:2ff18de8d48b 198 */
dudmuck 0:2ff18de8d48b 199 SRV_MAC_PING_SLOT_CHANNEL_REQ = 0x11,
dudmuck 0:2ff18de8d48b 200 /*!
dudmuck 0:2ff18de8d48b 201 * BeaconTimingAns
dudmuck 0:2ff18de8d48b 202 */
dudmuck 0:2ff18de8d48b 203 SRV_MAC_BEACON_TIMING_ANS = 0x12,
dudmuck 0:2ff18de8d48b 204 /*!
dudmuck 0:2ff18de8d48b 205 * BeaconFreqReq
dudmuck 0:2ff18de8d48b 206 */
dudmuck 0:2ff18de8d48b 207 SRV_MAC_BEACON_FREQ_REQ = 0x13,
dudmuck 0:2ff18de8d48b 208 }LoRaMacSrvCmd_t;
dudmuck 0:2ff18de8d48b 209
dudmuck 0:2ff18de8d48b 210 /* us915-single-channel spreading factors: */
dudmuck 0:2ff18de8d48b 211 const uint8_t LoRaWan::Datarates[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
dudmuck 0:2ff18de8d48b 212
dudmuck 0:2ff18de8d48b 213 mtype_e user_dowlink_mtype = MTYPE_UNCONF_DN;
dudmuck 0:2ff18de8d48b 214
dudmuck 0:2ff18de8d48b 215 void print_octets(char const* label, uint8_t const* buf, uint8_t buf_len)
dudmuck 0:2ff18de8d48b 216 {
dudmuck 0:2ff18de8d48b 217 int i;
dudmuck 0:2ff18de8d48b 218 printf("%s:", label);
dudmuck 0:2ff18de8d48b 219 for (i = 0; i < buf_len; i++)
dudmuck 0:2ff18de8d48b 220 printf(" %02x", buf[i]);
dudmuck 0:2ff18de8d48b 221 // printf("\n");
dudmuck 0:2ff18de8d48b 222 }
dudmuck 0:2ff18de8d48b 223
dudmuck 0:2ff18de8d48b 224 void print_octets_rev(char const* label, uint8_t const* buf, uint8_t buf_len)
dudmuck 0:2ff18de8d48b 225 {
dudmuck 0:2ff18de8d48b 226 int i;
dudmuck 0:2ff18de8d48b 227 printf("%s:", label);
dudmuck 0:2ff18de8d48b 228 for (i = buf_len-1; i >= 0; i--)
dudmuck 0:2ff18de8d48b 229 printf(" %02x", buf[i]);
dudmuck 0:2ff18de8d48b 230 // printf("\n");
dudmuck 0:2ff18de8d48b 231 }
dudmuck 0:2ff18de8d48b 232
dudmuck 0:2ff18de8d48b 233 uint8_t* Write4ByteValue(uint8_t output[], uint32_t input)
dudmuck 0:2ff18de8d48b 234 {
dudmuck 0:2ff18de8d48b 235 uint8_t* ptr = output;
dudmuck 0:2ff18de8d48b 236
dudmuck 0:2ff18de8d48b 237 *(ptr++) = (uint8_t)input,
dudmuck 0:2ff18de8d48b 238 input >>= 8;
dudmuck 0:2ff18de8d48b 239 *(ptr++) = (uint8_t)input,
dudmuck 0:2ff18de8d48b 240 input >>= 8;
dudmuck 0:2ff18de8d48b 241 *(ptr++) = (uint8_t)input,
dudmuck 0:2ff18de8d48b 242 input >>= 8;
dudmuck 0:2ff18de8d48b 243 *(ptr++) = (uint8_t)input;
dudmuck 0:2ff18de8d48b 244
dudmuck 0:2ff18de8d48b 245 return ptr;
dudmuck 0:2ff18de8d48b 246 }
dudmuck 0:2ff18de8d48b 247
dudmuck 0:2ff18de8d48b 248
dudmuck 0:2ff18de8d48b 249 uint8_t* Write3ByteValue(uint8_t output[], uint32_t input)
dudmuck 0:2ff18de8d48b 250 {
dudmuck 0:2ff18de8d48b 251 uint8_t* ptr = output;
dudmuck 0:2ff18de8d48b 252
dudmuck 0:2ff18de8d48b 253 *(ptr++) = (uint8_t)input,
dudmuck 0:2ff18de8d48b 254 input >>= 8;
dudmuck 0:2ff18de8d48b 255 *(ptr++) = (uint8_t)input,
dudmuck 0:2ff18de8d48b 256 input >>= 8;
dudmuck 0:2ff18de8d48b 257 *(ptr++) = (uint8_t)input;
dudmuck 0:2ff18de8d48b 258
dudmuck 0:2ff18de8d48b 259 return ptr;
dudmuck 0:2ff18de8d48b 260 }
dudmuck 0:2ff18de8d48b 261
dudmuck 0:2ff18de8d48b 262 uint8_t* Write2ByteValue(uint8_t output[], uint32_t input)
dudmuck 0:2ff18de8d48b 263 {
dudmuck 0:2ff18de8d48b 264 uint8_t* ptr = output;
dudmuck 0:2ff18de8d48b 265
dudmuck 0:2ff18de8d48b 266 *(ptr++) = (uint8_t)input,
dudmuck 0:2ff18de8d48b 267 input >>= 8;
dudmuck 0:2ff18de8d48b 268 *(ptr++) = (uint8_t)input;
dudmuck 0:2ff18de8d48b 269
dudmuck 0:2ff18de8d48b 270 return ptr;
dudmuck 0:2ff18de8d48b 271 }
dudmuck 0:2ff18de8d48b 272
dudmuck 0:2ff18de8d48b 273 uint8_t* Write1ByteValue(uint8_t output[], uint32_t input)
dudmuck 0:2ff18de8d48b 274 {
dudmuck 0:2ff18de8d48b 275 uint8_t* ptr = output;
dudmuck 0:2ff18de8d48b 276
dudmuck 0:2ff18de8d48b 277 *(ptr++) = (uint8_t)input;
dudmuck 0:2ff18de8d48b 278
dudmuck 0:2ff18de8d48b 279 return ptr;
dudmuck 0:2ff18de8d48b 280 }
dudmuck 0:2ff18de8d48b 281
dudmuck 0:2ff18de8d48b 282 void LoRa_GenerateJoinFrameIntegrityCode(const uint8_t key[], uint8_t const input[], uint16_t dataLength, uint8_t* output)
dudmuck 0:2ff18de8d48b 283 {
dudmuck 0:2ff18de8d48b 284 AES_CMAC_CTX cmacctx;
dudmuck 0:2ff18de8d48b 285 AES_CMAC_Init(&cmacctx);
dudmuck 0:2ff18de8d48b 286 AES_CMAC_SetKey(&cmacctx, key);
dudmuck 0:2ff18de8d48b 287
dudmuck 0:2ff18de8d48b 288 AES_CMAC_Update(&cmacctx, input, dataLength);
dudmuck 0:2ff18de8d48b 289 uint8_t temp[LORA_AUTHENTICATIONBLOCKBYTES];
dudmuck 0:2ff18de8d48b 290 AES_CMAC_Final(temp, &cmacctx);
dudmuck 0:2ff18de8d48b 291 memcpy(output, temp, LORA_FRAMEMICBYTES);
dudmuck 0:2ff18de8d48b 292 #if 0
dudmuck 0:2ff18de8d48b 293 TODO https://tls.mbed.org/kb/compiling-and-building/how-do-i-configure-mbedtls
dudmuck 0:2ff18de8d48b 294 int ret;
dudmuck 0:2ff18de8d48b 295 mbedtls_cipher_context_t m_ctx;
dudmuck 0:2ff18de8d48b 296 const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type( MBEDTLS_CIPHER_AES_128_CBC );
dudmuck 0:2ff18de8d48b 297 mbedtls_cipher_init(&m_ctx);
dudmuck 0:2ff18de8d48b 298 ret = mbedtls_cipher_setup( &m_ctx, cipher_info );
dudmuck 0:2ff18de8d48b 299 printf("%d = mbedtls_cipher_setup()\r\n", ret);
dudmuck 0:2ff18de8d48b 300 ret = mbedtls_cipher_cmac_starts(&m_ctx, key, LORA_CYPHERKEYBYTES * 8);
dudmuck 0:2ff18de8d48b 301 printf("%d = mbedtls_cipher_cmac_starts()\r\n", ret);
dudmuck 0:2ff18de8d48b 302
dudmuck 0:2ff18de8d48b 303 ret= mbedtls_cipher_cmac_update(&m_ctx, input, dataLength);
dudmuck 0:2ff18de8d48b 304 printf("%d = mbedtls_cipher_cmac_update returned\r\n",ret);
dudmuck 0:2ff18de8d48b 305
dudmuck 0:2ff18de8d48b 306 ret=mbedtls_cipher_cmac_finish(&m_ctx, output);
dudmuck 0:2ff18de8d48b 307 printf("%d = mbedtls_cipher_cmac_starts returned\r\n",ret);
dudmuck 0:2ff18de8d48b 308 #endif
dudmuck 0:2ff18de8d48b 309 }
dudmuck 0:2ff18de8d48b 310
dudmuck 0:2ff18de8d48b 311 void GenerateSessionKey(bool generateNetworkKey, const uint8_t* applicationKey, uint32_t networkId, uint32_t applicationNonce, uint16_t deviceNonce, uint8_t* output)
dudmuck 0:2ff18de8d48b 312 {
dudmuck 0:2ff18de8d48b 313 uint8_t input[LORA_ENCRYPTIONBLOCKBYTES];
dudmuck 0:2ff18de8d48b 314
dudmuck 0:2ff18de8d48b 315 input[0] = generateNetworkKey ? 0x01 : 0x02;
dudmuck 0:2ff18de8d48b 316 uint8_t* ptr = &input[1];
dudmuck 0:2ff18de8d48b 317
dudmuck 0:2ff18de8d48b 318 ptr = Write3ByteValue(ptr, applicationNonce);
dudmuck 0:2ff18de8d48b 319 ptr = Write3ByteValue(ptr, networkId);
dudmuck 0:2ff18de8d48b 320 ptr = Write2ByteValue(ptr, deviceNonce);
dudmuck 0:2ff18de8d48b 321 memset(ptr, 0, LORA_ENCRYPTIONBLOCKBYTES - (ptr - input));
dudmuck 0:2ff18de8d48b 322
dudmuck 0:2ff18de8d48b 323 aes_context aesContext;
dudmuck 0:2ff18de8d48b 324 aes_set_key(applicationKey, LORA_CYPHERKEYBYTES, &aesContext);
dudmuck 0:2ff18de8d48b 325
dudmuck 0:2ff18de8d48b 326 aes_encrypt(input, output, &aesContext);
dudmuck 0:2ff18de8d48b 327 }
dudmuck 0:2ff18de8d48b 328
dudmuck 0:2ff18de8d48b 329 void CryptJoinServer(uint8_t const* key, uint8_t const* input, uint16_t length, uint8_t* output)
dudmuck 0:2ff18de8d48b 330 {
dudmuck 0:2ff18de8d48b 331 aes_context aesContext;
dudmuck 0:2ff18de8d48b 332 memset(aesContext.ksch, '\0', 240);
dudmuck 0:2ff18de8d48b 333 aes_set_key(key, LORA_CYPHERKEYBYTES, &aesContext);
dudmuck 0:2ff18de8d48b 334
dudmuck 0:2ff18de8d48b 335 aes_decrypt(input, output, &aesContext);
dudmuck 0:2ff18de8d48b 336 if (length >= 16) {
dudmuck 0:2ff18de8d48b 337 aes_decrypt(input + 16, output + 16, &aesContext);
dudmuck 0:2ff18de8d48b 338 }
dudmuck 0:2ff18de8d48b 339
dudmuck 0:2ff18de8d48b 340 }
dudmuck 0:2ff18de8d48b 341
dudmuck 0:2ff18de8d48b 342 void LoRaWan::SendJoinComplete(uint16_t deviceNonce, uint8_t firstReceiveWindowDataRateoffset, ota_mote_t* mote)
dudmuck 0:2ff18de8d48b 343 {
dudmuck 0:2ff18de8d48b 344 uint8_t secondReceiveWindowDataRateNibble = 0; // unused
dudmuck 0:2ff18de8d48b 345 uint8_t networkSessionKey[LORA_CYPHERKEYBYTES];
dudmuck 0:2ff18de8d48b 346 uint8_t uncyphered[LORA_MAXDATABYTES];
dudmuck 0:2ff18de8d48b 347 uint8_t* current = uncyphered;
dudmuck 0:2ff18de8d48b 348 uint32_t applicationNonce = rand() & 0xffffff; // 24bit
dudmuck 0:2ff18de8d48b 349
dudmuck 0:2ff18de8d48b 350 if (do_downlink) {
dudmuck 0:2ff18de8d48b 351 printf("SendJoinComplete(): tx busy\r\n");
dudmuck 0:2ff18de8d48b 352 return;
dudmuck 0:2ff18de8d48b 353 }
dudmuck 0:2ff18de8d48b 354
dudmuck 0:2ff18de8d48b 355 GenerateSessionKey(true, mote->app_key, network_id, applicationNonce, deviceNonce, networkSessionKey);
dudmuck 0:2ff18de8d48b 356
dudmuck 0:2ff18de8d48b 357 memcpy(mote->network_session_key, networkSessionKey, LORA_CYPHERKEYBYTES);
dudmuck 0:2ff18de8d48b 358
dudmuck 0:2ff18de8d48b 359 printf("SendJoinComplete() ");
dudmuck 0:2ff18de8d48b 360 if (mote->dev_addr == DEVADDR_NONE) {
dudmuck 0:2ff18de8d48b 361 // new mote joining
dudmuck 0:2ff18de8d48b 362 printf("new-mote ");
dudmuck 0:2ff18de8d48b 363 if ( mote->tx_slot_offset >= PERIODICITY_SLOTS) {
dudmuck 0:2ff18de8d48b 364 printf("max motes reached\r\n");
dudmuck 0:2ff18de8d48b 365 return;
dudmuck 0:2ff18de8d48b 366 }
dudmuck 0:2ff18de8d48b 367 mote->dev_addr = ++networkAddress | (network_id << LORA_NETWORKADDRESSBITS);
dudmuck 0:2ff18de8d48b 368 mote->tx_slot_offset = next_available_tx_slot;
dudmuck 0:2ff18de8d48b 369 next_available_tx_slot += TX_SLOT_STEPPING;
dudmuck 0:2ff18de8d48b 370 } else
dudmuck 0:2ff18de8d48b 371 printf("rejoin ");
dudmuck 0:2ff18de8d48b 372
dudmuck 0:2ff18de8d48b 373 printf(" mote->dev_addr:%lx ", mote->dev_addr);
dudmuck 0:2ff18de8d48b 374 printf("networkAddress:%lu\r\n", networkAddress);
dudmuck 0:2ff18de8d48b 375 *(current++) = MTYPE_JOIN_ACC << 5; // MHDR 0
dudmuck 0:2ff18de8d48b 376 current = Write3ByteValue(current, applicationNonce);// 1 2 3
dudmuck 0:2ff18de8d48b 377 current = Write3ByteValue(current, network_id);// 4 5 6
dudmuck 0:2ff18de8d48b 378 current = Write4ByteValue(current, mote->dev_addr); // 7 8 9 10
dudmuck 0:2ff18de8d48b 379 current = Write1ByteValue(current, (firstReceiveWindowDataRateoffset << 4) | (secondReceiveWindowDataRateNibble & 0xf)); // 11
dudmuck 0:2ff18de8d48b 380 //current = Write1ByteValue(current, classARxWindowDelay_s - 1); // 12
dudmuck 0:2ff18de8d48b 381 current = Write1ByteValue(current, 0); // 12
dudmuck 0:2ff18de8d48b 382
dudmuck 0:2ff18de8d48b 383 /* put beacon timing answer */
dudmuck 0:2ff18de8d48b 384 printf("slots:%u\r\n", rx_slot);
dudmuck 0:2ff18de8d48b 385 current = Write2ByteValue(current, rx_slot); // 13 14
dudmuck 0:2ff18de8d48b 386 current = Write2ByteValue(current, mote->tx_slot_offset); // 15 16
dudmuck 0:2ff18de8d48b 387 current = Write2ByteValue(current, PERIODICITY_SLOTS); //
dudmuck 0:2ff18de8d48b 388 current = Write2ByteValue(current, 0); //
dudmuck 0:2ff18de8d48b 389 current = Write4ByteValue(current, 0); //
dudmuck 0:2ff18de8d48b 390 current = Write4ByteValue(current, 0); //
dudmuck 0:2ff18de8d48b 391
dudmuck 0:2ff18de8d48b 392 uint16_t authenticatedBytes = current - uncyphered;
dudmuck 0:2ff18de8d48b 393 LoRa_GenerateJoinFrameIntegrityCode(mote->app_key, uncyphered, authenticatedBytes, current);
dudmuck 0:2ff18de8d48b 394 current += LORA_FRAMEMICBYTES;
dudmuck 0:2ff18de8d48b 395
dudmuck 0:2ff18de8d48b 396 radio.tx_buf[0] = MTYPE_JOIN_ACC << 5; // MHDR
dudmuck 0:2ff18de8d48b 397 //encrypt
dudmuck 0:2ff18de8d48b 398 uint16_t cypherBytes = (current - uncyphered) - LORA_MACHEADERLENGTH;
dudmuck 0:2ff18de8d48b 399 CryptJoinServer(mote->app_key, &uncyphered[LORA_MACHEADERLENGTH], cypherBytes, &radio.tx_buf[LORA_MACHEADERLENGTH]);
dudmuck 0:2ff18de8d48b 400
dudmuck 0:2ff18de8d48b 401 /**** RF TX ********/
dudmuck 0:2ff18de8d48b 402 lora.RegPayloadLength = current - uncyphered;
dudmuck 0:2ff18de8d48b 403 uint32_t now_ms = timer.read_ms();
dudmuck 0:2ff18de8d48b 404 queue.call_in(rx_ms + 100 - now_ms, send_downlink);
dudmuck 0:2ff18de8d48b 405 do_downlink = true;
dudmuck 0:2ff18de8d48b 406 mote->FCntDown = 0;
dudmuck 0:2ff18de8d48b 407
dudmuck 0:2ff18de8d48b 408 GenerateSessionKey(false, mote->app_key, network_id, applicationNonce, deviceNonce, mote->app_session_key);
dudmuck 0:2ff18de8d48b 409 }
dudmuck 0:2ff18de8d48b 410
dudmuck 0:2ff18de8d48b 411 void LoRa_GenerateDataFrameIntegrityCode(const uint8_t key[], uint8_t const input[], uint16_t dataLength, uint32_t address, bool up, uint32_t sequenceNumber, uint8_t* output)
dudmuck 0:2ff18de8d48b 412 {
dudmuck 0:2ff18de8d48b 413 /*
dudmuck 0:2ff18de8d48b 414 Generate artificial B[0] block
dudmuck 0:2ff18de8d48b 415 Encrypt B[0] to give X[1]
dudmuck 0:2ff18de8d48b 416
dudmuck 0:2ff18de8d48b 417 for n = 1 to number of blocks
dudmuck 0:2ff18de8d48b 418 exclusive OR B[n] with X[n] to give Y[n]
dudmuck 0:2ff18de8d48b 419 encrypt Yi using key to give X[n+1]
dudmuck 0:2ff18de8d48b 420 */
dudmuck 0:2ff18de8d48b 421 uint8_t b0[LORA_AUTHENTICATIONBLOCKBYTES];
dudmuck 0:2ff18de8d48b 422 memset(b0, 0 , LORA_AUTHENTICATIONBLOCKBYTES);
dudmuck 0:2ff18de8d48b 423
dudmuck 0:2ff18de8d48b 424 b0[ 0] = 0x49; //authentication flags
dudmuck 0:2ff18de8d48b 425
dudmuck 0:2ff18de8d48b 426 b0[ 5] = up ? 0 : 1;
dudmuck 0:2ff18de8d48b 427 Write4ByteValue(&b0[6], address);
dudmuck 0:2ff18de8d48b 428 Write4ByteValue(&b0[10], sequenceNumber);
dudmuck 0:2ff18de8d48b 429
dudmuck 0:2ff18de8d48b 430 b0[15] = (uint8_t)dataLength;
dudmuck 0:2ff18de8d48b 431
dudmuck 0:2ff18de8d48b 432 AES_CMAC_CTX cmacctx;
dudmuck 0:2ff18de8d48b 433 AES_CMAC_Init(&cmacctx);
dudmuck 0:2ff18de8d48b 434 AES_CMAC_SetKey(&cmacctx, key);
dudmuck 0:2ff18de8d48b 435
dudmuck 0:2ff18de8d48b 436 AES_CMAC_Update(&cmacctx, b0, LORA_AUTHENTICATIONBLOCKBYTES);
dudmuck 0:2ff18de8d48b 437 AES_CMAC_Update(&cmacctx, input, dataLength);
dudmuck 0:2ff18de8d48b 438
dudmuck 0:2ff18de8d48b 439 uint8_t temp[LORA_AUTHENTICATIONBLOCKBYTES];
dudmuck 0:2ff18de8d48b 440 AES_CMAC_Final(temp, &cmacctx);
dudmuck 0:2ff18de8d48b 441
dudmuck 0:2ff18de8d48b 442 memcpy(output, temp, LORA_FRAMEMICBYTES);
dudmuck 0:2ff18de8d48b 443 }
dudmuck 0:2ff18de8d48b 444
dudmuck 0:2ff18de8d48b 445 static uint16_t FindBlockOverhang(uint16_t inputDataLength)
dudmuck 0:2ff18de8d48b 446 {
dudmuck 0:2ff18de8d48b 447 return inputDataLength & (LORA_ENCRYPTIONBLOCKBYTES - 1);
dudmuck 0:2ff18de8d48b 448 }
dudmuck 0:2ff18de8d48b 449
dudmuck 0:2ff18de8d48b 450 uint16_t CountBlocks(uint16_t inputDataLength)
dudmuck 0:2ff18de8d48b 451 {
dudmuck 0:2ff18de8d48b 452 uint16_t blockSizeMinus1 = LORA_ENCRYPTIONBLOCKBYTES - 1;
dudmuck 0:2ff18de8d48b 453 uint16_t inRoundDown = inputDataLength & ~blockSizeMinus1;
dudmuck 0:2ff18de8d48b 454 uint16_t roundUp = (FindBlockOverhang(inputDataLength) > 0) ? 1 : 0;
dudmuck 0:2ff18de8d48b 455 uint16_t result = inRoundDown / LORA_ENCRYPTIONBLOCKBYTES + roundUp;
dudmuck 0:2ff18de8d48b 456
dudmuck 0:2ff18de8d48b 457 return result;
dudmuck 0:2ff18de8d48b 458 }
dudmuck 0:2ff18de8d48b 459
dudmuck 0:2ff18de8d48b 460 void BlockExOr(uint8_t const l[], uint8_t const r[], uint8_t out[], uint16_t bytes)
dudmuck 0:2ff18de8d48b 461 {
dudmuck 0:2ff18de8d48b 462 uint8_t const* lptr = l;
dudmuck 0:2ff18de8d48b 463 uint8_t const* rptr = r;
dudmuck 0:2ff18de8d48b 464 uint8_t* optr = out;
dudmuck 0:2ff18de8d48b 465 uint8_t const* const end = out + bytes;
dudmuck 0:2ff18de8d48b 466
dudmuck 0:2ff18de8d48b 467 for (;optr < end; lptr++, rptr++, optr++)
dudmuck 0:2ff18de8d48b 468 *optr = *lptr ^ *rptr;
dudmuck 0:2ff18de8d48b 469 }
dudmuck 0:2ff18de8d48b 470
dudmuck 0:2ff18de8d48b 471 void LoRa_EncryptPayload(const uint8_t key[], const uint8_t* in, uint16_t inputDataLength, uint32_t address, bool up, uint32_t sequenceNumber, uint8_t out[])
dudmuck 0:2ff18de8d48b 472 {
dudmuck 0:2ff18de8d48b 473 if (inputDataLength == 0)
dudmuck 0:2ff18de8d48b 474 return;
dudmuck 0:2ff18de8d48b 475
dudmuck 0:2ff18de8d48b 476 uint8_t A[LORA_ENCRYPTIONBLOCKBYTES];
dudmuck 0:2ff18de8d48b 477
dudmuck 0:2ff18de8d48b 478 memset(A, 0, LORA_ENCRYPTIONBLOCKBYTES);
dudmuck 0:2ff18de8d48b 479
dudmuck 0:2ff18de8d48b 480 A[ 0] = 0x01; //encryption flags
dudmuck 0:2ff18de8d48b 481 A[ 5] = up ? 0 : 1;
dudmuck 0:2ff18de8d48b 482
dudmuck 0:2ff18de8d48b 483 Write4ByteValue(&A[6], address);
dudmuck 0:2ff18de8d48b 484 Write4ByteValue(&A[10], sequenceNumber);
dudmuck 0:2ff18de8d48b 485
dudmuck 0:2ff18de8d48b 486 uint16_t const blocks = CountBlocks(inputDataLength);
dudmuck 0:2ff18de8d48b 487 uint16_t const overHangBytes = FindBlockOverhang(inputDataLength);
dudmuck 0:2ff18de8d48b 488
dudmuck 0:2ff18de8d48b 489 uint8_t const* blockInput = in;
dudmuck 0:2ff18de8d48b 490 uint8_t* blockOutput = out;
dudmuck 0:2ff18de8d48b 491 for (uint16_t i = 1; i <= blocks; i++, blockInput += LORA_ENCRYPTIONBLOCKBYTES, blockOutput += LORA_ENCRYPTIONBLOCKBYTES)
dudmuck 0:2ff18de8d48b 492 {
dudmuck 0:2ff18de8d48b 493 A[15] = (uint8_t)i;
dudmuck 0:2ff18de8d48b 494
dudmuck 0:2ff18de8d48b 495 aes_context aesContext;
dudmuck 0:2ff18de8d48b 496 aes_set_key(key, LORA_CYPHERKEYBYTES, &aesContext);
dudmuck 0:2ff18de8d48b 497
dudmuck 0:2ff18de8d48b 498 uint8_t S[LORA_CYPHERKEYBYTES];
dudmuck 0:2ff18de8d48b 499 aes_encrypt(A, S, &aesContext);
dudmuck 0:2ff18de8d48b 500
dudmuck 0:2ff18de8d48b 501 uint16_t bytesToExOr;
dudmuck 0:2ff18de8d48b 502 if ((i < blocks) || (overHangBytes == 0))
dudmuck 0:2ff18de8d48b 503 bytesToExOr = LORA_CYPHERKEYBYTES;
dudmuck 0:2ff18de8d48b 504 else
dudmuck 0:2ff18de8d48b 505 bytesToExOr = overHangBytes;
dudmuck 0:2ff18de8d48b 506
dudmuck 0:2ff18de8d48b 507 BlockExOr(S, blockInput, blockOutput, bytesToExOr);
dudmuck 0:2ff18de8d48b 508 }
dudmuck 0:2ff18de8d48b 509 }
dudmuck 0:2ff18de8d48b 510
dudmuck 0:2ff18de8d48b 511 void put_queue_mac_cmds(ota_mote_t* mote, uint8_t cmd_len, uint8_t* cmd_buf)
dudmuck 0:2ff18de8d48b 512 {
dudmuck 0:2ff18de8d48b 513 int i;
dudmuck 0:2ff18de8d48b 514 uint8_t* this_cmd_buf = mote->macCmd_queue[mote->macCmd_queue_in_idx];
dudmuck 0:2ff18de8d48b 515 this_cmd_buf[0] = cmd_len;
dudmuck 0:2ff18de8d48b 516
dudmuck 0:2ff18de8d48b 517 printf("put_queue_mac_cmds %u: ", cmd_len);
dudmuck 0:2ff18de8d48b 518 for (i = 0; i < cmd_len; i++) {
dudmuck 0:2ff18de8d48b 519 this_cmd_buf[i+1] = cmd_buf[i];
dudmuck 0:2ff18de8d48b 520 printf("%02x ", cmd_buf[i]);
dudmuck 0:2ff18de8d48b 521 }
dudmuck 0:2ff18de8d48b 522 printf("\r\n");
dudmuck 0:2ff18de8d48b 523
dudmuck 0:2ff18de8d48b 524 if (++mote->macCmd_queue_in_idx == MAC_CMD_QUEUE_SIZE)
dudmuck 0:2ff18de8d48b 525 mote->macCmd_queue_in_idx = 0;
dudmuck 0:2ff18de8d48b 526
dudmuck 0:2ff18de8d48b 527 if (mote->macCmd_queue_in_idx == mote->macCmd_queue_out_idx) {
dudmuck 0:2ff18de8d48b 528 printf("macCmd_queue full\r\n");
dudmuck 0:2ff18de8d48b 529 }
dudmuck 0:2ff18de8d48b 530 }
dudmuck 0:2ff18de8d48b 531
dudmuck 0:2ff18de8d48b 532 void
dudmuck 0:2ff18de8d48b 533 LoRaWan::parse_mac_command(ota_mote_t* mote, uint8_t* rx_cmd_buf, uint8_t rx_cmd_buf_len)
dudmuck 0:2ff18de8d48b 534 {
dudmuck 0:2ff18de8d48b 535 uint8_t cmd_buf[MAC_CMD_SIZE];
dudmuck 0:2ff18de8d48b 536 uint8_t rx_cmd_buf_idx = 0;
dudmuck 0:2ff18de8d48b 537 int i;
dudmuck 0:2ff18de8d48b 538 printf("rx_mac_command(s):");
dudmuck 0:2ff18de8d48b 539 for (i = 0; i < rx_cmd_buf_len; i++)
dudmuck 0:2ff18de8d48b 540 printf("%02x ", rx_cmd_buf[i]);
dudmuck 0:2ff18de8d48b 541 printf("\n");
dudmuck 0:2ff18de8d48b 542
dudmuck 0:2ff18de8d48b 543 while (rx_cmd_buf_idx < rx_cmd_buf_len) {
dudmuck 0:2ff18de8d48b 544
dudmuck 0:2ff18de8d48b 545 switch (rx_cmd_buf[rx_cmd_buf_idx++]) {
dudmuck 0:2ff18de8d48b 546 //float diff;
dudmuck 0:2ff18de8d48b 547 uint16_t i_diff;
dudmuck 0:2ff18de8d48b 548 case MOTE_MAC_LINK_CHECK_REQ: // 0x02
dudmuck 0:2ff18de8d48b 549 printf("MOTE_MAC_LINK_CHECK_REQ\n");
dudmuck 0:2ff18de8d48b 550 /* no payload in request */
dudmuck 0:2ff18de8d48b 551 cmd_buf[0] = SRV_MAC_LINK_CHECK_ANS;
dudmuck 0:2ff18de8d48b 552 cmd_buf[1] = 20; // db margin above noise floor
dudmuck 0:2ff18de8d48b 553 cmd_buf[2] = 1; // gateway count
dudmuck 0:2ff18de8d48b 554 put_queue_mac_cmds(mote, 3, cmd_buf);
dudmuck 0:2ff18de8d48b 555 break;
dudmuck 0:2ff18de8d48b 556 #if 0
dudmuck 0:2ff18de8d48b 557 #endif
dudmuck 0:2ff18de8d48b 558 case MOTE_MAC_BEACON_TIMING_REQ: // 0x12
dudmuck 0:2ff18de8d48b 559 /* no payload in request */
dudmuck 0:2ff18de8d48b 560 /*diff = (float)(tick_at_next_beacon - tick_at_RxDone) / 30.0;
dudmuck 0:2ff18de8d48b 561 i_diff = (int)floor(diff);*/
dudmuck 0:2ff18de8d48b 562 i_diff = rx_slot;
dudmuck 0:2ff18de8d48b 563 //printf("MOTE_MAC_BEACON_TIMING_REQ slots:%.1f=%.1fms (int:%u,%u)", diff, diff*30.0, i_diff, i_diff*30);
dudmuck 0:2ff18de8d48b 564 printf("MOTE_MAC_BEACON_TIMING_REQ slots:%u", i_diff);
dudmuck 0:2ff18de8d48b 565 cmd_buf[0] = SRV_MAC_BEACON_TIMING_ANS; // 0x12
dudmuck 0:2ff18de8d48b 566 cmd_buf[1] = i_diff & 0xff; //lsbyte first byte
dudmuck 0:2ff18de8d48b 567 cmd_buf[2] = (i_diff >> 8) & 0xff;
dudmuck 0:2ff18de8d48b 568 cmd_buf[3] = 0; // beacon channel index
dudmuck 0:2ff18de8d48b 569 put_queue_mac_cmds(mote, 4, cmd_buf);
dudmuck 0:2ff18de8d48b 570 printf("%02x %02x %02x\n", cmd_buf[1], cmd_buf[2], cmd_buf[3]);
dudmuck 0:2ff18de8d48b 571 break;
dudmuck 0:2ff18de8d48b 572 case MOTE_MAC_PING_SLOT_FREQ_ANS:
dudmuck 0:2ff18de8d48b 573 i = rx_cmd_buf[rx_cmd_buf_idx++];
dudmuck 0:2ff18de8d48b 574 printf("PING_SLOT_FREQ_ANS status:0x%02x\n", i);
dudmuck 0:2ff18de8d48b 575 break;
dudmuck 0:2ff18de8d48b 576 case MOTE_MAC_BEACON_FREQ_ANS:
dudmuck 0:2ff18de8d48b 577 i = rx_cmd_buf[rx_cmd_buf_idx++];
dudmuck 0:2ff18de8d48b 578 printf("BEACON_FREQ_ANS status:0x%02x\n", i);
dudmuck 0:2ff18de8d48b 579 break;
dudmuck 0:2ff18de8d48b 580 case MOTE_MAC_RX_PARAM_SETUP_ANS:
dudmuck 0:2ff18de8d48b 581 i = rx_cmd_buf[rx_cmd_buf_idx++];
dudmuck 0:2ff18de8d48b 582 printf("RX_PARAM_SETUP_ANS status:0x%02x\n", i);
dudmuck 0:2ff18de8d48b 583 break;
dudmuck 0:2ff18de8d48b 584 case MOTE_MAC_NEW_CHANNEL_ANS:
dudmuck 0:2ff18de8d48b 585 i = rx_cmd_buf[rx_cmd_buf_idx++];
dudmuck 0:2ff18de8d48b 586 printf("NEW_CHANNEL_ANS status:0x%02x\n", i);
dudmuck 0:2ff18de8d48b 587 break;
dudmuck 0:2ff18de8d48b 588 default:
dudmuck 0:2ff18de8d48b 589 printf("TODO mac cmd %02x\n", rx_cmd_buf[rx_cmd_buf_idx-1]);
dudmuck 0:2ff18de8d48b 590 return;
dudmuck 0:2ff18de8d48b 591 } // ..switch (<mac_command>)
dudmuck 0:2ff18de8d48b 592 } // .. while have mac comannds
dudmuck 0:2ff18de8d48b 593
dudmuck 0:2ff18de8d48b 594 }
dudmuck 0:2ff18de8d48b 595
dudmuck 0:2ff18de8d48b 596 void LoRaWan::classA_downlink(ota_mote_t* mote)
dudmuck 0:2ff18de8d48b 597 {
dudmuck 0:2ff18de8d48b 598 fhdr_t* fhdr = (fhdr_t*)&radio.tx_buf[1];
dudmuck 0:2ff18de8d48b 599 uint8_t* mic_ptr;
dudmuck 0:2ff18de8d48b 600
dudmuck 0:2ff18de8d48b 601 fhdr->DevAddr = mote->dev_addr;
dudmuck 0:2ff18de8d48b 602 fhdr->FCnt = mote->FCntDown++;
dudmuck 0:2ff18de8d48b 603 lora.RegPayloadLength += LORA_MACHEADERLENGTH + sizeof(fhdr_t) + fhdr->FCtrl.dlBits.FOptsLen;
dudmuck 0:2ff18de8d48b 604 mic_ptr = &radio.tx_buf[lora.RegPayloadLength];
dudmuck 0:2ff18de8d48b 605
dudmuck 0:2ff18de8d48b 606 LoRa_GenerateDataFrameIntegrityCode(mote->network_session_key, radio.tx_buf, lora.RegPayloadLength, fhdr->DevAddr, false, fhdr->FCnt, mic_ptr);
dudmuck 0:2ff18de8d48b 607 lora.RegPayloadLength += LORA_FRAMEMICBYTES;
dudmuck 0:2ff18de8d48b 608
dudmuck 0:2ff18de8d48b 609
dudmuck 0:2ff18de8d48b 610 queue.call_in(rx_ms + RECEIVE_DELAY_ms - timer.read_ms(), send_downlink);
dudmuck 0:2ff18de8d48b 611 do_downlink = true;
dudmuck 0:2ff18de8d48b 612 }
dudmuck 0:2ff18de8d48b 613
dudmuck 0:2ff18de8d48b 614 void LoRaWan::parse_uplink(ota_mote_t* mote)
dudmuck 0:2ff18de8d48b 615 {
dudmuck 0:2ff18de8d48b 616 uint8_t decrypted[256];
dudmuck 0:2ff18de8d48b 617 uint32_t calculated_mic, rx_mic;
dudmuck 0:2ff18de8d48b 618 fhdr_t *rx_fhdr = (fhdr_t*)&radio.rx_buf[1];
dudmuck 0:2ff18de8d48b 619 mhdr_t* rx_mhdr = (mhdr_t*)&radio.rx_buf[0];
dudmuck 0:2ff18de8d48b 620 int rxofs = sizeof(mhdr_t) + sizeof(fhdr_t) + rx_fhdr->FCtrl.ulBits.FOptsLen;
dudmuck 0:2ff18de8d48b 621 int rxFRMPayload_length = 0;
dudmuck 0:2ff18de8d48b 622 uint8_t* rxFRMPayload = NULL;
dudmuck 0:2ff18de8d48b 623 uint8_t* rx_fport_ptr = NULL;
dudmuck 0:2ff18de8d48b 624
dudmuck 0:2ff18de8d48b 625 if ((lora.RegRxNbBytes - LORA_FRAMEMICBYTES) > rxofs) {
dudmuck 0:2ff18de8d48b 626 rxFRMPayload_length = (lora.RegRxNbBytes - LORA_FRAMEMICBYTES) - (rxofs + 1);
dudmuck 0:2ff18de8d48b 627 rxFRMPayload = &radio.rx_buf[rxofs+1];
dudmuck 0:2ff18de8d48b 628 rx_fport_ptr = &radio.rx_buf[rxofs];
dudmuck 0:2ff18de8d48b 629 printf("port:%d len:%d\r\n", *rx_fport_ptr, rxFRMPayload_length);
dudmuck 0:2ff18de8d48b 630 } else
dudmuck 0:2ff18de8d48b 631 printf("no-payload\r\n");
dudmuck 0:2ff18de8d48b 632
dudmuck 0:2ff18de8d48b 633 LoRa_GenerateDataFrameIntegrityCode(mote->network_session_key, radio.rx_buf, lora.RegRxNbBytes-LORA_FRAMEMICBYTES, rx_fhdr->DevAddr, true, rx_fhdr->FCnt, (uint8_t*)&calculated_mic);
dudmuck 0:2ff18de8d48b 634
dudmuck 0:2ff18de8d48b 635 rx_mic = radio.rx_buf[lora.RegRxNbBytes-1] << 24;
dudmuck 0:2ff18de8d48b 636 rx_mic += radio.rx_buf[lora.RegRxNbBytes-2] << 16;
dudmuck 0:2ff18de8d48b 637 rx_mic += radio.rx_buf[lora.RegRxNbBytes-3] << 8;
dudmuck 0:2ff18de8d48b 638 rx_mic += radio.rx_buf[lora.RegRxNbBytes-4];
dudmuck 0:2ff18de8d48b 639 if (calculated_mic != rx_mic) {
dudmuck 0:2ff18de8d48b 640 printf("genMic:%08lx, rxMic:%08lx\r\n", calculated_mic, rx_mic);
dudmuck 0:2ff18de8d48b 641 printf("mic fail\n");
dudmuck 0:2ff18de8d48b 642 return;
dudmuck 0:2ff18de8d48b 643 }
dudmuck 0:2ff18de8d48b 644
dudmuck 0:2ff18de8d48b 645 if (rx_fport_ptr != NULL && *rx_fport_ptr == 0) {
dudmuck 0:2ff18de8d48b 646 /* mac commands are encrypted onto port 0 */
dudmuck 0:2ff18de8d48b 647 LoRa_EncryptPayload(mote->network_session_key, rxFRMPayload, rxFRMPayload_length, rx_fhdr->DevAddr, true, rx_fhdr->FCnt, decrypted);
dudmuck 0:2ff18de8d48b 648 printf("mac commands encrypted on port 0\r\n");
dudmuck 0:2ff18de8d48b 649 parse_mac_command(mote, decrypted, rxFRMPayload_length);
dudmuck 0:2ff18de8d48b 650 } else {
dudmuck 0:2ff18de8d48b 651 if (rx_fhdr->FCtrl.ulBits.FOptsLen > 0) {
dudmuck 0:2ff18de8d48b 652 /* mac commands are in header */
dudmuck 0:2ff18de8d48b 653 printf("mac commands in header\r\n");
dudmuck 0:2ff18de8d48b 654 rxofs = sizeof(mhdr_t) + sizeof(fhdr_t);
dudmuck 0:2ff18de8d48b 655 parse_mac_command(mote, &radio.rx_buf[rxofs], rx_fhdr->FCtrl.ulBits.FOptsLen);
dudmuck 0:2ff18de8d48b 656 }
dudmuck 0:2ff18de8d48b 657 if (rxFRMPayload != NULL) {
dudmuck 0:2ff18de8d48b 658 LoRa_EncryptPayload(mote->app_session_key, rxFRMPayload, rxFRMPayload_length, rx_fhdr->DevAddr, true, rx_fhdr->FCnt, decrypted);
dudmuck 0:2ff18de8d48b 659 printf("app-decrypt:");
dudmuck 0:2ff18de8d48b 660 for (rxofs = 0; rxofs < rxFRMPayload_length; rxofs++) {
dudmuck 0:2ff18de8d48b 661 printf("%02x ", decrypted[rxofs]);
dudmuck 0:2ff18de8d48b 662 }
dudmuck 0:2ff18de8d48b 663 printf(" ");
dudmuck 0:2ff18de8d48b 664 for (rxofs = 0; rxofs < rxFRMPayload_length; rxofs++) {
dudmuck 0:2ff18de8d48b 665 if (decrypted[rxofs] >= ' ' && decrypted[rxofs] < 0x7f)
dudmuck 0:2ff18de8d48b 666 printf("%c", decrypted[rxofs]);
dudmuck 0:2ff18de8d48b 667 }
dudmuck 0:2ff18de8d48b 668 printf("\n");
dudmuck 0:2ff18de8d48b 669 }
dudmuck 0:2ff18de8d48b 670 }
dudmuck 0:2ff18de8d48b 671
dudmuck 0:2ff18de8d48b 672 fhdr_t* tx_fhdr = (fhdr_t*)&radio.tx_buf[1];
dudmuck 0:2ff18de8d48b 673 tx_fhdr->FCtrl.dlBits.FOptsLen = 0;
dudmuck 0:2ff18de8d48b 674
dudmuck 0:2ff18de8d48b 675 /* TODO get queued mac cmds */
dudmuck 0:2ff18de8d48b 676
dudmuck 0:2ff18de8d48b 677 lora.RegPayloadLength = 0;
dudmuck 0:2ff18de8d48b 678
dudmuck 0:2ff18de8d48b 679 if (tx_fhdr->FCtrl.dlBits.FOptsLen > 0 || rx_mhdr->bits.MType == MTYPE_CONF_UP ||
dudmuck 0:2ff18de8d48b 680 mote->user_downlink_length > 0 || rx_fhdr->FCtrl.ulBits.ADCACKReq)
dudmuck 0:2ff18de8d48b 681 {
dudmuck 0:2ff18de8d48b 682 /* something to send via downlink */
dudmuck 0:2ff18de8d48b 683 if (rx_mhdr->bits.MType == MTYPE_CONF_UP)
dudmuck 0:2ff18de8d48b 684 tx_fhdr->FCtrl.dlBits.ACK = 1;
dudmuck 0:2ff18de8d48b 685 else
dudmuck 0:2ff18de8d48b 686 tx_fhdr->FCtrl.dlBits.ACK = 0;
dudmuck 0:2ff18de8d48b 687
dudmuck 0:2ff18de8d48b 688 if (mote->user_downlink_length > 0) {
dudmuck 0:2ff18de8d48b 689 /* add user payload */
dudmuck 0:2ff18de8d48b 690 int txo = sizeof(mhdr_t) + sizeof(fhdr_t) + tx_fhdr->FCtrl.dlBits.FOptsLen;
dudmuck 0:2ff18de8d48b 691 uint8_t* tx_fport_ptr = &radio.tx_buf[txo];
dudmuck 0:2ff18de8d48b 692 uint8_t* txFRMPayload = &radio.tx_buf[txo+1];
dudmuck 0:2ff18de8d48b 693 LoRa_EncryptPayload(mote->app_session_key, user_downlink, mote->user_downlink_length, mote->dev_addr, false, mote->FCntDown, txFRMPayload);
dudmuck 0:2ff18de8d48b 694 if (rx_fport_ptr != NULL)
dudmuck 0:2ff18de8d48b 695 *tx_fport_ptr = *rx_fport_ptr;
dudmuck 0:2ff18de8d48b 696 else
dudmuck 0:2ff18de8d48b 697 *tx_fport_ptr = DEFAULT_DOWNLINK_PORT;
dudmuck 0:2ff18de8d48b 698
dudmuck 0:2ff18de8d48b 699 lora.RegPayloadLength = tx_fhdr->FCtrl.dlBits.FOptsLen + mote->user_downlink_length + 1; // +1 for fport
dudmuck 0:2ff18de8d48b 700 radio.tx_buf[0] = user_dowlink_mtype << 5; // MHDR
dudmuck 0:2ff18de8d48b 701 printf("DL-send %d\r\n", mote->user_downlink_length);
dudmuck 0:2ff18de8d48b 702 } else {
dudmuck 0:2ff18de8d48b 703 /* downlink not triggered by user_downlink */
dudmuck 0:2ff18de8d48b 704 /* downlink triggered by FOpotsLen > 0 or conf_uplink */
dudmuck 0:2ff18de8d48b 705 radio.tx_buf[0] = MTYPE_UNCONF_DN << 5; // MHDR
dudmuck 0:2ff18de8d48b 706 }
dudmuck 0:2ff18de8d48b 707
dudmuck 0:2ff18de8d48b 708 classA_downlink(mote);
dudmuck 0:2ff18de8d48b 709
dudmuck 0:2ff18de8d48b 710 mote->user_downlink_length = 0; // mark as sent
dudmuck 0:2ff18de8d48b 711 }
dudmuck 0:2ff18de8d48b 712 }
dudmuck 0:2ff18de8d48b 713
dudmuck 0:2ff18de8d48b 714 void LoRaWan::parse_join_req(ota_mote_t* mote)
dudmuck 0:2ff18de8d48b 715 {
dudmuck 0:2ff18de8d48b 716 join_req_t* jreq_ptr = (join_req_t*)&radio.rx_buf[0];
dudmuck 0:2ff18de8d48b 717 uint32_t rx_mic, calculated_mic;
dudmuck 0:2ff18de8d48b 718
dudmuck 0:2ff18de8d48b 719 LoRa_GenerateJoinFrameIntegrityCode(mote->app_key, radio.rx_buf, lora.RegRxNbBytes-LORA_FRAMEMICBYTES, (uint8_t*)&calculated_mic);
dudmuck 0:2ff18de8d48b 720
dudmuck 0:2ff18de8d48b 721
dudmuck 0:2ff18de8d48b 722 rx_mic = radio.rx_buf[lora.RegRxNbBytes-1] << 24;
dudmuck 0:2ff18de8d48b 723 rx_mic += radio.rx_buf[lora.RegRxNbBytes-2] << 16;
dudmuck 0:2ff18de8d48b 724 rx_mic += radio.rx_buf[lora.RegRxNbBytes-3] << 8;
dudmuck 0:2ff18de8d48b 725 rx_mic += radio.rx_buf[lora.RegRxNbBytes-4];
dudmuck 0:2ff18de8d48b 726 if (calculated_mic != rx_mic) {
dudmuck 0:2ff18de8d48b 727 printf("join_req mic fail: %08lx, %08lx\r\n", calculated_mic, rx_mic);
dudmuck 0:2ff18de8d48b 728 return;
dudmuck 0:2ff18de8d48b 729 }
dudmuck 0:2ff18de8d48b 730
dudmuck 0:2ff18de8d48b 731 /* TODO check devNonce */
dudmuck 0:2ff18de8d48b 732 SendJoinComplete(jreq_ptr->DevNonce, 0, mote);
dudmuck 0:2ff18de8d48b 733 }
dudmuck 0:2ff18de8d48b 734
dudmuck 0:2ff18de8d48b 735
dudmuck 0:2ff18de8d48b 736 int memcmp_rev(const uint8_t* a, const uint8_t* b, uint8_t len)
dudmuck 0:2ff18de8d48b 737 {
dudmuck 0:2ff18de8d48b 738 int a_i, b_i = len - 1;
dudmuck 0:2ff18de8d48b 739 for (a_i = 0; a_i < len; a_i++) {
dudmuck 0:2ff18de8d48b 740 if (a[a_i] != b[b_i])
dudmuck 0:2ff18de8d48b 741 return a[a_i] - b[b_i];
dudmuck 0:2ff18de8d48b 742 else
dudmuck 0:2ff18de8d48b 743 b_i--;
dudmuck 0:2ff18de8d48b 744 }
dudmuck 0:2ff18de8d48b 745 return 0;
dudmuck 0:2ff18de8d48b 746 }
dudmuck 0:2ff18de8d48b 747
dudmuck 0:2ff18de8d48b 748 void LoRaWan::init()
dudmuck 0:2ff18de8d48b 749 {
dudmuck 0:2ff18de8d48b 750 int i;
dudmuck 0:2ff18de8d48b 751 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 752 motes[i].dev_addr = DEVADDR_NONE;
dudmuck 0:2ff18de8d48b 753 }
dudmuck 0:2ff18de8d48b 754 }
dudmuck 0:2ff18de8d48b 755
dudmuck 0:2ff18de8d48b 756 int LoRaWan::parse_receive()
dudmuck 0:2ff18de8d48b 757 {
dudmuck 0:2ff18de8d48b 758 int i;
dudmuck 0:2ff18de8d48b 759 ota_mote_t* mote = NULL;
dudmuck 0:2ff18de8d48b 760 mhdr_t *mhdr = (mhdr_t*)radio.rx_buf;
dudmuck 0:2ff18de8d48b 761
dudmuck 0:2ff18de8d48b 762 printf("len%d ", lora.RegRxNbBytes);
dudmuck 0:2ff18de8d48b 763 if (lora.RegRxNbBytes <= (sizeof(fhdr_t) + LORA_FRAMEMICBYTES)) {
dudmuck 0:2ff18de8d48b 764 printf("too small %d, snr:%.1f %ddBm\r\n", lora.RegRxNbBytes, lora.RegPktSnrValue/4.0, lora.get_pkt_rssi());
dudmuck 0:2ff18de8d48b 765 return 1;
dudmuck 0:2ff18de8d48b 766 }
dudmuck 0:2ff18de8d48b 767 if (mhdr->bits.major != 0) {
dudmuck 0:2ff18de8d48b 768 printf("unsupported major:%u\n", mhdr->bits.major);
dudmuck 0:2ff18de8d48b 769 return 0;
dudmuck 0:2ff18de8d48b 770 }
dudmuck 0:2ff18de8d48b 771
dudmuck 0:2ff18de8d48b 772 if (mhdr->bits.MType == MTYPE_JOIN_REQ) {
dudmuck 0:2ff18de8d48b 773 join_req_t* join_req = (join_req_t*)&radio.rx_buf[0];
dudmuck 0:2ff18de8d48b 774 printf("MTYPE_JOIN_REQ ");
dudmuck 0:2ff18de8d48b 775
dudmuck 0:2ff18de8d48b 776 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 777 if ((memcmp_rev(join_req->AppEUI, motes[i].app_eui, LORA_EUI_LENGTH) == 0) &&
dudmuck 0:2ff18de8d48b 778 (memcmp_rev(join_req->DevEUI, motes[i].dev_eui, LORA_EUI_LENGTH) == 0))
dudmuck 0:2ff18de8d48b 779 {
dudmuck 0:2ff18de8d48b 780 printf("found mote\r\n");
dudmuck 0:2ff18de8d48b 781 mote = &motes[i];
dudmuck 0:2ff18de8d48b 782 }
dudmuck 0:2ff18de8d48b 783 }
dudmuck 0:2ff18de8d48b 784 if (mote != NULL) {
dudmuck 0:2ff18de8d48b 785 printf("Join-Found\r\n");
dudmuck 0:2ff18de8d48b 786 parse_join_req(mote);
dudmuck 0:2ff18de8d48b 787 } else {
dudmuck 0:2ff18de8d48b 788 printf("join-not-found:\r\n");
dudmuck 0:2ff18de8d48b 789 print_octets_rev("app_eui", join_req->AppEUI, 8);
dudmuck 0:2ff18de8d48b 790 print_octets_rev("\r\ndev_eui", join_req->DevEUI, 8);
dudmuck 0:2ff18de8d48b 791 printf("\r\n");
dudmuck 0:2ff18de8d48b 792 }
dudmuck 0:2ff18de8d48b 793 } else if (mhdr->bits.MType == MTYPE_UNCONF_UP || mhdr->bits.MType == MTYPE_CONF_UP) {
dudmuck 0:2ff18de8d48b 794 fhdr_t *fhdr = (fhdr_t*)&radio.rx_buf[1];
dudmuck 0:2ff18de8d48b 795 if (mhdr->bits.MType == MTYPE_UNCONF_UP)
dudmuck 0:2ff18de8d48b 796 printf("MTYPE_UNCONF_UP ");
dudmuck 0:2ff18de8d48b 797 else if (mhdr->bits.MType == MTYPE_CONF_UP)
dudmuck 0:2ff18de8d48b 798 printf("MTYPE_CONF_UP ");
dudmuck 0:2ff18de8d48b 799
dudmuck 0:2ff18de8d48b 800 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 801 if (motes[i].dev_addr == fhdr->DevAddr) {
dudmuck 0:2ff18de8d48b 802 mote = &motes[i];
dudmuck 0:2ff18de8d48b 803 }
dudmuck 0:2ff18de8d48b 804 }
dudmuck 0:2ff18de8d48b 805
dudmuck 0:2ff18de8d48b 806 if (mote != NULL) {
dudmuck 0:2ff18de8d48b 807 printf("mote:%lx ", mote->dev_addr);
dudmuck 0:2ff18de8d48b 808 parse_uplink(mote);
dudmuck 0:2ff18de8d48b 809 } else {
dudmuck 0:2ff18de8d48b 810 printf("mote-not-found %08lx", fhdr->DevAddr);
dudmuck 0:2ff18de8d48b 811 }
dudmuck 0:2ff18de8d48b 812
dudmuck 0:2ff18de8d48b 813 printf("\r\n");
dudmuck 0:2ff18de8d48b 814 } else
dudmuck 0:2ff18de8d48b 815 printf(" %02x mtype:%d\r\n", radio.rx_buf[0], mhdr->bits.MType);
dudmuck 0:2ff18de8d48b 816
dudmuck 0:2ff18de8d48b 817
dudmuck 0:2ff18de8d48b 818 return 0;
dudmuck 0:2ff18de8d48b 819 }
dudmuck 0:2ff18de8d48b 820