Winkelmessungen mit MPU6050 auf SD -Karte speichern?

Hallo,

Ich habe folgendes Problem: Ich messe Neigungswinkel mit einer MPU6050. Das funktioniert super, aber nun möchte ich diese Daten auf einer SD-Karte speichern. Leider klappt irgendetwas nicht und ich komme bei der Meldung "Error writing to file !“ raus.
Hat irgendjemand Ahnung was schief läuft?

//
#include <SD.h>
#include<SPI.h>
#include<Wire.h>
//
const int MPU_addr=0x68;
double AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
uint32_t timer; 
double compAngleX, compAngleY; 
#define degconvert 57.2957786 

String dataString =""; 
File data;
//
//
void setup()
{
// Set up MPU 6050:
  Wire.begin();
  #if ARDUINO >= 157
  Wire.setClock(400000UL); 
  #else
    TWBR = ((F_CPU / 400000UL) - 16) / 2; 
  #endif

  
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  
  Wire.write(0);     
  Wire.endTransmission(true);
  
// Open serial communications
Serial.begin(9600);
Serial.print("Initializing SD card...");
pinMode(4, OUTPUT);
//
// see if the card is present and can be initialized:
if (!SD.begin(4)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
  //setup starting angle
  //1) collect the data
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  
  AcX=Wire.read()<<8|Wire.read();     
  AcY=Wire.read()<<8|Wire.read();  
  AcZ=Wire.read()<<8|Wire.read();  
  Tmp=Wire.read()<<8|Wire.read();  
  GyX=Wire.read()<<8|Wire.read();  
  GyY=Wire.read()<<8|Wire.read();  
  GyZ=Wire.read()<<8|Wire.read();  


  double roll = atan2(AcY, AcZ)*degconvert;
  double pitch = atan2(-AcX, AcZ)*degconvert;

 
  double gyroXangle = roll;
  double gyroYangle = pitch;
  double compAngleX = roll;
  double compAngleY = pitch;

  //start a timer
  timer = micros();
}
//
void loop(){

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  
  AcX=Wire.read()<<8|Wire.read();       
  AcY=Wire.read()<<8|Wire.read();  
  AcZ=Wire.read()<<8|Wire.read();  
  Tmp=Wire.read()<<8|Wire.read();  
  GyX=Wire.read()<<8|Wire.read();  
  GyY=Wire.read()<<8|Wire.read();  
  GyZ=Wire.read()<<8|Wire.read();  
  
  double dt = (double)(micros() - timer) / 1000000; 
  timer = micros(); 

  double roll = atan2(AcY, AcZ)*degconvert;
  double pitch = atan2(-AcX, AcZ)*degconvert;

  double gyroXrate = GyX/131.0;
  double gyroYrate = GyY/131.0;

  compAngleX = 0.99 * (compAngleX + gyroXrate * dt) + 0.01 * roll; 
  compAngleY = 0.99 * (compAngleY + gyroYrate * dt) + 0.01 * pitch; 

dataString = String(compAngleX) + "," + String(compAngleY); // convert to CSV
saveData(); // save to SD card
delay(60000); // delay before next write to SD Card, adjust as required
}

//
void saveData(){
if(SD.exists("data.csv")){ // check the card is still there
// now append new data file
Serial.println("Creating data.csv...");

if (data){
File data = SD.open("data.csv", FILE_WRITE);
data.println(dataString);
data.close(); // close the file
}
}
else{
Serial.println("Error writing to file !");
}
}

Moment, Meldung ist: "Creating data.csv..." aber data.csv ist leer!

if (data)

läuft schief. Dieses globale

 File data;

hat nichts mitif(SD.exists("data.csv")){zu tun.

Lösch einfach die globale Variable, rücke richtig ein und vereinfache deine Funktion:

if(SD.exists("data.csv")){ // check the card is still there ?
  // now append new data file
  Serial.println("Creating data.csv...");
  File data = SD.open("data.csv", FILE_WRITE);
  if (data) {
    data.println(dataString);
    data.close(); // close the file
  }
  else {
    Serial.println("Error writing to file !");
  }
else {
    Serial.println("data.csv existiert nicht !");
}

Bin übrigens nicht sicher, ob SD.exists richtig erkennt, ob die Karte noch oder wieder drin ist.
Gab mindestens mal eine Version, da war nach SD-Kartenwechsel ein SD.begin() erforderlich.

michael_x:

if (data)

läuft schief. Dieses globale

 File data;

hat nichts mitif(SD.exists("data.csv")){zu tun.

Lösch einfach die globale Variable, rücke richtig ein und vereinfache deine Funktion:

Hallo michael_x,
schon mal Danke fuer die schnelle Antwort!
File data habe ich nun raus.
Jetzt bekomme ich die Fehlermeldung das ich den input abschliessen soll. Treibt mich zum Wahnsinn, da ich den Fehler nicht finde.

sketch_aug17a:121: error: expected '}' at end of input

}

^

sketch_aug17a:121: error: expected '}' at end of input

sketch_aug17a:121: error: expected '}' at end of input

exit status 1
expected '}' at end of input

treehugger:
Hallo michael_x,
schon mal Danke fuer die schnelle Antwort!
File data habe ich nun raus.
Jetzt bekomme ich die Fehlermeldung das ich den input abschliessen soll. Treibt mich zum Wahnsinn, da ich den Fehler nicht finde.

Bitte geänderten Sketch richtig eingerückt wieder hier reinstellen. Wir wissen nicht was wo geändert wurde und was jetzt fehlt.

Gruß, Katsumi

Habe den Fehler gefunden!

Vielen Dank an michael_x!

Hier noch der funktionierden Sketch:

// This sketch seems to work
#include <SD.h>
#include<SPI.h>
#include<Wire.h>
//
const int MPU_addr=0x68;
double AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
uint32_t timer; 
double compAngleX, compAngleY; 
#define degconvert 57.2957786 

String dataString =""; 
//
//
void setup()
{
// Set up MPU 6050:
  Wire.begin();
  #if ARDUINO >= 157
  Wire.setClock(400000UL); 
  #else
    TWBR = ((F_CPU / 400000UL) - 16) / 2; 
  #endif

  
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  
  Wire.write(0);     
  Wire.endTransmission(true);
  
// Open serial communications
Serial.begin(9600);
Serial.print("Initializing SD card...");
pinMode(4, OUTPUT);
//
// see if the card is present and can be initialized:
if (!SD.begin(4)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
  //setup starting angle
  //1) collect the data
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  
  AcX=Wire.read()<<8|Wire.read();     
  AcY=Wire.read()<<8|Wire.read();  
  AcZ=Wire.read()<<8|Wire.read();  
  Tmp=Wire.read()<<8|Wire.read();  
  GyX=Wire.read()<<8|Wire.read();  
  GyY=Wire.read()<<8|Wire.read();  
  GyZ=Wire.read()<<8|Wire.read();  


  double roll = atan2(AcY, AcZ)*degconvert;
  double pitch = atan2(-AcX, AcZ)*degconvert;

 
  double gyroXangle = roll;
  double gyroYangle = pitch;
  double compAngleX = roll;
  double compAngleY = pitch;

  //start a timer
  timer = micros();
}
//
void loop(){

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  
  AcX=Wire.read()<<8|Wire.read();       
  AcY=Wire.read()<<8|Wire.read();  
  AcZ=Wire.read()<<8|Wire.read();  
  Tmp=Wire.read()<<8|Wire.read();  
  GyX=Wire.read()<<8|Wire.read();  
  GyY=Wire.read()<<8|Wire.read();  
  GyZ=Wire.read()<<8|Wire.read();  
  
  double dt = (double)(micros() - timer) / 1000000; 
  timer = micros(); 

  double roll = atan2(AcY, AcZ)*degconvert;
  double pitch = atan2(-AcX, AcZ)*degconvert;

  double gyroXrate = GyX/131.0;
  double gyroYrate = GyY/131.0;

  compAngleX = 0.99 * (compAngleX + gyroXrate * dt) + 0.01 * roll; 
  compAngleY = 0.99 * (compAngleY + gyroYrate * dt) + 0.01 * pitch; 

dataString = String(compAngleX) + "," + String(compAngleY); 
saveData(); 
delay(60000); 
}

//
void saveData(){
if(SD.exists("data.csv")){ // check the card is still there ?
  Serial.println("Creating data.csv...");
  File data = SD.open("data.csv", FILE_WRITE);
  if (data) {
    data.println(dataString);
    data.close(); // close the file
  }
  else {
    Serial.println("Error writing to file !");
  }}
else {
    Serial.println("data.csv does not exist !");
}
}
  }}
else {
    Serial.println("Wer so einrückt, gehört verhauen !");
}
}

michael_x:

  }}

