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 Aug 10 17:56:02 2017 -0700
Revision:
16:67fa19bc6331
Parent:
13:fa2095be01c4
Child:
17:763412df6872
add print filter on dev_addr

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