(Uno R3)Uart data receiving and write data to SD card, but some data were lost. Is there any strategy(mulit-thread, multi-tesk, etc.) can solve ?

Hi,

I am using Arduino R3 to receive data through UART and writing them to SD card.
But the logged data are not complete.
According to my present software framework, I think uart data receiving is affected while writing data to SD card.
Is there any method(mulit-thread, multi-tesk, etc.) or sample can solve this problem?

// include the SD library:
#include <SPI.h>
#include <SD.h>

#define BUF_LEN 512

char receivedChar;
unsigned int receivedCnt = 0;
char tempBuf[BUF_LEN] = {0};

const int chipSelect = 10;
File myFile;

void setup() {
  // put your setup code here, to run once:

  Serial.begin(115200);
  while (!Serial);

  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("1. is a card inserted?");
    Serial.println("2. is your wiring correct?");
    Serial.println("3. did you change the chipSelect pin to match your shield or module?");
    Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!");
    while (true);
  }

  Serial.println("initialization done.");
}

void loop() {
  // put your main code here, to run repeatedly:
  
  while (Serial.available() > 0)
  {
    tempBuf[receivedCnt++] = Serial.read();

    if(receivedCnt >= BUF_LEN)
    {
      receivedCnt = 0;
      myFile = SD.open("test.txt",FILE_WRITE);

      // if the file is available, write to it:
      if (myFile)
      {
        myFile.write(tempBuf, BUF_LEN);
        myFile.close();
      }
      else
      {
        Serial.println("error opening txt file");
      }
    }   
  }
}

You are correct with that assumption. Note that opening and closing a file will also take time so you're advised to open once and only close when the transfer is complete.

What are you using to send the data?

You will need to implement a protocol. Something in the line of

  1. Sender sends 512 bytes.
  2. Sender waits till receiver tells it to continue.
  3. Receiver receives 512 bytes and writes to SD.
  4. Receiver sends message to sender that it's ready for next data.

You can let the sender send an initial message to tell the receiver how many bytes are expected.

So based on the above, the question is how much control you have over the sender.

Note:

  1. In the unlikely case that your Uno uses a FTDI232 (e.g. SparkFun Redboard), you can implement Xon/Xoff at the Arduino side and use any terminal program that supports that.

Hi sterretje,

Data are sent from an electronic product. My purpose is just for receiving any datum it sends and writes it to SD card.
The product is not programable and the data it sends have no format, so I can't implement a protocol to tell it when to stop/start.
So, I am wondering is it possible to implement multi-thread or multi-task on Uno R3 to solve this problem?
If it is, could you provide related sample or tutorial?
Thank you.

Does it have to be SD? Or can you maybe use alternative

Which product? Can you provide a datasheet / user manual / protocol description / .... Does the product support some form of flow control / handshake? How big is the data to be transferred?

An Uno can only fake multitasking and definitely can't do multithreading.

It sounds like what you need is the equivalent of TCP. That requires a protocol, CRC32 checks and other bits and pieces. But before that, how do you know you got all the data from the UART in the first place. You have two places where you can lose data, so you need the above solution times two.

You might try opening the file in setup(), and then write each received byte to the file when received. Let the library worry about collecting the data into a buffer and writing it to the card in 512-byte chunks, which it will do automatically. Don't close the file until you are ready to shut down.

I'm not sure these functions necessarily interfere with each other. They run in separate peripherals in the chip. I don't think either function shuts off interrupts, and I thought there was a separate 64-byte serial receive buffer that operates in the background. So I'm not sure what's going on with the loss of data, unless there's just too much coming in.

Consider the Sparkfun OpenLog, which does just that. It works "out of the box".

But before that, how do you know you got all the data from the UART in the first place.

I also got log from PC so that I can compare it with ".txt" file.

That is why you use CTC to ensure you have all the data. I see an "out of the box" solution in post#7, check that out.

Have a look at the LowLatencyLogger example in the SdFat library, you might be able to adapt that.

Can you give us an idea of the volume of data being transmitted? How many bytes per second?

Opening a file, writing a line of data and closing it again is a common beginner mistake, and explains why you are losing data. The operation is not only very slow, it vastly increases the SD card error rate and current draw.

Open the file once in setup() and close it only when you are done collecting data.

For long term data collection, issue myFile.flush(); every hour or day to update the file pointers, otherwise you lose everything if the power fails.

Running Serial at 115200 baud can fill a 512 byte buffer in a little over 44mS, and overflows the Serial receive buffer in a little over 5mS, so you will need to have fairly fast write time to the SD card, and either a larger Serial receive buffer, or be transferring serial data into a buffer while the SD write is taking place.

Thanks for all suggestions.
I modified the program as below. Open a file once in setup(), and write each received byte to SD card, then close it when my setting time is up. The situation seems better, but there are still a few data lost.

// include the SD library:
#include <SPI.h>
#include <SD.h>

const int chipSelect = 10;

File myFile;

unsigned long timeNow = 0;
unsigned long interval = 1000;//1000 ms
unsigned int secCnt = 0;

void setup() {
  // put your setup code here, to run once:

  Serial.begin(115200);
  while (!Serial);

  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("1. is a card inserted?");
    Serial.println("2. is your wiring correct?");
    Serial.println("3. did you change the chipSelect pin to match your shield or module?");
    Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!");
    while (true);
  }

  Serial.println("initialization done.");

  myFile = SD.open("test.txt", FILE_WRITE);

}

void loop() {
  // put your main code here, to run repeatedly:
  if( (millis() > (timeNow + interval))  )
  {
    timeNow = millis();
    secCnt++;
  }

  if(myFile)
  {
    while(Serial.available() > 0)
    {
      myFile.write(Serial.read());
    }

    if(secCnt >= 30) // close the file after 30 seconds
    {
      secCnt = 0;
      myFile.close();
    }
  }
}

Looks like you are only losing a few characters.

You might try increasing the size of the Serial receive buffer by modifying the HardwareSerial.h file in the boards package. That would be simpler than buffering the data in your code, which would require using interrupts during the write to the SD card to transfer data out of the Serial buffer.

1 Like

Have you tried using the SdFat.h library instead of SD.h? I don't know if it would make any difference, but it might. That library also has an SDFormatter.ino example that can fully erase the entire card before formatting it, which could save some time when writing - the card's controller wouldn't need to erase a sector before writing to it. But I don't know if the difference would be material.

Wow! I have tried and it really works.
Thank you so much for your help.
Thank all people who gave me suggestions in the thread.