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:
Wed Aug 02 11:33:56 2017 -0700
Revision:
13:fa2095be01c4
Parent:
12:9a8c13c4298b
Child:
16:67fa19bc6331
add pwm broadcast and 434MHz band

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