Writing Data from Serial Port to SD Card with MEGA

I am very new to Arduino and have only started doing projects about a month and a half ago. Apologies in advance if this has been answered countless times, but I couldn't figure this out from what I've already looked at.

I am currently trying to read data from an MPU6050 that is connected to an Elegoo Uno R3, and then send that data to an Arduino Mega 2560, and finally save that data onto an SD Card which is connected to the Arduino Mega.

How fast I need to record this data is not yet confirmed, but it needs to be at least a few times a second.

I am fairly certain all my problems lie in the receiver code, but I'll post the sender as well. Using the code below, only pieces of my data are printed (attached is serial monitor) and then after awhile, my SD card fails to open.

Do I need to incorporate a buffer, or a ring buffer? If so, how would I do that for this situation. Also, I can only write to the SD Card in 512 byte blocks, correct? Is this why it fails to open after a certain amount of time?

If anyone could help me with any of these questions, I'd be very grateful!

SENDER (ELEGOO UNO R3):

// Includes
#include <Wire.h> // I2C library
#include <math.h> // for atan2(double y, double x)

#include <SD.h> // Load the SD library
#include <SPI.h> // Load the SPI communication library 

// Defines
#define DegConvert 57.29577951

// Global Variables
const int MPU1_addr = 0x68; // 0110 1000 from data sheet. AD0 is grounded Sec. 9.2

double accelX, accelY, accelZ;
double gForceX, gForceY, gForceZ;

double gyroX, gyroY, gyroZ;
double rotX, rotY, rotZ;

double Temp, TempF, RawTemp;

double roll;

unsigned long timestamp;

void setup() 
{
  Serial.begin(9600);
  Wire.begin(); // join the I2C bus
  setupMPU();
}

void loop() 
{
  timestamp = millis();
  recordAccelRegisters();
  recordGyroRegisters();
  recordTempRegisters();
  printData();
  delay(250);
}

void setupMPU() 
{
  Wire.beginTransmission(MPU1_addr); //MPU1_addr = 0x68
  Wire.write(0x6B); //Accessing the register 0x6B - Power Management (Sec. 4.28)
  Wire.write(0x00); //Setting SLEEP register to 0. (Required to wake up MPU; see Note on p. 9)
  Wire.endTransmission();
  Wire.beginTransmission(MPU1_addr); //I2C address of the MPU
  Wire.write(0x1B); //Accessing the register 0x1B - Gyroscope Configuration (Sec. 4.4)
  Wire.write(0x00); //Setting the gyro to full scale +/- 250deg./s
  Wire.endTransmission();
  Wire.beginTransmission(MPU1_addr); //I2C address of the MPU
  Wire.write(0x1C); //Accessing the register 0x1C - Acccelerometer Configuration (Sec. 4.5)
  Wire.write(0x00); //Setting the accel to +/- 2g
  Wire.endTransmission();
}

void recordAccelRegisters() 
{
  Wire.beginTransmission(MPU1_addr); //I2C address of the MPU
  Wire.write(0x3B); //Starting register for Accel Readings
  Wire.endTransmission();
  Wire.requestFrom(MPU1_addr, 6, true); //Request Accel Registers (0x3B - 0x40)
  //while(Wire.available() < 6);
  accelX = Wire.read() << 8 | Wire.read(); //0x3B (ACCEL_XOUT_H) and 0x3C (ACCEL_XOUT_L)
  accelY = Wire.read() << 8 | Wire.read(); //0x3D (ACCEL_YOUT_H) and 0x3E (ACCEL_YOUT_L)
  accelZ = Wire.read() << 8 | Wire.read(); //0x3F (ACCEL_ZOUT_H) and 0x40 (ACCEL_ZOUT_L)
  processAccelData();
}

void processAccelData() {
  gForceX = accelX / 16384.0; // +/- 2g setting. 16834/°/g Sec 4.17 in datasheet
  gForceY = accelY / 16384.0;
  gForceZ = accelZ / 16384.0;
}

void recordGyroRegisters() 
{
  Wire.beginTransmission(MPU1_addr); //I2C address of the MPU
  Wire.write(0x43); //Starting register for Gyro Readings
  Wire.endTransmission();
  Wire.requestFrom(MPU1_addr, 6, true); //Request Gyro Registers (0x43 - 0x48)
  //while(Wire.available() < 6);
  gyroX = Wire.read() << 8 | Wire.read(); //0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
  gyroY = Wire.read() << 8 | Wire.read(); //0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
  gyroZ = Wire.read() << 8 | Wire.read(); //0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)
  processGyroData();
}

void processGyroData() {
  rotX = gyroX / 131.0; // The +/- 250°/sec setting --> Maximum detection = 41.67 RPM
  rotY = gyroY / 131.0;
  rotZ = gyroZ / 131.0;
  roll = atan2(rotZ, rotY) * DegConvert;
}

void recordTempRegisters() {
  Wire.beginTransmission(MPU1_addr); //I2C address of the MPU
  Wire.write(0x41); //Accessing the register 0x41 - Temperature Registers (Sec. 4.18)
  Wire.endTransmission();
  Wire.requestFrom(MPU1_addr, 2, true); // Stop transmission after request
  RawTemp = Wire.read() << 8 | Wire.read(); // Store the upper and lower byte into RawTemp
  processTemp();
}

void processTemp() {
  Temp = ((RawTemp / 340.0) + 36.532); // From section 4.18 in Register Map Datasheet
  TempF = (Temp * (9.0 / 5.0)) + 32; // Temp in F
}

void printData() {
  
  Serial.print(rotX);
  Serial.print(",");
  Serial.print(rotY);
  Serial.print(",");
  Serial.print(rotZ);
  Serial.print(",");
  Serial.print(gForceX);
  Serial.print(",");
  Serial.print(gForceY);
  Serial.print(",");
  Serial.print(gForceZ);
  Serial.print(",");
  Serial.print(Temp);
  Serial.print(",");
  Serial.println(timestamp);
}

RECEIVER (ARDUINO MEGA 2560):

  if (Serial3.available() > 0) {
    mySensorData = SD.open("Test.txt", FILE_WRITE);

    if (mySensorData) // If SD opens correctly, go into this conditional
    {
      while (Serial3.available() > 0)
      {
        reading = Serial3.read();
        mySensorData.print(reading);
        Serial.print(reading);

      }

    } else {
      Serial.println("File failed to open");
    }
  }
   mySensorData.close();
  delay(250);
}

Each line in you'r serial should be consisted of 8 values separated by a comma, that is not what you'r serial monitor shows; why is that?

misha782:
Each line in you'r serial should be consisted of 8 values separated by a comma, that is not what you'r serial monitor shows; why is that?

I think it is because some of the data isn't being read and skipped over. The data is being sent by the UNO, but the Mega is doing other tasks when that data is incoming. Is that correct?

Read this tutorial by Robin2 for better understanding of serial comm.

Try wrapping the data with begin transaction and end transaction symbols; So you would know when one line ends and other begins. Only then print the line to SD.

Try something of this form:

#define START_MARKER '<'
#define INTERMEDIATE_MARKET '#'
#define END_MARKER '>'
#define SERIAL_BUFFER 160

void loop(void){
serialEvent();
  if(isData){
          mySensorData.print(reading);
        Serial.print(reading);
  isDate=false;
}
}


byte receivedBytes[SERIAL_BUFFER];
bool isData=flase;
void serialEvent() {
  static int ndx = -1;
  byte rb;

  while (Serial.available() && !isData) {
    rb = Serial.read();
    if (ndx == SERIAL_BUFFER)
      ndx--;

    if (ndx >= 0)
      if (rb != END_MARKER)
        receivedBytes[ndx++] = rb;
      else {
        receivedBytes[ndx] = END_MARKER;
        isData = true;

        if (DEBUG_SERIAL_EVENT) {
          Serial.print(ndx + 1);
          Serial.print(" bytes recived. isData=true; Serial:");
          Serial.println(Serial.available());
        }

        ndx = -1;
      }
    else if (rb == START_MARKER) {
      //START_MARKER received.
      ndx = 0;
    }
  }
}

Hey, that seemed to get it to work. I really appreciate you taking the time to reply to me!

Thanks! :slight_smile:

Actually, after editing my code, it looks somewhat correct. But after running for awhile it will begin to print the same line over and over again, or print some lines with pieces of the data missing (See attached Serial Monitor).

//ARDUINO MEGA 2560: RECEIVER


#include <SD.h> // Load the SD library
#include <SPI.h> // Load the SPI communication library 

File mySensorData;
int chipSelect = 53;

const byte numChars = 64;
char data[numChars];
boolean newData = false;


void setup() {
  Serial.begin(9600);
  Serial3.begin(9600); //TX3 and RX3
  pinMode(chipSelect, OUTPUT);
  SD.begin(chipSelect);
}

void loop() {
  receiveData();
  writeToSD();
}

void receiveData(){

  static byte ndx = 0;
  char endMarker = '\n';
  char reading;

  if(Serial3.available() > 0){
    while (Serial3.available() > 0 && newData == false){

      reading = Serial3.read();
      
      if (reading != endMarker){
        data[ndx] = reading;
        ndx++;
        if(ndx >= numChars){
          ndx = numChars - 1;
        }
      }else{
        data[ndx] = '\0'; //terminate the string
        ndx = 0;
        newData = true;
      }
    }
  }
}

void writeToSD(){
  
  if(newData = true){
    mySensorData = SD.open("Test.txt", FILE_WRITE);
    mySensorData.print(data);
    mySensorData.print("\n");
    mySensorData.close();
    Serial.print(data);
    Serial.print("\n");
    newData = false;
  
  }
  

}