Skip to main content
replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

Thanks to the feedback obtained in this questionthis question on StackOverflow, I understood that the problem was my excessive use of strings, and now i understand why.

Thanks to the feedback obtained in this question on StackOverflow, I understood that the problem was my excessive use of strings, and now i understand why.

Thanks to the feedback obtained in this question on StackOverflow, I understood that the problem was my excessive use of strings, and now i understand why.

Tweeted twitter.com/StackArduino/status/844762189948841984
replaced textual reference to a question with actual url to resource
Source Link

Thanks to Patrick Trentin I understood on Stack Overflow “MPU 6050 SD Writing Frequency Arduino is not constantthe feedback obtained in this question on StackOverflow, how to have a constant writing frequency? “I understood that the problem was my excessive use of strings, and now i understoodunderstand why.

So I started to godig into thatthis issue.

Thanks to Patrick Trentin I understood on Stack Overflow “MPU 6050 SD Writing Frequency Arduino is not constant , how to have a constant writing frequency? “ that the problem was my excessive use of strings, and now i understood why.

So I started to go into that.

Thanks to the feedback obtained in this question on StackOverflow, I understood that the problem was my excessive use of strings, and now i understand why.

So I started to dig into this issue.

Source Link
Andrea Ciufo
  • 235
  • 1
  • 3
  • 9

Understanding why should avoid “String” and alternative solutions

  1. Why “Strings” are bad for Arduino?
  2. Which is the most efficient and fastest solution to read and store the data from Accelerometer and GPS?

Stings are evil for Arduino


An Uno or other ATmega328-based board only has 2048 bytes SRAM.

The SRAM is composed by three parts:

  1. The Static
  2. The Heap
  3. The Stack

The Heap is where, randomly, a C++ string information is allocated.

If I add an information to my C++ String the system copies itself and deletes the old copy leaving a hole in the heap, that grows up with the cycles.

When I wrote my first sketch for reading and writing the information from an MPU6050 and stored on a SD (whithout connecting to a GPS) I noticed that the sampling time was not constant.

enter image description here

Thanks to Patrick Trentin I understood on Stack Overflow “MPU 6050 SD Writing Frequency Arduino is not constant , how to have a constant writing frequency? “ that the problem was my excessive use of strings, and now i understood why.

So I started to go into that.

This was one of the first version of the code:

/******************************************************************************
  CSV_Logger_TinyGPSPlus.ino
  Log GPS data to a CSV file on a uSD card
  By Jim Lindblom @ SparkFun Electronics
  February 9, 2016
  https://github.com/sparkfun/GPS_Shield

  This example uses SoftwareSerial to communicate with the GPS module on
  pins 8 and 9, then communicates over SPI to log that data to a uSD card.

  It uses the TinyGPS++ library to parse the NMEA strings sent by the GPS module,
  and prints interesting GPS information - comma separated - to a newly created
  file on the SD card.

  Resources:
  TinyGPS++ Library  - https://github.com/mikalhart/TinyGPSPlus/releases
  SD Library (Built-in)
  SoftwareSerial Library (Built-in)

  Development/hardware environment specifics:
  Arduino IDE 1.6.7
  GPS Logger Shield v2.0 - Make sure the UART switch is set to SW-UART
  Arduino Uno, RedBoard, Pro, Mega, etc.
******************************************************************************/

#include <SPI.h>
#include <SD.h>
#include <TinyGPS++.h>
#include<Wire.h>
#define ARDUINO_USD_CS 10 // uSD card CS pin (pin 10 on SparkFun GPS Logger Shield)

