Arduino Yun interrupt and bridge issues

Greetings,

I am currently working on a project where I need to use the MCU interrupt to give commands to MPU (Linux) via the Bridge. I have stumbled upon a problem which for some reason prevents me from doing so.

When calling the Bridge Process through Interrupt Service Routine, the ATmega32U4 seems to hang up and freeze. However, Bridge and interrupts are working flawlessly separately.

I have replicated the problem using the following short code. In this example, I use AVR interrupt INT6 (Int 4, Pin 7) to generate IRQ on the falling edge. Although Pin 7 is reserved for “Handshake”, the problem still persists if I use other interrupts (INT0…INT3) and even Pin Change Interrupts. The problem seems to affect the ISR itself.

#include <Bridge.h>

void Send() {
  Process proc;
  char c;
  
  proc.runShellCommand("echo Hello World!");
  while (proc.available()) {
    c = proc.read();
    Serial.print(c);
  }
}

void IRQ() {
  Serial.print("IRQ: ");
  Send();
}

void setup() {
  Serial.begin(115200);
  Bridge.begin();
  delay(1000);

  Serial.print("Main: ");
  Send();

  pinMode(7, INPUT_PULLUP);
  attachInterrupt(4, IRQ, FALLING);
}

void loop() {
  Serial.println(millis());
  delay(1000);
}

PS. The interesting thing is that I don’t even see the “IRQ”, which should be sent via Serial before issuing the Bridge command, no matter how long delay I put between the two. It seems that having any reference to Process class inside or through the ISR seems to break the whole function.

I tried to find other occurrences or references to this problem but with no avail.

Has anyone run into this issue and does anyone know how to fix this? Any help would be appreciated.

Best Regards,

MJ.

Both the serial output (“IRQ”) and the bridge communications to the Linux side use interrupts. Interrupts are disabled inside an ISR, so those serial communications calls will never have a chance to run. Your sketch is guaranteed to lock up 100% of the time.

Good interrupt service coding practices dictate that you should do as little as possible in an ISR, and you should never call long blocking processes like serial communications. A good discussion is >HERE<.

A more appropriate option would be to set up a global variable (make sure it is declared as volatile.) Set the flag true in the interrupt routine then return. Then, in loop(), look for the flag to be set, and when it is you do your processing and clear the flag.

However, in this case, you might as well just poll for the input state change directly, rather than using an ISR and a flag. The ISR doesn’t buy you anything here. An ISR is useful when you have lengthy operations going on in loop(), and you have a short critical operation that must be done immediately. That is not the case here: your loop() is doing nothing else, and by definition your processing is neither short nor real time. Any calls to Linux will be long and non-deterministic in response time as there is a lot of communications overhead in both directions, and Linux is not a real time operating system: there is no way to predict what is happening at the time of the call, nor how long it will take to be processed.

ShapeShifter: Both the serial output ("IRQ") and the bridge communications to the Linux side use interrupts. Interrupts are disabled inside an ISR, so those serial communications calls will never have a chance to run. Your sketch is guaranteed to lock up 100% of the time.

That's what I was afraid of. However, 32u4 Serial (which actually uses USB) runs without problems inside an ISR, although that was just for debug purposes only. The Bridge fail even if there is no Serial output. That still doesn't 100% completely explain why I can't receive Serial data with the Bridge code present. For example, with:

void IRQ() {
  Serial.print("IRQ: ");
}

...I get the Serial output, but with:

void IRQ() {
  Serial.print("IRQ: ");
  Send();
}

...I do not.

ShapeShifter: However, in this case, you might as well just poll for the input state change directly, rather than using an ISR and a flag. The ISR doesn't buy you anything here. An ISR is useful when you have lengthy operations going on in loop(), and you have a short critical operation that must be done immediately.

That is what my code looked like in the first place before I decided to try the interrupt option because I needed an immediate response to pin change. However, the code in this was only a minimalistic example to show the problem and in reality it is doing a lot more and I need to register this interrupt as soon as possible and report it to the MPU, where my Linux application takes over.

