Closed Pythonaire closed 4 years ago
you could add a time.sleep(0.1) in there to give a pause, the module does do some buffering, you dont have to be listening literally every moment :) try it!
Thx @ladyada,
I want to have a "permanently listening", because I plan to have some sensor devices, which send data anytime (mostly in 30 minutes interval, but I can't control that by time). In my actually program, I made a threading loop to call receive() with a time interval 0.5 seconds (that's equal to your sleep advice) and set the receive(timeout) value on 10 seconds. So, receive() wait 10 seconds for incoming data, then the threading loop waits for additional 0.5 seconds, then restart. Yes, the cpu usage (htop command) drop slightly from 95% down to 60% between the receive() calls. But why using 90% cpu usage while waiting for incoming packets? Have we no chance to control that by interrupts?
Questions/Ideas:
I read about an Interrupt "PayloadReady" on DIO0 on the RFM69 chip ? is that Pin connected to the Raspberry if I'm following the learning manual? In the listen function I found:
Enable payload ready interrupt for D0 line self.dio_0_mapping = 0b01
I saw other libraries (older rfm69 micros) who use GPIO.events on DIO 0... hm, should work ... but what is the Pin, C0?
you could add interrupt support with a python thread, but we have no code examples for that
my fault, bad question ... is the Pin "G0" on the RFM69 breakout equal to DIO 0?
@Pythonaire Yes, that should be the pin. I was hoping to try to work on this in the next few days as well. I'll be very interested how it works for you and share any results I have.
@Pythonaire which breakout board are you using? the Adafruit breakout schematic shows DIO0 connected to the JP3 header https://learn.adafruit.com/assets/31773 -- I'll try to verify this later today. I did just verify that it is connected using a multimeter.
@Pythonaire Yes, that should be the pin. I was hoping to try to work on this in the next few days as well. I'll be very interested how it works for you and share any results I have.
the G0 Pin on the breakout module seems to be death. Probably not connected to the RFM69 chip itself. In the driver library the programmer described that, but I hoped, that it my misunderstanding, because the description on Adafruit say: "G0 - the radio's "GPIO 0" pin, also known as the IRQ pin, used for interrupt request notification from the radio to the microcontroller, 3.3V logic level".
I don't know the board layout, to find out, which on the hookuped RFM69 board is the DIO 0 Pin.
@Pythonaire What breakout board are you using? Can you provide a link to it?
@Pythonaire which breakout board are you using? the Adafruit breakout schematic shows DIO0 connected to the JP3 header https://learn.adafruit.com/assets/31773 -- I'll try to verify this later today. I did just verify that it is connected using a multimeter.
Hm, why describe the programmer "note:: The D0/interrupt line is currently unused by this module and can remain unconnected." and not use the interrupt ... What board I have ... i'm not sure. Many numbers ... The Product ID is 3071.
https://learn.adafruit.com/adafruit-rfm69hcw-and-rfm96-rfm95-rfm98-lora-packet-padio-breakouts
Could you share your results? Could you raise an GPIO.event?
I have verified that the G0 pin does go high when a packet is received. I'm just starting to work on the code to raise a GPIO.event. I'll let you know how it works.
@Pythonaire Yes. I can trigger an event -- Here is my test code - just a small change to a basic receive test. At this point I am just detecting the Interupt, not actually using it.
# Simple example to send a message and then wait indefinitely for messages
# to be received. This uses the default RadioHead compatible GFSK_Rb250_Fd250
# modulation and packet format for the radio.
# Author: Tony DiCola
import board
import busio
import digitalio
import RPi.GPIO as io
import adafruit_rfm69
def rfm69_callback(rfm69_irq):
print("IRQ detected ",rfm69_irq)
# Define radio parameters.
RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your
# module! Can be a value like 915.0, 433.0, etc.
# Define pins connected to the chip, use these if wiring up the breakout according to the guide:
CS = digitalio.DigitalInOut(board.CE1)
RESET = digitalio.DigitalInOut(board.D17)
# Or uncomment and instead use these if using a Feather M0 RFM69 board
# and the appropriate CircuitPython build:
#CS = digitalio.DigitalInOut(board.RFM69_CS)
#RESET = digitalio.DigitalInOut(board.RFM69_RST)
# Initialize SPI bus.
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
# Initialze RFM radio
rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ)
# Optionally set an encryption key (16 byte AES key). MUST match both
# on the transmitter and receiver (or be set to None to disable/the default).
rfm69.encryption_key = b'\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08'
# Print out some chip state:
print('Temperature: {0}C'.format(rfm69.temperature))
print('Frequency: {0}mhz'.format(rfm69.frequency_mhz))
print('Bit rate: {0}kbit/s'.format(rfm69.bitrate/1000))
print('Frequency deviation: {0}hz'.format(rfm69.frequency_deviation))
io.setmode(io.BCM)
RFM69_G0 = 25
io.setup(RFM69_G0, io.IN,pull_up_down=io.PUD_DOWN) # activate input
io.add_event_detect(RFM69_G0,io.RISING,bouncetime=500)
io.add_event_callback(RFM69_G0,rfm69_callback)
# Send a packet. Note you can only send a packet up to 60 bytes in length.
# This is a limitation of the radio packet size, so if you need to send larger
# amounts of data you will need to break it into smaller send calls. Each send
# call will wait for the previous one to finish before continuing.
#rfm69.send(bytes('Hello world!\r\n',"utf-8"))
#print('Sent hello world message!')
# Wait to receive packets. Note that this library can't receive data at a fast
# rate, in fact it can only receive and process one 60 byte packet at a time.
# This means you should only use this for low bandwidth scenarios, like sending
# and receiving a single message at a time.
print('Waiting for packets...')
while True:
packet = rfm69.receive()
# Optionally change the receive timeout from its default of 0.5 seconds:
#packet = rfm69.receive(timeout=5.0)
# If no packet was received during the timeout then None is returned.
if packet is None:
continue
else:
# Received a packet!
# Print out the raw bytes of the packet:
print('Received (raw bytes): {0}'.format(packet))
print([hex(x) for x in packet])
print('RSSI: {0}'.format(rfm69.rssi))
here is the result when a packet is received
Temperature: 24.0C
Frequency: 915.0mhz
Bit rate: 250.0kbit/s
Frequency deviation: 250000.0hz
Waiting for packets...
IRQ detected 25
Received (raw bytes): bytearray(b'\x15%\x00\x00\x00\x00')
['0x15', '0x25', '0x0', '0x0', '0x0', '0x0']
much more to do to see if it can be used to capture the data
made some tests ... etc. nothing happened on "G0" PIN if packets are incoming
@ladyada Just curious -- Why did you suggest using a python thread? I have this "sort of" working using the callback (the callback just calls a slightly modified version of receive - one that does not start will a call to listen) This seems so the working -- lots more to test, but I don't see any need for a thread. Am I missing something? Or is the thread just where the eventual processing of the data is to be done. Haven't gotten to that yet.... so far I just put a time.sleep(1) in the the While True: loop. The CPU usage goes way down!
@Pythonaire I don't know why you are not seeing the G0 signal. It is working well for me. Have you double checked that you are looking at the right pin on the Raspberry Pi? haw do you have the pins configured (BCM or BOARD) I am using BCM and GPIO25 for the signal.
@Pythonaire Yes. I can trigger an event -- Here is my test code - just a small change to a basic receive test. At this point I am just detecting the Interupt, not actually using it.
# Simple example to send a message and then wait indefinitely for messages # to be received. This uses the default RadioHead compatible GFSK_Rb250_Fd250 # modulation and packet format for the radio. # Author: Tony DiCola import board import busio import digitalio import RPi.GPIO as io import adafruit_rfm69 def rfm69_callback(rfm69_irq): print("IRQ detected ",rfm69_irq) # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. # Define pins connected to the chip, use these if wiring up the breakout according to the guide: CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D17) # Or uncomment and instead use these if using a Feather M0 RFM69 board # and the appropriate CircuitPython build: #CS = digitalio.DigitalInOut(board.RFM69_CS) #RESET = digitalio.DigitalInOut(board.RFM69_RST) # Initialize SPI bus. spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). rfm69.encryption_key = b'\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08' # Print out some chip state: print('Temperature: {0}C'.format(rfm69.temperature)) print('Frequency: {0}mhz'.format(rfm69.frequency_mhz)) print('Bit rate: {0}kbit/s'.format(rfm69.bitrate/1000)) print('Frequency deviation: {0}hz'.format(rfm69.frequency_deviation)) io.setmode(io.BCM) RFM69_G0 = 25 io.setup(RFM69_G0, io.IN,pull_up_down=io.PUD_DOWN) # activate input io.add_event_detect(RFM69_G0,io.RISING,bouncetime=500) io.add_event_callback(RFM69_G0,rfm69_callback) # Send a packet. Note you can only send a packet up to 60 bytes in length. # This is a limitation of the radio packet size, so if you need to send larger # amounts of data you will need to break it into smaller send calls. Each send # call will wait for the previous one to finish before continuing. #rfm69.send(bytes('Hello world!\r\n',"utf-8")) #print('Sent hello world message!') # Wait to receive packets. Note that this library can't receive data at a fast # rate, in fact it can only receive and process one 60 byte packet at a time. # This means you should only use this for low bandwidth scenarios, like sending # and receiving a single message at a time. print('Waiting for packets...') while True: packet = rfm69.receive() # Optionally change the receive timeout from its default of 0.5 seconds: #packet = rfm69.receive(timeout=5.0) # If no packet was received during the timeout then None is returned. if packet is None: continue else: # Received a packet! # Print out the raw bytes of the packet: print('Received (raw bytes): {0}'.format(packet)) print([hex(x) for x in packet]) print('RSSI: {0}'.format(rfm69.rssi))
here is the result when a packet is received
Temperature: 24.0C Frequency: 915.0mhz Bit rate: 250.0kbit/s Frequency deviation: 250000.0hz Waiting for packets... IRQ detected 25 Received (raw bytes): bytearray(b'\x15%\x00\x00\x00\x00') ['0x15', '0x25', '0x0', '0x0', '0x0', '0x0']
much more to do to see if it can be used to capture the data
Ok, let me try. I didn't made "pull_up_down=io.PUD_DOWN". Could you show me "rfm69_callback"? The receive() function itself trigger "timeout" and register value, not an interrupt. if you didn't change that, the receive function will give you data, no matter interrupts take place or not.
no need to use a thread if if you're using rpigpio, the callback irq is handled that way
@Pythonaire Yes. I can trigger an event -- Here is my test code - just a small change to a basic receive test. At this point I am just detecting the Interupt, not actually using it.
# Simple example to send a message and then wait indefinitely for messages # to be received. This uses the default RadioHead compatible GFSK_Rb250_Fd250 # modulation and packet format for the radio. # Author: Tony DiCola import board import busio import digitalio import RPi.GPIO as io import adafruit_rfm69 def rfm69_callback(rfm69_irq): print("IRQ detected ",rfm69_irq) # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. # Define pins connected to the chip, use these if wiring up the breakout according to the guide: CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D17) # Or uncomment and instead use these if using a Feather M0 RFM69 board # and the appropriate CircuitPython build: #CS = digitalio.DigitalInOut(board.RFM69_CS) #RESET = digitalio.DigitalInOut(board.RFM69_RST) # Initialize SPI bus. spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). rfm69.encryption_key = b'\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08' # Print out some chip state: print('Temperature: {0}C'.format(rfm69.temperature)) print('Frequency: {0}mhz'.format(rfm69.frequency_mhz)) print('Bit rate: {0}kbit/s'.format(rfm69.bitrate/1000)) print('Frequency deviation: {0}hz'.format(rfm69.frequency_deviation)) io.setmode(io.BCM) RFM69_G0 = 25 io.setup(RFM69_G0, io.IN,pull_up_down=io.PUD_DOWN) # activate input io.add_event_detect(RFM69_G0,io.RISING,bouncetime=500) io.add_event_callback(RFM69_G0,rfm69_callback) # Send a packet. Note you can only send a packet up to 60 bytes in length. # This is a limitation of the radio packet size, so if you need to send larger # amounts of data you will need to break it into smaller send calls. Each send # call will wait for the previous one to finish before continuing. #rfm69.send(bytes('Hello world!\r\n',"utf-8")) #print('Sent hello world message!') # Wait to receive packets. Note that this library can't receive data at a fast # rate, in fact it can only receive and process one 60 byte packet at a time. # This means you should only use this for low bandwidth scenarios, like sending # and receiving a single message at a time. print('Waiting for packets...') while True: packet = rfm69.receive() # Optionally change the receive timeout from its default of 0.5 seconds: #packet = rfm69.receive(timeout=5.0) # If no packet was received during the timeout then None is returned. if packet is None: continue else: # Received a packet! # Print out the raw bytes of the packet: print('Received (raw bytes): {0}'.format(packet)) print([hex(x) for x in packet]) print('RSSI: {0}'.format(rfm69.rssi))
here is the result when a packet is received
Temperature: 24.0C Frequency: 915.0mhz Bit rate: 250.0kbit/s Frequency deviation: 250000.0hz Waiting for packets... IRQ detected 25 Received (raw bytes): bytearray(b'\x15%\x00\x00\x00\x00') ['0x15', '0x25', '0x0', '0x0', '0x0', '0x0']
much more to do to see if it can be used to capture the data
Ok, let me try. I didn't made "pull_up_down=io.PUD_DOWN". Could you show me "rfm69_callback"? The receive() function itself trigger "timeout" and register value, not an interrupt. if you didn't change that, the receive function will give you data, no matter interrupts take place or not.
for this simple test the callback is just
def rfm69_callback(rfm69_irq):
print("IRQ detected ",rfm69_irq)
It does not acutaully do anything with the interrupt.
I have a more complete example that I am testing now -- I have to go out for awhile but will post it later today for you too look at and test.
@jerryneedell as you say, the rfm69_callback just tell us the number of the selected GPIO. That one step further then me. Great. If im right, you call the receive function from adafruit_rfm69 library in parallel. This function check timeout value and the "_RegisterBits(_REG_IRQ_FLAGS2, offset=2)" value in a while loop. If the bit ist set and timeout reached, you get data. No matter of Interrupt GPIO25.
Thats the point. the while loop produce 90% cpu usage. If we can catch the interrupt, all thinks fine.
@Pythonaire here is an example that uses a modified version of the library -- I have renamed it adafruit_rfm69_rpi.py for this example The call back now calls receive() and the call to listen() is now executed before the While True loop at the end. The While True loop is just time.sleep(1) When a packet arrives it is handled by the callback. With this example the CPU usage goes to almost zero.
Here is a zip file with the modified library adafruit_rfm69_rpi.py and a test program rfm69_irq.py put them in the same folder and then you can execute the example. The only change in the library is to comment out the first call to listen() in the receive() function. Whenever listen() is called, the input FIFO is cleared. There is still much to be done to improve this, but I hope it is good starting point.
@ladyada Do you have any desire to see this incorporated into the adafruit_rfm69 library, possibly with some kwargs that allow a user to use it in an "interrupt" environment ? Or should we close this issue and carry on in a new repository.
@jerryneedell Thx. First I have to solve my "DIO problem". Wrote a little procedure to test the GPIO state, if the receive function is called. The DIO should come up. Hope it helps....
Now, I can catch the Dio interrupt. I don’t know why now... anyway. I will add a interrupt based routine to the library and let all other untouched for later use. Thx for your advices.
@Pythonaire here is an example that uses a modified version of the library -- I have renamed it adafruit_rfm69_rpi.py for this example The call back now calls receive() and the call to listen() is now executed before the While True loop at the end. The While True loop is just time.sleep(1) When a packet arrives it is handled by the callback. With this example the CPU usage goes to almost zero.
Here is a zip file with the modified library adafruit_rfm69_rpi.py and a test program rfm69_irq.py put them in the same folder and then you can execute the example. The only change in the library is to comment out the first call to listen() in the receive() function. Whenever listen() is called, the input FIFO is cleared. There is still much to be done to improve this, but I hope it is good starting point.
tip: the correct sequence is important. The chip mod "ModeContinuousSync" need to be set (the listen function do that) before we can trigger the DIO. After theGPIO event, the chip is set into idle --> read the FIFO --> restart with listen().
my code snippet:
def detect_dio(self, dio0_pin=None): self.dio0_pin = dio0_pin self.rfm69.listen() GPIO.setup(self.dio0_pin, GPIO.IN,pull_up_down=GPIO.PUD_DOWN) logging.info("Start event detection on Pin: {0}".format(self.dio0_pin)) GPIO.add_event_detect(self.dio0_pin, GPIO.RISING, callback=self.get_data)
get_data call receive() sibling. The receive sibling is modified by delete the preceding listen (as you mention too) and delete the while loop (timeout and self.payload_ready). Now the result is:
seems to work good.
cpu usage under 5 %.
@Pythonaire here is an example that uses a modified version of the library -- I have renamed it adafruit_rfm69_rpi.py for this example The call back now calls receive() and the call to listen() is now executed before the While True loop at the end. The While True loop is just time.sleep(1) When a packet arrives it is handled by the callback. With this example the CPU usage goes to almost zero. Here is a zip file with the modified library adafruit_rfm69_rpi.py and a test program rfm69_irq.py put them in the same folder and then you can execute the example. The only change in the library is to comment out the first call to listen() in the receive() function. Whenever listen() is called, the input FIFO is cleared. There is still much to be done to improve this, but I hope it is good starting point. rfm69_rpi_demo.zip
tip: the correct sequence is important. The chip mod "ModeContinuousSync" need to be set (the listen function do that) before we can trigger the DIO. After theGPIO event, the chip is set into idle --> read the FIFO --> restart with listen().
my code snippet:
def detect_dio(self, dio0_pin=None): self.dio0_pin = dio0_pin self.rfm69.listen() GPIO.setup(self.dio0_pin, GPIO.IN,pull_up_down=GPIO.PUD_DOWN) logging.info("Start event detection on Pin: {0}".format(self.dio0_pin)) GPIO.add_event_detect(self.dio0_pin, GPIO.RISING, callback=self.get_data)
get_data call receive() sibling. The receive sibling is modified by delete the preceding listen (as you mention too) and delete the while loop (timeout and self.payload_ready). Now the result is:
seems to work good.
cpu usage under 5 %.
That looks good -- I'm glad it is working for you.
@Pythonaire FYI - I submitted a PR https://github.com/adafruit/Adafruit_CircuitPython_RFM69/pull/19 that allows for using interrupts for received packets. An example is included. Please take a look and add any comments you have. There are a few more changes from the original example I posted here. I added some code to help distinguish receive interrupts from transmit interrupts since both are generated. Thank you for your help with this. I think it is a nice feature to have in the library.
@Pythonaire FYI - I submitted a PR #19 that allows for using interrupts for received packets. An example is included. Please take a look and add any comments you have. There are a few more changes from the original example I posted here. I added some code to help distinguish receive interrupts from transmit interrupts since both are generated. Thank you for your help with this. I think it is a nice feature to have in the library.
Didn‘t find that in the changed code … we need to call listen() before we can wait on DIO0 interrupts.
You can call listen or call send with keep_listening=True. That is what I did in my example. I should make that more clear.
You can call listen or call send with keep_listening=True. That is what I did in my example. I should make that more clear.
I read the rfm69_rpi_interrupt.py.
As I tested, the rfm69 chip need to be set into "SyncMode" mode before we can detect D0 interrupts. In the original script by Tony DiCola it is done by setting the self.dio_0_mapping variable. You can find that in the listen and the transmit function. (value 0b01 or 0b00) You calling send() in line 65, that starts the transmit function, enable packet sent interrupt for the D0 line, and send "hello World". If that happened, we got an interrupt on D0 - that call rfm69_callback(rfm69_irq). I'm wondering, you receive data packets, because the chip is set in TX_MODE. If that work, it would mean the different values of self.dio_0_mapping and definition of RX_MODE and TX_MODE are useless.
I'm not sure I understand your concern. Both TX and RX map the interrupt to DI0. That is why in my callback example, I check to see if the rfm69.payload_ready flag is set. It is only set for packet receive, not on transmit. I was trying to make as few changes as possible to the existing code.
I'm not sure I understand your concern. Both TX and RX map the interrupt to DI0. That is why in my callback example, I check to see if the rfm69.payload_ready flag is set. It is only set for packet receive, not on transmit. I was trying to make as few changes as possible to the existing code.
Oh, sorry, I'm not concerned. I don't know about the chip function, register etc. I see two values (value 0b01 or 0b00). One of them let us deal with the D0 interrupt and set the chip into listen mode. The other value let us deal with the D0 interrupt too and set the chip into send mode. Your script, if I'm right, using the second value (send mode) and you check the payload_ready_flag. If we are in send mode, how can we reveive payload?
I'm not sure I understand your concern. Both TX and RX map the interrupt to DI0. That is why in my callback example, I check to see if the rfm69.payload_ready flag is set. It is only set for packet receive, not on transmit. I was trying to make as few changes as possible to the existing code.
Oh, sorry, I'm not concerned. I don't know about the chip function, register etc. I see two values (value 0b01 or 0b00). One of them let us deal with the D0 interrupt and set the chip into listen mode. The other value let us deal with the D0 interrupt too and set the chip into send mode. Your script, if I'm right, using the second value (send mode) and you check the payload_ready_flag. If we are in send mode, how can we reveive payload?
Ah. I don’t check payload_ready in send mode. It switches back to receive mode by calling listen after the send is complete. But the send operation triggers a DI0 interrupt but payload_ready is not set. So in the callback I ignore it if it was not from a payload_ready.
Ok, I thought there are two ways to get informed about received packets: 1) the payload_ready variable or 2) the D0 interrupt. I take the D0 interrupt and ignore the payload_ready variable. It worked good so fare.
Hi,
I need to listen permanently on incoming packets. Doing that by receive() (ex. "while not None") the CPU goes nearly 100%. Pull the listen function incoming packets automatically into the buffer? Is there any chance to check that (new packets are in the buffer= True), instead of permanently looping the receive function ?