/////////////////////////
// Log File Defintions //
/////////////////////////
// Keep in mind, the SD library has max file name lengths of 8.3 - 8 char prefix,
// and a 3 char suffix.
// Our log files are called "gpslogXX.csv, so "gpslog99.csv" is our max file.
#define LOG_FILE_PREFIX "gpslog" // Name of the log file.
#define MAX_LOG_FILES 100 // Number of log files that can be made
#define LOG_FILE_SUFFIX "csv" // Suffix of the log file
char logFileName[13]; // Char string to store the log file name
// Data to be logged:
#define LOG_COLUMN_COUNT 15
char * log_col_names[LOG_COLUMN_COUNT] = {
  "longitude", "latitude", "altitude", "speed", "course", "date", "time", "satellites","Acc.X","Acc.Y","Acc.Z","Gy.X","Gy.Y","Gy.Z","Temp"
}; // log_col_names is printed at the top of the file.

//////////////////////
// Log Rate Control //
//////////////////////
#define LOG_RATE 1000 // Log every 1 seconds
unsigned long lastLog = 0; // Global var to keep of last time we logged

/////////////////////////
// TinyGPS Definitions //
/////////////////////////
TinyGPSPlus tinyGPS; // tinyGPSPlus object to be used throughout
#define GPS_BAUD 9600 // GPS module's default baud rate

/////////////////////////////////
// GPS Serial Port Definitions //
/////////////////////////////////
// If you're using an Arduino Uno, Mega, RedBoard, or any board that uses the
// 0/1 UART for programming/Serial monitor-ing, use SoftwareSerial:
#include <SoftwareSerial.h>
#define ARDUINO_GPS_RX 9 // GPS TX, Arduino RX pin
#define ARDUINO_GPS_TX 8 // GPS RX, Arduino TX pin
SoftwareSerial ssGPS(ARDUINO_GPS_TX, ARDUINO_GPS_RX); // Create a SoftwareSerial

// Set gpsPort to either ssGPS if using SoftwareSerial or Serial1 if using an
// Arduino with a dedicated hardware serial port
#define gpsPort ssGPS  // Alternatively, use Serial1 on the Leonardo

// Define the serial monitor port. On the Uno, Mega, and Leonardo this is 'Serial'
//  on other boards this may be 'SerialUSB'
#define SerialMonitor Serial
const int MPU=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

 

void setup()
{
   Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  SerialMonitor.begin(9600);
  gpsPort.begin(GPS_BAUD);

  SerialMonitor.println("Setting up SD card.");
  // see if the card is present and can be initialized:
  if (!SD.begin(ARDUINO_USD_CS))
  {
    SerialMonitor.println("Error initializing SD card.");
  }
  updateFileName(); // Each time we start, create a new file, increment the number
  printHeader(); // Print a header at the top of the new file
}

void loop()
{
 Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
{
  if ((lastLog + LOG_RATE) <= millis())
  { // If it's been LOG_RATE milliseconds since the last log:
    if (tinyGPS.location.isUpdated()) // If the GPS data is vaild
    {
      if (logGPSData()) // Log the GPS data
      {
        SerialMonitor.println("GPS logged."); // Print a debug message
        lastLog = millis(); // Update the lastLog variable
      }
      else // If we failed to log GPS
      { // Print an error, don't update lastLog
        SerialMonitor.println("Failed to log new GPS data.");
      }
    }
    else // If GPS data isn't valid
    {
      // Print a debug message. Maybe we don't have enough satellites yet.
      SerialMonitor.print("No GPS data. Sats: ");
      SerialMonitor.println(tinyGPS.satellites.value());
    }
  }

  // If we're not logging, continue to "feed" the tinyGPS object:
  while (gpsPort.available())
    tinyGPS.encode(gpsPort.read());
}
}


