
“Wireless Made Easy!" - Adding LoRa Peer-to-Peer Connection to an existing LoRaWAN Application
Subject to your compliance with these terms, you may use Microchip software and any derivatives exclusively with Microchip products. It is your responsibility to comply with third party license terms applicable to your use of third party software (including open source software) that may accompany Microchip software.
THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
Interact with your peers about this software in LoRa Forum.
Based on LoRaWAN Mote Application generated from ASFv3, this sample code demonstrates the coexistence of a LoRaWAN Application and a pure radio communication between 2x ATSAMR34 Xplained Pro boards using LoRa modulation.
For more information on Microchip ATSAMR34 LoRa SiP devices, visit Microchip webpage:
https://www.microchip.com/design-centers/wireless-connectivity/low-power-wide-area-networks/lora-technology/sam-r34-r35

The ATSAMR34 Xplained Pro evaluation kit is a hardware platform used to evaluate the ATSAMR34 Low Power LoRa® Sub-GHz SiP. It is supported by Microchip Studio 7.0 IDE and a collection of sample codes are available from Advanced Software Framework (ASFv3) since the 3.44.0 release. The Xplained Pro MCU series evaluation kit includes an on-board Embedded Debugger (EDBG), and no external tools are necessary to program or debug the ATSAMR34. The kit offers a set of features that enable the user to get started with the ATSAMR34 Low Power LoRa® Sub-GHz SiP peripherals right away, and to understand how to integrate the device in your own design. The ATSAMR34 Xplained Pro kit contains the following items:

To demonstrate peer-to-peer communication, you do need at least 2x ATSAMR34 Xplained Pro boards.
This project integrates the Microchip LoRaWAN Stack (MLS) Software API which provide an interface to the different software modules.
For this application, we will “Pause” the LoRaWAN MAC Layer (MAC) and use the LoRaWAN Radio Layer (TAL) for peer-to-peer communication and “Resume” the LoRaWAN MAC Layer to perform the LoRaWAN operations.
In peer-to-peer configuration, the LoRaWAN MAC layer is bypassed and let the ability to drive the radio directly with no protocol, no security, no unique identifier for a device and obviously no interoperability and ecosystem (in opposite with LoRaWAN). This can serve to demonstrate the ability to use P2P communication in a LoRaWAN application.
Please ensure to not violate the local regulations for the corresponding frequency bands. E.g. 25mW and 1% duty cycle for the 868MHz-band inside Europe.
The MAC Layer provides the functionality of operations defined in the LoRaWAN Specification.
The TAL layer uses the radio drivers and provides access to the SX1276 transceiver.
To be able to setup the devices for a point to point connection, the code needs to:
void LORAWAN_Init(AppDataCb_t appdata, JoinResponseCb_t joindata);
StackRetStatus_t LORAWAN_Reset (IsmBand_t ismBand);
uint32_t LORAWAN_Pause (void);
RadioError_t RADIO_SetAttr(RadioAttribute_t attribute, void *value);
RADIO_SetAttr(WATCHDOG_TIMEOUT,(void *)&wdt) ;
RadioError_t RADIO_Receive(RadioReceiveParam_t *param);
This function set the device in receive mode to receive data and store it in the buffer pointer space by doing a task post to the RADIO_RxHandler.
RadioError_t RADIO_Transmit(RadioTransmitParam_t *param);
This function transmits the data by doing a task post to the RADIO_TxHandler.
For peer-to-peer communication, the sample code configures the radio with the following parameters:
typedef enum _AppTaskIds_t
{
DISPLAY_TASK_HANDLER,
PROCESS_TASK_HANDLER,
APP_TASKS_COUNT
}AppTaskIds_t;
typedef enum _AppTaskState_t
{
RESTORE_BAND_STATE,
DEMO_CERT_APP_STATE,
DEMO_APP_STATE,
JOIN_SEND_STATE
}AppTaskState_t;
static SYSTEM_TaskStatus_t (*appTaskHandlers[APP_TASKS_COUNT])(void) = {
/* In the order of descending priority */
displayTask,
processTask
};
appTaskState = JOIN_SEND_STATE;
appPostTask(DISPLAY_TASK_HANDLER);
/*********************************************************************//**
brief Calls appropriate functions based on state variables
*************************************************************************/
static SYSTEM_TaskStatus_t displayTask(void)
{
switch(appTaskState)
{
case RESTORE_BAND_STATE:
displayRunRestoreBand();
break;
case DEMO_CERT_APP_STATE:
displayRunDemoCertApp();
break;
case DEMO_APP_STATE:
displayRunDemoApp();
break;
case JOIN_SEND_STATE:
displayJoinAndSend();
break;
default:
printf("Error STATE Enteredrn");
break;
}
return SYSTEM_TASK_SUCCESS;
}
/*********************************************************************//**
brief Displays and activates LED's for joining to a network
and sending data to a network
*************************************************************************/
static void displayJoinAndSend(void)
{
printf("rn1. Send Join Requestrn");
printf("2. Send Datarn");
// new menu with p2p
printf("3. Main Menurn") ;
printf("4. MAC Pausern") ;
printf("5. MAC Resumern") ;
printf("6. Configure Radiorn") ;
printf("7. Send Radio Datarn") ;
printf("8. Enter Radio Receive modern") ;
printf("9. Exit Radio Receive modern") ;
#ifdef CONF_PMM_ENABLE
printf("0. Sleeprn") ;
#endif
printf("rnEnter your choice: ");
set_LED_data(LED_AMBER,&off);
set_LED_data(LED_GREEN,&off);
startReceiving = true;
}
/*********************************************************************//**
brief Pulls the data from UART when activated
*************************************************************************/
void serial_data_handler(void)
{
int rxChar;
char serialData;
/* verify if there was any character received*/
if (startReceiving == true)
{
if((-1) != (rxChar = sio2host_getchar_nowait()))
{
serialData = (char)rxChar;
if((serialData != 'r') && (serialData != 'n') && (serialData != 'b'))
{
startReceiving = false;
serialBuffer = rxChar;
appPostTask(PROCESS_TASK_HANDLER);
printf("rn");
}
}
}
}
/*********************************************************************//**
brief Calls appropriate functions based on state variables
*************************************************************************/
static SYSTEM_TaskStatus_t processTask(void)
{
switch(appTaskState)
{
case RESTORE_BAND_STATE:
processRunRestoreBand();
break;
case DEMO_CERT_APP_STATE:
processRunDemoCertApp();
break;
case DEMO_APP_STATE:
processRunDemoApp();
break;
case JOIN_SEND_STATE:
processJoinAndSend();
break;
default:
printf("Error STATE Enteredrn");
break;
}
return SYSTEM_TASK_SUCCESS;
}
/*********************************************************************//**
brief Sends Join request or Data to the network
*************************************************************************/
static void processJoinAndSend(void)
{
StackRetStatus_t status = LORAWAN_SUCCESS;
if(serialBuffer == '1')
{
status = LORAWAN_Join(DEMO_APP_ACTIVATION_TYPE);
if (LORAWAN_SUCCESS == (StackRetStatus_t)status)
{
set_LED_data(LED_GREEN,&on);
printf("nJoin Request Sentnr");
}
else
{
set_LED_data(LED_AMBER,&on);
print_stack_status(status);
appTaskState = JOIN_SEND_STATE;
appPostTask(DISPLAY_TASK_HANDLER);
}
}
else if(serialBuffer == '2' && joined == true)
{
sendData();
}
else if(serialBuffer == '2' && !joined)
{
set_LED_data(LED_AMBER,&on);
printf("Device not joined to the networkrn");
appTaskState = JOIN_SEND_STATE;
appPostTask(DISPLAY_TASK_HANDLER);
}
#ifdef CONF_PMM_ENABLE
else if(serialBuffer == '0')
{
static bool deviceResetsForWakeup = false;
PMM_SleepReq_t sleepReq;
/* Put the application to sleep */
sleepReq.sleepTimeMs = DEMO_CONF_DEFAULT_APP_SLEEP_TIME_MS;
sleepReq.pmmWakeupCallback = appWakeup;
sleepReq.sleep_mode = CONF_PMM_SLEEPMODE_WHEN_IDLE;
if (CONF_PMM_SLEEPMODE_WHEN_IDLE == SLEEP_MODE_STANDBY)
{
deviceResetsForWakeup = false;
}
if (true == LORAWAN_ReadyToSleep(deviceResetsForWakeup))
{
app_resources_uninit();
if (PMM_SLEEP_REQ_DENIED == PMM_Sleep(&sleepReq))
{
HAL_Radio_resources_init();
sio2host_init();
appTaskState = JOIN_SEND_STATE;
appPostTask(DISPLAY_TASK_HANDLER);
printf("rnsleep_not_okrn");
}
}
else
{
printf("rnsleep_not_okrn");
appTaskState = JOIN_SEND_STATE;
appPostTask(DISPLAY_TASK_HANDLER);
}
}
#endif
else if (serialBuffer == '3')
{
// main menu
appTaskState = DEMO_APP_STATE;
appPostTask(DISPLAY_TASK_HANDLER);
}
else if (serialBuffer == '4')
{
// Pause the Microchip LoRaWAN Stack
uint32_t time_ms ;
time_ms = LORAWAN_Pause() ;
printf("rnMAC Pause %ldrn", time_ms) ;
appTaskState = JOIN_SEND_STATE ;
appPostTask(DISPLAY_TASK_HANDLER) ;
}
else if (serialBuffer == '5')
{
// Resume the Microchip LoRaWAN Stack
LORAWAN_Resume() ;
printf("rnMAC Resumern") ;
appTaskState = JOIN_SEND_STATE ;
appPostTask(DISPLAY_TASK_HANDLER) ;
}
else if (serialBuffer == '6')
{
// Configure Radio Parameters
// --------------------------
// Bandwidth = BW_125KHZ
// Channel frequency = FREQ_868100KHZ
// Channel frequency deviation = 25000
// CRC = enabled
// Error Coding Rate = 4/5
// IQ Inverted = disabled
// LoRa Sync Word = 0x34
// Modulation = LoRa
// PA Boost = disabled (disabled for EU , enabled for NA)
// Output Power = 1 (up to +14dBm for EU / up to +20dBm for NA)
// Spreading Factor = SF7
// Watchdog timeout = 60000
// Bandwidth
RadioLoRaBandWidth_t bw = BW_125KHZ ;
RADIO_SetAttr(BANDWIDTH, &bw) ;
printf("Configuring Radio Bandwidth: 125kHzrn") ;
// Channel Frequency
uint32_t freq = FREQ_868100KHZ ;
RADIO_SetAttr(CHANNEL_FREQUENCY, &freq) ;
printf("Configuring Channel Frequency %ldrn", freq) ;
// Channel Frequency Deviation
uint32_t fdev = 25000 ;
RADIO_SetAttr(CHANNEL_FREQUENCY_DEVIATION, &fdev) ;
printf("Configuring Channel Frequency Deviation %ldrn", fdev) ;
// CRC
uint8_t crc_state = 1 ;
RADIO_SetAttr(CRC, &crc_state) ;
printf("Configuring CRC state: %drn", crc_state) ;
// Error Coding Rate
RadioErrorCodingRate_t cr = CR_4_5 ;
RADIO_SetAttr(ERROR_CODING_RATE, &cr) ;
printf("Configuring Error Coding Rate 4/5rn") ;
// IQ Inverted
uint8_t iqi = 0 ;
RADIO_SetAttr(IQINVERTED, &iqi) ;
printf("Configuring IQ Inverted: %drn", iqi) ;
// LoRa Sync Word
uint8_t sync_word = 0x34 ;
RADIO_SetAttr(LORA_SYNC_WORD, &sync_word) ;
printf("Configuring LoRa sync word 0x%xrn", sync_word) ;
// Modulation
RadioModulation_t mod = MODULATION_LORA ;
RADIO_SetAttr(MODULATION, &mod) ;
printf("Configuring Modulation: LORArn") ;
// PA Boost
uint8_t pa_boost = 0 ;
RADIO_SetAttr(PABOOST, &pa_boost) ;
printf("Configuring PA Boost: %drn", pa_boost) ;
// Tx Output Power
int16_t outputPwr = 1 ;
RADIO_SetAttr(OUTPUT_POWER, (void *)&outputPwr) ;
printf("Configuring Radio Output Power %drn", outputPwr) ;
// Spreading Factor
int16_t sf = SF_7 ;
RADIO_SetAttr(SPREADING_FACTOR, (void *)&sf) ;
printf("Configuring Radio SF %drn", sf) ;
// Watchdog Timeout
uint32_t wdt = 60000 ;
RADIO_SetAttr(WATCHDOG_TIMEOUT, (void *)&wdt) ;
printf("Configuring Radio Watch Dog Timeout %ldrn", wdt) ;
appTaskState = JOIN_SEND_STATE ;
appPostTask(DISPLAY_TASK_HANDLER) ;
}
else if (serialBuffer == '7')
{
// Radio Transmit
// the counter value
counter++ ;
if (counter > 255) counter = 0 ;
tx_buffer[0] = counter ;
printf("Buffer transmitted: ") ;
print_array(tx_buffer, 1) ;
RadioError_t radioStatus ;
RadioTransmitParam_t radioTransmitParam ;
radioTransmitParam.bufferLen = 1 ;
radioTransmitParam.bufferPtr = (uint8_t *)&tx_buffer ;
radioStatus = RADIO_Transmit(&radioTransmitParam) ;
switch (radioStatus)
{
case ERR_NONE:
{
printf("Radio Transmit Successrn") ;
}
break ;
case ERR_DATA_SIZE:
{
// do nothing, status already set to invalid
}
break ;
default:
{
printf("Radio Busyrn") ;
}
}
appTaskState = JOIN_SEND_STATE ;
appPostTask(DISPLAY_TASK_HANDLER) ;
}
else if (serialBuffer == '8')
{
// Enter Radio Receive mode
RadioReceiveParam_t radioReceiveParam ;
uint32_t rxTimeout = 0 ; // forever
radioReceiveParam.action = RECEIVE_START ;
radioReceiveParam.rxWindowSize = rxTimeout ;
if (RADIO_Receive(&radioReceiveParam) == 0)
{
printf("Radio in Receive modern") ;
}
appTaskState = JOIN_SEND_STATE ;
appPostTask(DISPLAY_TASK_HANDLER) ;
}
else if (serialBuffer == '9')
{
// Stop Radio Receive mode
RadioReceiveParam_t radioReceiveParam ;
radioReceiveParam.action = RECEIVE_STOP ;
if (RADIO_Receive(&radioReceiveParam) == 0)
{
printf("Radio Exit Receive modern") ;
}
appTaskState = JOIN_SEND_STATE ;
appPostTask(DISPLAY_TASK_HANDLER) ;
}
else
{
set_LED_data(LED_AMBER,&on);
printf("Invalid choice enteredrn");
appTaskState = JOIN_SEND_STATE;
appPostTask(DISPLAY_TASK_HANDLER);
}
}
/* OTAA Join Parameters */
#define DEMO_DEVICE_EUI {0xde, 0xaf, 0xfa, 0xce, 0xde, 0xaf, 0xfa, 0xce}
#define DEMO_APPLICATION_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05}
#define DEMO_APPLICATION_KEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05}
conf_board.h file:
/* TODO: If Board is having EDBG with DEV_EUI flashed in
Userpage Enable this Macro otherwise make it as 0 */
#define EDBG_EUI_READ 1