beegee-tokyo / SX126x-Arduino

Arduino library to use Semtech SX126x LoRa chips and modules to communicate
MIT License
235 stars 64 forks source link

Low power mode using semaphore in RAK4630 #42

Closed Ganesh-Shanbhag-face closed 3 years ago

Ganesh-Shanbhag-face commented 3 years ago

Hi I am working on low power application firmware on RAK4630 by making use of semaphores I am following the examples given. I am using only one semaphore for all the tasks my aim is to hold the execution until some other task got finished and returns success or failure. But I am not getting the expected output.

I have given a sample code from my work

int eventNum = 0;

/* Semaphore used by events to wake up loop task */
SemaphoreHandle_t taskEvent = NULL;

/* Timer to wakeup task frequently and send message */
SoftwareTimer taskWakeupTimer;

void periodicWakeup(TimerHandle_t unused)
{
  eventNum = 1;

  // Give the semaphore, so the loop task will wake up
  xSemaphoreGiveFromISR(taskEvent, pdFALSE);
}

void setup() {
  // Create the LoRaWan event semaphore
  taskEvent = xSemaphoreCreateBinary();

  // Initialize semaphore
  xSemaphoreGive(taskEvent);

  // Initialize timer
  taskWakeupTimer.begin(60000, periodicWakeup);
  taskWakeupTimer.start();

  // Please assume these things are properly done
  // Initialize LoRa chip.
  // Initialize LoRaWAN in OTAA mode
}

void loop() {
    switch (eventNum) {
        case 0:
              lmh_join();
              // Hold here until released from the LoRa handlers
              // <<< It should hold here until lorawan_has_joined_handler or lorawan_has_join_failed should be executed >>>
              // But its not holding here it will continue to execute 
              xSemaphoreTake(taskEvent, 10);

              eventNum = 1;
              break;

         // Other cases that don't use semaphores
         case 1:
             break;
    }

    // Sleep here till wake up by timer or other events
    if (xSemaphoreTake(taskEvent, portMAX_DELAY) == pdTRUE) 
    {
        eventNum = 1;
    }
}

/* I am providing only two handlers here please assume other handlers also present */
void lorawan_has_joined_handler(void)
{
    eventNum = 1;
     // Give the semaphore, so the loop task will continue
     xSemaphoreGiveFromISR(taskEvent, pdFALSE);
}

void lorawan_has_join_failed(void)
{
     eventNum = 1;
     // Give the semaphore, so the loop task will continue
     xSemaphoreGiveFromISR(taskEvent, pdFALSE);
}
beegee-tokyo commented 3 years ago

In case 0: you are using xSemaphoreTake(taskEvent, 10);, which waits only 10 ticks to get the semaphore and then returns, whether it got the semaphore or not. If you want to wait here until the join procedure was successful, you need to use here xSemaphoreTake(taskEvent, portMAX_DELAY) as well.

Ganesh-Shanbhag-face commented 3 years ago

Hi I tried in multiple ways and still its not working. Is there any other way to achieve sleep in nRF modules to save power. Don't they have sleep modes as found on other controllers. The problem may be I am not that much known to freeRTOS.

Ganesh-Shanbhag-face commented 3 years ago

While working I noticed that if the gateway which is running Network Server if turned off then in the lmh_send function it will return LMH_SUCCESS and continuously displays like below.

19:34:06.403 -> <LM> OnRadioTxDone
19:34:06.403 -> <LM> OnRadioTxDone => RX Windows #1 995 #2 2002
19:34:11.379 -> <RADIO> RadioIrqProcess => IRQ_RX_TX_TIMEOUT
19:34:11.379 -> <LM> OnRadioRxTimeout
19:34:11.645 -> <LM> OnRadioTxDone
19:34:11.645 -> <LM> OnRadioTxDone => RX Windows #1 995 #2 2002
19:34:16.621 -> <RADIO> RadioIrqProcess => IRQ_RX_TX_TIMEOUT
19:34:16.621 -> <LM> OnRadioRxTimeout
19:34:17.118 -> <LM> OnRadioTxDone
19:34:17.118 -> <LM> OnRadioTxDone => RX Windows #1 1002 #2 2002
19:34:22.097 -> <RADIO> RadioIrqProcess => IRQ_RX_TX_TIMEOUT
19:34:22.097 -> <LM> OnRadioRxTimeout
19:34:22.628 -> <LM> OnRadioTxDone
19:34:22.628 -> <LM> OnRadioTxDone => RX Windows #1 1002 #2 2002
19:34:27.636 -> <RADIO> RadioIrqProcess => IRQ_RX_TX_TIMEOUT
19:34:27.636 -> <LM> OnRadioRxTimeout
19:34:28.563 -> <LM> OnRadioTxDone

How to stop sending if it fails to send after some intervals.

beegee-tokyo commented 3 years ago

If you send the packet as LMH_CONFIRMED_MSG, the node is waiting for an ACK from the LoRaWAN server. If the server (or gateway) is down, it retries 8 times resending the packet and waiting for the ACK before it gives up.

If you send the packet as LMH_UNCONFIRMED_MSG, the packet is sent once and there is no check if the packet was received by a gateway or server.

The type of message is selected with the second parameter in lmh_send()

/**@brief Send data
 *
 * @param app_data Pointer to data structure to be sent
 * @param is_txconfirmed do we need confirmation?
 *
 * @retval error status
 */
lmh_error_status lmh_send(lmh_app_data_t *app_data, lmh_confirm is_txconfirmed);
beegee-tokyo commented 3 years ago

About your code, I do not understand why you make it so difficult. Here is my approach:

/** 
 * Type of event
 * 0 = Join success
 * 1 = Join fail
 * 2 = Timer wakeup
 */
int eventNum = 0;

/* Semaphore used by events to wake up loop task */
SemaphoreHandle_t taskEvent = NULL;

/* Timer to wakeup task frequently and send message */
SoftwareTimer taskWakeupTimer;

void periodicWakeup(TimerHandle_t unused)
{
    // Set eventNum to 2 ==> Timer wakeup
    eventNum = 2;

    // Give the semaphore, so the loop task will wake up
    xSemaphoreGiveFromISR(taskEvent, pdFALSE);
}

void setup()
{
    // Create the LoRaWan event semaphore
    taskEvent = xSemaphoreCreateBinary();

    // Initialize semaphore
    xSemaphoreGive(taskEvent);

    // Initialize timer
    taskWakeupTimer.begin(60000, periodicWakeup);

    /************************************************************/
    // Don't start timer here. Start it after JOIN was successful
    /************************************************************/
    // taskWakeupTimer.start();

    // Please assume these things are properly done
    // Initialize LoRa chip.
    // Initialize LoRaWAN in OTAA mode

    // Take the semaphore at the end of setup() makes sure the loop() goes to sleep
    xSemaphoreTake(taskEvent, 1);
}

void loop()
{
    // Sleep here till wake up by timer or other events
    xSemaphoreTake(taskEvent, portMAX_DELAY);

    Serial.println("Loop woke up, time to do something");
    switch (eventNum)
    {
    case 0:
        Serial.println("Join successfull, start your work");
        break;
    case 1:
        Serial.println("Join failed. You can retry or blink a LED or ...");
        break;
    case 2:
        Serial.println("Loop woke up, time to do something");
        break;
    default:
        Serial.println("This should never happen");
    }
}

/* I am providing only two handlers here please assume other handlers also present */
void lorawan_has_joined_handler(void)
{
    // Set eventNum to 0 ==> Successful joined
    eventNum = 0;
    // Give the semaphore, so the loop task will continue
    xSemaphoreGiveFromISR(taskEvent, pdFALSE);

    /************************************************************/
    // Start timer here after JOIN was successful
    /************************************************************/
    taskWakeupTimer.start();
}

void lorawan_has_join_failed(void)
{
    // Set eventNum to 1 ==> Join failed
    eventNum = 1;
    // Give the semaphore, so the loop task will continue
    xSemaphoreGiveFromISR(taskEvent, pdFALSE);
}
Ganesh-Shanbhag-face commented 3 years ago

@beegee-tokyo That helped me a lot. I noticed while sending using LMH_CONFIRMED_MSG although it fails to send it returns LMH_SUCCESS and then it will show IRQ_RX_TX_TIMEOUT 8 times on the terminal. Why it returns success if it fails?

beegee-tokyo commented 3 years ago

lmh_send() is not blocking. It only gives the data packet to the LoRaMAC state machine, which is processing the sending (and retries). In the current version of the library there is no feedback if the packet was actually sent.

I am testing right now the next version of the library which has 2 additional callbacks for confirmed and unconfirmed packet sending finished.

Ganesh-Shanbhag-face commented 3 years ago

@beegee-tokyo Thanks for the clarification. It would be better if we know is the transmission a success or a failure in LMH_CONFIRMED_MSG with a callbacks or by any flags.

beegee-tokyo commented 3 years ago

Callback for confirmed message will have a flag which reports success (ACK received) or failure (No ACK received). For the unconfirmed message the callback is triggered when the TX - RX1 window - RX2 window sequence is finished. For unconfirmed message it is not possible to tell if it was successful or not.

Ganesh-Shanbhag-face commented 3 years ago

@beegee-tokyo That's a good idea. But I am not sure for unconfirmed message why we need a callback because the application is not bothered about send success or failures. Its like just send and forget.

beegee-tokyo commented 3 years ago

You need it for example in this case when you need to send multiple packets, you need to know when the transceiver is ready to send the next packet.

Ganesh-Shanbhag-face commented 3 years ago

Yes got it.

Ganesh-Shanbhag-face commented 3 years ago

@beegee-tokyo I noticed once it successfully joins in OTAA and then by chance the gateway is turned off lmh_join_status_get() is returning LMH_SET by which it is getting difficulty in checking if it is connected to the gateway properly before sending the payload. And it tries to send the payload and fails. So is there any way to know before sending it is connected to the gateway properly?

beegee-tokyo commented 3 years ago

No, LoRaWAN does not support such a connection check when using unconfirmed packets. The only option you have is to use confirmed packets and if transmission fails (no ACK received), assume you lost connection to the gateway or LoRaWAN server.

Ganesh-Shanbhag-face commented 3 years ago

Okay in the present version of the library if I use confirmed packets then how can I get the status of ACK or No ACK?

beegee-tokyo commented 3 years ago

With the next version of the library I add callbacks for both confirmed and unconfirmed TX finished. These new features are in testing right now.

beegee-tokyo commented 3 years ago

Callbacks for TX-RX cycle finished and result in case of confirmed message added with library version 2.0.2.

Examples how to use will be added soon. Until then, you can see usage here:

Ganesh-Shanbhag-face commented 3 years ago

@beegee-tokyo Good to have this feature. Thanks for adding it for better handling in the code.