Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Join request is sent by node to chirpstack server and a join accept is sent back from server to node, but the node still sends another join request. #201

Closed
mirhamza708 opened this issue Sep 23, 2024 · 7 comments
Assignees

Comments

@mirhamza708
Copy link

mirhamza708 commented Sep 23, 2024

Issue

  • The node device sends a join request and is accepted by the chirpstack server but the node device does not stop sending a join request. This is repeated a few times and then finally the node device prints "joined". Is this because of the timeout in the library for the join accept?
    Repeated join request snap

  • Also some time I get the Validate devnonce error in chirpstack server, what does that mean and how to resolve that?

Thank you
Hamza

@Eric286
Copy link
Contributor

Eric286 commented Sep 23, 2024

Hello @mirhamza708

This library has a built-in timeout for receiving the join accept message, and if the join accept is delayed or not properly acknowledged within this window, the node may continue sending join requests until it finally receives and processes the join accept.

Ensure there is minimal delay in the communication between the ChirpStack server and your gateway. Sometimes network or processing delays can cause the join accept message to reach the node outside the expected window.

Regarding the "Validate devnonce error" in the ChirpStack server, this error typically occurs when the device's devnonce has already been used or is invalid.

If the same devnonce is sent by the node multiple times (likely due to repeated join requests), ChirpStack will reject it to prevent replay attacks. Make sure that the node device properly increments the devnonce with each join request.

Kind Regards!

@mirhamza708
Copy link
Author

Thank you for the reply eric, I tried to increase the timeout in the library from 6000ms to 30000ms but that didn't help either.

Also I have some questions for the devnonce increment. Do i have to increment the devnonce for each join request even if the reason was timeout for last join request?
if we increment the devnonce for each timeout it will reach the max value of devnonce quickly. Then is it possible to clear the devnonce in chirpstack automatically? is it cleared automatically?

And can we have the devnonce increment feature inside the library so the user of library does not have to worry about keeping track of the devnonce?

@mirhamza708
Copy link
Author

/*
*****************************************************************************************
* Description : Function to switch RFM to single receive mode, used for Class A motes
*
* Arguments   : *LoRa_Settings pointer to sSettings struct
*
* Return	  : message_t Status of the received message
*****************************************************************************************
*/

message_t RFM_Single_Receive(sSettings *LoRa_Settings)
{
  message_t Message_Status = NO_MESSAGE;
  
  //Change DIO 0 back to RxDone
  if (RFM_pins.DIO0 != -1)
    RFM_Write(RFM_REG_DIO_MAPPING1, 0x00);

  //Invert IQ Back
  RFM_Write(RFM_REG_INVERT_IQ, 0x67);
  RFM_Write(RFM_REG_INVERT_IQ2, 0x19);

  //Change Datarate
  RFM_Change_Datarate(LoRa_Settings->Datarate_Rx);

  //Change Channel
  RFM_Change_Channel(LoRa_Settings->Channel_Rx);

  //Switch RFM to Single reception
  RFM_Switch_Mode(RFM_MODE_RXSINGLE);

  //Wait until RxDone or Timeout
  //Wait until timeout or RxDone interrupt
  byte RegIrqFlags;
  if (RFM_pins.DIO0 != -1)
    while((digitalRead(RFM_pins.DIO0) == LOW) && (digitalRead(RFM_pins.DIO1) == LOW));
  else
  {
    RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f);
    while ((RegIrqFlags & (IRQ_RX_DONE_MASK | IRQ_RX_TIMEOUT_MASK)) == 0)
    {
      RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f);
    }
  }
  
  //Added 2ms delay before checking for timeout. 
  delay(2);
  //Check for Timeout
  bool isTimeout;
  if (RFM_pins.DIO0 != -1)
  {
    isTimeout = digitalRead(RFM_pins.DIO1) == HIGH;
  }
  else
  {
    isTimeout = (RegIrqFlags & IRQ_RX_TIMEOUT_MASK) != 0;
  }
  if (isTimeout)
  {
    //Clear interrupt register
    RFM_Write(RFM_REG_IRQ_FLAGS,0xE0);
    Message_Status = TIMEOUT;
  }

  //Check for RxDone
  bool isRxDone;
  if (RFM_pins.DIO0 != -1)
  {
    isRxDone = digitalRead(RFM_pins.DIO0) == HIGH;
  }
  else
  {
    isRxDone = (RegIrqFlags & IRQ_RX_DONE_MASK) != 0;
  }
  if(isRxDone)
  {
	  Message_Status = NEW_MESSAGE;
  }

  return Message_Status;
}

