beegee-tokyo / SX126x-Arduino

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

DeepSleep enqueue multiple transmissions #35

Closed jpmeijers closed 3 years ago

jpmeijers commented 3 years ago

Using the RAK4631-DeepSleep-LoRaWan example, I want to wake up periodically (say every 60 minutes) to send multiple LoRaWan messages.

My code looks similar to this:

      /// \todo read sensor or whatever you need to do frequently
      uint8_t dataBuffer[64];
      size_t bufferSize;
      POWER ON SENSORS();

      bufferSize = READ_SENSOR_A(dataBuffer);
      if(sendLoRaFrame(1, dataBuffer, bufferSize)) {
        Serial.println("LoRaWan: sent READ_SENSOR_A");
      }
      else
      {
        Serial.println("LoRaWan: packet send failed");
      }

      bufferSize = READ_SENSOR_B(dataBuffer);
      if(sendLoRaFrame(2, dataBuffer, bufferSize)) {
        Serial.println("LoRaWan: sent READ_SENSOR_B");
      }
      else
      {
        Serial.println("LoRaWan: packet send failed");
      }

      bufferSize = READ_SENSOR_C(dataBuffer);
      if(sendLoRaFrame(3, dataBuffer, bufferSize)) {
        Serial.println("LoRaWan: sent READ_SENSOR_C");
      }
      else
      {
        Serial.println("LoRaWan: packet send failed");
      }

      bufferSize = READ_SENSOR_D(dataBuffer);
      if(sendLoRaFrame(4, dataBuffer, bufferSize)) {
        Serial.println("LoRaWan: READ_SENSOR_D");
      }
      else
      {
        Serial.println("LoRaWan: packet send failed");
      }

The problem is that only the the first call to sendLoRaFrame is accepted. The other three calls to this function fails. This is understandable as the TX is likely still in progress, as sendLoRaFrame only enqueues the message for transmission, and does not wait for it to finish.

What would the best solution be to enqueue multiple messages for transmission?

beegee-tokyo commented 3 years ago

@jpmeijers yes, that is a problem. What is missing is a callback that tells you that the packet (I hope that is the correct term šŸ˜‰) is sent.

I am thinking about it for some time, but was too busy to implement it.

Until it is done the only work-around I can think of is a delay() with the length of RX window 1 + 2 time + estimated time of sending. Or retry to send until it succeeds.

beegee-tokyo commented 3 years ago

@jpmeijers I had a look into the library. There are two possibilities: 1) new function call lmh_send_blocking() which will only return after sending is finished, including the RX1 and RX2 windows. 2) new function call lmh_mac_busy() which will return true while the sending is ongoing, including the RX1 and RX2 windows.

With solution (1), users can decide if they a) want to wait for sending/receiving finished before continuing or b) just use lmh_send() and only check if the packet was enqueued successfully for sending. With solution (2) the user has to implement the waiting by himself, again it is up to him if he wants to wait or not.

A callback function would be possible as well, but that requires more effort in the user code.

jpmeijers commented 3 years ago

In the meantime I implemented a blocking call myself (in the RAK deepsleep example):

bool sendLoRaFrameBlocking(uint8_t port, uint8_t* dataBuffer, size_t bufferSize, unsigned long timeout=30000) {
  unsigned long startTime = millis();
  bool result = sendLoRaFrame(1, dataBuffer, bufferSize);
  while(!result && millis()-startTime < timeout) {
    delay(1000);
    result = sendLoRaFrame(1, dataBuffer, bufferSize);
  }

  return result;
}

All I am wondering is how all this fit in with the deepsleep and scheduling. As far as I can see there is a RTOS running in the background handling the scheduling. Will the delay(1000); in the blocking function give control over to another process, or maybe even the deepSleep() process? In essence I want to keep power usage down, and I'm afraid that a blocking TX function will increase power usage.

beegee-tokyo commented 3 years ago

While the SX1262 is sending the packet, then opening RX1 and RX2 window, there are background activities running that control the MAC state machine workflow. So neither the SX1262 nor the nRF52 are going into deep sleep while the MAC state-machine is running. There are 2 processes active, one is for the SX1262 IRQ handling (called on IRQ event), a second one is for the MAC state machine (called every 1 second).

I don't think you will see a big difference between the blocking TX call or your delay() solution.

jpmeijers commented 3 years ago

Unfortunately my code doesn't seem to work. The sketch goes into a locked-up state (freezes) somewhere in or between these blocking tx calls. I'm trying to trace it down better.

Update: I think the lockup is related to something else in the DeepSleep example. Serial debug output is intermittent, and the lockup happens while reading data from RS485.

beegee-tokyo commented 3 years ago

From where do you initiate the lmh_send()? I am testing my lmh_send_blocking() and it fails when I call it from within the timer callback (SoftwareTimer or the appTimer callback from the example code). But if I use separate task to initiate lmh_send_blocking(), it works fine. I can send 3 consecutive packets without error. Log:

