How does SoftwareSerial buffer data?

I'm curious about the underlying mechanism. I know that it uses a 64 byte buffer, but is it using a hardware interrupt to regularly fill that buffer?

I arrived at this question because of a project writing data from a GPS receiver to an SD card. I have it working, but it doesn't seem to make sense to me that writing to the SD card is happening fast enough to not miss the next character from the GPS. In fact, even a 64 byte buffer doesn't seem enough, given that writing a block of data to the SD card can take up to 250ms (SD spec) for the card to finish writing. And in 250ms that would be a loss of 240 characters from the GPS.

SoftwareSerial is bit like using a shovel and a wheelbarrow compared to a modern excavator. By comparison with HardwareSerial it is slow and it uses a lot of Arduino CPU cycles to do stuff - for example to detect each separate bit as it arrives.

If you need performance get an Arduino (such as Mega, Leonardo or Micro) with a spare HardwareSerial port.

...R

Huh? I didn't say that I need performance. I even said that the sketch runs fine.

I wondered if there's something else going on, but I guess the answer is that the Arduino SD library doesn't allow the specified value (from SD) for timing out after writing a block. Or, rather, the sketch that I have will indeed miss data if the SD writes take too long (but still within the SD spec).

MK1888:
I even said that the sketch runs fine.

[...]

Or, rather, the sketch that I have will indeed miss data if the SD writes take too long (but still within the SD spec).

I find it hard to reconcile those comments.

...R

Robin2:
I find it hard to reconcile those comments.

...R

I don't. :slight_smile:

The SD spec allows for up to 250ms to finish writing. Many (most?) cards will finish sooner. My (and others') sketch works because the card doesn't take all of the allowed 250ms.

SoftwareSerial buffers up to 64 bytes (as you mentioned), but the SD library you're using buffers much, much more data.

1. How many charcaters are written in the SD card in that 250 ms time period?

2. What is Bd?

3. Please, post your codes; we would like to see how you are receiving charcaters from the GPS.

4. In the meantime, let me tell you briefly the functional mechanism of the Serial BUFFer.
A charterer/data byte arrives at the UART Port; the MCU is interrupted, and it goes to ISR; reads the character/data byte from the RX-register and stores in the very first location of the 64-byte FIFO type BUFFer. If you read the character from the BUFFer just after the storage, the BUFFer becomes empty. The following diagram (Fig-1) may give you a glimpse of the structure/operation of the FIFO BUFFer.
uartblk.png

uartblk.png

The code I used is below. My assertion is that if the dataFile.close(); command takes as long as it could (250ms) then the next read from byDataByte won't be the actual next byte but will be missing quite a lot of bytes.

I've read of other people storing a lot of data quickly, but I really don't see how they can do that reliably given the SD spec of 250ms.

/*
   This Arduino sketch will log GPS NMEA data to a SD card every second
*/

#include <SoftwareSerial.h>
#include <SD.h>


// The Arduino pins used by the GPS module
static const int GPS_RXPin = A1;
static const int GPS_TXPin = A0;
static const int GPSBaud = 9600;
static const int chipSelect = 10;

// The GPS connection is attached with a software serial port
SoftwareSerial Gps_serial(GPS_RXPin, GPS_TXPin);


void setup()
{    
  // Open the debug serial port at 9600
  Serial.begin(9600);
 
  // Open the GPS serial port  
  Gps_serial.begin(GPSBaud);  
   
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");   
}


int inByte = 0;         // incoming serial byte
byte pbyGpsBuffer[100];
int byBufferIndex = 0;

void loop()
{
  byte byDataByte;
  
  if (Gps_serial.available())
  {
     byDataByte = Gps_serial.read();
    
     Serial.write(byDataByte);
     pbyGpsBuffer[ byBufferIndex++ ] = byDataByte;
     
     if( byBufferIndex >= 100 )
     {
       byBufferIndex = 0;       
       File dataFile = SD.open("gps.txt", FILE_WRITE);
    
       // if the file is available, write to it:
       if (dataFile) {
        dataFile.write(pbyGpsBuffer, 100);
        dataFile.close();
      }  
      // if the file isn't open, pop up an error:
      else {
        Serial.println("error opening gps.txt");
      }        
     }      
  }
}

MK1888:
I've read of other people storing a lot of data quickly, but I really don't see how they can do that reliably given the SD spec of 250ms.

It's really difficult to. I do SD logging on my Arduino rc plane, but because of the buffer write-out lag, I ended up using 2 microcontrollers connected via UART. the one microcontroller is for control and the second one is completely dedicated for asynchronous telemetry logging.

Try the following sketch (yours one with slight modifications) and check if there is any improvements.

#include <SoftwareSerial.h>
#include <SD.h>
#include<SPI.h>
static const int GPS_RXPin = A1;
static const int GPS_TXPin = A0;
static const int GPSBaud = 9600;
static const int chipSelect = 10;
SoftwareSerial Gps_serial(GPS_RXPin, GPS_TXPin);
int inByte = 0;         // incoming serial byte
byte pbyGpsBuffer[100];
int byBufferIndex = 0;
byte byDataByte;
File dataFile;

void setup()
{
  Serial.begin(9600);
  Gps_serial.begin(GPSBaud);
  Serial.print("Initializing SD card...");
  pinMode(10, OUTPUT);
  if (!SD.begin(chipSelect))
  {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
  dataFile = SD.open("gps.txt", FILE_WRITE);
}

void loop()
{
  if (Gps_serial.available())
  {
    dataFile.write(Gps_serial.read());
    byBufferIndex++;
    if ( byBufferIndex == 100 )
    {
      byBufferIndex = 0;
      dataFile.close();
      //add codes to read SD and display on Serial Monitor to see if you miss any data from GPS
    }
  }
}