1

I have an Arduino Leonardo (master) and a Polulu A-star 328 PB(slave). I intend to use the slave to actuate some servos. I've modified the code from https://www.arduino.cc/en/Tutorial/MasterWriter to also blink the builtin led of the slave and it hangs after a few loops. Without the blink, it works fine. With blink, it hangs after a few loops.

Master:

#include <Wire.h>

void setup() {
  Wire.begin(); // join i2c bus (address optional for master)
}

byte x = 0;

void loop() {
  Wire.beginTransmission(8); // transmit to device #8
  Wire.write("x is ");        // sends five bytes
  Wire.write(x);              // sends one byte
  Wire.endTransmission();    // stop transmitting
  x++;
  delay(2000);
}

Slave:

#include <Wire.h>

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Wire.begin(8);                // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600);           // start serial for output
}

void loop() {
delay(100);
}

void receiveEvent(int howMany) {
while (1 < Wire.available()) {
  char c = Wire.read(); 
  Serial.print(c);
}
int x = Wire.read();
Serial.println(x);
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}

Serial monitor:

x is 16
x is 17
x is 18
x 

Any idea what's causing it and what can I do to further debug it or fix it? Thank you!

  • 1
    onRecieve is an interrupt routine. You can't use things like delay(), millis() or Serial.print inside interrupt routines. – Majenko Sep 4 at 12:07
2

The onReceive callback get's called from an ISR (interrupt service routine). During the execution of this ISR, the MCU cannot react to other interrupts, since interrupts get automatically turned off at the start of an ISR.

Some functions like delay() and millis() rely on interrupts to work correctly, since the timekeeping in the Arduino framework is done via a timer interrupt. This means, that you cannot use delay() inside of an ISR. It will block forever, waiting for the internal time increment from the pointer, which will never come.

You can also get a problem with big Serial.print()s. These Serial functions fill the libraries internal buffer, and the library will use interrupts to actually transmitt the data. If interrupts are turned off (like in an ISR), the buffer get's filled, until it is full. If you then again call Serial.print() or one of it's siblings, the library will block the execution, until there is enough space in the buffer, which will never happen. (You don't have this problem here, since you are only printing a few characters, but you should know this)

Generally ISR's should be as short as possible to prevent, that the Arduino misses an interrupt (which may mean data loss). Instead doing the blink in the ISR, you should simply set a flag variable, which you check in your loop() function and execute the blink correspondingly. That looks about like this:

volatile byte flag=0;

void loop(){
    if(flag){
        // Execute blink here
    }
}

void receiveEvent(int howMany) {
    // Do receiving here
    flag=1; //Set flag to a non-zero value to activate the corresponding code in loop()
}
4

After a bit of tinkering, I noticed that removing the delay() call in the slave fixed it and there were no more hangs. Also, when using the Servo library to move a servo instead of blinking the led, Servo.write() hangs while Servo.writeMicroseconds() doesn't. I'd mark the answer that has an explanation for this strange behavior.

  • upvote for continuing your investigation ... one thing that you could try to find out is will the code work with minimal delay(1) call? – jsotola Sep 4 at 17:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.