I’ve been using lmh_send() to send out data over lora. As far as i can tell, it can return 3 statuses:
LMH_SUCCESS
LMH_BUSY
LMH_ERROR
As far as i understand, the LMH_SUCCESS is returned whenever the sending can be done and i should trust that it is actually send. If i turn on confirmation, then i just have to trust that it is confirmed. Is there a way t ofigure out if it’s not confirmed and/or to retry it?
LMH_SUCCESS
means the packet is queued up for sending. It does not mean the packet was already sent.
Use provided callbacks to check whether the packet was really sent.
LMH_BUSY
can have multiple reasons
you are sending another packet while the previous packet is still processed, then it is ok to retry. But the better solution is to wait for the TX finished and TX error callbacks before sending another packet.
Something went wrong in the sequencer of the stack. Can happen, the library still has some bugs deep down in the stack. In this case it is better to re-initialize the whole LoRa setup
LMH_ERROR
Is thrown when the packet is too large for the selected data rate. Adjust the data rate and retry to send the packet.
And just to be clear - when you get LMH_SUCCESS is it the code that handles the sending and retry-ing, or is it purely the chip itself?
I’m wondering about deep sleep and/or power consumption. I use a semaphore on the RAK4630 to make it go into deep sleep and save power. So, i’d do a LMH_SEND() and then eventually a xSemaphoreTake() to wait for the next time-based interrupt to process.
In that case, will it just send? When with a message that needs confirmation be timed out? or cancelled from sending?
The sending is handled in the stack with timers and interrupts. You can use the semaphore to put your loop into sleep immediately after calling lms_send().
For feedback when the TX is finished, if you look into the library readme, you can see the callbacks for TX: Callbacks
Unfortunately the examples are not fully up to date and these callbacks are not shown anywhere.
For TX the two callbacks are lorawan_unconfirmed_finished:
lorawan_unconfirmed_finished is called after a unconfirmed packet send is finished. It is called after RX1 window and RX2 window timed out or after a downlink from the LoRaWAN server was received.
It just tells you that TX is finished.
lorawan_confirmed_finished:
lorawan_confirmed_finished is called after a confirmed packet send is finished. It has a paramter that tells if a confirmation (ACK) was received from the LoRaWAN server or not.
Yes, i’ve seen the callbacks and have been using it to set a global variable there.
After the lmh_send, i then check that global variable after a delay() to figure out if the confirmed message failed. If it did fail, i try to send again. The delay() is in a loop.
The resaon i’ve been doing that is because it seems like the callback doesn’t happen when the device is in sleep mode. Is there a way around that?
The callbacks happen all the time if you use semaphores. I am doing this in the WisBlock-API and it works.
The callbacks are called from a separate task that wakes up after the LoRa transceiver has initiated an interrupt because of an event.
Ok. Is the serial output not provided from the callback or something? I don’t have an option to test now, but from what i saw, the callback code wasn’t run until the device woke up again - i’m probably wrong…
My code is like this (edited to reduce the paste):
SoftwareTimer g_taskWakeupTimer;
SemaphoreHandle_t g_taskEvent = NULL;
EventType g_EventType = EventType::None;
void setup() {
g_taskEvent = xSemaphoreCreateBinary();
xSemaphoreGive(g_taskEvent);
g_EventType = EventType::Timer;
g_taskWakeupTimer.begin(300 * 1000, periodicWakeup);
g_taskWakeupTimer.start();
}
void periodicWakeup(TimerHandle_t unused)
{
// Give the semaphore, so the loop task will wake up
g_EventType = EventType::Timer;
xSemaphoreGiveFromISR(g_taskEvent, pdFALSE);
}
void loop()
{
SERIAL_LOG("LOOP()");
if (xSemaphoreTake(g_taskEvent, portMAX_DELAY) == pdTRUE)
{
switch (g_EventType)
{
case EventType::LoraDataReceived:
handleConfigurationReceived();
break;
case EventType::Timer:
// do stuff
lmh_send(data, confirmed);
break;
case EventType::None:
default:
SERIAL_LOG("In loop, but without correct g_EventType")
break;
};
}
xSemaphoreTake(g_taskEvent, 10);
}
In the above code, the callback code should simply run even though i’m sleeping the device? Happy if that’s the case
You have to set the g_EventType in the callback and give the g_taskEvent semaphore in the callback.
Then xSemaphoreTake(g_taskEvent, portMAX_DELAY) will stop waiting for the semaphore and handle the event.