byte logGPSData()
{
  File logFile = SD.open(logFileName, FILE_WRITE); // Open the log file

  if (logFile)
  { // Print longitude, latitude, altitude (in feet), speed (in mph), course
    // in (degrees), date, time, and number of satellites.
    logFile.print(tinyGPS.location.lng(), 6);
    logFile.print(',');
    logFile.print(tinyGPS.location.lat(), 6);
    logFile.print(',');
    logFile.print(tinyGPS.altitude.feet(), 1);
    logFile.print(',');
    logFile.print(tinyGPS.speed.mph(), 1);
    logFile.print(',');
    logFile.print(tinyGPS.course.deg(), 1);
    logFile.print(',');
    logFile.print(tinyGPS.date.value());
    logFile.print(',');
    logFile.print(tinyGPS.time.value());
    logFile.print(',');
    logFile.print(tinyGPS.satellites.value());
    logFile.print(AcX);
    logFile.print(',');
    logFile.print(AcY);
    logFile.print(',');
    logFile.print(AcZ);
    logFile.print(',');
    logFile.print(GyX);
    logFile.print(',');
    logFile.print(GyY);
    logFile.print(',');
    logFile.print(GyZ);
    logFile.print(',');
    logFile.print(Tmp);
    logFile.println();
    logFile.close();

    return 1; // Return success
  }

  return 0; // If we failed to open the file, return fail
}

// printHeader() - prints our eight column names to the top of our log file
void printHeader()
{
  File logFile = SD.open(logFileName, FILE_WRITE); // Open the log file

  if (logFile) // If the log file opened, print our column names to the file
  {
    int i = 0;
    for (; i < LOG_COLUMN_COUNT; i++)
    {
      logFile.print(log_col_names[i]);
      if (i < LOG_COLUMN_COUNT - 1) // If it's anything but the last column
        logFile.print(','); // print a comma
      else // If it's the last column
        logFile.println(); // print a new line
    }
    logFile.close(); // close the file
  }
}

// updateFileName() - Looks through the log files already present on a card,
// and creates a new file with an incremented file index.
void updateFileName()
{
  int i = 0;
  for (; i < MAX_LOG_FILES; i++)
  {
    memset(logFileName, 0, strlen(logFileName)); // Clear logFileName string
    // Set logFileName to "gpslogXX.csv":
    sprintf(logFileName, "%s%d.%s", LOG_FILE_PREFIX, i, LOG_FILE_SUFFIX);
    if (!SD.exists(logFileName)) // If a file doesn't exist
    {
      break; // Break out of this loop. We found our index
    }
    else // Otherwise:
    {
      SerialMonitor.print(logFileName);
      SerialMonitor.println(" exists"); // Print a debug statement
    }
  }
  SerialMonitor.print("File name: ");
  SerialMonitor.println(logFileName); // Debug print the file name
}

Then I started:

  1. Studying how Arduino Memory works
  2. Breaking the problem, focusing only to learn how to read and write on a SD the accelerometer information (the device is not connected to the PC)

The following code is my latest version:

 void Read_Write()
 // function that reads the MPU and writes the data to the SD.
  {
   // Local variables:
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; // Variables read from MPU6050

// Read data:
Wire.beginTransmission(MPU);
Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
AcX = Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
AcY = Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ = Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)

GyX = Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY = Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ = Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)




// Data preparation for file saving:
String dataString = ""; // string for assembling the data to log:

// Add time tag:
dataString += String(Time0); dataString += ",";

// Append the MPU6050 data to the string:
dataString += String(AcX); dataString += ",";
dataString += String(AcY); dataString += ",";
dataString += String(AcZ); dataString += ",";
dataString += String(GyX); dataString += ",";
dataString += String(GyY); dataString += ",";
dataString += String(GyZ);

// Open the file in append mode:
File dataFile = SD.open("datalog.txt", FILE_WRITE);

// If the file is available, write to it:
if (dataFile) 
{
dataFile.println(dataString);
dataFile.close();
if (Serial_plus_SD)
  Serial.println(dataString);
}
// if the file does not open, pop up an error:
else 
errorFW();

return;
}

I recived other suggestions such as "create a Buffer", but without a technical description and motivation I am having difficulties understand how to do it.

(I don’t want to simply copy&paste the code as i have done intitially with the first version)

I have to use C Strings instead of C++ Strings?

I have a civil engineer background, so is my first time with coding.

Thank you for your patience.