else {
    Serial.println("Wer so einrückt, gehört verhauen !");
}
}

Sorry! Absoluter Anfänger!
Danke für die Hilfe….bemühe mich das Einrücken auch noch zu lernen.

um es auch verstehen zu können... das letzte else wäre überflüssig? oder?

mratix:
um es auch verstehen zu können... das letzte else wäre überflüssig? oder?

Nein, mir ging es nur ums Einrücken.
Verhaut wird man, wenn man deswegen eine falsche Klammer-anzahl hat und im Forum fragt.
Wenn man Fehlerbehandlung einbaut, auch wenn hinterher keiner guckt, sollte man eher gelobt werden. :slight_smile:

@treehugger Einrücken ist mit Ctrl+T (Menüleiste: Werkzeuge/Automatische Formatierung/Klick) schnell erledigt.

Aber die Schleife funktioniert so nicht.
Die erste if muss negiert werden. Außerdem ist es total verwurschtelt und unübersichtlich.

Ich würde das ganze so herunterkürzen:

void saveData() {
  if (!SD.exists("data.csv")) { // check the file is still there ?
    Serial.println(F("data.csv does not exist !"));
    Serial.println(F("Creating data.csv..."));
    File data = SD.open("data.csv", FILE_WRITE); // create file through open
    data.close(); // close the file
    Serial.println(F("data.csv created..."));
  }
  else
  {
    File data = SD.open("data.csv", FILE_WRITE); // open file for write
    data.println(dataString); // write datastring
    data.close(); // close the file
  }

@all ist das inhaltlich OK?

michael_x:
Verhaut wird man, wenn man deswegen eine falsche Klammer-anzahl hat und im Forum fragt.

Nun ist leider ein neus Problem aufgetreten. Die Daten welche ich auf der SD Karte bekomme stimmen nicht mit denen vom 'Serial Monitor" ueberein (also mit denen vom urspruenglichen sketch, bevor ich versucht hatte das auf Sd zu speichen).

Anbei eine schnelle Grafik zum Vergleich. Woran kann es liegen?

treehugger:
... Grafik zum Vergleich. Woran kann es liegen?

Tja, die zwei haben nicht viel miteinander zu tun. Ausser, du erklärst uns den Zusammenhang.

michael_x:
Tja, die zwei haben nicht viel miteinander zu tun. Ausser, du erklärst uns den Zusammenhang.

Der Zusammenhang: Eigentlich sollte bei beiden das gleiche raus kommen. Der Sketch welcher denke ich die richtigen Daten ausgibt kommt im Original von dieser Seite:

http://www.pitt.edu/~mpd41/Angle.ino

Das funktioniert super und man bekommt sehr genau die beiden Winkel wenn man die MPU6050 an einem Onjekt befestigt.

Bei der zweiten Datenreihe habe ich im Sketch rumgepfuscht (deswegen war der auch so schlecht eingerueckt) und versucht die Daten auf die SD Karte zu schreiben. Nun ja eigentlich sollten aehnliche Werte raus kommen. Aber das tut es eben nicht.

Koennte ich eventuell die MPU Rohdaten anders als mit einem String auf die SD Karte schreiben?

Oder kommt das vom delay(), im Tutorial heisst es ja so schoen: "An analogy would be warming up a pizza in your microwave, and also waiting some important email. You put the pizza in the microwave and set it for 10 minutes. The analogy to using delay() would be to sit in front of the microwave watching the timer count down from 10 minutes until the timer reaches zero. If the important email arrives during this time you will miss it."

Koennte ich eventuell die MPU Rohdaten anders als mit einem String auf die SD Karte schreiben

Könntest du natrürlich.
Im einen Fall wird Serial.println(String(double)) aufgerufen, im andern Fall Serial.print(double).

Ob das einen Unterschied macht, musst du wissen/rauskriegen...

Im Prinzip muss jedes Byte, das auf die SD-Karte geschrieben wird, hinterher richtig interpretiert werden.

Bau doch einfach noch einSerial.println(dataString); dazu, schau dir ein, zwei oder drei Zeilen genau an. Und krieg die exakten Unterschiede raus.

Das Ganze ist, wie immer, fitzelige Kleinarbeit. Viel Spass.

Nun gut, den string ahbe ich jetzt ersetzt und ich lass mir die Daten zusaetzlich ueber den Serial Monitor geben. Jetzt liegen die nur noch etwa 20 Grad daneben. Auch habe ich versucht von dem delay() weg zu kommen, hatte erhofft weniger Daten auf zu zeichenen. Leider Fehlanzeige das sprudelt immer noch so raus.

saveData();  
unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;}
}

