Interrupt on Serial.available() == true?

Hello all. I don't have any code to post, I am really just looking for an idea here.

I am creating a class to drive an Xbee in API2 mode. One of the issues I am running into is how to capture serial input and store it within the class. I know that I can poll Serial.available() within the loop to see if any data has come in, but this seems inelegant since I can't just create the class and then leave it at that, I would have to call some sort of update method on the class in the loop to see if any data came in and deal with it.

Is there a clean way of creating some sort of interrupt handler for Serial that gets attached when a class is instantiated?

Thanks

There's serialEvent, but it's not well-supported.

Having a class.update() in your loop is the cleanest way, IMHO.

If you have multiple instances you want to refresh, you can insert all of them into a static linked list upon construction, and have a static function that refreshes all instances at once.

Pieter

Edit: Serial.available() == true is not the correct way to check for serial data. It should be used as if(Serial.available()) or if(Serial.available() > 0).

1 Like

One of the issues I am running into is how to capture serial input and store it within the class.

If you simply "store [characters] within the class", you might as well let the Serial object hold on to the characters until you want to use them. Otherwise, you're using twice as much buffering RAM. I agree with PieterP: just call instance.update() from loop.

What will eventually happen to those characters?

Have you ever written code that runs in an interrupt context (e.g., an ISR)? There are many restrictions.

Thanks all. I think I will just go for class.update() in the loop.

I have worked with interrupts before and I understand that the Serial class uses interrupts. I am however, using an Arm Cortex M4 (Teensy 3.6) so unlike with AVR I could potentially make use of the interrupt priorities here. For now i'll stick with an update method.

The class does more than store characters. It also interprets them. I have a fixed-length array that the characters are stored in. These characters form an escaped API frame from an Xbee. So the class needs to also read these characters and do stuff with them. It has to, for example, read the length MSB and LSB bytes, put them together with a union, and then compare to the bytes received so that I can set a bool stating that an entire frame has been received. It also needs to check for 0x7D, discard it, and then XOR the following incoming byte with 0x20 to restore the original data.

The class also handles sending data, and will also handle sending AT commands too so that I can remotely upgrade a router to a coordinator to improve network reliability in the event the coordinator goes down.

Speaking of which, is it possible to declare a struct (just a simple data structure with no member functions) within my class, then return that entire struct with a public class method? Would it copy the entire struct and would I need to deal with that?

jwllorens:
I know that I can poll Serial.available() within the loop to see if any data has come in, but this seems inelegant since I can't just create the class and then leave it at that,

Considering the serial data arrives with time in between arrivals, what would you do different to hide the inelegance?

Do you plan on using dynamic allocation while you're at it?

GoForSmoke:
what would you do different to hide the inelegance?

Isn't that exactly the question I raised in the OP?

GoForSmoke:
Do you plan on using dynamic allocation while you're at it?

No, I don't. What does that have to do with anything?

Cheers.

jwllorens:
Hello all. I don't have any code to post, I am really just looking for an idea here.

I am creating a class to drive an Xbee in API2 mode. One of the issues I am running into is how to capture serial input and store it within the class. I know that I can poll Serial.available() within the loop to see if any data has come in, but this seems inelegant since I can't just create the class and then leave it at that, I would have to call some sort of update method on the class in the loop to see if any data came in and deal with it.

Is there a clean way of creating some sort of interrupt handler for Serial that gets attached when a class is instantiated?

Thanks

There are 3 vectors that could potentially trigger an ISR that are related to serial functions:

USART Rx Complete	USART_RX_vect
USART Data Register Empty	USART_UDRE_vect
USART Tx Complete	USART_TX_vect

None of them look like Serial.available()
Since you are already looping, your entire sketch, for everything you will be doing, it won't hurt once a cycle to poll...

If (Serial.available() > 0) xbeeApi2Object.encode(Serial.read());

polling in loop() is the correct solution here.

Even serialEvent - that's all it does: It polls any attached serial event handlers between calls to loop().

If you want to make it actually handle it in an interrupt, that is much harder, because the serial receive interrupt is already used by Serial, so you might need to modify the hardwareserial implementation in the core to achieve that. It's an inferior solution that requires great effort to implement and makes the code harder to understand and less portable (because of the necessary core changes) - I recommend polling in loop. It is the typical solution to this sort of thing.

