Skip to content

Implement "Reliable Datagram" #20

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

Closed
jerryneedell opened this issue Dec 16, 2019 · 23 comments
Closed

Implement "Reliable Datagram" #20

jerryneedell opened this issue Dec 16, 2019 · 23 comments
Assignees

Comments

@jerryneedell
Copy link
Contributor

The Radiohead library has a "Reliable Datagram" mode that uses acknowledgement and retries to insure packet delivery.

Is there interest in implementing that here. I'll be happy to take it on, but wanted to see if there was any concern or interest.

@brentru
Copy link
Member

brentru commented Dec 18, 2019

@jerryneedell I'd love reliable packet delivery for the RFM9x/69 modules (adafruit/Adafruit_CircuitPython_RFM9x#34). I feel this would be a mode which should be enabled by default.

@BiffoBear
Copy link

@jerryneedell reliable datagram in the driver would be awesome. At the moment, I work around this by sending every packet twice and having the server ignore the duplicate if it sees it. I drop about 1 packet in 5000 with this, but it's not elegant. If I can help with testing, I'll be available mid-January with a Raspi and a couple of ItsyBitsy M4s.

@jerryneedell jerryneedell self-assigned this Dec 27, 2019
@Pythonaire
Copy link

Pythonaire commented Jan 1, 2020

@jerryneedell reliable datagram in the driver would be awesome. At the moment, I work around this by sending every packet twice and having the server ignore the duplicate if it sees it. I drop about 1 packet in 5000 with this, but it's not elegant. If I can help with testing, I'll be available mid-January with a Raspi and a couple of ItsyBitsy M4s.

I use the middle way, RH Datagram stack on SAMD21 board on sender side and raspi as a receiver.. It's not the full reliable version included the ACK requirement, but you can set sender functions ex. to wait until the packets is properly sended. My tests: it's a good idea to give the receiver time to handle the packets. In my case, after "RHDatagram.waitPacketSent()" I set a delay of 500ms. Now it works perfect. No bad packets, no ignores.

@jerryneedell
Copy link
Contributor Author

@jerryneedell reliable datagram in the driver would be awesome. At the moment, I work around this by sending every packet twice and having the server ignore the duplicate if it sees it. I drop about 1 packet in 5000 with this, but it's not elegant. If I can help with testing, I'll be available mid-January with a Raspi and a couple of ItsyBitsy M4s.

I use the middle way, RH Datagram stack on SAMD21 board on sender side and raspi as a receiver.. It's not the full reliable version included the ACK requirement, but you can set sender functions ex. to wait until the packets is properly sended. My tests: it's a good idea to give the receiver time to handle the packets. In my case, after "RHDatagram.waitPacketSent()" I set a delay of 500ms. Now it works perfect. No bad packets, no ignores.

Thank you for this example of how you are using it. I have been working on implementing the Reliable Datagram into the CircuitPython Library and I have also found it useful to allow a small delay before sending the ACK especially when the receiver is a Raspberry Pi. -- I have made it configurable. I am still working out some issues but hope to heave a version ready for others to test soon.

@BiffoBear
Copy link

I use the middle way, RH Datagram stack on SAMD21 board on sender side and raspi as a receiver.. It's not the full reliable version included the ACK requirement, but you can set sender functions ex. to wait until the packets is properly sended. My tests: it's a good idea to give the receiver time to handle the packets. In my case, after "RHDatagram.waitPacketSent()" I set a delay of 500ms. Now it works perfect. No bad packets, no ignores.

Thanks for sharing that, it's an interesting solution.

I use CircuitPython on SAMD21 and SAMD51 boards for sending and Python on the Raspberry Pi 3B as the server.

On my Raspi, which receives from multiple stations, I've implemented threading in the Python app that receives data and writes it out to the database. The main thread sleeps until the RFM69 interrupt occurs. Then the interrupt callback function reads the radio data, writes it to a queue and goes back to sleep. A daemon thread sleeps until something is added to the queue, reads the data, processes it, writes it to the database and goes back to sleep.

On average, the program cannot handle more than one packet per the time interval it takes to process the data and write it to the database. However, the Raspi can handle a quick burst of packets arriving from multiple stations without dropping any because the queue is a buffer. The callback function can continue to respond to new packets while queued packets are being processed by the other thread.

