end node on synchronous star LoRa network.
Dependencies: SX127x sx12xx_hal TSL2561
radio chip selection
Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.
This project for use with LoRaWAN_singlechannel_gateway project.
Alternately gateway running on raspberry pi can be used as gateway.
LoRaWAN on single radio channel
Network description is at gateway project page. Synchronous star network.
Hardware Support
This project supports SX1276 and SX1272, sx126x kit, sx126x shield, and sx128x 2.4GHz. The ST board B-L072Z-LRWAN1 is also supported (TypeABZ module). When B-L072Z-LRWAN1
target is selected, TARGET_DISCO_L072CZ_LRWAN1
is defined by tools, allowing correct radio driver configuration for this platform. Alternately, any mbed board that can use LoRa radio shield board should work, but NUCLEO boards are tested.
End-node Unique ID
DevEUI is created from CPU serial number. AppEUI and AppKey are declared as software constants.
End-node Configuration
Data rate definition LORAMAC_DEFAULT_DATARATE
configured in LoRaMac-definitions.h
. See gateway project page for configuration of gateway.
LoRaWAN addressing is configured in Comissioning.h
; only OTA mode is functional.
Header file board/lora_config.h
, selects application layer options (i.e. sensors) to be compiled in.
Serial Interface
Serial port operates at 115200bps.
Application layer single_us915_main.cpp
User button triggers uplink (i.e. blue button on nucleo board), or jumper enables continuously sends repeated uplink packets. The MAC layer holds each uplink request until the allocated timeslot.
command | arguments | description |
---|---|---|
? | - | print available commands |
. (period) | - | print status (DevEUI, DevAddr, etc) |
ul | length integer | set payload length of test uplink packets |
sensor demo
Selected grove sensors may be plugged into SX1272 shield.
To enable, edit lora_config.h
to define SENSORS
.
Sensor connections on SX1272MB2xAS:
D8 D9: button | RX TX: (unused) | A3 A4: Rotary Angle Sensor |
D6 D7: RGB LED | SCL SDA: digital light sensor | A1 A2: Rotary Angle Sensor |
Digital input pin, state reported via uplink: PC8
Digital output pin, controlled via downlink: PC6
PWM out: PB_10
Jumper enables auto-repeated transmit: PC10
and PC12
on NUCLEO board, located on end of morpho headers nearby JP4.
Diff: mac/LoRaMac.cpp
- Revision:
- 30:1c35c4f56e50
- Parent:
- 29:ad409c68c0a6
--- a/mac/LoRaMac.cpp Fri Dec 07 17:57:41 2018 -0800 +++ b/mac/LoRaMac.cpp Thu Dec 13 16:53:02 2018 -0800 @@ -168,9 +168,6 @@ #ifdef SX128x_H - - #define LORA_MAX_NB_CHANNELS 8 - // 0 1 2 3 4 5 6 7 const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 6, 5 }; #define SF_FROM_DR0 12 /* DR0 is sf12 */ @@ -186,6 +183,7 @@ const int8_t TxPowers[] = { 12, 5 }; #define LORAMAC_FIRST_CHANNEL ( (uint32_t)2486.9e6 ) #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)300e3 ) + #define LORA_MAX_NB_CHANNELS 8 #define LORA_BANDWIDTH_KHZ 200 @@ -241,7 +239,7 @@ #define LORAMAC_FIRST_CHANNEL ( (uint32_t)433.32e6 ) #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)200e3 ) - #define LORA_MAX_NB_CHANNELS 7 + #define LORA_MAX_NB_CHANNELS 7 /* end USE_BAND_433 */ #else @@ -253,67 +251,24 @@ #ifdef SX128x_H - #define LATENCY_FROM_1600KHZ_SF12 17300 - #define LATENCY_FROM_1600KHZ_SF11 16900 - #define LATENCY_FROM_1600KHZ_SF10 4300 - #define LATENCY_FROM_1600KHZ_SF9 4000 - #define LATENCY_FROM_1600KHZ_SF8 1050 - #define LATENCY_FROM_1600KHZ_SF7 1000 - #define LATENCY_FROM_1600KHZ_SF6 504 - #define LATENCY_FROM_1600KHZ_SF5 256 - - #define _LATENCY_FROM_SF(sf) LATENCY_FROM_1600KHZ_SF ## sf - #define LATENCY_FROM_SF_(x) _LATENCY_FROM_SF(x) - - /* 6 byte implict packet */ - #define BEACON_TOA_FROM_1600KHZ_SF12 66000 - #define BEACON_TOA_FROM_1600KHZ_SF11 33100 - #define BEACON_TOA_FROM_1600KHZ_SF10 16400 - #define BEACON_TOA_FROM_1600KHZ_SF9 8260 - #define BEACON_TOA_FROM_1600KHZ_SF8 5100 - #define BEACON_TOA_FROM_1600KHZ_SF7 2560 - #define BEACON_TOA_FROM_1600KHZ_SF6 1350 - #define BEACON_TOA_FROM_1600KHZ_SF5 800 - - #define BEACON_RXDONE_LATENCY_us (LATENCY_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (1600/LORA_BANDWIDTH_KHZ)) - - #define _BEACON_TOA_FROM_SF(sf) BEACON_TOA_FROM_1600KHZ_SF ## sf - #define BEACON_TOA_FROM_SF_(x) _BEACON_TOA_FROM_SF(x) - #define BEACON_TOA_us (BEACON_TOA_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (1600/LORA_BANDWIDTH_KHZ)) + #define FASTEST_SF 5 + /* ratio of symbol period to time from end of packet to RxDone interrupt */ +// sp 200KHz 160 320 640 1280 2560 5120 10240 20480 +// usLatency 200KHz implicit-6byte 90 228 436 880 2020 4200 8800 19300 + // SF: 5 6 7 8 9 10 11 12 + const float rxLatencyFactorFromSF[8] = { 0.56, 0.72, 0.68, 0.69, 0.79, 0.82, 0.86, 0.94 }; #elif defined(SX126x_H) || defined(SX127x_H) - /* 6 byte implict packet */ - #define BEACON_TOA_FROM_500KHZ_SF12 198000 - #define BEACON_TOA_FROM_500KHZ_SF11 97750 - #define BEACON_TOA_FROM_500KHZ_SF10 49630 - #define BEACON_TOA_FROM_500KHZ_SF9 24500 - #define BEACON_TOA_FROM_500KHZ_SF8 12500 - #define BEACON_TOA_FROM_500KHZ_SF7 6250 - #define BEACON_TOA_FROM_500KHZ_SF6 3320 - #define BEACON_TOA_FROM_500KHZ_SF5 1940 + #define FASTEST_SF 5 + /* ratio of symbol period to time from end of packet to RxDone interrupt */ +// sp 500KHz 64 128 256 512 1024 2048 4096 8192 +// usLatency 500KHz implicit-6byte 92 184 376 780 1680 3680 7720 + // SF: 5 6 7 8 9 10 11 12 + const float rxLatencyFactorFromSF[8] = { 0.72, 0.72, 0.72, 0.73, 0.76, 0.82, 0.90, 0.94 }; - #define LATENCY_FROM_500KHZ_SF12 7750 - #define LATENCY_FROM_500KHZ_SF11 3600 - #define LATENCY_FROM_500KHZ_SF10 1675 - #define LATENCY_FROM_500KHZ_SF9 7875 - #define LATENCY_FROM_500KHZ_SF8 385 - #define LATENCY_FROM_500KHZ_SF7 195 - #define LATENCY_FROM_500KHZ_SF6 103 - #define LATENCY_FROM_500KHZ_SF5 48 - - #define _LATENCY_FROM_SF(sf) LATENCY_FROM_500KHZ_SF ## sf - #define LATENCY_FROM_SF_(x) _LATENCY_FROM_SF(x) - - #define BEACON_RXDONE_LATENCY_us (LATENCY_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (500/LORA_BANDWIDTH_KHZ)) - - #define _BEACON_TOA_FROM_SF(sf) BEACON_TOA_FROM_500KHZ_SF ## sf - #define BEACON_TOA_FROM_SF_(x) _BEACON_TOA_FROM_SF(x) - #define BEACON_TOA_us (BEACON_TOA_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (500/LORA_BANDWIDTH_KHZ)) #endif /* SX126x_H || SX127x_H */ -const us_timestamp_t beaconDuration = BEACON_TOA_us + BEACON_RXDONE_LATENCY_us; - static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS]; // populated in init #define BEACON_GUARD_us 2000000 // pre-beacon start @@ -385,11 +340,13 @@ uint16_t nSymbsTimeout; unsigned SymbolTimeout_us; uint8_t num_missed; + uint8_t num_consecutive_ok; beacon_state_e state; uint16_t tx_slot_offset; uint16_t periodicity_slots; + uint32_t beaconStartToRxDone; uint16_t sendOpportunities; LowPowerTimeoutAbs _timeout_rx; @@ -749,18 +706,6 @@ RxWindowSetup(Channels[Channel].Frequency, RxWindowsParam.Datarate, RxWindowsParam.RxWindowTimeout, false); Radio::Rx( LoRaMacParams.MaxRxWindow ); //mac_printf("rxwinTo:%u\r\n", RxWindowsParam.RxWindowTimeout); -#ifdef SX128x_H - { - LoRaPktPar1_t LoRaPktPar1; - LoRaPktPar1.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR1, 1); - mac_printf("dnRxiq:"); - if (LoRaPktPar1.bits.rxinvert_iq) { - mac_printf("IQ_STD\r\n"); - } else { - mac_printf("IQ_INV\r\n"); - } - } -#endif /* SX128x_H */ } static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError ); @@ -885,48 +830,44 @@ _tx_timeout.attach_us(&send_callback, BeaconCtx.sendAt + err); BeaconCtx.lastSendAtErr = err; - if (LoRaMacFlags.Bits.uplink_pending) - LoRaMacFlags.Bits.send = 1; + if (LoRaMacFlags.Bits.uplink_pending) { + us_timestamp_t untilGuard = (BeaconCtx.rx_setup_at - BEACON_GUARD_us) - BeaconCtx._timeout_guard.read_us(); + if (untilGuard > (RECEIVE_DELAY_us + TARGET_PRECESSION_us)) + LoRaMacFlags.Bits.send = 1; + } } void OnRxBeaconSetup() { - Radio::Standby( ); - + BeaconCtx.guard = false; LoRaMacFlags.Bits.expecting_beacon = true; - Radio::LoRaModemConfig(LORA_BANDWIDTH_KHZ, Datarates[BEACON_CHANNEL_DR], 1); - - // preambleLen, fixLen, crcOn, invIQ - Radio::LoRaPacketConfig(PREAMBLE_SYMBS, true, false, false); - Radio::SetFixedPayloadLength(BEACON_SIZE); - Radio::SetLoRaSymbolTimeout(BeaconCtx.nSymbsTimeout); - Radio::Rx(2000); BeaconCtx.lastSendAtErr = 0; - -#ifdef SX128x_H - { - LoRaPktPar1_t LoRaPktPar1; - LoRaPktPar1.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR1, 1); - mac_printf("beaconRxiq:"); - if (LoRaPktPar1.bits.rxinvert_iq) { - mac_printf("IQ_STD\r\n"); - } else { - mac_printf("IQ_INV\r\n"); - } - } -#endif /* SX128x_H */ } void guard_callback() { BeaconCtx.guard = true; + Radio::Standby( ); + Radio::SetChannel( Channels[Channel].Frequency ); _tx_timeout.detach(); mac_printf("sendOpportunities:%u\r\n", BeaconCtx.sendOpportunities); BeaconCtx.sendOpportunities = 0; + + // preambleLen, fixLen, crcOn, invIQ + Radio::LoRaPacketConfig(PREAMBLE_SYMBS, true, false, false); + Radio::LoRaModemConfig(LORA_BANDWIDTH_KHZ, Datarates[BEACON_CHANNEL_DR], 1); + Radio::SetFixedPayloadLength(BEACON_SIZE); + +#ifdef SX128x_H + /* explicit to implicit header: does sx1280 really need this a 2nd time? */ + // preambleLen, fixLen, crcOn, invIQ + Radio::LoRaPacketConfig(PREAMBLE_SYMBS, true, false, false); +#endif /* SX128x_H */ + Radio::SetLoRaSymbolTimeout(BeaconCtx.nSymbsTimeout); } static void us_to_nSymbTimeout(unsigned us) @@ -970,10 +911,10 @@ { static bool compensate_precession = false; int32_t compensation = 0; - us_timestamp_t ThisBeaconRx_us = Radio::irqAt + lpt_offset - beaconDuration; + us_timestamp_t ThisBeaconRx_us = Radio::irqAt + lpt_offset - BeaconCtx.beaconStartToRxDone; + BeaconCtx.num_consecutive_ok++; mac_printf("rx_beacon %llu ", Radio::irqAt); - BeaconCtx.guard = false; BeaconCtx.rx_precession_us = ThisBeaconRx_us - BeaconCtx.rx_setup_at; if (BeaconCtx.state != BEACON_STATE_FIRST_ACQ) { BeaconCtx.known_working_BeaconRxTimerError_us = BeaconCtx.last_BeaconRxTimerError_us; @@ -989,7 +930,6 @@ mac_printf("-->LOCKED "); BeaconCtx.state = BEACON_STATE_LOCKED; compensate_precession = true; - us_to_nSymbTimeout(BEACON_RX_TIMEOUT_LOCKED_us); } } else { /* ignore precession at first acquisition because it has slot resolution added */ @@ -1030,11 +970,6 @@ BeaconCtx._timeout_rx.attach_us(&OnRxBeaconSetup, BeaconCtx.rx_setup_at); BeaconCtx._timeout_guard.attach_us(&guard_callback, BeaconCtx.rx_setup_at - BEACON_GUARD_us); - if (BeaconCtx.num_missed > 0) { - /* restore rx symbol timeout */ - us_to_nSymbTimeout(BEACON_RX_TIMEOUT_LOCKED_us); - } - BeaconCtx.num_missed = 0; MlmeIndication.MlmeIndication = MLME_BEACON; @@ -1140,6 +1075,7 @@ if( micRx == mic ) { + uint32_t beaconDur; LoRaMacJoinComputeSKeys( LoRaMacAppKey, _jaDecrypted + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey ); LoRaMacNetID = ( uint32_t )_jaDecrypted[4]; @@ -1180,6 +1116,16 @@ BeaconCtx.periodicity_slots = _jaDecrypted[17]; BeaconCtx.periodicity_slots |= _jaDecrypted[18] << 8; + beaconDur = _jaDecrypted[22]; + beaconDur <<= 8; + beaconDur |= _jaDecrypted[21]; + beaconDur <<= 8; + beaconDur |= _jaDecrypted[20]; + beaconDur <<= 8; + beaconDur |= _jaDecrypted[19]; + BeaconCtx.beaconStartToRxDone = beaconDur + (rxLatencyFactorFromSF[SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE)-FASTEST_SF] * BeaconCtx.symbol_period_us); + + /* nowSlot: now vs previous beacon */ BeaconCtx.LastBeaconRx_us = BeaconCtx.rx_setup_at - BEACON_INTERVAL_us; unsigned us_since_last_beacon = _rx_timeout.read_us() - BeaconCtx.LastBeaconRx_us; @@ -1188,7 +1134,7 @@ while (useSlot < nowSlot) useSlot += BeaconCtx.periodicity_slots; - mac_printf("useSlot:%u nowSlot:%u ", useSlot, nowSlot); + mac_printf("beaconDur:0x%x (%u) useSlot:%u nowSlot:%u ", beaconDur, BeaconCtx.beaconStartToRxDone, useSlot, nowSlot); BeaconCtx.sendAt = BeaconCtx.LastBeaconRx_us + (useSlot * PING_SLOT_RESOLUTION_us); mac_printf("sendIn:%u\r\n", BeaconCtx.sendAt - _tx_timeout.read_us()); _tx_timeout.attach_us(send_callback, BeaconCtx.sendAt); @@ -1198,11 +1144,12 @@ BeaconCtx.guard = false; BeaconCtx.num_missed = 0; BeaconCtx.rx_precession_us = 0; + BeaconCtx.num_consecutive_ok = 0; BeaconCtx.last_BeaconRxTimerError_us = -PPM_BEACON_INTERVAL; BeaconCtx.known_working_BeaconRxTimerError_us = -PPM_BEACON_INTERVAL; /* first beacon reception needs to open for 30ms timing resolution */ BeaconCtx.Precess_symbols = ceil(TARGET_PRECESSION_us / BeaconCtx.symbol_period_us); - BeaconCtx.SymbolTimeout_us = PING_SLOT_RESOLUTION_us + (PPM_BEACON_INTERVAL * 2); // error unknown at start + BeaconCtx.SymbolTimeout_us = PING_SLOT_RESOLUTION_us + (PPM_BEACON_INTERVAL * 4); // error unknown at start BeaconCtx.nSymbsTimeout = BeaconCtx.SymbolTimeout_us / BeaconCtx.symbol_period_us; if (BeaconCtx.nSymbsTimeout < MIN_SYMBOL_TIMEOUT) BeaconCtx.nSymbsTimeout = MIN_SYMBOL_TIMEOUT; @@ -1216,8 +1163,9 @@ } else { + unsigned x; MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; - mac_printf("join-mic-fail\r\n"); + mac_printf("join-mic-fail size:%u\r\n", size); JoinRequestTrials = MaxJoinRequestTrials; // stop trying } LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm ); @@ -1575,13 +1523,12 @@ if (LoRaMacFlags.Bits.expecting_beacon) { float ourErrSecs = BeaconCtx.known_working_BeaconRxTimerError_us / 1000000.0; - BeaconCtx.guard = false; LoRaMacFlags.Bits.expecting_beacon = false; BeaconCtx.rx_setup_at += BEACON_INTERVAL_us + BeaconCtx.known_working_BeaconRxTimerError_us; BeaconCtx.rx_setup_at -= (PPM_BEACON_INTERVAL / 4); - us_to_nSymbTimeout(BeaconCtx.SymbolTimeout_us + (PPM_BEACON_INTERVAL / 2)); - mac_printf("beacon timeout ourErr:%f SymbTo:%u\r\n", ourErrSecs, BeaconCtx.nSymbsTimeout); + us_to_nSymbTimeout(BeaconCtx.SymbolTimeout_us + PPM_BEACON_INTERVAL); + mac_printf("beacon timeout ourErr:%f SymbTo:%u(%uus)\r\n", ourErrSecs, BeaconCtx.nSymbsTimeout, BeaconCtx.SymbolTimeout_us); BeaconCtx._timeout_rx.attach_us(&OnRxBeaconSetup, BeaconCtx.rx_setup_at); BeaconCtx._timeout_guard.attach_us(&guard_callback, BeaconCtx.rx_setup_at - BEACON_GUARD_us); @@ -1598,6 +1545,7 @@ BeaconCtx.sendAt += BeaconCtx.tx_slot_offset * PING_SLOT_RESOLUTION_us; _tx_timeout.attach_us(&send_callback, BeaconCtx.sendAt); + BeaconCtx.num_consecutive_ok = 0; } else { if (LoRaMacFlags.Bits.MlmeReq && ( MlmeConfirm.MlmeRequest == MLME_JOIN )) { /* no join accept received: join retry */