Author Topic: RFM69 driver: Interrupt based wait for PacketSent  (Read 1557 times)

mharizanov

  • NewMember
  • *
  • Posts: 14
RFM69 driver: Interrupt based wait for PacketSent
« on: September 18, 2020, 08:06:04 AM »
Folks,
is there a particular reason why the MCU isn't slept and woken by an interrupt in the RFM69 sendFrame routine, but rather is actively polling for PacketSent?

https://github.com/LowPowerLab/RFM69/blob/e0e9318f1590eb2b6cfbbe2237128d976b9b1e9c/RFM69.cpp#L351

I haven't tested how long it takes in this loop for a 66 byte max size payload TX, but my gut tells me it might be worth changing to IRQ based handling?

Cheers

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #1 on: September 18, 2020, 11:28:58 AM »
Hi mharizanov!

Very good question. I guess I need more motivating questions like this to get my attention out of my busy routines.

Thsi was never optimized and it generally works fine. In terms of battery power I can say that other hardware optimizations are far more important.
From my measurements, a 61byte packet takes around 10-12ms to transmit. So a good saving could be achieved here.
PacketSent interrupt is indeed available in Packet Mode on DIO0 (wired to Moteino pin D2, INT0).

As far as polling the register, I believe I implemented that "recently" because a digital read may perhaps miss the HIGH on DIO0.
BUT it would be a good opportunity to improve this. A new interrupt handler would probably be needed along with attach/detach while packet is sending. But at the very least sleeping with WDT is better than just actively polling for up to ~10ms.

Would that add weeks, months, years to a mote? Not sure, depends on many factors, how often and at what power it transmits, etc.
Any help in coding and testing this would be appreciated.

I would start with basic WDT sleep and go from there see below. Note this library is now supporting SAMD as well, so it needs some #ifdefs to sleep things properly in both platforms.
If you have working code and want to jump start this let me know.

EDIT: WDT sleep however has a downside, the lowest resolution is 15ms. So ... this has to be looked at in closer detail and evaluate what is the best approach. ListenMode sleep could sleep for less time but it cannot be used while the radio is in TX mode so its not an option.
See this forum post that talks about this and perhaps possible to trigger a WDT interrupt on counter match rather than overflow and thus achieve higher resolution (ex: 1ms sleep):
https://lowpowerlab.com/forum/low-power-techniques/how-to-do-put-the-mega-into-a-1-millisecond-low-power-sleep-mode/
EDIT2: OK the interrupt approach is probably better than trying to sleep for low resolution times and poll in between.
« Last Edit: September 18, 2020, 11:50:23 AM by Felix »

mharizanov

  • NewMember
  • *
  • Posts: 14
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #2 on: September 18, 2020, 02:37:42 PM »
Thanks for the detailed comments. I haven't done any coding yet - just wanted to check if there was a previous discussion on this subject first. Probably such optimization would make sense for payloads above certain size and/or frequent transmissions. Another possible benefit of such approach would be the potentially reduced voltage drop when using low capacity power sources (eg CR2032), energy harvesters or old batteries with increased internal resistance and thus preventing brown-outs.
I'll spend some time experimenting and post updates here.
Cheers!

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #3 on: September 18, 2020, 04:34:20 PM »
I'm certainly interested in this.
I just don't have the time to dedicate right now, but if you're willing to code and test before I can get to it you will certainly have my attention.
Much appreciated!

mharizanov

  • NewMember
  • *
  • Posts: 14
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #4 on: September 19, 2020, 07:40:25 AM »
Some quick and dirty testing results, AVR specific.
Replacing the register poll loop pointed out in my original post with:

Code: [Select]
  writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_00); // Enable interrupt on DIO0 when "Packet Sent"
  LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); // 1s max wait then resume regardless
  _haveData = false; //We abused the ISR to wake up, so cleanup after ourselves

results in 3.6mA power consumption drop for the duration of sending (10ms for 60 byte payload):



White line is the power consumption with the improved code posted above, blue is with the original library. Captured with my Current ranger and a DS1052E.
The test sketch is sending with retry a 60 byte payload (10ms time to send, 30ms ACK wait). The payload is intentionally not received on the other end, so 3 retries commence.
Obviously in a "send with retry" scenario the benefits are multiplied by the number of failed sends.

Another area for improvement is the "sendWithRetry" routine, will try a IRQ based wait rather than polling, since the wait for ACK loop is typically 30ms, improvements will be significant.

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #5 on: September 19, 2020, 08:22:35 AM »
Excellent. And you use a CurrentRanger - w00t  w00t ! 8)

What if we SLEEP_FOREVER (WDT off)? That would shave another ~4mA. We are then assuming the PACKET_SENT interrupt will happen, not sure if there's the possibility for it not to.
And yes improving the sendWithRetry() while would also be great.

mharizanov

  • NewMember
  • *
  • Posts: 14
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #6 on: September 19, 2020, 09:10:46 AM »
I think the overhead for the WDT is more like 6ยตA - not so significant for such a short duration. Keeping it on puts a safety net in case the IRQ never comes - thus preventing a node falling in a forever deep sleep.

One issue I see with sleeping in TX wait is the damages serial output. Since it is async, it gets corrupted while we sleep. A Serial.flush() is needed before doing any TX. This shorcoming makes in unsuitable for release as it will break many existing sketches.


Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #7 on: September 19, 2020, 01:15:17 PM »
Ah yes I really should have meant 4uA, my mind was somewhere else. I guess you're correct, its a saving but not much, and safer to sleep 1s rather than forever.

It would damage the serial output if there's data still in the serial output buffer, so a Serial.flush() would take care of that.
It would also stop other timers that are going. And if someone uses the WDT timer, I think that would cause a compile error since we can't have 2 handlers for the WDT (one in the LowPower library - enabled by the LowPower.powerdown() and one in the sketch).

Maybe a higher current sleep mode then, which keeps timers enabled so stuff can still crank out of the MCU. Let me know your thoughts.

mharizanov

  • NewMember
  • *
  • Posts: 14
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #8 on: September 21, 2020, 02:59:29 AM »
Taking a step back - fixing this using AutoMode will be non-breaking, non-blocking and all is done in the hardware with this configuration:

Automatic transmission (AutoTx) : Mode = Standby, IntermediateMode = Tx, EnterCondition = FifoLevel, ExitCondition =
PacketSent


I'll fiddle with this idea later, but it looks like the proper way to solve the issue.


[edit] I think I got excited about AutoMode too early, it *will* break existing code when using sendWithRetry, as being non-blocking and will immediately return control. ACKReceived will then force the mode to RX, possibly corrupting the ongoing transmission...

« Last Edit: September 21, 2020, 03:11:23 AM by mharizanov »

mharizanov

  • NewMember
  • *
  • Posts: 14
Re: RFM69 driver: Interrupt based wait for PacketSent
« Reply #9 on: September 23, 2020, 09:34:36 AM »
Update:

I'm not sure how to properly fix this in the current library without breaking existing code.

FYI I wrote my own RFM69 driver sometime ago (FreeRTOS on ESP32, non Arduino compatible) for a commercial project (closed source) that is event/message driven finite-state machine (FSM) with message queue and callbacks. Diagram of what I have:

It is efficient because it is non-bloking and allows for the uC to do other work (or sleep) as much as possible, but unfortunately not compatible with smaller uCs like the Atmega328p - this is why I used your library and noticed it can be improved.

So I guess I leave it here - anyone interested can apply the change mentioned above and get instant power consumption savings at the cost of broken Serial output.
« Last Edit: September 23, 2020, 09:36:59 AM by mharizanov »