In the future, I will be sending commands from the Raspi to the remote stations, and this is where I'll need the reliable datagrams.

@jerryneedell
Copy link
Contributor Author

jerryneedell commented Jan 27, 2020

This is just to update the status of "reliable datagram".
The good news is that the basic function is working but the bad news is that the timing performance with Circuitpython and especially on the Raspberry Pi is making it very difficult to get ti to work reliably with boards using the Arduino/Radiohead library. I am having a lot of trouble getting the Circuitpython library to be able to respond fast enough to receive the "ack" packets.
I added a workaround to the Circuitpython side to allow a delay to be specified, but this does not help when talking to Arduino/Radiohead.

I'm still working on this -- trying to find ways to make it respond better.
my current work is in my fork https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/tree/jerryn_ack

@Pythonaire
Copy link

This is just to update the status of "reliable datagram".
The good news is that the basic function is working but the bad news is that the timing performance with Circuitpython and especially on the Raspberry Pi is making it very difficult to get ti to work reliably with boards using the Arduino/Radiohead library. I am having a lot of trouble getting the Circuitpython library to be able to respond fast enough to receive the "ack" packets.
I added a workaround to the Circuitpython side to allow a delay to be specified, but this does not help when talking to Arduino/Radiohead.

I'm still working on this -- trying to find ways to make it respond better.
my current work is in my fork https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/tree/jerryn_ack

maybe it helps - take a look on receive function (if fifo_length < 4 ...). Sometimes I got "bad packets" and the fifo_length was 0. Then the function stop working.

@jerryneedell
Copy link
Contributor Author

jerryneedell commented Jan 28, 2020

This is just to update the status of "reliable datagram".
The good news is that the basic function is working but the bad news is that the timing performance with Circuitpython and especially on the Raspberry Pi is making it very difficult to get ti to work reliably with boards using the Arduino/Radiohead library. I am having a lot of trouble getting the Circuitpython library to be able to respond fast enough to receive the "ack" packets.
I added a workaround to the Circuitpython side to allow a delay to be specified, but this does not help when talking to Arduino/Radiohead.
I'm still working on this -- trying to find ways to make it respond better.
my current work is in my fork https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/tree/jerryn_ack

maybe it helps - take a look on receive function (if fifo_length < 4 ...). Sometimes I got "bad packets" and the fifo_length was 0. Then the function stop working.

Thank you for that warning. I think that issue will be resolved by some of the changes I have made, especially https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/blob/jerryn_ack/adafruit_rfm69.py#L848

@Pythonaire
Copy link

This is just to update the status of "reliable datagram".
The good news is that the basic function is working but the bad news is that the timing performance with Circuitpython and especially on the Raspberry Pi is making it very difficult to get ti to work reliably with boards using the Arduino/Radiohead library. I am having a lot of trouble getting the Circuitpython library to be able to respond fast enough to receive the "ack" packets.
I added a workaround to the Circuitpython side to allow a delay to be specified, but this does not help when talking to Arduino/Radiohead.
I'm still working on this -- trying to find ways to make it respond better.
my current work is in my fork https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/tree/jerryn_ack

maybe it helps - take a look on receive function (if fifo_length < 4 ...). Sometimes I got "bad packets" and the fifo_length was 0. Then the function stop working.

Thank you for that warning. I think that issue will be resolved by some of the changes I have made, especially https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/blob/jerryn_ack/adafruit_rfm69.py#L848

you are right, that's better. if the fifo length is between 0 and 4 it seems to be the datagram header, but no payload data - a useless data frame.

@Pythonaire
Copy link

Pythonaire commented Feb 2, 2020

I use the middle way, RH Datagram stack on SAMD21 board on sender side and raspi as a receiver.. It's not the full reliable version included the ACK requirement, but you can set sender functions ex. to wait until the packets is properly sended. My tests: it's a good idea to give the receiver time to handle the packets. In my case, after "RHDatagram.waitPacketSent()" I set a delay of 500ms. Now it works perfect. No bad packets, no ignores.

Thanks for sharing that, it's an interesting solution.

I use CircuitPython on SAMD21 and SAMD51 boards for sending and Python on the Raspberry Pi 3B as the server.