Added delay before checking for timeout has resolved my issue of repeated join for now, will have to test a little bit more to be sure that it's the main reason of repeated join requests.

As for the devnonce increment I am doing this:

//global varaibles
uint16_t myDevNonce = 0;
int eeDevNonceAddress = 10;
uint8_t isFreshStart = 0;
int eeFreshStartAddress = 15;

  //in main before initializing lora
  //get device nonces from eeprom
  EEPROM.get(eeFreshStartAddress, isFreshStart);
  DEBUG_PRINTS("isFreshStart: ");
  DEBUG_PRINTSLN(isFreshStart);
  if (isFreshStart == 1) {
    myDevNonce = 0;
    isFreshStart = 2;
    EEPROM.put(eeFreshStartAddress, isFreshStart);
  } else if (isFreshStart == 2) {
    EEPROM.get(eeDevNonceAddress, myDevNonce);
  } else {
    myDevNonce = 0;
    isFreshStart = 2;
    EEPROM.put(eeDevNonceAddress, myDevNonce);
    EEPROM.put(eeFreshStartAddress, isFreshStart);
  }
  delay(100);
  
//in the LoRaMAC.cpp file add

extern uint16_t myDevNonce;
/*
*****************************************************************************************
* Description : Function that is used to generate device nonce used in the join request function
*				This is based on a pseudo random function in the arduino library
*
* Arguments   : *Devnonce pointer to the devnonce arry of withc is unsigned char[2]
*****************************************************************************************
*/
static void Generate_DevNonce(unsigned char *DevNonce)
{
	unsigned int RandNumber;

	// RandNumber = random(0xFFFF);
	RandNumber = myDevNonce;

	DevNonce[0] = RandNumber & 0x00FF;
	DevNonce[1] = (RandNumber >> 8) & 0x00FF;
}


//in main after join successful 
  EEPROM.put(eeDevNonceAddress, myDevNonce);
  delay(100);

one suggestion/feature request for the library, can we have a structure to be passed to the lora.join() so that we can pass the joining information. This will help if user want to store and pass the parameters again after resets.

@Eric286
Copy link
Contributor

Eric286 commented Oct 2, 2024

Hello @mirhamza708

Great to hear that adding the delay has helped resolve the repeated join request issue so far!

As for your suggestion about passing a structure to lora.join(), that’s a great idea! Having a way to pass joining parameters after resets would indeed be useful for devices that need to retain state between power cycles or recover after an unexpected reset. This could be particularly valuable in scenarios where consistent device behavior and reduced network overhead are critical.

In the meantime, if you have any further questions or additional feature requests, feel free to share!

Kind regards!

@areytechai
Copy link

/*
*****************************************************************************************
* Description : Function to switch RFM to single receive mode, used for Class A motes
*
* Arguments   : *LoRa_Settings pointer to sSettings struct
*
* Return	  : message_t Status of the received message
*****************************************************************************************
*/

