Join Procedure Timeout

Hello, I would like to know if it is possible to set a timeout for the Join procedure in order to abort the execution when no messages are received. As I reviewed in the code, currently only undefined re-join is supported, despite number of trials is established to 1.

I would like to stop it if no responses are received because the device is in an out-of-coverage area, and put the device in low power mode for a period of time.

Thanks!

Currently even after a timeout it automatically goes into low power and you can take a look at the print. That timeout can be modified.

I have examined the code in LoRaWAN_APP.cpp and LoRaMac.c, but it only stops the Join procedure after 48 tries (in EU868, file RegionEU868.h) because the number of trials (NbJoinTrials variable) is always overwritten by the default number in the region (EU868_DEFAULT_PHY_NB_JOIN_TRIALS constant). I would like to manage the number of trials from the application side.

In addition, it only goes to low power mode for 30 seconds, it would be great to have the chance to control this sleep time also from the application part.

for the confirmedNbTrials i would stick to a 3 or higher.
as for changing the 30sec update you can expose this variable uint32_t rejoin_delay = 30000; to the application layer and control it from their, similar to how extern uint8_t devEui[]; is exposed in LoRaWan_APP.h

open LoRaWan_APP.cpp and look for :
uint32_t rejoin_delay = 30000; then comment that line
//uint32_t rejoin_delay = 30000;
then open LoRaWan_APP.h and add this
extern uint32_t rejoin_delay;
above this
extern uint8_t devEui[];

now in you application header add:
uint32_t rejoin_delay = 60000;

save and compile, now the rejoin will happen every 60 seconds.

Jay.

That is a good and simple solution to control the sleep time if timeout expires, but it would be nicer to make it in a way that we can make a decision from the application side if we want to sleep or make a different action. In that case, I suggest modifying that piece of code that sleeps for the rejoin_delay variable for a simple deviceState = DEVICE_STATE_JOIN;, what do you think?

Regarding the number of trials, how would you do? Modifying directly the RegionEU868.h file, there should be a smarter solution that takes in consideration the fact that 46 trials are needed if we use ADR by default, but if we want to force a maximum number of trials if we don’t use ADR make it possible.

in that case you will have to implement it yourself as follow:
in LoRaWan_APP.h look for downLinkAckHandle and comment it as shown below add the new two lines:

//extern "C" void downLinkAckHandle();
void downLinkAckHandle(McpsConfirm_t *mcpsConfirm);
void downLinkAckHandle(McpsIndication_t *mcpsIndication);

in the LoRaWan_APP.cpp add the following code:

void __attribute__((weak)) downLinkAckHandle(McpsConfirm_t *mcpsConfirm)
{
	// printf("\n **** ack received\r\n");
}
void __attribute__((weak)) downLinkAckHandle(McpsIndication_t *mcpsIndication)
{
	// printf("\n **** ack received\r\n");
}

and then look for this:

if (mcpsIndication->AckReceived)
    {
        downLinkAckHandle();
    }
and change it to this:

 //if (mcpsIndication->AckReceived)
  //   {
         downLinkAckHandle(mcpsIndication);
  //   }
next look for this:

static void McpsConfirm(McpsConfirm_t *mcpsConfirm)
{
//and add this line on Top
downLinkAckHandle(mcpsConfirm);

	if (mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
	{ .....
}

now in your Application layer add the following two functions:

void downLinkAckHandle(McpsIndication_t *mcpsIndication)
{
  printf("\nMcpsIndication_t");
  printf("\n\t+REV DATA:%s,\n\tRXSIZE %d,\n\tPORT %d, RSSI: %d,\n\t SNR: %d,\n\t DATA_RATE:%d,\n\t AckReceived:%d,\n\t Status:%d,\r\n",
            mcpsIndication->RxSlot ? "RXWIN2" : "RXWIN1", mcpsIndication->BufferSize, mcpsIndication->Port,
            mcpsIndication->Rssi, (int)mcpsIndication->Snr, (int)mcpsIndication->RxDoneDatarate, mcpsIndication->AckReceived, mcpsIndication->Status);
}

and

void downLinkAckHandle(McpsConfirm_t *mcpsConfirm)
{
  printf("\t\n ****\n1484 McpsConfirm_t mcpsConfirm->AckReceived:%d \n mcpsConfirm->Status:%d \n mcpsConfirm->McpsRequest:%d \n mcpsConfirm->NbRetries:%d \n mcpsConfirm->UpLinkCounter:%d \n DATA_RATE:%d  \n Channel:%d  \n TX Power:%d PRIi8  \n TxTimeOnAir:%d \n*****\r\n",
        mcpsConfirm->AckReceived, mcpsConfirm->Status, mcpsConfirm->McpsRequest, mcpsConfirm->NbRetries,
        mcpsConfirm->UpLinkCounter, (int)mcpsConfirm->Datarate, mcpsConfirm->Channel, (int)mcpsConfirm->TxPower, mcpsConfirm->TxTimeOnAir);
 //react on the timeouts basically a failed join...
  switch (mcpsConfirm->Status)
  {
  case LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT:
  case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT:
  case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR:
  case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR:
  {
// your code here to decide what to do when a join fails...
// note this will get triggered every time a join fails so if ADR is on this will be triggered as it travels through available ADR if you want to act on the last ADR i would suggest you put an if statement here to look for ADR 0 for example:
if (!mcpsConfirm->AckReceived && (int)mcpsConfirm->Datarate == 0)
{
  printf("\n failed to get acknowledgement... Force it to re-join\n");
  deviceState = DEVICE_STATE_INIT; // force an initialization or restart using the code below
  // delay(1000);
  // CySoftwareReset(); // restart the board so it can go through the joining process
}
else if (!mcpsConfirm->AckReceived) // failed to join but here we will go to the next ADR set a timer here if you'd like
{

}
break;
}
default:
printf("default state set the default timer here");
break;
}
}

Note: i’m not sure the code above cover all and every situation that could occur in the field so please thinker carefully with it, and share your findings.

if you improve the above please share back here for the community…

Hello again:
Actually the above code will be triggered when a send frame fails and never reaches the it’s destination… and not on JoinRequest timeout… in order to react on JoinRequest timeout you will need to apply the following modifications:

in LoRaWan_APP.h add this:

extern "C" void onJoinRequest(MlmeConfirm_t *mlmeConfirm);

and in LoRaWan_APP.cpp add this:

void __attribute__((weak)) onJoinRequest(MlmeConfirm_t *mlmeConfirm)
{
    printf("\n **** onJoinRequest triggered\r\n");
}

and this:

        {
//comment the Timer and control that from your application.
            // uint32_t rejoin_delay = 30000;
            //printf("join failed, join again at 30s later\r\n");
            delay(5);
            //TimerSetValue(&TxNextPacketTimer, rejoin_delay);
            //TimerStart(&TxNextPacketTimer);
        }
        onJoinRequest(mlmeConfirm); // this will call the onJoinRequest with the join status either a LORAMAC_EVENT_INFO_STATUS_OK means joined successfully or 4 or another number means failed.

now in you application layer declare this in your header:

void onJoinRequest(MlmeConfirm_t *mlmeConfirm);

add this to your code:

void onJoinRequest(MlmeConfirm_t *mlmeConfirm)
{
  DEBUG_MSG(false, "\t\n ****\n1484 McpsConfirm_t mcpsConfirm->MlmeRequest:%d \n mcpsConfirm->Status:%d \n mcpsConfirm->NbRetries:%d \n TxTimeOnAir:%d \n*****\r\n",
            mlmeConfirm->MlmeRequest, mlmeConfirm->Status, mlmeConfirm->NbRetries,
            mlmeConfirm->TxTimeOnAir);
  switch (mlmeConfirm->Status)
  {
  case LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT:
  case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT:
  case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR:
  case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR:
  {
    printf("\n Join Request Time out\n");
//set your own timer for a rejoin here and inside it call deviceState = DEVICE_STATE_INIT; to force a rejoin.
    break;
  case LORAMAC_EVENT_INFO_STATUS_OK:
    printf("\n Device has Joined succssfully");
    break;
  default:
    printf("\n Status:%d\n", mlmeConfirm->Status);
    break;
  }
  }
}

Hello @jayjay ,
I am looking for simmilar solution maybe you will be able to help , want to force to re-join when for example miss 3x TxDutyCycle time on my heltec AB01 .
Let say , my int32_t appTxDutyCycle = 300000; // 5 min , so when miss 3 in row , then do the re-join.
Anyone can help ?