On my Raspi, which receives from multiple stations, I've implemented threading in the Python app that receives data and writes it out to the database. The main thread sleeps until the RFM69 interrupt occurs. Then the interrupt callback function reads the radio data, writes it to a queue and goes back to sleep. A daemon thread sleeps until something is added to the queue, reads the data, processes it, writes it to the database and goes back to sleep.

On average, the program cannot handle more than one packet per the time interval it takes to process the data and write it to the database. However, the Raspi can handle a quick burst of packets arriving from multiple stations without dropping any because the queue is a buffer. The callback function can continue to respond to new packets while queued packets are being processed by the other thread.

In the future, I will be sending commands from the Raspi to the remote stations, and this is where I'll need the reliable datagrams.

See the example for RH Datagram : https://www.airspayce.com/mikem/arduino/RadioHead/rf69_client_8pde-example.html. I tested that and it works well. Sending a packet, you can get ACK with values (maybe commands) without the full reliable version.
My point is battery consumption. Using the full sequences of reliable version and got some transmission problems, it can drop the battery capacity of an SAMD21 board very fast - because of permanent looping.

@Pythonaire
Copy link

Pythonaire commented Feb 2, 2020

This is just to update the status of "reliable datagram".
The good news is that the basic function is working but the bad news is that the timing performance with Circuitpython and especially on the Raspberry Pi is making it very difficult to get ti to work reliably with boards using the Arduino/Radiohead library. I am having a lot of trouble getting the Circuitpython library to be able to respond fast enough to receive the "ack" packets.
I added a workaround to the Circuitpython side to allow a delay to be specified, but this does not help when talking to Arduino/Radiohead.
I'm still working on this -- trying to find ways to make it respond better.
my current work is in my fork https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/tree/jerryn_ack

maybe it helps - take a look on receive function (if fifo_length < 4 ...). Sometimes I got "bad packets" and the fifo_length was 0. Then the function stop working.

Thank you for that warning. I think that issue will be resolved by some of the changes I have made, especially https://github.com/jerryneedell/Adafruit_CircuitPython_RFM69/blob/jerryn_ack/adafruit_rfm69.py#L848

you are right, that's better. if the fifo length is between 0 and 4 it seems to be the datagram header, but no payload data - a useless data frame.

after some new test cycles I saw a surprising fact. My sensor send packets with around 47 byte length. Sometime I got 0 or 10 bytes length. But these are not "bad packets" as I thought. These are packets from other 433 MHz systems in the neighborhood, like remote controls for garage doors. I use the "from" value in the receive function to identify the sender, but that taken place in the program after DIO is triggered and the packet is received.

@jerryneedell
Copy link
Contributor Author

Sorry for the delay. It will likely be another 2 weeks before I can get back to working on this.

@geekguy-wy
Copy link

I tinkered with this a bit. I have two Feather M0 (SAMD21) RFM69 boards, with Circuitpython. I was just using the Adafruit supplied demo scripts. Neither board could keep up with a full-speed exchange between them. There were a lot of message losses. I tried several ways of tweaking the scripts, but could still not get a good exchange stream between them. I stopped tinkering with these at this point because these would not have any chance of keeping up with RadioHead running on an Arduino.

I would try implementing the simplest and less involved protocols in the RadioHead library firs and see where that goes. I just do not think the M0 boards with Circuitpython are fast enough for this kind of thing. The M4 and faster boards should do much better but would require using an RFM69 module.

@Pythonaire
Copy link

I tinkered with this a bit. I have two Feather M0 (SAMD21) RFM69 boards, with Circuitpython. I was just using the Adafruit supplied demo scripts. Neither board could keep up with a full-speed exchange between them. There were a lot of message losses. I tried several ways of tweaking the scripts, but could still not get a good exchange stream between them. I stopped tinkering with these at this point because these would not have any chance of keeping up with RadioHead running on an Arduino.

I would try implementing the simplest and less involved protocols in the RadioHead library firs and see where that goes. I just do not think the M0 boards with Circuitpython are fast enough for this kind of thing. The M4 and faster boards should do much better but would require using an RFM69 module.

Yes, that's my experience too. I use the Feather M0 (SAMD21) RFM69 board as client and Raspberry Pi Zero as server. Both units not really high speed devices.
I use the Radio Head Datagram, because this version has a minimum of "reliability" - we can wait until the full packet is send (manager.waitPacketSent()) and get the "from, to" header flags, that I need for my use case. Because of battery powered client in my case, it could lead into trouble using the full reliable version if one side (server or client) have any problems - it could drain the battery fast.

I have some different tasks/function running in parallel on the server side to receive and handle the data. Maybe it sounds curious, but using threading helps a bit on the single core raspberry.
Now, i got data from the client in 30 min interval and have one "bad packet" per week in average.

@Pythonaire
Copy link

Pythonaire commented Mar 1, 2020

The "bad packet" issue seems to be a "signal flank" problem. I set bouncetime = 200 for the GPIO event detection, now I have stable packets.

@jerryneedell
Copy link
Contributor Author

The "bad packet" issue seems to be a "signal flank" problem. I set bouncetime = 200 for the GPIO event detection, now I have stable packets.

Thank you for the update. That is good to know. I'll try it here as well.

@Pythonaire
Copy link

question: could we simple take "pybind11" to bind the Reliabllity functions of the c++ RadioHead library into the python code?

@jerryneedell
Copy link
Contributor Author

question: could we simple take "pybind11" to bind the Reliabllity functions of the c++ RadioHead library into the python code?

I have my doubts about the "simple" part ;-)
Have you tried anything like this? I have no experience with pybind.
From my experience, the code is not all that complex. I have the reliability functionality implemented. The issues I have are with the execution speed on the Raspberry Pi and with the lack of interrupts in Circuitpython on the microcontrollers. I am assuming that pybind would only really be applicable to the Raspberry Pi implementation and not really applicable to CircuitPython.

@Pythonaire
Copy link

Pythonaire commented Mar 6, 2020

Hm, if im right, CPython is a subset of python functions/modules written in C.
If pybind work as I read (there are other like boost.python, Swip ...), we wouldn't need the CPython implementation ( with some lacks of functions). We could use this kind of API and call the c++ function directly from the Python code. I will try that...

@jerryneedell
Copy link
Contributor Author

Hm, if im right, CPython is a subset of python functions/modules written in C.
If pybind work as I read (there are other like boost.python, Swip ...), we wouldn't need the CPython implementation ( with some lacks of functions). We could use this kind of API and call the c++ function directly from the Python code. I will try that...

Good luck - It'll be interesting to hear how it works. This may be a good approach for the Raspberry Pi.

@Pythonaire
Copy link

Pythonaire commented Mar 8, 2020

question: could we simple take "pybind11" to bind the Reliabllity functions of the c++ RadioHead library into the python code?

I have my doubts about the "simple" part ;-)
Have you tried anything like this? I have no experience with pybind.
From my experience, the code is not all that complex. I have the reliability functionality implemented. The issues I have are with the execution speed on the Raspberry Pi and with the lack of interrupts in Circuitpython on the microcontrollers. I am assuming that pybind would only really be applicable to the Raspberry Pi implementation and not really applicable to CircuitPython.

I'm a bit confused, sorry. Maybe a dump question.If we use small low-power mcu (ESP-, SAMD-, AVR-based, whatever), we have C/C++ libraries like RadioHead to work with. What is the idea, to convert these existing libraries into python-like language for these mcu?

@jerryneedell
Copy link
Contributor Author

question: could we simple take "pybind11" to bind the Reliabllity functions of the c++ RadioHead library into the python code?

I have my doubts about the "simple" part ;-)
Have you tried anything like this? I have no experience with pybind.
From my experience, the code is not all that complex. I have the reliability functionality implemented. The issues I have are with the execution speed on the Raspberry Pi and with the lack of interrupts in Circuitpython on the microcontrollers. I am assuming that pybind would only really be applicable to the Raspberry Pi implementation and not really applicable to CircuitPython.

I'm a bit confused, sorry. Maybe a dump question.If we use small low-power mcu (ESP-, SAMD-, AVR-based, whatever), we have C/C++ libraries like RadioHead to work with. What is the idea, to convert these existing libraries into python-like language for these mcu?

That is what I have been working on -- creating python code (for use with CircuitPython on the supported MCUs) that implements the same functionality as in the RadioHead library.

@jerryneedell
Copy link
Contributor Author

implemented by #24

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

5 participants