ASR6502 LoRa Interrupt on Receive?

Is there a way to set an ISR for a receive interrupt on LoRa receive on the ASR6502/CubeCell? I want to go off and do things like read the GPS, update the screen check for user input, and generally not just hang around in Radio.Rx(0) waiting for inbound packets.
This is for raw LoRa mode, not LoraWAN. I just want to set an interrupt handler on a GPIO that the SX1262 toggles when it receives LoRa data. Is this possible? Is there sample code for it?


EDIT 2: Removed ‘packetReceived’ boolean (it was a remnant from an earlier test and not used) and updated the Serial Monitor output to reflect the final state of the test code.

Well, I’ve been wondering this myself for quite a while so your question prompted me to revisit this. I won’t bore you with the details of my reasoning but I ultimately went back to the CubeCell factory test sketch (Factory_Test_AB01.ino) as the simplest starting point. That sketch alone looks like it is doing what we want, effectively doing something else while waiting for a packet to come in—in this case sleeping.

To verify that the code would work if we were doing something other than sleeping, I made the following modifications:

  1. Add/modify the following definitions:
typedef enum
} States_t;

long time, interval;
long start = 0;
long reportPeriod = 1000;
  1. Modify the loop switch statement to include:
	case PROCESS:
		Serial.println("into PROCESS mode");

