Why can I not draw in the serialEvent() function?

When I put the text("Bla", 30,30) in the loop, it works, i.e. it prints “Bla” on my Canvas.

void Draw() {
 text("Bla", 30,30); 
}

But when I but the same code into the serialEvent() function and trigger it by receiving serial data, it doesn’t work, i.e. it draws nothing on my canvas.

void serialEvent(Serial mySerial) { 
  value = mySerial.readStringUntil('\n');
         if ( value != null ) {
              textSize(20);
              fill(120);
              text("Bla", 30,30); 
              println("Bla");            
         }
} 

To make sure that the if clause is properly triggered, I added println("Bla"); and, indeed, I get a “Bla” in the console for every data package I receive. But nothing on the canvas.

How is this possible? :weary:

And, no, my background is not set to the same colour as the text.

1 Like
  • serialEvent() is called back by the Serial’s internal Thread.
  • However we can’t mutate anything on the canvas outside Processing’s own “Animation” Thread.
  • Instead just have some global boolean “flag” variable to indicate it’s time to draw().
1 Like

Why does it work in these examples shown in the video below? Note that the Draw() loop in both examples are completely empty.

Could you explain what you mean by that? Or how to implement it?

B/c older versions were less restricted. At least when using the default JAVA2D renderer.

Even back then, drawing outside the “Animation” Thread could display some glitches.

// ...

boolean flag;

// ...

void draw() {
  // ...

  if (flag) {
    flag = false;

    // ...
  }

  // ...
}

// ...

void serialEvent(final Serial s) {
    // ...

    flag = true;
}

// ...
1 Like

Alternatively, if you just need to refresh the canvas when new data arrives, you can use the noLoop() / redraw() technique: :bulb:

2 Likes

A quick note to the people maintaining the processing reference pages (in particular this one): it would be really great if it could be mentioned that no drawing can be done in serialEvent(). (This would have saved me hours of dispair, struggling to get the impossible to work.)

Thanks for the cide example. Now I understand the idea. I’ll try it out as soon as I’m back at the computer.

But I’d like to understand this way of working a bit better and any help with that is greatly appreciated.

So what exactly happens when a serial event triggers the flag? For better understanding, let’s assume the loop (i.e. void draw() { }) is not empty but, in the contrary is quite full of other drawing business. So as long as there is no serial event, it relentlessly cycles through those drawing exercises.

Now, let’s say we receive data via the serial port which triggers the flag. This flag, just to be clear:

No, wait, before the flag (which is at the end of the function) is triggered, other code will be executed (probably processing the received data).

Here is my first question: while this processing occurs, is the loop interrupted or does the drawing continue? (On Arduino, my understanding is that only one thread can be processed at a time so the main loop would have to be interrupted, but I’m not sure whether this applies in processing running on a fully fledged operating system.)

So, let’s continue with what happens next: after the data is processed, the flag is triggered and the function exits and we’re either back in the main thread (if it was interrupted by the serial event) or we are left with the main thread (if it was happily drawing while we were processing serial data). In any case, what has changed now is that the flag is true so that once we reach the if clause, this one:

we will draw something based on our serial data, right?

Second question: Assuming, as we have, that there is lots of stuff in the main thread, doesn’t this flag method mean that there can be a significant delay between the serial event happening and the drawing occurring? More specifically, the delay can be up to the duration of one cycle through the main loop, right? If so, is there any way to avoid such a delay?

Edit: We were replying at the same time, so I didn’t see your second reply about redraw(). At first I thought this might indeed be the cleaner solution, but the I saw the following on the reference page:

redraw() does not run draw() immediately (it only sets a flag that indicates an update is needed).

I guess this means that it does exactly what you proposed above, with the flag, right. And, interestingly, the reference don’t mention either when exactly the redraw will occur, only that it will not be immediately, exactly as I suspected above…

1 Like
  • Each Thread follows its own code path in a multitask, concurrent way.
  • Processing’s “Animation” Thread is responsible to run the code within callback draw().
  • While Serial’s is the 1 responsible for callback serialEvent().
  • So code in draw() isn’t interrupted at all while the other code is at the same time being run in serialEvent(), and vice-versa.
  • Notice that flag in my example isn’t a method but just a field.
  • When a Thread reassigns another value to a field, it takes some time before other Threads can notice that.
  • That’s why concurrent programming is such a complex endeavor, which needs to ensure that data mutation happens in a synchronized, atomic way across Threads.
  • However, in this particular case, where your Arduino code video has a delay() of 50 milliseconds, you shouldn’t worry about data loss.
  • By default Processing’s “Animation” Thread calls back draw() at 60 FPS (about 16.7 milliseconds): frameRate() / Reference / Processing.org
  • Even an Arduino delay() as low as 20 milliseconds would be OK I believe.
3 Likes