Serial Buffering Library
Spent today working on a serial library for Wiring. I'm trying to buffer characters and process a whole line at a time. Lots of problems with synchronization, as the serial data arrives as an interrupt, and so I had to be very careful not to disturb the line parsing routines. Also, I set up a CVS repository on my machine, and spent some time struggling with its command-line arguments.
Anyway, here's what I came up with:
// serial.c // David A. Mellis // Interaction Design Institute Ivrea // 17 January 2005 // This file documented with Doxygen. // See http://www.stack.nl/~dimitri/doxygen/docblocks.html for details. /// \file /// \brief A library for processing serial data one line at a time. /// /// Replaces (and uses) Wiring's default serialEvent() function for /// processing serial data character-by-character and inside an interrupt. /// Instead, buffers serial data and allows access to entire lines from /// within the loop. #include#include #include #include #include "BConstants.h" /*****************************************************************/ /* Serial Line Library */ /*****************************************************************/ // ser_buf buffers incoming serial data. // ser_write_index and ser_read_index chase each other around ser_buf. // ser_write_index is the index to which the next character of incoming // serial data will written. // ser_read_index is the index of the next character of data to process. char ser_buf[1024]; int ser_buf_size = 1024; int ser_read_index = 0; int ser_write_index = 0; int ser_overflow = 0; /// \brief Initialize the library. void beginSerialLine() { } /// \brief Call when done with a line to free the memory it uses. void serialLineDispose(char *line) { if (line) free(line); } /// \brief Gets a copy of the next line of serial data. /// /// Returns 0 if no line or not enough memory is available. Call /// \ref serialLineDispose when done with the line. char *serialLine() { int len, i, j; char c, *line; boolean found_newline = false, hit_end = false; boolean overflowed; // check overflow now, otherwise an overflow between the calculation // of line length and the overflow check would trick us into // thinking that we had counted all the characters received // and leaving part of the line in the buffer. overflowed = ser_overflow > 0; // starting at the current read index, look for a line of serial data. // two things can end a line: a newline or an overflow. // we have to remember which condition we encounter, because an // interruption by more serial data could make it impossible to // figure out later. i = ser_read_index; len = 0; for (;;) { if (i == ser_write_index) { hit_end = true; break; } if (ser_buf[i] == '\n') { found_newline = true; break; } i = (i + 1) % ser_buf_size; len++; } // if we hit the end of the buffered data and there's no overflow, // we're processing data faster than it's coming in and we haven't // actually received a full line yet. if (hit_end && overflowed == 0) return 0; line = malloc(len + 1); // XXX: we should distinguish between no line and out-of-memory if (line == 0) return 0; for (j = 0; j < len; j++) line[j] = ser_buf[(ser_read_index + j) % ser_buf_size]; line[len] = 0; if (found_newline) len++; ser_overflow = 0; ser_read_index = (ser_read_index + len) % ser_buf_size; return line; } void serialEvent() { // stop at the character before the last character that was // processed. if instead we stopped when the indices were equal, // we would never buffer any characters when processing was // caught up (e.g. at program start, when both indices are 0). if ((ser_write_index + 1) % ser_buf_size != ser_read_index) { ser_buf[ser_write_index] = serial; ser_write_index++; ser_write_index %= ser_buf_size; } else { ser_overflow++; } }