jwllorens:
Isn't that exactly the question I raised in the OP?

I missed your use of the word HIDE. Did you mention making a function to bury the inelegant code in?

No, I don't. What does that have to do with anything?

Cheers.

In the very small RAM of Arduinos like Uno, dynamic allocation most often in the form of C++ String object use has gotten loads of threads about projects crashing for "no reason".

These MCU's have enough hardware differences from PC's to make coding practices different if you want to do much.

Define your objects. If you already have the data to fill them/it ahead of time, put it in flash and load the object(s) in setup.

If the data only comes through serial then why is it not elegant to collect it as it arrives? If there's a pattern to it then process each character as it arrives else buffer them and process when collection is done.
I have an example of doing that but it's not very simple. It looks for key words in text (out of 255 keys max) and can keep up with 250000 baud text while showing trace and output prints, on the fly an 40 usecs per char speed.

That stuff is what I call work. Do I want to help you at that level? I don't think I have the time.

Serial is so frikking slow and it does have an input buffer, Serial.available() means 1+ chars are in that buffer.

GoForSmoke:
Serial is so frikking slow and it does have an input buffer, Serial.available() means 1+ chars are in that buffer.

Even at 115200 baud, the microcontroller, running at 16 MHz can perform 138.88 instructions, based on the theoretical 1 MIPS per MHz, between each character received.

Perehama:
Even at 115200 baud, the microcontroller, running at 16 MHz can perform 138.88 instructions, based on the theoretical 1 MIPS per MHz, between each character received.

You may want to recheck your arithmetic, or your understanding of how serial works.

AWOL:
You may want to recheck your arithmetic, or your understanding of how serial works.

Crap! 8N1 means it's going to be 1388.8...

GoForSmoke:
I missed your use of the word HIDE. Did you mention making a function to bury the inelegant code in?

In the very small RAM of Arduinos like Uno, dynamic allocation most often in the form of C++ String object use has gotten loads of threads about projects crashing for "no reason".

These MCU's have enough hardware differences from PC's to make coding practices different if you want to do much.

Define your objects. If you already have the data to fill them/it ahead of time, put it in flash and load the object(s) in setup.

If the data only comes through serial then why is it not elegant to collect it as it arrives? If there's a pattern to it then process each character as it arrives else buffer them and process when collection is done.
I have an example of doing that but it's not very simple. It looks for key words in text (out of 255 keys max) and can keep up with 250000 baud text while showing trace and output prints, on the fly an 40 usecs per char speed.

That stuff is what I call work. Do I want to help you at that level? I don't think I have the time.

To be honest your reply never read as if you want to help me at any level.

I am not working with string objects here, and I am well aware of the need to avoid dynamic allocation. I am working with Xbees and they send packets of bytes that are written to and read from the serial ports. It seems like a waste to check Serial.available() every cycle, and I was simply wondering if anyone had a nifty way of using an ISR to handle an incoming byte rather than polling.

On another note, I do however have a question that relates to dynamic allocation, but not really to the topic. So if anyone is interested, can you tell me if I am dynamically allocating in the following function, and perhaps provide a workaround? I suspect that I am. I guess I could go up in scope and statically allocate the array with as much room as I might need, but perhaps there is a cleaner solution? The array in question is _data.

      void transmit (char * _payload, uint64_t _longAddr = 0xFFFF, uint16_t _shortAddr = 0xFFFE, uint8_t _frameID = 0x01, uint8_t _brdcstRadius = 0x00, uint8_t _options = 0x00) {
      //determine the length, will always be equal to payload + 14 bytes for transmit request frame
      twoByte _length;
      _length.data = ((strlen(_payload)) + 14);
      //declare variables for fields that must be calculated, array for data field
      uint8_t _data[_length.data];
      eightByte _LA;
      _LA.data = _longAddr;
      twoByte _SA;
      _SA.data = _shortAddr;
      uint8_t _checksum;
      //construct the data field
      _data[0] = 0x10;
      _data[1] = _frameID;
      for (int i = 2; i < 10; i++) {
        _data[i] = _LA.bytes[9 - i];
      }
      _data[10] = _SA.bytes[1];
      _data[11] = _SA.bytes[0];
      _data[12] = _brdcstRadius;
      _data[13] = _options;
      for (int i = 0; i < (_length.data - 14); i++) {
        _data[i + 14] = *(_payload + i);
      }
      //calculate the checksum
      for (int i = 0; i < _length.data; i++) {
        _checksum += _data[i];
      }
      _checksum = 0xFF - _checksum;
      //write the frame to the serial port
      Serial2.write(START_DELIM); 
      escapeSend(_length.bytes[1]);
      escapeSend(_length.bytes[0]);
      for (int i = 0; i < _length.data; i++) {
        escapeSend(_data[i]);
      }
      escapeSend(_checksum);
    }