This case doesn’t really do anything other than tell me that PROCESS state has been set, so it’s probably entirely unnecessary in the overall scheme of things, but delete the LOWPOWER option and any references to LOWPOWER in any case;

  1. Add the following before the break statement at the end of the TX case:
		state = PROCESS;
  1. Add the following before the break statement at the end of the RX case:
	    state = PROCESS;
  1. Add the following at the end of the main loop, after the call to Radio.IrqProcess():
  while ( state == PROCESS ) {

EDIT: Better still, just place this loop inside the PROCESS case at 2. above (I don’t know why I didn’t do that to begin with!)

  1. Define the following function:
void doStuff() {
  time = millis();
  interval = time - start;
  if ( interval > reportPeriod ) {
    Serial.println("[doStuff] Doing stuff...");
    start = time;
  1. (Deleted at EDIT 2)

  2. I uploaded this to a CubeCell (AB01) V2 module and noted the following output on the Serial Monitor:

09:51:01.536 -> Copyright @2019-2020 Heltec Automation.All rights reserved.
09:51:07.422 -> 
09:51:07.422 -> sending packet "hello1 rssi : 0" , length 15
09:51:07.422 -> into PROCESS mode
09:51:07.422 -> [doStuff] Doing stuff...
09:51:07.456 -> TX done......into RX mode
09:51:09.531 -> into PROCESS mode
09:51:09.531 -> [doStuff] Doing stuff...
09:51:10.519 -> [doStuff] Doing stuff...
09:51:11.524 -> [doStuff] Doing stuff...
09:51:12.544 -> [doStuff] Doing stuff...
09:51:13.546 -> [doStuff] Doing stuff...
09:51:14.545 -> [doStuff] Doing stuff...
09:51:15.541 -> [doStuff] Doing stuff...
09:51:16.539 -> [doStuff] Doing stuff...
09:51:17.551 -> [doStuff] Doing stuff...
09:51:18.516 -> [doStuff] Doing stuff...
09:51:19.526 -> [doStuff] Doing stuff...
09:51:19.846 -> 
09:51:19.846 -> received packet "��E�,�������" with rssi -45 , length 18
09:51:19.846 -> wait to send next packet
09:51:20.872 -> 
09:51:20.872 -> sending packet "hello2 rssi : -45" , length 17
09:51:20.872 -> into PROCESS mode
09:51:20.872 -> [doStuff] Doing stuff...
09:51:20.905 -> TX done......into RX mode
09:51:20.939 -> into PROCESS mode
09:51:21.852 -> [doStuff] Doing stuff...
09:51:22.367 -> 
09:51:22.367 -> received packet "�d��)��6����" with rssi -56 , length 18
09:51:22.367 -> wait to send next packet
09:51:23.388 -> 
09:51:23.388 -> sending packet "hello3 rssi : -56" , length 17
09:51:23.388 -> into PROCESS mode
09:51:23.423 -> [doStuff] Doing stuff...
09:51:23.456 -> TX done......into RX mode
09:51:23.456 -> into PROCESS mode
09:51:23.867 -> 
09:51:23.904 -> received packet "�d��@)��Y��-" with rssi -37 , length 18
09:51:23.904 -> wait to send next packet
09:51:24.924 -> 
09:51:24.924 -> sending packet "hello4 rssi : -37" , length 17
09:51:24.924 -> into PROCESS mode
09:51:24.924 -> [doStuff] Doing stuff...
09:51:24.956 -> TX done......into RX mode
09:51:24.991 -> into PROCESS mode
09:51:25.910 -> [doStuff] Doing stuff...
09:51:26.905 -> [doStuff] Doing stuff...

I have several Nodes in my LoRa network, transmitting packets at 60 second intervals. This sketch makes no attempt to interpret the content of those packets, but they are clearly being received while our sketch is looping around ‘Doing other stuff’. I left most of the other Factory_Test sketch code in place, so it still sends out a packet before waiting for something to be received, but I don’t believe that this would have any bearing on the receiving process.

1 Like

Hi Pete

I had the same query as John and I’m interested in your response, but very puzzled by how its working. I’m using a wireless stick rather than a Cubecell, so couldn’t recreate your experiment exactly, but also modified the test sketch as per your response and my sketch just sat in doStuff() for ever, despite LoRa packes coming in every 60 seconds.

My understanding of the hardware and Heltec LoRa code (and please correct me if I have any of this wrong) is that the Wireless Stick uses GPIO26 (connected to the LoRa module DIO0 Pin to trigger an interrupt (on GPIO26) to signal that a LoRa packet has been received. The ISR for this interrupt is handled somewhere within Radio.IRQProcess (setting a flag, I’m guessing) causing it to call the callback function OnRxDone() if the Interrupt is set, otherwise Radio.IRQProcess() just immediately returns (I’ve confirmed that in my code with a few Serial.println statements). However, theOnRxDone() function (which is printing the "Received packet … " message) is only ever called from within the Radio.IRQProcess() function when it has checked the interrupt flag and not independently as a result of the hardware interrupt triggered by the LoRa module. - ie OnRxDone() is not an ISR, which is why I can’t see how your sketch is doing what it looks like its doing - i.e. with doStuff() being interrupted when a new LoRa packet comes in (although its undeniable that this is exactly what it looks like its doing … :slight_smile: )

With my (possibly entirely incorrect) understanding of the Heltec software, it looks to me as if Radio.IRQProcess() has to be regularly called one way or another, and it will invoke the OnRxDone() callback function when a packet has been received - rather than an ISR being asynchronously called completely independently of Loop() which I think John was looking for (and is what I’m seeking)

I hope that makes some sense, and would welcome your comments.


Hi David,

Well, I don’t have a Wireless Stick but I do have a Wireless Stick Lite V3 and can confirm that I see the same behaviour that you describe when I try to run the sketch on this board.

I must also confess that I do not have a deep understanding of what is going on, either specifically in the Heltec code or even more generally. I just modified the code as I originally described and it worked, pretty much first time round (so I didn’t have to go digging around for any ‘deeper meaning’ in what I was doing).

I pored over the respective schematics (the various ESP32 and ASR650x boards) at the time and was troubled that I couldn’t see any connection to what I thought might have been an interrupt pin on the ASR650x configuration (there’s no mention of DIO0 and DIO1 seems to be explicitly noted with ‘no connection’), so I really didn’t expect things to work—although the processor and LoRa chip on the ASR650x seem to be more closely integrated than do the processor and LoRa chips on the ESP32 boards.

I still can’t see any real correlation between the configuration on the ESP32 boards and the ASR650x boards. I see the connection between DIO0 and GPIO26 on the original Wireless Stick schematic, as you also note, and a similar connection between DIO0 and GPIO14 on the ESP32 (Wifi LoRa 32, Wireless Stick and Wireless Stick Lite) V3 boards, but nothing like that on the ASR650x schematics, which has me wondering whether the two might not be the same at the [LoRa] hardware integration level and so might, logically enough, require different programming, if indeed they could be encouraged to behave the same way at all. I can see no good reason why they shouldn’t be able to behave the same way, if the hardware was configured appropriately…

As far as the Radio.IRQProcess function is concerned, I could never really understand how that worked. As you describe, I would have expected it to simply go off and check to see if an interrupt had been triggered. Indeed, I’ve just gone and had a look at the code and that is what it does. The curious thing is that there is also a function RadioOnDioIrq which does exactly what you might expect of an ISR, it sets a flag indicating that an interrupt has been triggered. The first thing that RadioIrqProcess does is check this flag.

But there’s a curious difference between the radio.c libraries used for the ASRT650x and ESP32 boards. The ASR650x version contains the following:

void RadioOnDioIrq( void )
	IrqFired = true;

but the ESP32 version contains:

void RadioOnDioIrq( void )
	IrqFired = true;
//	RadioIrqProcess();

I wouldn’t actually expect the ISR to do anything more than set a flag, but the ASR650x version calls the RadioIrqProcess function, which begs the question of what the call in my code is actually doing (and may well make me feel a bit better about never really understanding what it was doing). This might also explain why the two processors are behaving differently.

Anyway, I’ve not had the opportunity to investigate this further at this point, but later this evening I will see what happens if I include the call to RadioIrqProcess in the ESP32 version of RadioOnDioIrq. I have also noted that the default settings for the DIO pins appear to be incorrectly specified in the config file—they appear to still be set to what they were on the original ESP32 modules (e.g. DIO0 on GPIO26), not the V3 versions (where DIO0 in on GPIO14, which appears to be configured as the RST pin, which should be GPIO12 on the V3 modules…!).

EDIT: Yep, that’s all it took, just uncomment the call to RadioIrqProcess in RadioOnDioIrq, that’s line 1128 in the file Library > Arduino15 > packages > Heltec-esp32 > hardware > esp32 > 0.0.7 > libraries > LoraWan102 > src > radio > radio.c (on my Mac, although note that I’m still using the 0.0.7 software release). I didn’t have to change any of the default pin definitions—they must be set elsewhere.

Now, what impact that might have on other things, I don’t know at this stage, but it does indicate that the ESP32/SX1262 combination also works this way.

Hi Pete

Thats brilliant ! Thank you very much for the trouble you have taken to investigate this. I’ll give it a shot on my Wireless Stick and I’m sure I’ll re-create your results. (its the same code on the 0.0.8 software). Its interesting that they chose to comment out that call in the ESP32 version (something only the coder would be able to tell us, I guess !). The only thought I’d have is that maybe they decided it was not quite good practice to embed a callback function into an ISR - I’m only thinking that because not knowing that the callback was in an ISR the user (customer coder) might create a large and complex callback that would effectively become part of the ISR and cause subsequent interrupts to be lost. Pure conjecture on my part as I don’t have nearly enough experience in coding or microcontroller hardware to speak with any authority (!)

Anyway, thanks again for your great assistance

David F

That could well be the case. As it stands, RadioIrqProcess calls the RxDone function, so that should probably be kept as tight as possible.

My first, naive attempt at an ISR, which included much more code than it should have, worked fine on the CubeCell Plus (ASR6502) but ‘fell over’ on the Wifi LoRa 32 (V2, ESP32-D0WDQ6). My current test was running on a Wireless Stick Lite V3 (ESP32-S3), a faster processor than the V2 boards, but OnRxDone doesn’t do very much. Having said that, as I recall the thing that was killing the ESP32 in that original ISR was the Serial.print stuff, which is present in the current test…