18:26:33.143 -> =====================================
18:26:33.143 -> LoRaWAN Test
18:26:33.143 -> Region: AS923
18:26:33.143 -> =====================================
18:26:33.143 -> Starting lora_hardware_init
18:26:33.190 -> lmh_init
18:26:33.377 -> lmh_setSubBandChannels
18:26:33.473 -> lmh_join
18:26:59.276 -> Network Joined
18:26:59.276 -> Starting app timer
18:26:59.276 -> Sending frame
18:27:01.295 -> Sending 1 result = 0
18:27:03.314 -> Sending 2 result = 0
18:27:05.091 -> Sending 3 result = 0
18:27:19.299 -> Sending frame
18:27:21.321 -> Sending 1 result = 0
18:27:26.807 -> Sending 2 result = 0
18:27:32.333 -> Sending 3 result = 0
18:27:39.268 -> Sending frame
18:27:44.792 -> Sending 1 result = 0
18:27:50.313 -> Sending 2 result = 0
18:27:55.838 -> Sending 3 result = 0
18:27:59.288 -> Sending frame
18:28:04.809 -> Sending 1 result = 0
18:28:10.336 -> Sending 2 result = 0
18:28:15.812 -> Sending 3 result = 0
18:28:19.295 -> Sending frame
18:28:24.798 -> Sending 1 result = 0
18:28:30.325 -> Sending 2 result = 0
18:28:35.851 -> Sending 3 result = 0
jpmeijers commented 3 years ago

I think the lora transmit is working fine. I've tracked the issue down to RS485. I'll file a proper bug report in the correct place (RAK4630 repo maybe).

christianpaetz commented 3 years ago

I like to run the Lorawan on ESP32 that goes into deep sleep between sends. I did:

So far so good. It works but I would prefer a better way to indicate to go back to sleep. A 'blocking send' is perfect! At the moment I just inserted a callback into OnRadioRxDone which sends the ESP back to deep sleep after confirmation received but I don't want to alter your library code too much.

I wonder if this change should become part of the code. Can share diff plus example code if needed.

beegee-tokyo commented 3 years ago

@christianpaetz I am still thinking what the best way is to notify the application that the TX (+RX windows) is finished. Just using OnRadioRxDone is not sufficient, because there might be as well an RX timeout if the LoRaWan server or gateway fails to send the confirmation. And another failure that can happen is that the received confirmation got compromised and fails with CRC error.

christianpaetz commented 3 years ago

@beegee-tokyo Agree. This was more of a quick hack to validate if it's generally possible to let a connection survive ESP deep sleep. Let me know if I can help with something.

anythingwithawire commented 3 years ago

are you able to roughly indicate power usage over a day/hour with deep sleep?

beegee-tokyo commented 3 years ago

@anythingwithawire No, I cannot, it depends on too many factors

anythingwithawire commented 3 years ago

Ok, just trying to understand with the ESP32 and E22 or E32 900D, what the sleep current draw is like, eg <1mA

beegee-tokyo commented 3 years ago

You will have to measure with your specific ESP32 board, I have 5 or 6 different brands of ESP32 breakout boards and all are different in power consumption.

christianpaetz commented 3 years ago

The ESP32 as such or better the ULP needs few uA in deep sleep but the electronics burns the power. If you use e.g. DoiT ESP32 DKIT V1 you have a power-hungry LDO on board. Best for such tests is the firebeetle from DTRobots. Here I was able to measure 2 uA in deep sleep.

anythingwithawire commented 3 years ago

thx, I am a little familiar with the ESP32 deep sleep and the different boards, I usually just go for a bare WROOM or ESP32S chip if really looking to avoid the various associated power draws, but trying to understand the LORA board it's deep sleep. Does it actually shut down and wake up at predetermined time intervals to align with a potential message being sent to it (with associated time syching to remain aligned), or does it have a low power listening (as in rx) mode that hears the preamable over the air and then wakes the rest of it up?

On Fri, May 28, 2021 at 4:15 PM christianpaetz @.***> wrote:

The ESP32 as such or better the ULP needs few uA in deep sleep but the electronics burns the power. If you use e.g. DoiT ESP32 DKIT V1 you have a power-hungry LDO on board. Best for such tests is the firebeetle from DTRobots. Here I was able to measure 2 uA in deep sleep.

ā€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/beegee-tokyo/SX126x-Arduino/issues/35#issuecomment-850239417, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJOHDS4FNXZS3QLA5YXPMFDTP5GK5ANCNFSM42LN5AKA .

christianpaetz commented 3 years ago

I think what you refer on (intervals) is called Class B and no, this lib does not support it. We currently try using wakeup on RX.. To avoid unwanted wakeups by 'foreign' packets we play with FSK preample as wakeup beam and then switch over to LORA. However, thats WIP.

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: