Writing Data to Teensy 4.1 microSD

goBlueHuskers

New member
Hello, I am quite new to microcontrollers and coding so I will do my best to explain my problem and what I want to accomplish. I apologize for any confusion from my part.

I have an external device sending data over a UART cable to my Teensy 4.1. The UART wires are connected to the RX1, TX1, Ground, and Vin ports, respectively. The data being sent over is in the form of ascii character strings, 10 columns each, with a new line (\n) at the end of each string (Ex: 1,0,A,0,4,4,0,0,0,0) at a baud rate of 115200. I just need to simply read exactly what is coming in and write it to the microSD card on the Teensy. The current code I have below is able to successfully create a new 'data.txt' folder on the microSD card; however, I don't get any data written/populated in it. Any assistance on how to accomplish this is greatly appreciated. Thank you.

#include <SD.h>
#include <SPI.h>
File myFile;
// Teensy 3.5 & 3.6 & 4.1 on-board: BUILTIN_SDCARD
const int chipSelect = BUILTIN_SDCARD;
void setup()
{

// Open serial communications and wait for port to open:
Serial.begin(115200);
Serial1.begin(115200);
while (!Serial) {
; // wait for serial port to connect.
}
Serial.print("Initializing SD card....");
if (!SD.begin(chipSelect)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");

// open the file.
myFile = SD.open("data.txt", FILE_WRITE);

// if the file opened okay, write to it:
if (myFile) {
Serial.print("Ready to receive data from outside source");
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening data.txt");
}
}
void loop() {
// Open the file for writing (append mode)
myFile = SD.open("nffData.txt", FILE_WRITE | O_APPEND);
if (myFile) {
while (Serial1.available()) {
char incomingChar = Serial1.read();
myFile.write(incomingChar);
}
// Close the file
myFile.close();
} else {
Serial.println("Error opening data.txt");
}
}
 
If you look at the source for SDClass::eek:pen(), you will see that the only supported value for mode are:
FILE_READ - which sets flags to O_READ
FILE_WRITE - which sets flags to O_RDWR | O_CREAT | O_AT_END
and
FILE_WRITE_BEGIN - which sets flags to O_RDWR | O_CREAT

so try removing the O_APPEND from your SD.open()
 
Looking at your loop, it seems that you will keep opening and writing to your file indefinitely. Is that why you have append as an option/mode?
 
I would open the file once and then keep writing to it. That is, open it in setup, write to it in loop. You already have myFile as a global variable so this will work. Periodically you may then need to do myFile.flush() to make it actually write to the sdcard. Otherwise it will go to cache instead. Now, the cache is written when full but often it's nice to write the data more frequently. But, you'd need some way to keep track of when you want to flush and when to exit the loop and close the file. If you are periodically flushing then the file will end up with data in it, even if you pull the power or reset things without properly closing the file.
 
So the code below is successful at writing data to the microSD card; however it doesn't take long for data to be left out/not written to the card and line spacing to get screwed up, how could I approach solving this issue? Thanks



#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
#include <cassert>
File myFile;
const int chipSelect = BUILTIN_SDCARD;
const int BUFF_SIZE = 200;
byte buff[BUFF_SIZE];
// counter
int counter = 0;
// Specify the file name here
const char* fileName = "Data1.txt";
void setup() {
Serial.begin(115200);
Serial1.begin(115200);
//Line below here added from nnf-sample //// IF CODE DOESN"T WORK REMOVE THIS LINE BELOW FIRST
//Serial.setTimeout(20);
//Line above here added from nnf-sample //// IF CODE DOESN"T WORK REMOVE THIS LINE ABOVE FIRST
while (!Serial1) {
; // wait for the serial port to connect.
}
Serial.print("Initializing the SD card....");
if (!SD.begin(chipSelect)) {
Serial.println("Initialization failed!");
return;
}
Serial.println("Initialization done... Begin the Simulation.");
myFile = SD.open(fileName, FILE_WRITE);
if (myFile) {
Serial.println("Ready to Receive Data from the Simulator");
} else {
Serial.println("Error opening the file");
}
}
void loop() {
int available = Serial1.available();
int bytes_to_transfer;
if (available > BUFF_SIZE) {
bytes_to_transfer = BUFF_SIZE; // Can't fit everything into the buffer, get as much as we can
} else {
bytes_to_transfer = available; // We have some data to grab
}
static bool received_within_last_frame = false;
if (bytes_to_transfer > 0) {
received_within_last_frame = true;
int read_bytes = Serial1.readBytes(buff, bytes_to_transfer);
assert(read_bytes == bytes_to_transfer); // Something weird happened, this should be impossible to fail
int written_bytes = myFile.write(buff, read_bytes);
myFile.flush();
assert(written_bytes == read_bytes); // Again, should be impossible :)
} else {
delay(20);
received_within_last_frame = false;
}
counter += 1;
if (counter > 150) {
if (received_within_last_frame) {
Serial.println("(We did something this period!)");
myFile.flush();
} else {
Serial.println("Did nothing this period");
}
counter = 0;
}
}
 
In future when you insert code can you place it between code tags using the </> button.
It makes it much easier to read.

I have put your code between code tags below.
Code:
#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
#include <cassert>
File myFile;
const int chipSelect = BUILTIN_SDCARD;
const int BUFF_SIZE = 200;
byte buff[BUFF_SIZE];
// counter
int counter = 0;
// Specify the file name here
const char* fileName = "Data1.txt";
void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
    //Line below here added from nnf-sample //// IF CODE DOESN"T WORK REMOVE THIS LINE BELOW FIRST
    //Serial.setTimeout(20);
    //Line above here added from nnf-sample //// IF CODE DOESN"T WORK REMOVE THIS LINE ABOVE FIRST
    while (!Serial1) {
        ; // wait for the serial port to connect.
    }
    Serial.print("Initializing the SD card....");
    if (!SD.begin(chipSelect)) {
        Serial.println("Initialization failed!");
        return;
    }
    Serial.println("Initialization done... Begin the Simulation.");
    myFile = SD.open(fileName, FILE_WRITE);
    if (myFile) {
        Serial.println("Ready to Receive Data from the Simulator");
    }
    else {
        Serial.println("Error opening the file");
    }
}
void loop() {
    int available = Serial1.available();
    int bytes_to_transfer;
    if (available > BUFF_SIZE) {
        bytes_to_transfer = BUFF_SIZE; // Can't fit everything into the buffer, get as much as we can
    }
    else {
        bytes_to_transfer = available; // We have some data to grab
    }
    static bool received_within_last_frame = false;
    if (bytes_to_transfer > 0) {
        received_within_last_frame = true;
        int read_bytes = Serial1.readBytes(buff, bytes_to_transfer);
        assert(read_bytes == bytes_to_transfer); // Something weird happened, this should be impossible to fail
        int written_bytes = myFile.write(buff, read_bytes);
        myFile.flush();
        assert(written_bytes == read_bytes); // Again, should be impossible :)
    }
    else {
        delay(20);
        received_within_last_frame = false;
    }
    counter += 1;
    if (counter > 150) {
        if (received_within_last_frame) {
            Serial.println("(We did something this period!)");
            myFile.flush();
        }
        else {
            Serial.println("Did nothing this period");
        }
        counter = 0;
    }
}
 
however it doesn't take long for data to be left out/not written to the card and line spacing to get screwed up, how could I approach solving this issue?

Maybe delay(20) is causing problems?

Why would you ever delay in this sort of program? Don't you want to always be ready to receive more data?
 
Writing to a file on the SD card is ‘blocking’. The main loop() will (can) ‘hang’ for up to tens if not hundreds of millisecond while doing transactions with the card.
During that time, the Serial1 reception still works in parallel because it’s interrupt driven. But it only has a small buffer. Once that’s full, you will miss rx bytes.
Workaround1: in startup, use addMemoryForRead on Serial1, with a, say, 4 kb of buffer.
But that may not be enough. Workaround2: add a timer that generates say 10 ms interrupts, in the timer isr read the serial input and push it in a very large fifo buffer, in loop() read the fifo buffer and write to SD card.
 
I have an external device sending data over a UART cable to my Teensy 4.1. The UART wires are connected to the RX1, TX1, Ground, and Vin ports, respectively.
Not sure if connection is correct. Teensy4.1 does not tollerate 5V (Vin is 5V). I would connect to 3.3V to avoid any problems. OK, I hardly use UART, so I'm not sure if +V is required and GND is sufficient.
 
Back
Top