ShapeShifter: A more appropriate option would be to set up a global variable (make sure it is declared as volatile.) Set the flag true in the interrupt routine then return. Then, in loop(), look for the flag to be set, and when it is you do your processing and clear the flag.

That is what I have also thought about and should probably do. In that case must increase the polling speed by incorporating other timing methods for my other functions on ATMega32u4.

Anyway, thanks for confirming my fears and letting me to continue exploring other options to achieve my goals.

PS. One more question: Is the Pin 7 (Handshake) used or will ever be used in any intrinsic functions of Arduino Yun in the future? I would really like to keep this pin as an interrupt because Pins 0 and 1 are used for Bridge Serial and 2 and 3 for I2C. Pin change interrupt is also an option but it requires me to determine the edge direction.

Best Regards,

MJ.

Manki: That's what I was afraid of. However, 32u4 Serial (which actually uses USB) runs without problems inside an ISR, although that was just for debug purposes only.

No, not really. The serial output is most definitely NOT running in the ISR. Interrupts are disabled in the ISR, so the serial output can't possibly be running.

The way the serial output works, the data you are sending gets placed in an output buffer. An interrupt routine then takes characters from the output buffer and sends them out the serial port each time that the hardware interrupts signalling that it's ready for a new character.

So, in reality, what's happening is that in the ISR the Serial command is putting those three characters into the output buffer. It fits, so after queuing the data, it returns and your ISR continues. Once the ISR is over, the serial interrupts take that queued data and send it out. It sounds like picky semantics, but the data is NOT being sent during the ISR.

Where this becomes important is when you try to send more data than what can fit in the serial output buffer: the print() call will block until there is room. But if you are calling print() inside an ISR, interrupts are disabled and no characters will be removed from the buffer to be sent, so room will never be made available, therefor the print() call will never return. You are deadlocked.

The Bridge fail even if there is no Serial output.

The Bridge communications may send more data than will fit in the Serial1 output buffer, so that could cause a deadlock there. But even if the output buffer doesn't fill, the Bridge communications is waiting for a response from the Linux side. That will never happen for two reasons in an ISR: first off, the request won't actually be sent to the Linux side because interrupts are disabled, and secondly any response from Linux won't be received, also because interrupts are disabled.

That still doesn't 100% completely explain why I can't receive Serial data with the Bridge code present.

Hopefully this additional explanation covers the reasons 100%. If not, please let us know what is still confusing.

in reality it is doing a lot more and I need to register this interrupt as soon as possible and report it to the MPU, where my Linux application takes over.

But using an interrupt to send the notification to Linux is a waste of effort since the Bridge communications takes time, and Linux is not a real time operating system. You will never get serial communications to Linux working reliably in an ISR. The actual communications MUST happen in loop(), not in an ISR. That means that even if you use an ISR to set the flag, the state of the flag will still be polled - using an ISR accomplishes nothing in this case other than adding complexity, unless you are also doing some other real time processing like controlling other hardware outputs, or calling micros() to capture the time that the interrupt occurred.

Even if you do start the Bridge communications immediately, you still have no control over how long it will take to complete, or how quickly Linux will actually service the request and respond. It may be fast, but it will not be a reliable real time event.

PS. One more question: Is the Pin 7 (Handshake) used or will ever be used in any intrinsic functions of Arduino Yun in the future?

It is not currently used, and I have no insight to anyone's future plans for it.

But the pin can easily be used. I have used it for the stated handshaking purpose, where the sketch monitors the line waiting for the Linux side to indicate that it is ready. I need to hit the road right now, and I don't have time to give a reasonable answer and example, but I will when I get back.[/quote] [/quote]

ShapeShifter: Hopefully this additional explanation covers the reasons 100%. If not, please let us know what is still confusing.

Yes. Thank you for explaining this in detail.

