serialEvent trap

Alright, here's my problem:

I've rewritten parts of my program (interfacing a C328 serial camera via USART), and spent an hour just to find out that the problem isn't in my program.
During the setup it should send up to 60 Sync commands, till it receives an ACK, but instead, it just kept going.
Then I found out that serialEvent() isn't called when data is available, like stated in the Reference.
The description of the serialEvent example says: "SerialEvent() is called after a loop(), if there is serial data in the buffer." (Why call it event then?)
I highly recommend correcting the documentation, but that's not my problem...

The whole thing is going to be part of a robot platform for students, and everything should be as simple as possible.
I can't just make a new ISR, because the Interrupt Vector is already used by Arduinos HardwareSerial, so I'm looking for a way to make serialEvent() Interrupt driven?

Thanks in advance for any suggestions,
Fabian

Post your code please?

Then I found out that serialEvent() isn't called when data is available, like stated in the Reference.

At least the part that demonstrates this point, preferably all of it.

Haven't thought it would be useful:

#define F_UART 9600

byte sd [6], serial;

void setup()
{
  Serial.begin(F_UART);
  camSync();
}

void loop()
{
//misc stuff, only debug yet
}

void serialEvent()
{
  static byte cnt=0;
    for(;Serial.available()&&cnt<6;cnt++) //Load package till complete or buffer empty
      sd[cnt]=Serial.read();
    if(cnt==6)                            //Decode if complete
    {
      cnt=0;
      serial=serialDecode();
    }
}

//serialDecode just sets the correct bits if ACK is received

//try to get a sync
byte camSync()
{ 
  serial=0xA0;                     
  for(byte n=0;n<60;n++)
  {
    Serial.write(0xAA);
    Serial.write(0x0D);
    Serial.write(0x00);
    Serial.write(0x00);
    Serial.write(0x00);
    Serial.write(0x00);    
    delayMicroseconds(2/F_UART);
    if(serial==0x01&&ACK_ID==0x0D) //if ACK received
    {
      serial=0x00;                    //in sync, idle state
      return serial;
    }
  }
  if(serial==0xA0)
    serial=0xFF;                    //no response -> out of sync
  return serial;
}

I haven't saved the graphs, but belief me, it keeps sending the data even after receiving the whole package.
Once the main-loop went through it reads in the data...

Yes but it says "SerialEvent() is called after a loop()". Once you leave the loop function it will call SerialEvent if there is serial data. However you are hoping it will arrive inside camSync.

Inside the loop (where you are sending out sync bytes) you need to check if Serial.available(). Then grab the byte that is there and add it to your buffer.

This is the Arduino main processing loop:

#include <Arduino.h>

int main(void)
{
	init();

#if defined(USBCON)
	USBDevice.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

You can see that the test for serialEventRun is done after each iteration of the loop function.

Yea, now I know that too, but I had to read the description of the example sketch, the commands reference doesn't tell anything about that.
But now I need to turn serialEvent() into a ISR somehow, because the cam can for ex. send me a Sync command, and I need to ack that.
I can't tell the students that they have to check the serial in their code every now and then, a ISR would be way better?

Thanks in advance,
Fabian

Every now and then? A normal design would have loop call various things and then, well, loop. In that design SerialEvent will be called, and you have a chance to ack the sync.

The hardware serial library already has an ISR (that is how the incoming data gets into the serial buffer). In any case, you should not be doing serial writes inside an ISR (as they rely on interrupts) so all your custom ISR should do is set a flag, which you test in loop. Well, effectively that is what SerialEvent does anyway.

BlackBirdf:
Once the main-loop went through it reads in the data...

I'd expect serialEvent() to get called after loop(), if there was serial data available. Isn't that what happens? (I'm not sure what 'it reads in the data' means.)

BlackBirdf:
I can't tell the students that they have to check the serial in their code every now and then, a ISR would be way better?

I would teach them to use the main loop as an "event dispatcher". There might be outstanding things to do (handing incoming data, read sensors, turn motors) and you do each one in turn, without blocking. In other words, start an action, and let the main loop keep going. Some stuff about that here:

I'd expect serialEvent() to get called after loop(), if there was serial data available. Isn't that what happens?

That seems to be a common expectation, but I don't see anything in the code that the Arduino actually compiles that supports that assumption. It appears as though serialEvent() gets called after every iteration of loop, whether there is data to be read, or not.

In the serialEvent() function, it appears to still be necessary to call Serial.available() to determine if there is anything to do.

This page:

says:

SerialEvent() is called after a loop(), if there is serial data in the buffer.

However as I showed further up, such a test does not seem to exist (in main).

That statement is repeated here:

Called when data is available. Use Serial.read() to capture this data.

Ah, I see what is happening. From HardwareSerial.cpp:

void serialEventRun(void)
{
#ifdef serialEvent_implemented
  if (Serial.available()) serialEvent();
#endif
#ifdef serialEvent1_implemented
  if (Serial1.available()) serialEvent1();
#endif
#ifdef serialEvent2_implemented
  if (Serial2.available()) serialEvent2();
#endif
#ifdef serialEvent3_implemented
  if (Serial3.available()) serialEvent3();
#endif
}

serialEventRun is called from main regardless, but the test is done inside that function.

PaulS:

I'd expect serialEvent() to get called after loop(), if there was serial data available. Isn't that what happens?

That seems to be a common expectation, but I don't see anything in the code that the Arduino actually compiles that supports that assumption. It appears as though serialEvent() gets called after every iteration of loop, whether there is data to be read, or not.

In the serialEvent() function, it appears to still be necessary to call Serial.available() to determine if there is anything to do.

Nick seems to have found evidence that the common expectation is correct. Although tbh I don't see what the motivation is for providing a callback for the serial port - there's no functional or performance advantage that I can see against having the sketch execute the same logic in loop(), and if it was in loop() then that would be consistent with the way all other events need to be detected and handled.

PeterH:
Although tbh I don't see what the motivation is for providing a callback for the serial port - there's no functional or performance advantage that I can see against having the sketch execute the same logic in loop(), and if it was in loop() then that would be consistent with the way all other events need to be detected and handled.

I agree. I think it is a silly* addition. One extra function in the "main loop" that people don't expect, or even if they do, it slows loop down while it is executed.