Perehama:
Even at 115200 baud, the microcontroller, running at 16 MHz can perform 138.88 instructions, based on the theoretical 1 MIPS per MHz, between each character received.

for beginners:
115200 baud transfers 1 start bit, 8 data bits and 1 stop bit per character to give 11520 cps. Invert that to get secs per char.
You can do this with any baud rate that uses 10 bit frames (Arduino default) but do know that not all serial uses 10 bits per char.

16000000 cycles/sec * 1/11250 seconds per char = cycles per char at 115200 baud = 1388 rounded down.

In code that does not block, that can easily be 60-100 passes through loop() to get 1 char.

I regard code that uses tasks that run on their own events through cooperative data and signals to be very elegant, at least when it's done right.

jwllorens:
To be honest your reply never read as if you want to help me at any level.

Yes, I objected to "elegance" as a criterion for code suitability and do wonder how disconnected from the hardware that you are.
What to say, people I've known who go on about code elegance tend to have funny ideas about hardware.

I am not working with string objects here, and I am well aware of the need to avoid dynamic allocation. I am working with Xbees and they send packets of bytes that are written to and read from the serial ports. It seems like a waste to check Serial.available() every cycle, and I was simply wondering if anyone had a nifty way of using an ISR to handle an incoming byte rather than polling.

Using an interrupt is the wasteful way. It can also be the bug.

Your void loop() should run 40+ times per millisecond. Serial data is asynchronous to that. Of course you check for available characters with code that expects then to arrive 1 at a time.
Most people automatically buffer then process but I tell you that it's often possible to process on a per-char basis that needs no buffering. I know because I have done that since 1980-81.

On another note, I do however have a question that relates to dynamic allocation, but not really to the topic. So if anyone is interested, can you tell me if I am dynamically allocating in the following function, and perhaps provide a workaround?

Local variables are created on the stack right after the return address. They are lost when the function exits.

Global and static variables are created on the heap. You can use new and delete in Arduino but unless you're real careful the heap gets "shotgunned" with holes and your thin AVR RAM margin takes you steps closer to heap and stack crossing and if you're real unlucky it won't crash (possible) but you get data you trust that you should not.

Write a function to process serial characters. Perhaps it parses packets, perhaps "lines". whatever...
To start with, just have it use Serial.read() to read data. Make sure it doesn't block. It should just read whatever serial data is there, do something quick with it, and set some sort of "other" signal (ie a return value) if it's time for some other code to do something.

This is very much like an interrupt service routine. But not quite - you can call it from loop() (as long as nothing else in your loop blocks for longer than it takes the serial input buffer to fill.) Or from Serial_event(). Or, with small changes, it could be a real ISR (but then you have to undo the normal Arduino Serial ISR code.)

As an example, here's some code from parser/parser.cpp at master · WestfW/parser · GitHub that does line-oriented input without blocking.

uint8_t parseGetline_nb(void) {
    int c;

    c = Serial.read();
    switch (c) {
    case 127:
    case CTRL('H'):
	// Destructive backspace: remove last character
	if (inptr > 0) {
	    Serial.print("\010 \010");
	    linebuffer[--inptr] = 0;
	}
        break;
    case CTRL('R'):
    	// Ctrl-R retypes the line
	Serial.print("\r\n");
	Serial.print(linebuffer);
	break;
    case CTRL('U'):
    	// Ctrl-U deletes the entire line and starts over.
	Serial.println("XXX");
	memset(linebuffer, 0, sizeof(linebuffer));
	inptr = 0;
	break;
    case CTRL('M'):
	Serial.println(); /* Echo newline too. */
	return inptr;     // indicate that we have a line!
    default:
	// Otherwise, echo the character and put it into the buffer
	linebuffer[inptr++] = c;
	Serial.write(c);
    case -1:  // no data
	return 0;
	break;
    }
}

