/*!
* \file RegionAU915.h
*
* \brief Region definition for AU915
*
* \copyright Revised BSD License, see section \ref LICENSE.
*
* \code
* ______ _
* / _____) _ | |
* ( (____ _____ ____ _| |_ _____ ____| |__
* \____ \| ___ | (_ _) ___ |/ ___) _ \
* _____) ) ____| | | || |_| ____( (___| | | |
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
* (C)2013-2017 Semtech
*
* ___ _____ _ ___ _ _____ ___ ___ ___ ___
* / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
* \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
* |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
* embedded.connectivity.solutions===============
*
* \endcode
*
* \author Miguel Luis ( Semtech )
*
* \author Gregory Cristian ( Semtech )
*
* \author Daniel Jaeckle ( STACKFORCE )
*
* \author Johannes Bruder ( STACKFORCE )
*
* \defgroup REGIONAU915 Region AU915
* Implementation according to LoRaWAN Specification v1.0.2.
* \{
*/
#ifndef __REGION_AU915_H__
#define __REGION_AU915_H__
#include "region/Region.h"
#define AU915_PING_SLOT_CHANNEL_FREQ 923300000
/*!
* LoRaMac maximum number of channels
*/
#define AU915_MAX_NB_CHANNELS 72
/*!
* Minimal datarate that can be used by the node
*/
#define AU915_TX_MIN_DATARATE DR_0
/*!
* Maximal datarate that can be used by the node
*/
#define AU915_TX_MAX_DATARATE DR_5
/*!
* Minimal datarate that can be used by the node
*/
#define AU915_RX_MIN_DATARATE DR_8
/*!
* Maximal datarate that can be used by the node
*/
#define AU915_RX_MAX_DATARATE DR_13
/*!
* Default datarate used by the node
*/
#define AU915_DEFAULT_DATARATE DR_0
/*!
* The minimum datarate which is used when the
* dwell time is limited.
*/
#define AU915_DWELL_LIMIT_DATARATE DR_2
/*!
* Minimal Rx1 receive datarate offset
*/
#define AU915_MIN_RX1_DR_OFFSET 0
/*!
* Maximal Rx1 receive datarate offset
*/
#define AU915_MAX_RX1_DR_OFFSET 3
/*!
* Default Rx1 receive datarate offset
*/
#define AU915_DEFAULT_RX1_DR_OFFSET 0
/*!
* Minimal Tx output power that can be used by the node
*/
#define AU915_MIN_TX_POWER TX_POWER_10
/*!
* Maximal Tx output power that can be used by the node
*/
#define AU915_MAX_TX_POWER TX_POWER_0
/*!
* Default Tx output power used by the node
*/
#define AU915_DEFAULT_TX_POWER TX_POWER_5
/*!
* Default uplink dwell time configuration
*/
#define AU915_DEFAULT_UPLINK_DWELL_TIME 0
/*!
* Default downlink dwell time configuration
*/
#define AU915_DEFAULT_DOWNLINK_DWELL_TIME 0
/*!
* Default Max EIRP
*/
#define AU915_DEFAULT_MAX_EIRP 30.0f
/*!
* Default antenna gain
*/
#define AU915_DEFAULT_ANTENNA_GAIN 0.0f
/*!
* ADR Ack limit
*/
#define AU915_ADR_ACK_LIMIT 64
/*!
* ADR Ack delay
*/
#define AU915_ADR_ACK_DELAY 32
/*!
* Enabled or disabled the duty cycle
*/
#define AU915_DUTY_CYCLE_ENABLED 0
/*!
* Maximum RX window duration
*/
#define AU915_MAX_RX_WINDOW 1000
/*!
* Receive delay 1
*/
#define AU915_RECEIVE_DELAY1 5000
/*!
* Receive delay 2
*/
#define AU915_RECEIVE_DELAY2 6000
/*!
* Join accept delay 1
*/
#define AU915_JOIN_ACCEPT_DELAY1 5000
/*!
* Join accept delay 2
*/
#define AU915_JOIN_ACCEPT_DELAY2 6000
/*!
* Maximum frame counter gap
*/
#define AU915_MAX_FCNT_GAP 16384
/*!
* Ack timeout
*/
#define AU915_ACKTIMEOUT 2000
/*!
* Random ack timeout limits
*/
#define AU915_ACK_TIMEOUT_RND 1000
/*!
* Second reception window channel frequency definition.
*/
#define AU915_RX_WND_2_FREQ 923300000
/*!
* Second reception window channel datarate definition.
*/
#define AU915_RX_WND_2_DR DR_8
/*
* CLASS B
*/
/*!
* Beacon frequency
*/
#define AU915_BEACON_CHANNEL_FREQ 923300000
/*!
* Beacon frequency channel stepwidth
*/
#define AU915_BEACON_CHANNEL_STEPWIDTH 600000
/*!
* Number of possible beacon channels
*/
#define AU915_BEACON_NB_CHANNELS 8
/*!
* Payload size of a beacon frame
*/
#define AU915_BEACON_SIZE 19
/*!
* Size of RFU 1 field
*/
#define AU915_RFU1_SIZE 3
/*!
* Size of RFU 2 field
*/
#define AU915_RFU2_SIZE 1
/*!
* Datarate of the beacon channel
*/
#define AU915_BEACON_CHANNEL_DR DR_10
/*!
* Bandwith of the beacon channel
*/
#define AU915_BEACON_CHANNEL_BW 2
/*!
* Ping slot channel datarate
*/
#define AU915_PING_SLOT_CHANNEL_DR DR_10
/*!
* LoRaMac maximum number of bands
*/
#define AU915_MAX_NB_BANDS 1
/*!
* Band 0 definition
* { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
*/
#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 0, 0 } // 100.0 %
/*!
* Defines the first channel for RX window 1 for US band
*/
#define AU915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 )
/*!
* Defines the last channel for RX window 1 for US band
*/
#define AU915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 )
/*!
* Defines the step width of the channels for RX window 1
*/
#define AU915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 )
/*!
* Data rates table definition
*/
static const uint8_t DataratesAU915[] = { 12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
/*!
* Bandwidths table definition in Hz
*/
static const uint32_t BandwidthsAU915[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
/*!
* Up/Down link data rates offset definition
*/
static const int8_t DatarateOffsetsAU915[7][6] =
{
{ DR_8 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_0
{ DR_9 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_1
{ DR_10, DR_9 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_2
{ DR_11, DR_10, DR_9 , DR_8 , DR_8 , DR_8 }, // DR_3
{ DR_12, DR_11, DR_10, DR_9 , DR_8 , DR_8 }, // DR_4
{ DR_13, DR_12, DR_11, DR_10, DR_9 , DR_8 }, // DR_5
{ DR_13, DR_13, DR_12, DR_11, DR_10, DR_9 }, // DR_6
};
/*!
* Maximum payload with respect to the datarate index. Cannot operate with repeater.
* The table is valid for the dwell time configuration of 0 for uplinks.
*/
static const uint8_t MaxPayloadOfDatarateDwell0AU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
/*!
* Maximum payload with respect to the datarate index. Can operate with repeater.
* The table is valid for the dwell time configuration of 0 for uplinks. The table provides
* repeater support.
*/
static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AU915[] = { 51, 51, 51, 115, 222, 222, 222, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
/*!
* Maximum payload with respect to the datarate index. Cannot operate with repeater.
* The table is valid for the dwell time configuration of 1 for uplinks.
*/
static const uint8_t MaxPayloadOfDatarateDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 129, 242, 242, 242, 242 };
/*!
* Maximum payload with respect to the datarate index. Can operate with repeater.
* The table is valid for the dwell time configuration of 1 for uplinks. The table provides
* repeater support.
*/
static const uint8_t MaxPayloadOfDatarateRepeaterDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 33, 119, 129, 242, 242, 242, 242 };
/*!
* \brief The function gets a value of a specific phy attribute.
*
* \param [IN] getPhy Pointer to the function parameters.
*
* \retval Returns a structure containing the PHY parameter.
*/
PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy );
/*!
* \brief Updates the last TX done parameters of the current channel.
*
* \param [IN] txDone Pointer to the function parameters.
*/
void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone );
/*!
* \brief Initializes the channels masks and the channels.
*
* \param [IN] type Sets the initialization type.
*/
void RegionAU915InitDefaults( InitDefaultsParams_t* params );
/*!
* \brief Returns a pointer to the internal context and its size.
*
* \param [OUT] params Pointer to the function parameters.
*
* \retval Points to a structure where the module store its non-volatile context.
*/
void* RegionAU915GetNvmCtx( GetPhyParams_t* params );
/*!
* \brief Verifies a parameter.
*
* \param [IN] verify Pointer to the function parameters.
*
* \param [IN] type Sets the initialization type.
*
* \retval Returns true, if the parameter is valid.
*/
bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute );
/*!
* \brief The function parses the input buffer and sets up the channels of the
* CF list.
*
* \param [IN] applyCFList Pointer to the function parameters.
*/
void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList );
/*!
* \brief Sets a channels mask.
*
* \param [IN] chanMaskSet Pointer to the function parameters.
*
* \retval Returns true, if the channels mask could be set.
*/
bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet );
/*!
* Computes the Rx window timeout and offset.
*
* \param [IN] datarate Rx window datarate index to be used
*
* \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame.
*
* \param [IN] rxError System maximum timing error of the receiver. In milliseconds
* The receiver will turn on in a [-rxError : +rxError] ms
* interval around RxOffset
*
* \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields.
*/
void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams );
/*!
* \brief Configuration of the RX windows.
*
* \param [IN] rxConfig Pointer to the function parameters.
*
* \param [OUT] datarate The datarate index which was set.
*
* \retval Returns true, if the configuration was applied successfully.
*/
bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate );
/*!
* \brief TX configuration.
*
* \param [IN] txConfig Pointer to the function parameters.
*
* \param [OUT] txPower The tx power index which was set.
*
* \param [OUT] txTimeOnAir The time-on-air of the frame.
*
* \retval Returns true, if the configuration was applied successfully.
*/
bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir );
/*!
* \brief The function processes a Link ADR Request.
*
* \param [IN] linkAdrReq Pointer to the function parameters.
*
* \retval Returns the status of the operation, according to the LoRaMAC specification.
*/
uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed );
/*!
* \brief The function processes a RX Parameter Setup Request.
*
* \param [IN] rxParamSetupReq Pointer to the function parameters.
*
* \retval Returns the status of the operation, according to the LoRaMAC specification.
*/
uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq );
/*!
* \brief The function processes a Channel Request.
*
* \param [IN] newChannelReq Pointer to the function parameters.
*
* \retval Returns the status of the operation, according to the LoRaMAC specification.
*/
uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq );
/*!
* \brief The function processes a TX ParamSetup Request.
*
* \param [IN] txParamSetupReq Pointer to the function parameters.
*
* \retval Returns the status of the operation, according to the LoRaMAC specification.
* Returns -1, if the functionality is not implemented. In this case, the end node
* shall not process the command.
*/
int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq );
/*!
* \brief The function processes a DlChannel Request.
*
* \param [IN] dlChannelReq Pointer to the function parameters.
*
* \retval Returns the status of the operation, according to the LoRaMAC specification.
*/
uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq );
/*!
* \brief Alternates the datarate of the channel for the join request.
*
* \param [IN] currentDr Current datarate.
*
* \retval Datarate to apply.
*/
int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type );
/*!
* \brief Calculates the back-off time.
*
* \param [IN] calcBackOff Pointer to the function parameters.
*/
//void RegionAU915CalcBackOff( CalcBackOffParams_t* calcBackOff );
/*!
* \brief Searches and set the next random available channel
*
* \param [OUT] channel Next channel to use for TX.
*
* \param [OUT] time Time to wait for the next transmission according to the duty
* cycle.
*
* \param [OUT] aggregatedTimeOff Updates the aggregated time off.
*
* \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]
*/
LoRaMacStatus_t RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff );
/*!
* \brief Adds a channel.
*
* \param [IN] channelAdd Pointer to the function parameters.
*
* \retval Status of the operation.
*/
LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd );
/*!
* \brief Removes a channel.
*
* \param [IN] channelRemove Pointer to the function parameters.
*
* \retval Returns true, if the channel was removed successfully.
*/
bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove );
/*!
* \brief Sets the radio into continuous wave mode.
*
* \param [IN] continuousWave Pointer to the function parameters.
*/
//////void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave );
/*!
* \brief Computes new datarate according to the given offset
*
* \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms
*
* \param [IN] dr Current datarate
*
* \param [IN] drOffset Offset to be applied
*
* \retval newDr Computed datarate.
*/
uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset );
/*!
* \brief Sets the radio into beacon reception mode
*
* \param [IN] rxBeaconSetup Pointer to the function parameters
*/
void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr );
/*! \} defgroup REGIONAU915 */
#endif // __REGION_AU915_H__
/*!
* \file RegionAU915.c
*
* \brief Region implementation for AU915
*
* \copyright Revised BSD License, see section \ref LICENSE.
*
* \code
* ______ _
* / _____) _ | |
* ( (____ _____ ____ _| |_ _____ ____| |__
* \____ \| ___ | (_ _) ___ |/ ___) _ \
* _____) ) ____| | | || |_| ____( (___| | | |
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
* (C)2013-2017 Semtech
*
* ___ _____ _ ___ _ _____ ___ ___ ___ ___
* / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
* \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
* |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
* embedded.connectivity.solutions===============
*
* \endcode
*
* \author Miguel Luis ( Semtech )
*
* \author Gregory Cristian ( Semtech )
*
* \author Daniel Jaeckle ( STACKFORCE )
*/
/**
******************************************************************************
*
* Portions COPYRIGHT 2020 STMicroelectronics
*
* @file RegionAU915.c
* @author MCD Application Team
* @brief Region implementation for AU915
******************************************************************************
*/
#include "radio.h"
#include "RegionAU915.h"
#include "RegionBaseUS.h"
// Definitions
#define CHANNELS_MASK_SIZE 6
// A mask to select only valid 500KHz channels
#define CHANNELS_MASK_500KHZ_MASK 0x00FF
/* The HYBRID_DEFAULT_MASKx define the enabled channels in Hybrid mode*/
/* Note: they can be redefined in lorawan_conf.h*/
#ifndef HYBRID_DEFAULT_MASK0 /*enabled channels from channel 15 down to channel 0*/
#define HYBRID_DEFAULT_MASK0 0x00FF /*channel 7 down to channel 0 enabled*/
#endif
#ifndef HYBRID_DEFAULT_MASK1 /*enabled channels from channel 31 down to channel 16*/
#define HYBRID_DEFAULT_MASK1 0x0000
#endif
#ifndef HYBRID_DEFAULT_MASK2 /*enabled channels from channel 47 down to channel 32*/
#define HYBRID_DEFAULT_MASK2 0x0000
#endif
#ifndef HYBRID_DEFAULT_MASK3 /*enabled channels from channel 63 down to channel 48*/
#define HYBRID_DEFAULT_MASK3 0x0000
#endif
#ifndef HYBRID_DEFAULT_MASK4 /*enabled channels from channel 71 down to channel 64*/
#define HYBRID_DEFAULT_MASK4 0x0000
#endif
#if defined( REGION_AU915 )
/*
* Non-volatile module context.
*/
static RegionNvmDataGroup1_t* RegionNvmGroup1;
static RegionNvmDataGroup2_t* RegionNvmGroup2;
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 ))
static Band_t* RegionBands;
#endif /* REGION_VERSION */
static bool VerifyRfFreq( uint32_t freq )
{
// Check radio driver support
if( Radio.CheckRfFrequency( freq ) == false )
{
return false;
}
// Rx frequencies
if( ( freq < AU915_FIRST_RX1_CHANNEL ) ||
( freq > AU915_LAST_RX1_CHANNEL ) ||
( ( ( freq - ( uint32_t ) AU915_FIRST_RX1_CHANNEL ) % ( uint32_t ) AU915_STEPWIDTH_RX1_CHANNEL ) != 0 ) )
{
return false;
}
// Tx frequencies for 125kHz
// Also includes the range for 500kHz channels
if( ( freq < 915200000 ) || ( freq > 927800000 ) )
{
return false;
}
return true;
}
static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen )
{
int8_t phyDr = DataratesAU915[datarate];
uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsAU915 );
return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true );
}
#endif /* REGION_AU915 */
PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy )
{
PhyParam_t phyParam = { 0 };
#if defined( REGION_AU915 )
switch( getPhy->Attribute )
{
case PHY_MIN_RX_DR:
{
if( getPhy->DownlinkDwellTime == 0)
{
phyParam.Value = AU915_RX_MIN_DATARATE;
}
else
{
phyParam.Value = AU915_DWELL_LIMIT_DATARATE;
}
break;
}
case PHY_MIN_TX_DR:
{
if( getPhy->UplinkDwellTime == 0)
{
phyParam.Value = AU915_TX_MIN_DATARATE;
}
else
{
phyParam.Value = AU915_DWELL_LIMIT_DATARATE;
}
break;
}
case PHY_DEF_TX_DR:
{
phyParam.Value = AU915_DEFAULT_DATARATE;
break;
}
case PHY_NEXT_LOWER_TX_DR:
{
RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams =
{
.CurrentDr = getPhy->Datarate,
.MaxDr = ( int8_t )AU915_TX_MAX_DATARATE,
.MinDr = ( int8_t )( ( getPhy->UplinkDwellTime == 0 ) ? AU915_TX_MIN_DATARATE : AU915_DWELL_LIMIT_DATARATE ),
.NbChannels = AU915_MAX_NB_CHANNELS,
.ChannelsMask = RegionNvmGroup2->ChannelsMask,
.Channels = RegionNvmGroup2->Channels,
};
phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams );
break;
}
case PHY_MAX_TX_POWER:
{
phyParam.Value = AU915_MAX_TX_POWER;
break;
}
case PHY_DEF_TX_POWER:
{
phyParam.Value = AU915_DEFAULT_TX_POWER;
break;
}
case PHY_DEF_ADR_ACK_LIMIT:
{
phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT;
break;
}
case PHY_DEF_ADR_ACK_DELAY:
{
phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY;
break;
}
case PHY_MAX_PAYLOAD:
{
if( getPhy->UplinkDwellTime == 0 )
{
phyParam.Value = MaxPayloadOfDatarateDwell0AU915[getPhy->Datarate];
}
else
{
phyParam.Value = MaxPayloadOfDatarateDwell1AU915[getPhy->Datarate];
}
break;
}
/* ST_WORKAROUND_BEGIN: Keep repeater feature */
case PHY_MAX_PAYLOAD_REPEATER:
{
if( getPhy->UplinkDwellTime == 0)
{
phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AU915[getPhy->Datarate];
}
else
{
phyParam.Value = MaxPayloadOfDatarateRepeaterDwell1AU915[getPhy->Datarate];
}
break;
}
/* ST_WORKAROUND_END */
case PHY_DUTY_CYCLE:
{
phyParam.Value = AU915_DUTY_CYCLE_ENABLED;
break;
}
case PHY_MAX_RX_WINDOW:
{
phyParam.Value = AU915_MAX_RX_WINDOW;
break;
}
case PHY_RECEIVE_DELAY1:
{
phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1;
break;
}
case PHY_RECEIVE_DELAY2:
{
phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2;
break;
}
case PHY_JOIN_ACCEPT_DELAY1:
{
phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1;
break;
}
case PHY_JOIN_ACCEPT_DELAY2:
{
phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2;
break;
}
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 ))
case PHY_MAX_FCNT_GAP:
{
phyParam.Value = REGION_COMMON_DEFAULT_MAX_FCNT_GAP;
break;
}
case PHY_ACK_TIMEOUT:
{
phyParam.Value = ( REGION_COMMON_DEFAULT_ACK_TIMEOUT + randr( -REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND, REGION_COMMON_DEFAULT_ACK_TIMEOUT_RND ) );
break;
}
#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 ))
case PHY_RETRANSMIT_TIMEOUT:
{
phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) );
break;
}
#endif /* REGION_VERSION */
case PHY_DEF_DR1_OFFSET:
{
phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET;
break;
}
case PHY_DEF_RX2_FREQUENCY:
{
phyParam.Value = AU915_RX_WND_2_FREQ;
break;
}
case PHY_DEF_RX2_DR:
{
phyParam.Value = AU915_RX_WND_2_DR;
break;
}
case PHY_CHANNELS_MASK:
{
phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask;
break;
}
case PHY_CHANNELS_DEFAULT_MASK:
{
phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask;
break;
}
case PHY_MAX_NB_CHANNELS:
{
phyParam.Value = AU915_MAX_NB_CHANNELS;
break;
}
case PHY_CHANNELS:
{
phyParam.Channels = RegionNvmGroup2->Channels;
break;
}
case PHY_DEF_UPLINK_DWELL_TIME:
{
phyParam.Value = AU915_DEFAULT_UPLINK_DWELL_TIME;
break;
}
case PHY_DEF_DOWNLINK_DWELL_TIME:
{
phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME;
break;
}
case PHY_DEF_MAX_EIRP:
{
phyParam.fValue = AU915_DEFAULT_MAX_EIRP;
break;
}
case PHY_DEF_ANTENNA_GAIN:
{
phyParam.fValue = AU915_DEFAULT_ANTENNA_GAIN;
break;
}
case PHY_BEACON_CHANNEL_FREQ:
{
phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel,
AU915_BEACON_CHANNEL_FREQ,
AU915_BEACON_CHANNEL_STEPWIDTH );
break;
}
case PHY_BEACON_FORMAT:
{
phyParam.BeaconFormat.BeaconSize = AU915_BEACON_SIZE;
phyParam.BeaconFormat.Rfu1Size = AU915_RFU1_SIZE;
phyParam.BeaconFormat.Rfu2Size = AU915_RFU2_SIZE;
break;
}
case PHY_BEACON_CHANNEL_DR:
{
phyParam.Value = AU915_BEACON_CHANNEL_DR;
break;
}
case PHY_BEACON_NB_CHANNELS:
{
phyParam.Value = AU915_BEACON_NB_CHANNELS;
break;
}
case PHY_PING_SLOT_CHANNEL_FREQ:
{
phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel,
AU915_PING_SLOT_CHANNEL_FREQ,
AU915_BEACON_CHANNEL_STEPWIDTH );
break;
}
case PHY_PING_SLOT_CHANNEL_DR:
{
phyParam.Value = AU915_PING_SLOT_CHANNEL_DR;
break;
}
case PHY_PING_SLOT_NB_CHANNELS:
{
phyParam.Value = AU915_BEACON_NB_CHANNELS;
break;
}
case PHY_SF_FROM_DR:
{
phyParam.Value = DataratesAU915[getPhy->Datarate];
break;
}
case PHY_BW_FROM_DR:
{
phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsAU915 );
break;
}
default:
{
break;
}
}
#endif /* REGION_AU915 */
return phyParam;
}
void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone )
{
#if defined( REGION_AU915 )
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 ))
RegionCommonSetBandTxDone( &RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txDone->Channel].Band],
txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp );
#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 ))
RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band],
txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp );
#endif /* REGION_VERSION */
#endif /* REGION_AU915 */
}
void RegionAU915InitDefaults( InitDefaultsParams_t* params )
{
#if defined( REGION_AU915 )
Band_t bands[AU915_MAX_NB_BANDS] =
{
AU915_BAND0
};
switch( params->Type )
{
case INIT_TYPE_DEFAULTS:
{
if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) )
{
return;
}
RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1;
RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2;
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 ))
RegionBands = (Band_t*) params->Bands;
#endif /* REGION_VERSION */
// Initialize 8 bit channel groups index
RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0;
// Initialize the join trials counter
RegionNvmGroup1->JoinTrialsCounter = 0;
// Default bands
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 ))
memcpy1( ( uint8_t* )RegionNvmGroup1->Bands, ( uint8_t* )bands, sizeof( Band_t ) * AU915_MAX_NB_BANDS );
#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 ))
memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * AU915_MAX_NB_BANDS );
#endif /* REGION_VERSION */
// Channels
for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ )
{
// 125 kHz channels
RegionNvmGroup2->Channels[i].Frequency = 915200000 + i * 200000;
RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
RegionNvmGroup2->Channels[i].Band = 0;
}
for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ )
{
// 500 kHz channels
RegionNvmGroup2->Channels[i].Frequency = 915900000 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1600000;
RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_6 << 4 ) | DR_6;
RegionNvmGroup2->Channels[i].Band = 0;
}
// Initialize channels default mask
/* ST_WORKAROUND_BEGIN: Hybrid mode */
#if ( HYBRID_ENABLED == 1 )
RegionNvmGroup2->ChannelsDefaultMask[0] = HYBRID_DEFAULT_MASK0;
RegionNvmGroup2->ChannelsDefaultMask[1] = HYBRID_DEFAULT_MASK1;
RegionNvmGroup2->ChannelsDefaultMask[2] = HYBRID_DEFAULT_MASK2;
RegionNvmGroup2->ChannelsDefaultMask[3] = HYBRID_DEFAULT_MASK3;
RegionNvmGroup2->ChannelsDefaultMask[4] = HYBRID_DEFAULT_MASK4;
RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000;
#else
RegionNvmGroup2->ChannelsDefaultMask[0] = 0x00FF;
RegionNvmGroup2->ChannelsDefaultMask[1] = 0x0000;
RegionNvmGroup2->ChannelsDefaultMask[2] = 0x0000;
RegionNvmGroup2->ChannelsDefaultMask[3] = 0x0000;
RegionNvmGroup2->ChannelsDefaultMask[4] = 0x0000;
RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000;
#endif /* HYBRID_ENABLED == 1 */
/* ST_WORKAROUND_END */
// Copy channels default mask
RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE );
// Copy into channels mask remaining
RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE );
break;
}
case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS:
{
// Intentional fallthrough
}
case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS:
{
// Copy channels default mask
RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE );
for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ )
{ // Copy-And the channels mask
RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i];
}
break;
}
default:
{
break;
}
}
#endif /* REGION_AU915 */
}
bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
{
#if defined( REGION_AU915 )
switch( phyAttribute )
{
case PHY_FREQUENCY:
{
return VerifyRfFreq( verify->Frequency );
}
case PHY_TX_DR:
case PHY_DEF_TX_DR:
{
if( verify->DatarateParams.UplinkDwellTime == 0 )
{
return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE );
}
else
{
return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_DATARATE, AU915_TX_MAX_DATARATE );
}
}
case PHY_RX_DR:
{
if( verify->DatarateParams.UplinkDwellTime == 0 )
{
return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE );
}
else
{
return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_DATARATE, AU915_RX_MAX_DATARATE );
}
}
case PHY_DEF_TX_POWER:
case PHY_TX_POWER:
{
// Remark: switched min and max!
return RegionCommonValueInRange( verify->TxPower, AU915_MAX_TX_POWER, AU915_MIN_TX_POWER );
}
case PHY_DUTY_CYCLE:
{
return AU915_DUTY_CYCLE_ENABLED;
}
default:
return false;
}
#else
return false;
#endif /* REGION_AU915 */
}
void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList )
{
#if defined( REGION_AU915 )
// Size of the optional CF list must be 16 byte
if( applyCFList->Size != 16 )
{
return;
}
// Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields
if( applyCFList->Payload[15] != 0x01 )
{
return;
}
// ChMask0 - ChMask4 must be set (every ChMask has 16 bit)
for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 4; chMaskItr++, cntPayload+=2 )
{
RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]);
RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8);
if( chMaskItr == 4 )
{
RegionNvmGroup2->ChannelsMask[chMaskItr] = RegionNvmGroup2->ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK;
}
// Set the channel mask to the remaining
RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr];
}
#endif /* REGION_AU915 */
}
bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
{
#if defined( REGION_AU915 )
switch( chanMaskSet->ChannelsMaskType )
{
case CHANNELS_MASK:
{
RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE );
RegionNvmGroup2->ChannelsDefaultMask[4] = RegionNvmGroup2->ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK;
RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000;
for( uint8_t i = 0; i < 6; i++ )
{ // Copy-And the channels mask
RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i];
}
break;
}
case CHANNELS_DEFAULT_MASK:
{
RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE );
break;
}
default:
return false;
}
return true;
#else
return false;
#endif /* REGION_AU915 */
}
void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
{
#if defined( REGION_AU915 )
uint32_t tSymbolInUs = 0;
// Get the datarate, perform a boundary check
rxConfigParams->Datarate = MIN( datarate, AU915_RX_MAX_DATARATE );
rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsAU915 );
tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesAU915[rxConfigParams->Datarate], BandwidthsAU915[rxConfigParams->Datarate] );
RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
#endif /* REGION_AU915 */
}
bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
{
#if defined( REGION_AU915 )
int8_t dr = rxConfig->Datarate;
uint8_t maxPayload = 0;
int8_t phyDr = 0;
uint32_t frequency = rxConfig->Frequency;
if( Radio.GetStatus( ) != RF_IDLE )
{
return false;
}
if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
{
// Apply window 1 frequency
frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL;
}
// Read the physical datarate from the datarates table
phyDr = DataratesAU915[dr];
Radio.SetChannel( frequency );
// Radio configuration
Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
/* ST_WORKAROUND_BEGIN: Keep repeater feature */
if( rxConfig->RepeaterSupport == true )
{
maxPayload = MaxPayloadOfDatarateRepeaterDwell0AU915[dr];
}
else
{
maxPayload = MaxPayloadOfDatarateDwell0AU915[dr];
}
Radio.SetMaxPayloadLength( MODEM_LORA, maxPayload + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE );
/* ST_WORKAROUND_END */
/* ST_WORKAROUND_BEGIN: Print Rx config */
RegionCommonRxConfigPrint(rxConfig->RxSlot, frequency, dr);
/* ST_WORKAROUND_END */
*datarate = (uint8_t) dr;
return true;
#else
return false;
#endif /* REGION_AU915 */
}
bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
{
#if defined( REGION_AU915 )
int8_t phyDr = DataratesAU915[txConfig->Datarate];
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 ))
int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower );
#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 ))
int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower );
#endif /* REGION_VERSION */
uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsAU915 );
int8_t phyTxPower = 0;
// Calculate physical TX power
phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
// Setup the radio frequency
Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency );
Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
/* ST_WORKAROUND_BEGIN: Print Tx config */
RegionCommonTxConfigPrint(RegionNvmGroup2->Channels[txConfig->Channel].Frequency, txConfig->Datarate);
/* ST_WORKAROUND_END */
// Setup maximum payload length of the radio driver
Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen );
// Update time-on-air
*txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen );
*txPower = txPowerLimited;
return true;
#else
return false;
#endif /* REGION_AU915 */
}
uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
{
uint8_t status = 0x07;
#if defined( REGION_AU915 )
RegionCommonLinkAdrParams_t linkAdrParams = { 0 };
uint8_t nextIndex = 0;
uint8_t bytesProcessed = 0;
uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
GetPhyParams_t getPhy;
PhyParam_t phyParam;
RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
// Initialize local copy of channels mask
RegionCommonChanMaskCopy( channelsMask, RegionNvmGroup2->ChannelsMask, 6 );
while( bytesProcessed < linkAdrReq->PayloadSize )
{
nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
if( nextIndex == 0 )
break; // break loop, since no more request has been found
// Update bytes processed
bytesProcessed += nextIndex;
// Revert status, as we only check the last ADR request for the channel mask KO
status = 0x07;
if( linkAdrParams.ChMaskCtrl == 6 )
{
// Enable all 125 kHz channels
channelsMask[0] = 0xFFFF;
channelsMask[1] = 0xFFFF;
channelsMask[2] = 0xFFFF;
channelsMask[3] = 0xFFFF;
// Apply chMask to channels 64 to 71
channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK;
}
else if( linkAdrParams.ChMaskCtrl == 7 )
{
// Disable all 125 kHz channels
channelsMask[0] = 0x0000;
channelsMask[1] = 0x0000;
channelsMask[2] = 0x0000;
channelsMask[3] = 0x0000;
// Apply chMask to channels 64 to 71
channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK;
}
else if( linkAdrParams.ChMaskCtrl == 5 )
{
// Start value for comparison
uint8_t bitMask = 1;
// cntChannelMask for channelsMask[0] until channelsMask[3]
uint8_t cntChannelMask = 0;
// i will be 1, 2, 3, ..., 7
for( uint8_t i = 0; i <= 7; i++ )
{
// 8 MSBs of ChMask are RFU
// Checking if the ChMask is set, then true
if( ( ( linkAdrParams.ChMask & 0x00FF ) & ( bitMask << i ) ) != 0 )
{
if( ( i % 2 ) == 0 )
{
// Enable a bank of 8 125kHz channels, 8 LSBs
channelsMask[cntChannelMask] |= 0x00FF;
// Enable the corresponding 500kHz channel
channelsMask[4] |= ( bitMask << i );
}
else
{
// Enable a bank of 8 125kHz channels, 8 MSBs
channelsMask[cntChannelMask] |= 0xFF00;
// Enable the corresponding 500kHz channel
channelsMask[4] |= ( bitMask << i );
// cntChannelMask increment for uneven i
cntChannelMask++;
}
}
// ChMask is not set
else
{
if( ( i % 2 ) == 0 )
{
// Disable a bank of 8 125kHz channels, 8 LSBs
channelsMask[cntChannelMask] &= 0xFF00;
// Disable the corresponding 500kHz channel
channelsMask[4] &= ~( bitMask << i );
}
else
{
// Enable a bank of 8 125kHz channels, 8 MSBs
channelsMask[cntChannelMask] &= 0x00FF;
// Disable the corresponding 500kHz channel
channelsMask[4] &= ~( bitMask << i );
// cntChannelMask increment for uneven i
cntChannelMask++;
}
}
}
}
else
{
channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask;
}
}
// FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels
if( ( linkAdrParams.Datarate < DR_6 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) )
{
status &= 0xFE; // Channel mask KO
}
// Get the minimum possible datarate
getPhy.Attribute = PHY_MIN_TX_DR;
getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
phyParam = RegionAU915GetPhyParam( &getPhy );
linkAdrVerifyParams.Status = status;
linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
linkAdrVerifyParams.NbChannels = AU915_MAX_NB_CHANNELS;
linkAdrVerifyParams.ChannelsMask = channelsMask;
linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
linkAdrVerifyParams.MaxDatarate = AU915_TX_MAX_DATARATE;
linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels;
linkAdrVerifyParams.MinTxPower = AU915_MIN_TX_POWER;
linkAdrVerifyParams.MaxTxPower = AU915_MAX_TX_POWER;
linkAdrVerifyParams.Version = linkAdrReq->Version;
// Verify the parameters and update, if necessary
status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
// Update channelsMask if everything is correct
if( status == 0x07 )
{
// Copy Mask
RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, channelsMask, 6 );
RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0];
RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1];
RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2];
RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3];
RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4];
RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5];
}
// Update status variables
*drOut = linkAdrParams.Datarate;
*txPowOut = linkAdrParams.TxPower;
*nbRepOut = linkAdrParams.NbRep;
*nbBytesParsed = bytesProcessed;
#endif /* REGION_AU915 */
return status;
}
uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
{
uint8_t status = 0x07;
#if defined( REGION_AU915 )
// Verify radio frequency
if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
{
status &= 0xFE; // Channel frequency KO
}
// Verify datarate
if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ) == false )
{
status &= 0xFD; // Datarate KO
}
if( ( rxParamSetupReq->Datarate == DR_7 ) ||
( rxParamSetupReq->Datarate > DR_13 ) )
{
status &= 0xFD; // Datarate KO
}
// Verify datarate offset
if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AU915_MIN_RX1_DR_OFFSET, AU915_MAX_RX1_DR_OFFSET ) == false )
{
status &= 0xFB; // Rx1DrOffset range KO
}
#endif /* REGION_AU915 */
return status;
}
uint8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq )
{
// Do not accept the request
return -1;
}
int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
{
// Accept the request
return 0;
}
uint8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq )
{
// Do not accept the request
return -1;
}
int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type )
{
#if defined( REGION_AU915 )
// Alternates the data rate according to the channel sequence:
// Eight times a 125kHz DR_2 and then one 500kHz DR_6 channel
if( type == ALTERNATE_DR )
{
RegionNvmGroup1->JoinTrialsCounter++;
}
else
{
RegionNvmGroup1->JoinTrialsCounter--;
}
if( RegionNvmGroup1->JoinTrialsCounter % 9 == 0 )
{
// Use DR_6 every 9th times.
currentDr = DR_6;
}
else
{
currentDr = DR_2;
}
return currentDr;
#else
return -1;
#endif /* REGION_AU915 */
}
LoRaMacStatus_t RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
{
#if defined( REGION_AU915 )
uint8_t nbEnabledChannels = 0;
uint8_t nbRestrictedChannels = 0;
uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 };
RegionCommonIdentifyChannelsParam_t identifyChannelsParam;
RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams;
LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND;
// Count 125kHz channels
if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, 4 ) == 0 )
{ // Reactivate default channels
RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, 4 );
RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0;
}
// Check other channels
if( nextChanParams->Datarate >= DR_6 )
{
if( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 )
{
RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4];
}
}
// Search how many channels are enabled
countChannelsParams.Joined = nextChanParams->Joined;
countChannelsParams.Datarate = nextChanParams->Datarate;
countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining;
countChannelsParams.Channels = RegionNvmGroup2->Channels;
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 ))
countChannelsParams.Bands = RegionNvmGroup1->Bands;
#elif (defined( REGION_VERSION ) && ( REGION_VERSION == 0x02010001 ))
countChannelsParams.Bands = RegionBands;
#endif /* REGION_VERSION */
countChannelsParams.MaxNbChannels = AU915_MAX_NB_CHANNELS;
countChannelsParams.JoinChannels = NULL;
identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff;
identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx;
identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled;
identifyChannelsParam.MaxBands = AU915_MAX_NB_BANDS;
identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp;
identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest;
identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen );
identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams;
status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels,
&nbEnabledChannels, &nbRestrictedChannels, time );
if( status == LORAMAC_STATUS_OK )
{
if( nextChanParams->Joined == true )
{
// Choose randomly on of the remaining channels
*channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
}
else
{
// For rapid network acquisition in mixed gateway channel plan environments, the device
// follow a random channel selection sequence. It probes alternating one out of a
// group of eight 125 kHz channels followed by probing one 500 kHz channel each pass.
// Each time a 125 kHz channel will be selected from another group.
// 125kHz Channels (0 - 63) DR2
if( nextChanParams->Datarate == DR_2 )
{
if( RegionBaseUSComputeNext125kHzJoinChannel( ( uint16_t* ) RegionNvmGroup1->ChannelsMaskRemaining,
&RegionNvmGroup1->JoinChannelGroupsCurrentIndex, channel ) == LORAMAC_STATUS_PARAMETER_INVALID )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
}
// 500kHz Channels (64 - 71) DR6
else
{
// Choose the next available channel
uint8_t i = 0;
while( ( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) & ( 1 << i ) ) == 0 )
{
i++;
}
*channel = 64 + i;
}
}
// Disable the channel in the mask
RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS );
}
return status;
#else
return LORAMAC_STATUS_NO_CHANNEL_FOUND;
#endif /* REGION_AU915 */
}
LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
#if (defined( REGION_VERSION ) && ( REGION_VERSION == 0x01010003 ))
void RegionAU915SetContinuousWave( ContinuousWaveParams_t* continuousWave )
{
#if defined( REGION_AU915 )
int8_t txPowerLimited = RegionCommonLimitTxPower( continuousWave->TxPower, RegionNvmGroup1->Bands[RegionNvmGroup2->Channels[continuousWave->Channel].Band].TxMaxPower );
int8_t phyTxPower = 0;
uint32_t frequency = RegionNvmGroup2->Channels[continuousWave->Channel].Frequency;
// Calculate physical TX power
phyTxPower = RegionCommonComputeTxPower( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain );
Radio.SetTxContinuousWave( frequency, phyTxPower, continuousWave->Timeout );
#endif /* REGION_AU915 */
}
#endif /* REGION_VERSION */
uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
{
#if defined( REGION_AU915 )
int8_t datarate = DatarateOffsetsAU915[dr][drOffset];
if( datarate < 0 )
{
if( downlinkDwellTime == 0 )
{
datarate = AU915_TX_MIN_DATARATE;
}
else
{
datarate = AU915_DWELL_LIMIT_DATARATE;
}
}
return datarate;
#else
return 0;
#endif /* REGION_AU915 */
}
void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr )
{
#if defined( REGION_AU915 )
RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
regionCommonRxBeaconSetup.Datarates = DataratesAU915;
regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
regionCommonRxBeaconSetup.BeaconSize = AU915_BEACON_SIZE;
regionCommonRxBeaconSetup.BeaconDatarate = AU915_BEACON_CHANNEL_DR;
regionCommonRxBeaconSetup.BeaconChannelBW = AU915_BEACON_CHANNEL_BW;
regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
// Store downlink datarate
*outDr = AU915_BEACON_CHANNEL_DR;
#endif /* REGION_AU915 */
}
#define LSM_RF_CHANNEL_SWITCH 1
/**
******************************************************************************
* @file stm32wlxx_nucleo_radio.c
* @author MCD Application Team
* @brief This file provides set of firmware functions to manage:
* - RF circuitry available on STM32WLXX-Nucleo
* Kit from STMicroelectronics
******************************************************************************
* @attention
*
* Copyright (c) 2020-2021 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32wlxx_nucleo_radio.h"
/** @addtogroup BSP
* @{
*/
/** @addtogroup STM32WLXX_NUCLEO
* @{
*/
/** @addtogroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL
* @brief This file provides set of firmware functions to Radio switch
* available on STM32WLXX-Nucleo Kit from STMicroelectronics.
* @{
*/
/** @addtogroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_Exported_Functions
* @{
*/
/**
* @brief Init Radio Switch
* @retval BSP status
*/
int32_t BSP_RADIO_Init(void)
{
/* USER CODE END RBI_Init_1 */
#if defined(USE_BSP_DRIVER)
/* code generated by MX does not support BSP */
/* In order to use BSP driver, add the correspondent files in the IDE workspace */
/* and define USE_BSP_DRIVER in the preprocessor definitions or in platform.h */
return BSP_RADIO_Init();
#else
/* should be calling BSP_RADIO_Init() but not supported by MX*/
GPIO_InitTypeDef gpio_init_structure = {0};
#if LSM_RF_CHANNEL_SWITCH
/* Enable the Radio Switch Clock */
RF_SW_CTRL1_GPIO_CLK_ENABLE();
RF_SW_CTRL2_GPIO_CLK_ENABLE();
/* Configure the Radio Switch pin */
gpio_init_structure.Pin = RF_SW_CTRL1_PIN;
gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_structure.Pull = GPIO_NOPULL;
gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(RF_SW_CTRL1_GPIO_PORT, &gpio_init_structure);
gpio_init_structure.Pin = RF_SW_CTRL2_PIN;
HAL_GPIO_Init(RF_SW_CTRL2_GPIO_PORT, &gpio_init_structure);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
#else /* LSM_RF_CHANNEL_SWITCH */
/* Enable the Radio Switch Clock */
RF_SW_CTRL3_GPIO_CLK_ENABLE();
/* Configure the Radio Switch pin */
gpio_init_structure.Pin = RF_SW_CTRL1_PIN;
gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_structure.Pull = GPIO_NOPULL;
gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(RF_SW_CTRL1_GPIO_PORT, &gpio_init_structure);
gpio_init_structure.Pin = RF_SW_CTRL2_PIN;
HAL_GPIO_Init(RF_SW_CTRL2_GPIO_PORT, &gpio_init_structure);
gpio_init_structure.Pin = RF_SW_CTRL3_PIN;
HAL_GPIO_Init(RF_SW_CTRL3_GPIO_PORT, &gpio_init_structure);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET);
#endif /* LSM_RF_CHANNEL_SWITCH */
return 0;
#endif /* USE_BSP_DRIVER */
/* USER CODE BEGIN RBI_Init_3 */
/* USER CODE END RBI_Init_3 */
}
/**
* @brief DeInit Radio Switch
* @retval BSP status
*/
int32_t BSP_RADIO_DeInit(void)
{
/* USER CODE BEGIN RBI_DeInit_1 */
/* USER CODE END RBI_DeInit_1 */
#if defined(USE_BSP_DRIVER)
/* code generated by MX does not support BSP */
/* In order to use BSP driver, add the correspondent files in the IDE workspace */
/* and define USE_BSP_DRIVER in the preprocessor definitions or in platform.h */
return BSP_RADIO_DeInit();
#else
#if LSM_RF_CHANNEL_SWITCH
RF_SW_CTRL1_GPIO_CLK_ENABLE();
RF_SW_CTRL2_GPIO_CLK_ENABLE();
/* Turn off switch */
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
/* DeInit the Radio Switch pin */
HAL_GPIO_DeInit(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN);
HAL_GPIO_DeInit(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN);
#else /* LSM_RF_CHANNEL_SWITCH */
RF_SW_CTRL3_GPIO_CLK_ENABLE();
/* Turn off switch */
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET);
/* DeInit the Radio Switch pin */
HAL_GPIO_DeInit(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN);
HAL_GPIO_DeInit(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN);
HAL_GPIO_DeInit(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN);
#endif /* LSM_RF_CHANNEL_SWITCH */
return 0;
#endif /* USE_BSP_DRIVER */
/* USER CODE BEGIN RBI_DeInit_3 */
/* USER CODE END RBI_DeInit_3 */
}
/**
* @brief Configure Radio Switch.
* @param Config: Specifies the Radio RF switch path to be set.
* This parameter can be one of following parameters:
* @arg RADIO_SWITCH_OFF
* @arg RADIO_SWITCH_RX
* @arg RADIO_SWITCH_RFO_LP
* @arg RADIO_SWITCH_RFO_HP
* @retval BSP status
*/
int32_t BSP_RADIO_ConfigRFSwitch(BSP_RADIO_Switch_TypeDef Config)
{
/* USER CODE BEGIN RBI_ConfigRFSwitch_1 */
/* USER CODE END RBI_ConfigRFSwitch_1 */
#if defined(USE_BSP_DRIVER)
/* code generated by MX does not support BSP */
/* In order to use BSP driver, add the correspondent files in the IDE workspace */
/* and define USE_BSP_DRIVER in the preprocessor definitions or in platform.h */
return BSP_RADIO_ConfigRFSwitch((BSP_RADIO_Switch_TypeDef) Config);
#else
switch (Config)
{
case RADIO_SWITCH_OFF:
{
#if LSM_RF_CHANNEL_SWITCH
/* Turn off switch */
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
#else /* LSM_RF_CHANNEL_SWITCH */
/* Turn off switch */
HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
#endif /* LSM_RF_CHANNEL_SWITCH */
break;
}
case RADIO_SWITCH_RX:
{
#if LSM_RF_CHANNEL_SWITCH
/*Turns On in Rx Mode the RF Switch */
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
#else /* LSM_RF_CHANNEL_SWITCH */
/*Turns On in Rx Mode the RF Switch */
HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);
#endif /* LSM_RF_CHANNEL_SWITCH */
break;
}
case RADIO_SWITCH_RFO_LP:
{
#if LSM_RF_CHANNEL_SWITCH
/*Turns On in Tx Low Power the RF Switch */
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET);
#else /* LSM_RF_CHANNEL_SWITCH */
/*Turns On in Tx Low Power the RF Switch */
HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET);
#endif /* LSM_RF_CHANNEL_SWITCH */
break;
}
case RADIO_SWITCH_RFO_HP:
{
#if LSM_RF_CHANNEL_SWITCH
/*Turns On in Tx High Power the RF Switch */
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET);
#else /* LSM_RF_CHANNEL_SWITCH */
/*Turns On in Tx High Power the RF Switch */
HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET);
#endif /* LSM_RF_CHANNEL_SWITCH */
break;
}
default:
break;
}
return 0;
#endif /* USE_BSP_DRIVER */
/* USER CODE BEGIN RBI_ConfigRFSwitch_3 */
/* USER CODE END RBI_ConfigRFSwitch_3 */
}
/**
* @brief Return Board Configuration
* @retval
* RADIO_CONF_RFO_LP_HP
* RADIO_CONF_RFO_LP
* RADIO_CONF_RFO_HP
*/
int32_t BSP_RADIO_GetTxConfig(void)
{
/* USER CODE BEGIN RBI_GetTxConfig_1 */
/* USER CODE END RBI_GetTxConfig_1 */
#if defined(USE_BSP_DRIVER)
/* Important note: BSP code is board dependent
* STM32WL_Nucleo code can be found
* either in STM32CubeWL package under Drivers/BSP/STM32WLxx_Nucleo/
* or at https://github.com/STMicroelectronics/STM32CubeWL/tree/main/Drivers/BSP/STM32WLxx_Nucleo/
* 1/ For User boards, the BSP/STM32WLxx_Nucleo/ directory can be copied and replaced in the project. The copy must then be updated depending:
* on board RF switch configuration (pin control, number of port etc)
* on TCXO configuration
* on DC/DC configuration
* on maximum output power that the board can deliver*/
return BSP_RADIO_GetTxConfig();
#else
/* 2/ Or implement RBI_GetTxConfig here */
int32_t retcode = RADIO_CONF_RFO_LP_HP;
/* USER CODE BEGIN RBI_GetTxConfig_2 */
//#warning user to provide its board code or to call his board driver functions
/* USER CODE END RBI_GetTxConfig_2 */
return retcode;
#endif /* USE_BSP_DRIVER */
}
/**
* @brief Get If TCXO is to be present on board
* @note never remove called by MW,
* @retval
* RADIO_CONF_TCXO_NOT_SUPPORTED
* RADIO_CONF_TCXO_SUPPORTED
*/
int32_t BSP_RADIO_IsTCXO(void)
{
/* USER CODE BEGIN RBI_IsTCXO_1 */
/* USER CODE END RBI_IsTCXO_1 */
#if defined(USE_BSP_DRIVER)
/* Important note: BSP code is board dependent
* STM32WL_Nucleo code can be found
* either in STM32CubeWL package under Drivers/BSP/STM32WLxx_Nucleo/
* or at https://github.com/STMicroelectronics/STM32CubeWL/tree/main/Drivers/BSP/STM32WLxx_Nucleo/
* 1/ For User boards, the BSP/STM32WLxx_Nucleo/ directory can be copied and replaced in the project. The copy must then be updated depending:
* on board RF switch configuration (pin control, number of port etc)
* on TCXO configuration
* on DC/DC configuration
* on maximum output power that the board can deliver*/
return BSP_RADIO_IsTCXO();
#else
/* 2/ Or implement RBI_IsTCXO here */
int32_t retcode = RADIO_CONF_TCXO_SUPPORTED;
/* USER CODE BEGIN RBI_IsTCXO_2 */
//#warning user to provide its board code or to call his board driver functions
/* USER CODE END RBI_IsTCXO_2 */
return retcode;
#endif /* USE_BSP_DRIVER */
}
/**
* @brief Get If DCDC is to be present on board
* @note never remove called by MW,
* @retval
* RADIO_CONF_DCDC_NOT_SUPPORTED
* RADIO_CONF_DCDC_SUPPORTED
*/
int32_t BSP_RADIO_IsDCDC(void)
{
/* USER CODE BEGIN RBI_IsDCDC_1 */
/* USER CODE END RBI_IsDCDC_1 */
#if defined(USE_BSP_DRIVER)
/* Important note: BSP code is board dependent
* STM32WL_Nucleo code can be found
* either in STM32CubeWL package under Drivers/BSP/STM32WLxx_Nucleo/
* or at https://github.com/STMicroelectronics/STM32CubeWL/tree/main/Drivers/BSP/STM32WLxx_Nucleo/
* 1/ For User boards, the BSP/STM32WLxx_Nucleo/ directory can be copied and replaced in the project. The copy must then be updated depending:
* on board RF switch configuration (pin control, number of port etc)
* on TCXO configuration
* on DC/DC configuration
* on maximum output power that the board can deliver*/
return BSP_RADIO_IsDCDC();
#else
/* 2/ Or implement RBI_IsDCDC here */
int32_t retcode = RADIO_CONF_DCDC_SUPPORTED;
/* USER CODE BEGIN RBI_IsDCDC_2 */
//#warning user to provide its board code or to call his board driver functions
/* USER CODE END RBI_IsDCDC_2 */
return retcode;
#endif /* USE_BSP_DRIVER */
}
/**
* @brief Return RF Output Max Power Configuration
* @retval
* RADIO_CONF_RFO_LP_MAX_15_dBm for LP mode
* RADIO_CONF_RFO_HP_MAX_22_dBm for HP mode
*/
int32_t BSP_RADIO_GetRFOMaxPowerConfig(BSP_RADIO_RFOMaxPowerConfig_TypeDef Config)
{
/* USER CODE BEGIN RBI_GetRFOMaxPowerConfig_1 */
/* USER CODE END RBI_GetRFOMaxPowerConfig_1 */
#if defined(USE_BSP_DRIVER)
/* Important note: BSP code is board dependent
* STM32WL_Nucleo code can be found
* either in STM32CubeWL package under Drivers/BSP/STM32WLxx_Nucleo/
* or at https://github.com/STMicroelectronics/STM32CubeWL/tree/main/Drivers/BSP/STM32WLxx_Nucleo/
* 1/ For User boards, the BSP/STM32WLxx_Nucleo/ directory can be copied and replaced in the project. The copy must then be updated depending:
* on board RF switch configuration (pin control, number of port etc)
* on TCXO configuration
* on DC/DC configuration
* on maximum output power that the board can deliver*/
return BSP_RADIO_GetRFOMaxPowerConfig((BSP_RADIO_RFOMaxPowerConfig_TypeDef) Config);
#else
/* 2/ Or implement RBI_RBI_GetRFOMaxPowerConfig here */
int32_t ret = 0;
/* USER CODE BEGIN RBI_GetRFOMaxPowerConfig_2 */
//#warning user to provide its board code or to call his board driver functions
if (Config == RADIO_RFO_LP_MAXPOWER)
{
ret = 15; /*dBm*/
}
else
{
ret = 22; /*dBm*/
}
/* USER CODE END RBI_GetRFOMaxPowerConfig_2 */
return ret;
#endif /* USE_BSP_DRIVER */
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
static void OnRxData(LmHandlerAppData_t *appData, LmHandlerRxParams_t *params)
{
/* USER CODE BEGIN OnRxData_1 */
uint8_t RxPort = 0;
//MIGUEL
if (params->IsMcpsIndication == MCPS_CONFIRMED || params->IsMcpsIndication == MCPS_UNCONFIRMED)
{
if(!(appData->BufferSize==0))
APP_LOG(TS_ON, VLEVEL_L, "+EVT:RX_%d, PORT %d, DR %d, RSSI %d, SNR %d %s\r\n", params->RxSlot, appData->Port,
params->Datarate, params->Rssi, params->Snr,appData->Buffer);
APP_LOG(TS_ON, VLEVEL_L, "\r\n");
}
if (params != NULL)
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); /* LED_BLUE */
UTIL_TIMER_Start(&RxLedTimer);
if (params->IsMcpsIndication)
{
if (appData != NULL)
{
RxPort = appData->Port;
if (appData->Buffer != NULL)
{
switch (appData->Port)
{
case LORAWAN_SWITCH_CLASS_PORT:
/*this port switches the class*/
if (appData->BufferSize == 1)
{
switch (appData->Buffer[0])
{
case 0:
{
LmHandlerRequestClass(CLASS_A);
break;
}
case 1:
{
LmHandlerRequestClass(CLASS_B);
break;
}
case 2:
{
LmHandlerRequestClass(CLASS_C);
break;
}
default:
break;
}
}
break;
case LORAWAN_USER_APP_PORT:
if (appData->BufferSize == 1)
{
AppLedStateOn = appData->Buffer[0] & 0x01;
if (AppLedStateOn == RESET)
{
APP_LOG(TS_OFF, VLEVEL_H, "LED OFF\r\n");
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
}
else
{
APP_LOG(TS_OFF, VLEVEL_H, "LED ON\r\n");
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); /* LED_RED */
}
}
break;
default:
break;
}
}
}
}
if (params->RxSlot < RX_SLOT_NONE)
{
APP_LOG(TS_OFF, VLEVEL_H, "###### D/L FRAME:%04d | PORT:%d | DR:%d | SLOT:%s | RSSI:%d | SNR:%d\r\n",
params->DownlinkCounter, RxPort, params->Datarate, slotStrings[params->RxSlot],
params->Rssi, params->Snr);
}
}
/* USER CODE END OnRxData_1 */
}
- Link de download: https://www.st.com/en/development-tools/stm32cubeide.html
- Link do documento:
https://www.st.com/en/development-tools/stm32cubeide.html#documentation
- No menu File new stm32 project procure por LoRaWAN
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYvtW85dYU1YU5Dpdqn24iraxNeriS-yFQYL5SaGpovxXoweRbttUZYEgfkE_yk4QWbTMD9HT7W2uMbK4VPsAn7fsclekebY4ohd-oWwLnHBcvHEOkKm0ySHJjvx0ePIwar8-aNTxpyx91BbiBzxd2sMbOtpk6S90dpsRoR0l9U5U_RXJox6d_e3FjZrfD/w640-h360/4.png)
- Selecione em Example Selector o projeto LoRaWAN_End_Node
- Após o projeto ser importado, ele é criado e mostrado no Project Explorer (Tab) no canto superior esquerdo
Enquanto segurando o botão Reset do LSM110A Starter KIT, pressione Connect no STM32 CUBE Programmer e libere o botão de Reset.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgS0_voS-fs-o0hww2YTWpIRWhznUjTU-TnOTGLrhj2o8EIsa0SUyCZ0E9EwbC2a7ld9xIZB8rrH9i4qyKMm8ZIKuiLsqoSfg44ddV9-ewn1DKzaRq4d5zIDPgkF1JCGwkwerJZeOncF7ME1or43ulo2kbtGDEqJl4ygxi-05-ii2SHjnbrN_bn32L59A/w640-h30/3.png)
OTAA
1.2 Adicionar um dispositivo EndDevice LoRaWan
Esta seção mostra como adicionar um dispositivo EndDevice LoRaWAN à rede LoRaWAN e ver os dados pelo web site da EveryNet. Usamos o LSM110A como um dispositivo de referência - a configuração para outros dispositivos LoRaWAN será semelhante.
Três códigos são necessários para definir o dispositivo no EveryNet:
Device EUI - código de identificação único para um dispositivo em particular.
Application EUI - código de identificação para um aplicativo definido no Everynet.
Application key - Chave exclusiva para proteger as comunicações com um dispositivo em particular.
Pressione o sinal de + para abrir a tela abaixo e adicionar device!
Mude para Activation para OTAA e Encryption para NS
Nota-se que há uma APP EUI já criado por EveryNet, mas esta não é a definida no dispositivo. Para adicionar o APP EUI do dispositivo LSM110A, utilize comandos AT.
Dados são mostrados no final da página
- Configure agora o módulo LSM1x0A
static void McpsIndication( McpsIndication_t *mcpsIndication, LoRaMacRxStatus_t *rxStatus )
{
LmHandlerAppData_t appData;
DeviceClass_t deviceClass = CLASS_A;
RxParams.IsMcpsIndication = 1;
RxParams.Status = mcpsIndication->Status;
if( RxParams.Status != LORAMAC_EVENT_INFO_STATUS_OK )
{
return;
}
RxParams.Datarate = mcpsIndication->RxDatarate;
RxParams.Rssi = rxStatus->Rssi;
RxParams.Snr = rxStatus->Snr;
RxParams.RxSlot = rxStatus->RxSlot;
RxParams.DownlinkCounter = mcpsIndication->DownLinkCounter;
appData.Port = mcpsIndication->Port;
appData.BufferSize = mcpsIndication->BufferSize;
appData.Buffer = mcpsIndication->Buffer;
//MIGUEL
if (mcpsIndication->McpsIndication == MCPS_CONFIRMED || mcpsIndication->McpsIndication == MCPS_UNCONFIRMED)
{
if(!(mcpsIndication->BufferSize==0))
{
APP_LOG(TS_ON, VLEVEL_L, "Received downlink data of size: %d\r\n", mcpsIndication->BufferSize);
for (int i = 0; i < mcpsIndication->BufferSize; i++)
APP_LOG(TS_ON, VLEVEL_L, "Data[%d]: %c\r\n", i, mcpsIndication->Buffer[i]);
APP_LOG(TS_ON, VLEVEL_L, "\r\n");
}
}
//MIGUEL
if( LmHandlerCallbacks->OnRxData != NULL )
{
LmHandlerCallbacks->OnRxData( &appData, &RxParams );
}
if( ( LmHandlerCallbacks->OnSysTimeUpdate != NULL ) && ( mcpsIndication->DeviceTimeAnsReceived == true ) )
{
LmHandlerCallbacks->OnSysTimeUpdate( );
}
/* Call packages RxProcess function */
LmHandlerPackagesNotify( PACKAGE_MCPS_INDICATION, mcpsIndication );
LmHandlerGetCurrentClass( &deviceClass );
#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 ))
if( mcpsIndication->IsUplinkTxPending != 0 )
{
/* The server signals that it has pending data to be sent. */
/* We schedule an uplink as soon as possible to flush the server. */
/* Send an empty message */
LmHandlerAppData_t appData =
{
.Buffer = NULL,
.BufferSize = 0,
.Port = 0
};
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, true );
}
#elif (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 )))
if( mcpsIndication->IsUplinkTxPending != 0 )
{
/* The server signals that it has pending data to be sent. */
/* We schedule an uplink as soon as possible to flush the server. */
IsUplinkTxPending = true;
}
#endif /* LORAMAC_VERSION */
}
Kit de avaliação para módulo LoRa e Sigfox Wisol Seong JI SJI LSM110A
Operação em rede pública e privada LoRaWAN e também ponto a ponto (P2P)
Opera também em Sigfox
Alterne entre LoRa e Sigfox por comando AT, não precisa trocar o Firmware!
Permite embarcar sua aplicação
Módulo LoRa e Sigfox Wisol Seong JI SJI LSM110A
Módulo LoRa e Sigfox compacto
Escolha entre operação LoRa e Sigfox por comando "on the fly", sem precisar trocar firmware !
LoRa: para operação redes públicas e privadas LoRaWAN bem como ponto a ponto (P2P)
Sigfox: zonas RCZ2 e RCZ4 (inclui Brasil)
Caracteristicas:
- Permite embarcar a aplicação
- Dimensão: 14x15x2,8mm
- Frequência: RCZ2 Tx 902,2MHz Rx 905,2MHz
- Potência de saida: configurável até +22dBm
- Sensibilidade: -129dBm@LoRa(BW=500KHz, SF=12)
-124dBm@Sigfox(0.6Kbps)
- Tensão de operação: 1,8 - 3,6Vdc
- Interface UART 9600 8N1
- Baixo consumo
- Na operação P2P não necessita de gateway LoRaWAN
Aplicações:
- Medição individualizada de água, energia, gás
- Automação comercial, industrial residencial;
- Rede de sensores;
- Sistemas de alarme e segurança;
- Estações meteorológicas;
- Automação agrícola.
- utilize o SDK do STM32
- veja como é estruturado a troca de mensagens em (Message Sequence Chart) numa conexão com LoraWAN
- DevNonce is too small error in the device events: If the device is using LoRaWAN MAC version 1.1 or 1.0.4, this error occurs when DevNonce is not being incremented, so the Join Server ignores Join Requests with the same or lower DevNonce value comparing to the previous one. Contact your device’s manufacturer to find out the correct LoRaWAN MAC and PHY versions, and configure the device in The Things Stack accordingly
Nenhum comentário:
Postar um comentário