Because I have seen occasional questions about handling received characters with interrupts, and many GPS users have been stymied by HardwareSerial only providing polled reading/events, I have just published a drop-in replacement, called NeoHWSerial.
I started with the source code for HardwareSerial and made the minimal modifications to implement an attachInterrupt method, identical to the pre-existing [
Gee, Paul... It's not so much for handling a character immediately as for keeping the input buffer from overflowing when you try to do something else.
Many users have reported problems when modifying other GPS library's examples. It almost always boils down to an input buffer overflow. By handling the character in an interrupt, the GPS fix data will still be correct and current, even if they were wasting time do something they shouldn't.
There are legitimate uses, though. Any section of code that takes "too long" will cause the overflow, and there are many libraries that use delay.
jboyton found that writing to the SD card can take up to 90ms. At a 10Hz update rate, the GPS device will easily overflow the input buffer. Trying to write during the GPS quiet time is also futile, because it is so short. By using an interrupt, characters will still be handled, even during the SD write. The input buffer isn't even used: no store, no available() test, and no read() that does a retrieve.
There's an Issue from 2011 where this was proposed. Hilariously, dmellis says serialEvent() will do the same thing... but it polls. -_-
Frankly, things like smartDelay and using a Timer1 interrupt to poll the UART are silly, aren't they?
Frankly, things like smartDelay and using a Timer1 interrupt to poll the UART are silly, aren't they?
If you are referring to the absolutely ridiculous way that the TinyGPS example reads GPS data, that's beyond silly.
I guess I forgot the smiley face.
It's nice that you took the trouble to learn how to do this. But, I can't see how NeoHWSerial is a "drop in replacement". It might be, if the class that it implemented was (incorrectly) called HardwareSerial, so that any application that provided support for using either HardwareSerial or SoftwareSerial instances could use a NeoHWSerial instance instead.
And, as you pointed out, either reading GPS data is the important task that the Arduino is performing, and delays and other blocking functions are not part of the sketch, or it isn't, and a missed sentence is not critical.
PaulS:
...either reading GPS data is the important task that the Arduino is performing, and delays and other blocking functions are not part of the sketch, or it isn't, and a missed sentence is not critical.
Nonsense.
The obvious way to deal with blocking code (like SD) is to buffer the incoming data. The impetus for what /dev has done is to provide a path for Arduino projects where there is insufficient available RAM to do that buffering.
Phew! I was feeling skewered by the Post of Shameâ„¢.
I can't see how NeoHWSerial is a "drop in replacement". It might be, if the class that it implemented was (incorrectly) called HardwareSerial
I did struggle with that. NeoHWSerial seems to be somewhere between "drop in" (identical names, which requires replacing or renaming the files in the distribution) and "alternative" (different names, but different code base). I was a little nervous about telling users to modify the distribution, so I went with different names in the same code base.
I do believe that "any application that provided support for using either HardwareSerial or SoftwareSerial instances could use a NeoHWSerial instance instead." Just include it and use NeoSerial instead of Serial everywhere. That is all that's required, since it is literally the same class definition.
I do believe that "any application that provided support for using either HardwareSerial or SoftwareSerial instances could use a NeoHWSerial instance instead." Just include it and use NeoSerial instead of Serial everywhere. That is all that's required, since it is literally the same class definition.
If you just add your library, are you not going to get Serial (and Serial1, Serial2, and Serial3) in addition to NeoSerial (and NeoSerial1, NeoSerial2, and NewSerial3)?
I mean that since NeoSerial does not replace HardwareSerial, the IDE will continue to create instances of the HardwareSerial class, too won't it?
since NeoSerial does not replace HardwareSerial, the IDE will continue to create instances of the HardwareSerial class, too won't it?
HardwareSerial does not get linked in if it's never referenced. That is, as long as you don't mix Serial.print() with NeoSerial.print(), only one version gets linked in, and only one set of those variables gets instantiated. If you try to use both, you'll get a linker error about multiply defined vectors. DAMHIKT.
I'll bet the 1.6.5r2 HardwareSerial allows you to mix Serial and NeoSerial**1**. They broke out each instance into its own file (with the vector), so the linker could use one from HardwareSerial and a different one from NeoHWSerial. I'm not sure why you'd want do that, as it would include all the code for both, and the code is identical except for the new attachInterrupt bits...