jwllorens:
can you tell me if I am dynamically allocating in the following function:

       uint8_t _data[_length.data];

No, you are not using the heap. In a sense, you are "dynamically allocating" an array on the stack. It is popped back off when you exit the routine. There is no problem with using this kind of stack variable.

jwllorens:
It seems like a waste to check Serial.available() every cycle

If you do use an ISR instead, loop still gets called. It's the "idle task"; the MCU has to do something, unless:

  • You want to put the MCU into one of the sleep modes. Regardless of how you handle the characters, you can sleep immediately after a character is received. The next character will wake the MCU, and that's when you check Serial.available() or your ISR gets called.

  • Other parts of your sketch block for long periods of time, and they cause input buffer overflow. The SD library can block while waiting for a write to complete. If it takes longer than 64 character times (~64ms @ 9600 baud), you will lose characters. Handling the characters in the RX ISR is one solution.

  • You're trying to squeeze every last cycle out of your sketch. Either technique allows incremental parsing, which spreads the parsing time out over the entire packet time. This avoids one large packet parse routine that may be blocking some other time-sensitive process. The good news is that using an ISR eliminates queueing the character, calling Serial.available() and dequeueing the character. It's actually more efficient.

jwllorens:
I was simply wondering if anyone had a nifty way of using an ISR to handle an incoming byte rather than polling.

Of course there is a way, but I don't think it's necessary or worth the extra complexity. Given your description of what you're trying to do:

jwllorens:
It has to, for example, read the length MSB and LSB bytes, put them together with a union, and then compare to the bytes received so that I can set a bool stating that an entire frame has been received. It also needs to check for 0x7D, discard it, and then XOR the following incoming byte with 0x20 to restore the original data.

... this is a perfectly acceptable process to perform in an ISR (a background task). But it is also quick and easy to perform in the foreground (called from loop).

Delta_G:
What you'll do is just note that a character came in, put it into a buffer, and when the code gets to the point that it needs to know what's in the buffer it will read it. What use is it to parse the data before you're ready. And when you are ready, you'll have to get the data out of that buffer one character at a time.

Sorry, I disagree that the ISR can only buffer bytes; it could easily "parse" the bytes into a received command structure. When correctly implemented as a Finite-state Machine, this is an excellent candidate for an ISR.

(NB: This is one reason my NeoGPS library can handle the GPS characters in the RX character interrupt. GPS parsing in an ISR? Yes. It wasn't easy, but yes. NeoGPS completely consumes each character as it arrives. There is no buffering, and this is one of the reasons it can be almost twice as fast as other GPS libraries.)

I'm not sure yet that the OP planned on incremental parsing, so that's why I asked a few questions about the application. Simply saving bytes into a struct and validating a checksum can be performed either in an ISR or in a routine called from loop.

At this point, I agree with everyone else: use the easy approach and just call instance.update() from loop. Queue the valid structures into a ring buffer for some other process to use and call it done.

Cheers,
/dev

-dev:
No, you are not using the heap. In a sense, you are "dynamically allocating" an array on the stack. It is popped back off when you exit the routine. There is no problem with using this kind of stack variable.

Unless enough bytes of local variables are pushed on to the stack causing it to grow down into the heap or globals / static area.

  • You're trying to squeeze every last cycle out of your sketch. Either technique allows incremental parsing, which spreads the parsing time out over the entire packet time. This avoids one large packet parse routine that may be blocking some other time-sensitive process. The good news is that using an ISR eliminates queueing the character, calling Serial.available() and dequeueing the character. It's actually more efficient.

Yeah, what I want, ding ding ding!
Is this because you change the RX pin change vector so Serial RX doesn't run? How to reclaim the buffer space?

Last time I wanted to mess with Serial, I was running IDE 0023, I looked at the Serial source and I think it was Java.

Perhaps derive a ProcessedSerial class from Stream? Is that possible? And then don't use Serial?

I'm on good days working on some code that could use faster, leaner serial io. If you're interested?