//
void saveData(){
if(SD.exists("data.csv")){ // check the card is still there ?
  Serial.println("Creating data.csv...");
  File data = SD.open("data.csv", FILE_WRITE);
  if (data) {  
    Serial.print(compAngleX); Serial.print(compAngleY);
    data.print(compAngleX); data.println(compAngleY);
    data.close(); 
  }
  else {
    Serial.println("Error writing to file !");
  }}
else {
    Serial.println("data.csv does not exist !");
}
}

Probier mal:

saveData(); 
unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    saveData();
}
}

Also das saveData in die Zeitsteuerung nehmen.

Gruß Tommy

Hallo,
hier hast Du einen "Intervall"

unsigned long Intervall = 5000;
long AnzStartMillis;
int Zaehler = 1;

void setup()    
{ 
  Serial.begin(9600);
}
void loop()
{


  
if (millis() - AnzStartMillis > Intervall)  
 {   
  AnzStartMillis = millis();

// das wird alle...
(Zaehler++);

  Serial.print("Zaehler ");
  Serial.println(Zaehler);
    if (Zaehler == 12)
  {
  (Zaehler = 0);
 }
// 5 Sekunden ausgeführt
 }
 }

vielleicht hilft es Dir ja...
Dann noch durch eine logische Funktion nur einmal speichern.
Gruß und Spaß
Andreas

