Arduino - Serial Read and Interrupt - Page 1
Forum section: electronics / general

author: Leopoldo
author_rank: Contributor
timestamp: November 12, 2016, 11:45:32 pm
content: Hi there,I'm working on a project which uses a GPS and thus needs to acquire the data through the Serial port (software serial, in this case). I ran into a problem: since I'm doing other stuff inside the loop, which can take up to 350ms, sometimes the Serial buffer is not reached when the data comes from the GPS, and since the Serial buffer only holds up to 64B of memory, I end up losing most of the characters needed for the localization.I've read online that there shouldn't be a phisical interrupt because I might lose incoming characters.Also, the code is already quite efficent; the only "waste" of time is the refreshing of a display that is accomplished via I2C, and that's why it's so slow.I was wondering if it could be possible to immediately read the port as soon as the GPS starts sending the data, but I have no clue how to do so.To give you a rough idea how the loop is running, here's a brief explanation:while(1){while(serial.available()){acquire_data;}calculations, variables updates, etc.draw char and shapes on LCD;draw char and shapes on LCD;...various_functions();more LCD stuff;print_everything_on_LCD();}From "calculations, variables updates, etc." to "print_everything_on_LCD();" it takes roughly 350ms. I'm explaining this because you can clearly see that I can't just use a goto or some sort of well-nested loop, there is just too much stuff....there are about 300 lines of code.Thank you!

author: obiwanjacobi
author_rank: Super Contributor
timestamp: November 13, 2016, 07:32:21 am
content: Quote from: Leopoldo on November 12, 2016, 11:45:32 pmIf you're not able to stay ahead of 64 bytes of buffer, you are doing too much in the main loop. You need a faster MCU, or offload tasks to an external device (either receiving the GPS data or processing the LCD data).Quote from: Leopoldo on November 12, 2016, 11:45:32 pmI think the standard Serial does use an interrupt and from what I understood that is also the only way NOT to lose data - assuming you have a main loop running.Quote from: Leopoldo on November 12, 2016, 11:45:32 pmYes, you could, but that would not fix your problem, I suspect it'll make it worse.Realize that the Arduino libraries are not written for best performance - but for ease of access (easy to learn). So you can also think about ditching the Arduino libraries and write code directly on the MCUs registers. Get the datasheet for the MCU and lookup the peripherals you need. You can do this one "subject" at a time. If you are new to this, this can be a daunting task.Hint: see my signature...

author: FreddyVictor
author_rank: Regular Contributor
timestamp: November 13, 2016, 08:14:57 am
content: just knock up your own Serial code with bigger buffers - you'll learn something in the processwill also save a few bytesI had to add i2c to a nearly full project - adding wiring took it way over the maxadaped an existing i2c library (Peter Fleurey) and it was half the size ....

author: Jeroen3
author_rank: Super Contributor
timestamp: November 14, 2016, 06:35:20 am
content: Quote from: Leopoldo on November 12, 2016, 11:45:32 pmThat is most likely because the LCD uses some kind of "delay()". Get those out of there.

author: 2N3055
author_rank: Super Contributor
timestamp: November 14, 2016, 07:25:26 am
content: Take a look here :http://folk.uio.no/jeanra/Microelectronics/CustomSizeArduinoBuffer.html

author: rstofer
author_rank: Super Contributor
timestamp: November 14, 2016, 08:14:29 pm
content: If you have space for another 'sentence assembly buffer', you can call the 'acquire' routine several times in the super-loop to grab chars and put them in the 'assembly buffer'.You might be able to add flow control to the serial loop such that it only sends data when you are ready to accept it.The 'right' way to do it is to have the serial port generate interrupts and allow the interrupt routine to put the chars in a circular queue. Obviously, the queue needs to be long enough to hold however many characters might be received before you get back to fetching them.If you have enough RAM, you can have the main code empty out the circular buffer periodically (perhaps several times in the super-loop) and put the chars in the 'sentence assembly buffer'.When things happen asynchronous to the CPU and can't be controlled (flow control in this case), the only way to synchronize things is via interrupts. I really don't know where you read that interrupts shouldn't be used but the source is wrong!

author: slashdev
author_rank: Contributor
timestamp: November 15, 2016, 01:56:55 pm
content: Lots of people also have trouble when logging to an SD card, because an atomic write operation can take up to 100ms. This causes the same input buffer overflow (GPS character loss) that you are seeing.I developed my library,NeoGPS, to overcome this issue, among others. There's even a section aboutparsing during the RX char interrupt, which avoids the whole buffer size question. This requires using some alternative serial libraries that are drop-in replacements for the standard HardwareSerial, SoftwareSerial and AltSoftSerial libraries.NeoGPS is smaller, faster and more accurate than all other libraries, and you can edit the configuration files to ignore fields and sentences that you don't really use. For example, if you only need the time, you can configure the library to only parse the time from a ZDA message. This makes the library even smaller and faster.The other thing to note is that NeoGPS parses characters and emits a complete "fix". The fix structure contains all the fields from all the sentences received in one second (i.e., each update interval). This is unlike other libraries that parse characters and emit one sentence. Your main loop would look something like this:Code:gps_fix latest_fix; // a structure containing GPS fieldswhile(1){while (gps.available( gpsSerialPort )){latest_fix = gps.read();}calculations, variables updates, etc.draw char and shapes on LCD;if (latest_fix.valid.time)draw latest_fix.dateTime.seconds on LCD;...various_functions();more LCD stuff;print_everything_on_LCD();}However, this appears to be constantly updating the LCD. If the LCD is only displaying GPS information, or your calculations only need to be performed with the GPS information changes, you might want to restructure your loop like this:Code:gps_fix latest_fix; // a structure containing GPS fieldswhile(1){while (gps.available( gpsSerialPort )){latest_fix = gps.read(); // new info!calculations, variables updates, etc.draw char and shapes on LCD;if (latest_fix.valid.time)draw latest_fix.dateTime.seconds on LCD;...various_functions();more LCD stuff;print_everything_on_LCD();}other_non_GPS functions()}NeoGPS is available from the Library Manager if you're using a current version of the IDE, or you can get it from the link above. Be sure to check out the examples.Cheers,/dev

author: @rt
author_rank: Super Contributor
timestamp: November 18, 2016, 07:36:48 pm
content: Serial isn't the only interrupt source.Don't forget if you're using a UBlox or anything else withfrequency/PPS output, the serial data is sent immediatelyfollowing the rising or falling edge of the pulse, so the pulsecould be used to trigger spin interrupt to read the serial.

author: retrolefty
author_rank: Super Contributor
timestamp: November 18, 2016, 11:22:08 pm
content: The arduino serial library source is supplied as part of the IDE distribution. One can very easily change the buffer size to what one might require, larger or smaller. Keep in mind the SRAM limitations you might have and that buffer size defines two seperate buffers, one for send and one for receive data.