I was looking at the Yun schematic and got an idea. The Pin 7 i.e. "Handshake" is tied to Atheros MPU GPIO 19 via NTB0102 bidirectional voltage level transceiver, which is can be enabled from MPU side by GPIO22. So I looked around Linux in /sys/class/gpio/ and found that I could enable the transceiver, enable and define GPIO19 as input, and I could read the pin value from Linux and vice versa.

It is important, I think, that both Linux and Arduino should not set GPIO19 and Pin 7, respectively, as outputs and drive them at opposite polarities at the same time or it might result in short circuit, unless the transceiver or the hardware protects against such situations.

Now all I need is to poll the pin from Linux directly, thus removing the need to use the Bridge. Now I can manage my other hardware and functions on 32u4 and deal with this specific interrupt in Linux. My goal was not to achieve real-time events but to run an application in Linux as soon as possible after the pin state changes.

Or even better, would it be possible to attach an interrupt in Linux? Unfortunately, I do not know how to do that and would gladly appreciate any help on accomplishing this.

Best Regards,

MJ.

Manki: Or even better, would it be possible to attach an interrupt in Linux? Unfortunately, I do not know how to do that and would gladly appreciate any help on accomplishing this.

Sorry, I've not tried to do that, and have not investigated the possibilities.

As far as the example for using the handshake line, it seems like you have already figured out all of the details that the example was going to show, so I think the example would be redundant at this point. Good job!

Now, as far as the ISR in the sketch goes, if loop() could be doing some lengthy processing, and you need an interrupt on one pin to immediately trigger the handshake line to the Linux processor - now that is a reasonable reason to use an ISR.

Hello, I'm having a similar problem and was wondering if you or anyone else has a workaround or design tips to handle this issue. I'm using a watchdog timer rather than a hardware interrupt, so the discussion of Pin 7 and GPIO19 does not apply (if I understand it correctly).

In my application, the ATmega32U4 is controlling a NeoPixel-like LED strip. There are several animations available and the remote users are to select one. My plan was to select using the Mailbox (i.e. http://192.168.240.1/mailbox/12 to select animation number 12). A watchdog timer would interrupt every second or two and copy the mailbox messages into the animation queue. The animation length range from several seconds to a minute and run using SPI and delay() as in the NeoPixel examples.

I get the same behavior as Manki when the Mailbox.readMessage() is called from inside the ISR.

Any tips or suggestions would be appreciated.

Thanks, Kevin

Kevin07003, Manki.

I have made Water Leakage detected system based on YUN where i used ATmega interrupts to detect leakage and cut-off the valves.

Here is short code example:

#include <Bridge.h>
#define WS_PIN	7

Process proc;
volatile bool		WS_triggered;
unsigned long		mill;

void Send() {
	if (millis() - mill > 100) {	// delay 100mc to avoid Serial1 flooding by requests
		if (!proc.running()) {		// check is our program on linux siter still working
			proc.begin("/usr/test/test1.py");	// if Bridge is ready for new call - save my current value
			proc.addParameter("L");		        // Tell that we have leaking detected
			proc.runAsynchronously();			// Run my program assynchrously to avoid delays
		}
		mill = millis();			// start waiting 100mc delay for next testing Serial1 availability
	}
}

// ISR entered when water sensor detected
void WaterSensor_ISR()
{
	WS_triggered = (digitalRead(WS_PIN) == LOW);
}

void setup() {
  Serial.begin(115200);
  Bridge.begin();
  delay(1000);

  // Attach ISR to WaterSensor interrupt on pin WS_PIN (7)
  pinMode(WS_PIN, INPUT);
  attachInterrupt(4, WaterSensor_ISR, FALLING);

  Serial.println("Started!");
}

void loop() {
	if(WS_triggered) {
		WS_triggered = false;
		Serial.println(F("Water Leaking detected!"));
		Send();
	}
}

You can see speed test for this and other solution: https://forum.arduino.cc/index.php?topic=349269.0