message_t RFM_Single_Receive(sSettings *LoRa_Settings)
{
  message_t Message_Status = NO_MESSAGE;
  
  //Change DIO 0 back to RxDone
  if (RFM_pins.DIO0 != -1)
    RFM_Write(RFM_REG_DIO_MAPPING1, 0x00);

  //Invert IQ Back
  RFM_Write(RFM_REG_INVERT_IQ, 0x67);
  RFM_Write(RFM_REG_INVERT_IQ2, 0x19);

  //Change Datarate
  RFM_Change_Datarate(LoRa_Settings->Datarate_Rx);

  //Change Channel
  RFM_Change_Channel(LoRa_Settings->Channel_Rx);

  //Switch RFM to Single reception
  RFM_Switch_Mode(RFM_MODE_RXSINGLE);

  //Wait until RxDone or Timeout
  //Wait until timeout or RxDone interrupt
  byte RegIrqFlags;
  if (RFM_pins.DIO0 != -1)
    while((digitalRead(RFM_pins.DIO0) == LOW) && (digitalRead(RFM_pins.DIO1) == LOW));
  else
  {
    RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f);
    while ((RegIrqFlags & (IRQ_RX_DONE_MASK | IRQ_RX_TIMEOUT_MASK)) == 0)
    {
      RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f);
    }
  }
  
  //Added 2ms delay before checking for timeout. 
  delay(2);
  //Check for Timeout
  bool isTimeout;
  if (RFM_pins.DIO0 != -1)
  {
    isTimeout = digitalRead(RFM_pins.DIO1) == HIGH;
  }
  else
  {
    isTimeout = (RegIrqFlags & IRQ_RX_TIMEOUT_MASK) != 0;
  }
  if (isTimeout)
  {
    //Clear interrupt register
    RFM_Write(RFM_REG_IRQ_FLAGS,0xE0);
    Message_Status = TIMEOUT;
  }

  //Check for RxDone
  bool isRxDone;
  if (RFM_pins.DIO0 != -1)
  {
    isRxDone = digitalRead(RFM_pins.DIO0) == HIGH;
  }
  else
  {
    isRxDone = (RegIrqFlags & IRQ_RX_DONE_MASK) != 0;
  }
  if(isRxDone)
  {
	  Message_Status = NEW_MESSAGE;
  }

  return Message_Status;
}

Added delay before checking for timeout has resolved my issue of repeated join for now, will have to test a little bit more to be sure that it's the main reason of repeated join requests.

As for the devnonce increment I am doing this:

//global varaibles
uint16_t myDevNonce = 0;
int eeDevNonceAddress = 10;
uint8_t isFreshStart = 0;
int eeFreshStartAddress = 15;

  //in main before initializing lora
  //get device nonces from eeprom
  EEPROM.get(eeFreshStartAddress, isFreshStart);
  DEBUG_PRINTS("isFreshStart: ");
  DEBUG_PRINTSLN(isFreshStart);
  if (isFreshStart == 1) {
    myDevNonce = 0;
    isFreshStart = 2;
    EEPROM.put(eeFreshStartAddress, isFreshStart);
  } else if (isFreshStart == 2) {
    EEPROM.get(eeDevNonceAddress, myDevNonce);
  } else {
    myDevNonce = 0;
    isFreshStart = 2;
    EEPROM.put(eeDevNonceAddress, myDevNonce);
    EEPROM.put(eeFreshStartAddress, isFreshStart);
  }
  delay(100);
  
//in the LoRaMAC.cpp file add

extern uint16_t myDevNonce;
/*
*****************************************************************************************
* Description : Function that is used to generate device nonce used in the join request function
*				This is based on a pseudo random function in the arduino library
*
* Arguments   : *Devnonce pointer to the devnonce arry of withc is unsigned char[2]
*****************************************************************************************
*/
static void Generate_DevNonce(unsigned char *DevNonce)
{
	unsigned int RandNumber;

	// RandNumber = random(0xFFFF);
	RandNumber = myDevNonce;

	DevNonce[0] = RandNumber & 0x00FF;
	DevNonce[1] = (RandNumber >> 8) & 0x00FF;
}


//in main after join successful 
  EEPROM.put(eeDevNonceAddress, myDevNonce);
  delay(100);

one suggestion/feature request for the library, can we have a structure to be passed to the lora.join() so that we can pass the joining information. This will help if user want to store and pass the parameters again after resets.

where are you adding these codes, which class?

@mirhamza708
Copy link
Author

/*
*****************************************************************************************
* Description : Function to switch RFM to single receive mode, used for Class A motes
*
* Arguments   : *LoRa_Settings pointer to sSettings struct
*
* Return	  : message_t Status of the received message
*****************************************************************************************
*/