Tommy56:
Probier mal:

saveData(); 

unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    saveData();
}
}




Also das saveData in die Zeitsteuerung nehmen.

Gruß Tommy

Danke fuer den Tipp, Tommy!
Hab ich versucht, brachte leider nichts.

SkobyMobil:
hier hast Du einen "Intervall"
vielleicht hilft es Dir ja...
Dann noch durch eine logische Funktion nur einmal speichern.

Hallo Freunde der leichten Unterhaltung!

Jetzt habe ich den Vorschlag von Andreas eingebaut (schon mal vielen dank fuer die Idee). Der count funktioniert, ABER ich weiss nicht ob ich durch dieses if ( count = 12) auch wirklich nur jeden zwoelften Wert auf zeichen. Ohne dieses if wird immer von 1 bis 12 gezaehlt und alle Werte ausgedruckt. Stellt der Sketch so wirklich die Beziehung zu dem interval = 5000 her?

Hier mein Werk von heute und das was auf der SD-Karte landet (.txt da ich ja .csv nicht hochladen kann). Erste Reihe ist der count mit 12 und dann die beiden Winkel (haben immernoch diesen dummen Offset).

Ich denke, das ist schon sehr dran langsam....

// Still 20 degrees off and producing to much data!


#include <SD.h>
#include<SPI.h>
#include<Wire.h>
//
const int MPU_addr=0x68;
double AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
uint32_t timer; 
double compAngleX, compAngleY; 
#define degconvert 57.2957786 

unsigned long interval = 5000;
long previousMillis;
int count = 1;

void setup()
{
  Wire.begin();
  #if ARDUINO >= 157
  Wire.setClock(400000UL); 
  #else
    TWBR = ((F_CPU / 400000UL) - 16) / 2;
  #endif

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  
  Wire.write(0);     
  Wire.endTransmission(true);

Serial.begin(9600);
Serial.print("Initializing SD card...");
pinMode(4, OUTPUT);

if (!SD.begin(4)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
  //setup starting angle
  //1) collect the data
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  
  AcX=Wire.read()<<8|Wire.read();     
  AcY=Wire.read()<<8|Wire.read();  
  AcZ=Wire.read()<<8|Wire.read();  
  Tmp=Wire.read()<<8|Wire.read();  
  GyX=Wire.read()<<8|Wire.read();  
  GyY=Wire.read()<<8|Wire.read();  
  GyZ=Wire.read()<<8|Wire.read();  


  double roll = atan2(AcY, AcZ)*degconvert;
  double pitch = atan2(-AcX, AcZ)*degconvert;

 
  double gyroXangle = roll;
  double gyroYangle = pitch;
  double compAngleX = roll;
  double compAngleY = pitch;
  
  timer = millis(); 
}
//
void loop(){

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  
  AcX=Wire.read()<<8|Wire.read();       
  AcY=Wire.read()<<8|Wire.read();  
  AcZ=Wire.read()<<8|Wire.read();  
  Tmp=Wire.read()<<8|Wire.read();  
  GyX=Wire.read()<<8|Wire.read();  
  GyY=Wire.read()<<8|Wire.read();  
  GyZ=Wire.read()<<8|Wire.read();  
  
  double dt = (double)(micros() - timer) / 1000000; 
  timer = micros(); 

  double roll = atan2(AcY, AcZ)*degconvert;
  double pitch = atan2(-AcX, AcZ)*degconvert;

  double gyroXrate = GyX/131.0;
  double gyroYrate = GyY/131.0;

  compAngleX = 0.99 * (compAngleX + gyroXrate * dt) + 0.01 * roll; 
  compAngleY = 0.99 * (compAngleY + gyroYrate * dt) + 0.01 * pitch; 

saveData();  
}

//
void saveData(){
if(SD.exists("data.csv")){ 
  Serial.println("Creating data.csv...");
  File data = SD.open("data.csv", FILE_WRITE);
  if (data) {
    (millis() - previousMillis > interval);
    previousMillis = millis();
    (count++);
    if (count == 12){
    (count = 0);}
    if (count = 12){
    Serial.print("Count ");
    Serial.print(count); Serial.print(compAngleX); Serial.println(compAngleY);
    data.print(count); data.print (",");
    data.print(compAngleX);data.print (",");
    data.println(compAngleY);
    data.close();  }
    }
      
  }
  else {
    Serial.println("Error writing to file !");
 
}
}