message_t RFM_Single_Receive(sSettings *LoRa_Settings)
{
  message_t Message_Status = NO_MESSAGE;
  
  //Change DIO 0 back to RxDone
  if (RFM_pins.DIO0 != -1)
    RFM_Write(RFM_REG_DIO_MAPPING1, 0x00);

  //Invert IQ Back
  RFM_Write(RFM_REG_INVERT_IQ, 0x67);
  RFM_Write(RFM_REG_INVERT_IQ2, 0x19);

  //Change Datarate
  RFM_Change_Datarate(LoRa_Settings->Datarate_Rx);

  //Change Channel
  RFM_Change_Channel(LoRa_Settings->Channel_Rx);

  //Switch RFM to Single reception
  RFM_Switch_Mode(RFM_MODE_RXSINGLE);

  //Wait until RxDone or Timeout
  //Wait until timeout or RxDone interrupt
  byte RegIrqFlags;
  if (RFM_pins.DIO0 != -1)
    while((digitalRead(RFM_pins.DIO0) == LOW) && (digitalRead(RFM_pins.DIO1) == LOW));
  else
  {
    RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f);
    while ((RegIrqFlags & (IRQ_RX_DONE_MASK | IRQ_RX_TIMEOUT_MASK)) == 0)
    {
      RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f);
    }
  }
  
  //Added 2ms delay before checking for timeout. 
  delay(2);
  //Check for Timeout
  bool isTimeout;
  if (RFM_pins.DIO0 != -1)
  {
    isTimeout = digitalRead(RFM_pins.DIO1) == HIGH;
  }
  else
  {
    isTimeout = (RegIrqFlags & IRQ_RX_TIMEOUT_MASK) != 0;
  }
  if (isTimeout)
  {
    //Clear interrupt register
    RFM_Write(RFM_REG_IRQ_FLAGS,0xE0);
    Message_Status = TIMEOUT;
  }

  //Check for RxDone
  bool isRxDone;
  if (RFM_pins.DIO0 != -1)
  {
    isRxDone = digitalRead(RFM_pins.DIO0) == HIGH;
  }
  else
  {
    isRxDone = (RegIrqFlags & IRQ_RX_DONE_MASK) != 0;
  }
  if(isRxDone)
  {
	  Message_Status = NEW_MESSAGE;
  }

  return Message_Status;
}

Added delay before checking for timeout has resolved my issue of repeated join for now, will have to test a little bit more to be sure that it's the main reason of repeated join requests.
As for the devnonce increment I am doing this:

//global varaibles
uint16_t myDevNonce = 0;
int eeDevNonceAddress = 10;
uint8_t isFreshStart = 0;
int eeFreshStartAddress = 15;

  //in main before initializing lora
  //get device nonces from eeprom
  EEPROM.get(eeFreshStartAddress, isFreshStart);
  DEBUG_PRINTS("isFreshStart: ");
  DEBUG_PRINTSLN(isFreshStart);
  if (isFreshStart == 1) {
    myDevNonce = 0;
    isFreshStart = 2;
    EEPROM.put(eeFreshStartAddress, isFreshStart);
  } else if (isFreshStart == 2) {
    EEPROM.get(eeDevNonceAddress, myDevNonce);
  } else {
    myDevNonce = 0;
    isFreshStart = 2;
    EEPROM.put(eeDevNonceAddress, myDevNonce);
    EEPROM.put(eeFreshStartAddress, isFreshStart);
  }
  delay(100);
  
//in the LoRaMAC.cpp file add

extern uint16_t myDevNonce;
/*
*****************************************************************************************
* Description : Function that is used to generate device nonce used in the join request function
*				This is based on a pseudo random function in the arduino library
*
* Arguments   : *Devnonce pointer to the devnonce arry of withc is unsigned char[2]
*****************************************************************************************
*/
static void Generate_DevNonce(unsigned char *DevNonce)
{
	unsigned int RandNumber;

	// RandNumber = random(0xFFFF);
	RandNumber = myDevNonce;

	DevNonce[0] = RandNumber & 0x00FF;
	DevNonce[1] = (RandNumber >> 8) & 0x00FF;
}


//in main after join successful 
  EEPROM.put(eeDevNonceAddress, myDevNonce);
  delay(100);

one suggestion/feature request for the library, can we have a structure to be passed to the lora.join() so that we can pass the joining information. This will help if user want to store and pass the parameters again after resets.

where are you adding these codes, which class?

Keeping track of devnonce should be done in your application main.cpp file. There are no classes in the library I guess. These are C type functions in Loramac.cpp file and the RFM95.cpp file.

@areytechai
Copy link

@mirhamza708

i changed the library and source code with your code

but problem is,

first connects but if i made reset, stops retrying connect and devnonce problem still acquire,

kindly can you share the library to [email protected]

thanks alot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants