Speichern von CAN Bus Daten auf SD-Karte

Hallo zusammen,

wir arbeiten an der Uni an einem Projekt und müssen CAN-Bus Daten speichern.

Es liegt uns ein Arduino Uno mit einem CANBus Shield von Seeed vor. Die empfangenen CAN-Nachrichten simulieren wir momentan mit einem Arduino MEGA.

Wir haben den Code zum Empfangen und Auslesen der Daten, jedoch haben wir momentan Probleme damit, diese auf die SD-Karte zu speichern. Diese ist im CAN Bus Shield des Arduino UNOs eingebaut.
Wir wollen auch zusätzlich ein RTC mit anschließen, damit die gespeicherten Daten einen Zeitstempel bekommen, jedoch haben wir das noch nicht in den Code eingebaut.

Wir freuen über jede Hilfe, da wir noch Arduino Einsteiger sind.

Hier haben wir auch unseren Code hochgeladen :slight_smile:

#include <df_can.h>
#include <df_candfs.h>
#include <SPI.h>
#include "df_can.h"
#include <SD.h>

const int SPI_CS_PIN = 9;
MCPCAN CAN(SPI_CS_PIN);                                    // Set CS pin
unsigned char flagRecv = 0;
unsigned char len = 0;
unsigned char buf[8];
char str[20];
char  sd_cspin = 4; //pin 4 as spi_cs pin
File myFile;

void setup()
{
    Serial.begin(115200);
    int count = 50;                                     // the max numbers of initializint the CAN-BUS, if initialize failed first!.
    Serial.print("Initializing can controlor...");
    do {
        CAN.init();   //must initialize the Can interface here!
        CAN.init_Mask(MCP_RXM0, 0, 0x3ff);                         // there are 2 mask in mcp2515, you need to set both of them
        CAN.init_Mask(MCP_RXM1, 0, 0x3ff);
        /*
         * set filter, we can receive id from 0x04 ~ 0x09 except for 0x06
         * // there are 6 filter in mcp2515,so it can filter six id,i.e.0x04~0x09.
         */
        CAN.init_Filter(MCP_RXF0, 0, 0x04);                         // filter 0 for id = 0x04
        CAN.init_Filter(MCP_RXF1, 0, 0x05);                         // filter 1 for id = 0x05
        CAN.init_Filter(MCP_RXF2, 0, 0x60);                         // filter 2 for id = 0x60
        CAN.init_Filter(MCP_RXF3, 0, 0x07);                         // filter 3 for id = 0x07
        CAN.init_Filter(MCP_RXF4, 0, 0x08);                         // filter 4 for id = 0x08
        CAN.init_Filter(MCP_RXF5, 0, 0x09);                         // filter 5 for id = 0x09
        if(CAN_OK == CAN.begin(CAN_500KBPS))                   // init can bus : baudrate = 500k
        {
            Serial.println("DFROBOT's CAN BUS Shield init ok!");
            break;
        }
        else
        {
            Serial.println("DFROBOT's CAN BUS Shield init fail");
            Serial.println("Please Init CAN BUS Shield again");
            delay(100);
            if (count <= 1)
                Serial.println("Please give up trying!, trying is useless!");
        }
    }while(count--);
    attachInterrupt(0, MCP2515_ISR, FALLING); // start interrupt
    Serial.print("Initializing SD card...");

    if (!SD.begin(sd_cspin)) {
        Serial.println("initialization failed!");
        return;
    }
    Serial.println("initialization success!");
    myFile = SD.open("Node0x60.txt", FILE_WRITE); //the file named Node0x60.txt use to save the data
    // with the frame id equeling 0x06.
    if (!myFile){
        Serial.println("open Node0x60.text failed!");
    }
    else {
        Serial.println("open Node0x60.text success!");
    }
}
void MCP2515_ISR(){
    flagRecv = 1;
}
char filewrite = 1, fileread = 0;
int i = 0, j = 0;
void loop(){
    if(flagRecv)                   // check if get data
    {
        flagRecv = 0;                // clear flag
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf
        if (filewrite) {
            if (i++ < 1) //only recieve one frame
            {
                myFile.write(buf, len);
            }
            else   {
                myFile.close();
                filewrite = 0;
                myFile = SD.open("Node0x60.txt", FILE_WRITE);
                if (SD.exists("Node0x60.txt")) {
                    Serial.println("example.txt exists.");
                    fileread = 1;
                }
                else {
                    Serial.println("example.txt doesn't exist.");
                }
            }
        }
        if (fileread)    {
            Serial.println("printf the data that myFile has saved! ");
            myFile.read(buf, len);
            Serial.println((char *)buf);
            Serial.println("");
            myFile.close();

            Serial.println("myFile closed!!!!");
            fileread = 0;
        }
    }
}

Viele Grüße

Nicht_Funktionierender_Code_Empfanden_mit_SD_Karte.ino (4.4 KB)

Hi

Der Interrupt ist unnötig, wenn Interrupt, müssen alle variablen, Die INNEN und AUSSEN benutzt werden, volatile deklariert werden.
Da Ihr in der ISR nur ein Flag setzt, könnt Ihr auch ganz normal den INT-Pin des CAN-Modul pollen - kommt auf's Gleiche raus nur ohne ISR.

Klappt das Auslesen der CAN-Nachricht?
Erst dann würde ich mir einen Weg überlegen, wie ich die Nachricht (Uhrzeit,ID,Daten) auf der SD-Karte verewigt bekomme.

MfG

Hallo,

was funktioniert denn nicht?
Habt ihr schon die Bsp. durchgearbeitet?
Vorm lesen oder schreiben müßt ihr die Datei öffnen. Dann könnt ihr in die Datei schreiben oder aus ihr lesen. Am Ende immer closed. Weil erst mit closed wird wirklich auf die SD geschrieben. Bis dahin stehen die Daten nur in einem Buffer.

https://www.arduino.cc/en/Reference/SD

const char dateiname[] = {"test.txt"}; 

myFile = SD.open(dateiname, FILE_WRITE);

// if the file opened okay, write to it:
if (myFile)
{
  myFile.print/write ...
  ...
  myFile.close();
}
else
{
  // if the file didn't open, print an error:
  Serial.println("error opening test.txt");
}

// re-open the file for reading:
Serial.println("loop re-open");
myFile = SD.open(dateiname);
if (myFile)
{
  // read from the file until there's nothing else in it:
  while (myFile.available() )
  {
    Serial.write(myFile.read());
  }
  myFile.close(); 
}
else
{
  // if the file didn't open, print an error:
  Serial.println("error loop re-opening test.txt");
}

Vielen Dank für die schnellen Antworten!

Wir haben nun versucht den Code zu korrigieren und den Interrupt-Part entfernt. Leider kriegen wir keine richtige Ausgabe. Nun fragen wir uns, wo der Fehler im Code liegen könnte.

Wir sind über jede Hilfe dankbar!

Anbei unser neuer Code :slight_smile:

#include <df_can.h>
#include <df_candfs.h>
#include <SPI.h>
#include "df_can.h"
#include <SD.h>

const int SPI_CS_PIN = 9;
MCPCAN CAN(SPI_CS_PIN);                                    // Set CS pin
unsigned char len = 0;
unsigned char buf[8];
char  sd_cspin = 4; //pin 4 as spi_cs pin
File myFile;

void setup(){
    Serial.begin(115200);
    int count = 50;                                     // the max numbers of initializint the CAN-BUS, if initialize failed first!.
    Serial.print("Initializing can controlor...");
    do {
        CAN.init();   //must initialize the Can interface here!
       
        if(CAN_OK == CAN.begin(CAN_500KBPS))                   // init can bus : baudrate = 500k
        {
            Serial.println("DFROBOT's CAN BUS Shield init ok!");
            break;
        }
        else  {
            Serial.println("DFROBOT's CAN BUS Shield init fail");
            Serial.println("Please Init CAN BUS Shield again");
            delay(100);
            if (count <= 1)
                Serial.println("Please give up trying!, trying is useless!");
        }
    }while(count--);
    Serial.print("Initializing SD card...");
    if (!SD.begin(sd_cspin)) {
        Serial.println("initialization failed!");
        return;
    }
    Serial.println("initialization success!");
    myFile = SD.open("test.txt", FILE_WRITE); //the file named Node0x60.txt use to save the data
    // with the frame id equeling 0x06.
    if (!myFile) {
        Serial.println("open test.text failed!");
    }
    else {
        Serial.println("open test.text success!");
    }
}
const char dateiname[] = {"test.txt"};
void loop(){
  if(CAN_MSGAVAIL == CAN.checkReceive()) {
    CAN.readMsgBuf(&len, buf); 
  }
if(myFile){ 
  for(int i = 0; i<len; i++){
  myFile = SD.open(dateiname, FILE_WRITE);
  myFile.write(buf, &len);
  myFile.close();
}
}
else{
  Serial.println("error opening test.txt");
}
myFile = SD.open(dateiname);
if (myFile){
  // read from the file until there's nothing else in it:
  while (myFile.available() ){
    Serial.print(myFile.read());
  }
  myFile.close();
}
else{
  // if the file didn't open, print an error:
  Serial.println("error loop re-opening test.txt");  
  }
 Serial.println("loop re-open");
 myFile = SD.open(dateiname);
 if (myFile) {
          while (myFile.available()) {
            Serial.print(myFile.read());
          }
          myFile.close();
          }
          else  {
            Serial.println("error loop re-opening test.txt");
          }
        }

receive_sd_fail.ino (2.72 KB)

Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

hybridbagger:
Wir sind über jede Hilfe dankbar!

Vor dem Posten von Code:
Leerzeilen entfernen
STRG-T in der IDE drücken.

falsch:

if(myFile)
{ 
 for(int i = 0; i<len; i++){
 myFile = SD.open(dateiname, FILE_WRITE);
 myFile.write(buf, &len);
 myFile.close();
}

[edit: nicht ganz:] richtig:

myFile = SD.open(dateiname, FILE_WRITE);
if(myFile)
{ 
 for(int i = 0; i<len; i++){
 myFile.write(buf, &len);
}
 myFile.close();
}

Was soll das &len im write?

Gruß Tommy

Tommy56:
Was soll das &len im write?

Das hab ich sogar übersehen… weil schon die Eröffnung nicht funktionierte. :frowning:

Danke für den Tipp, habe es jetzt nachträglich geändert.

Den Code haben wir jetzt auch angepasst, danke für die Korrektur!, sodass es mit folgendem zwar besser klappt, aber immernoch nicht optimal, so wie wir es gerne hätten:

myFile = SD.open(dateiname, FILE_WRITE);
if(myFile)
{
 for(int i = 0; i<len; i++){
 myFile.write(buf, &len);
}
 myFile.close();
}

Unsere Ausgabe im seriellen Monitor sieht nämlich momentan so aus: siehe Anhang “serieller Monitor”
Wenn wir den Arduino jedoch vom Laptop entfernen, um nachzuschauen, was auf der SD Karte gespeichert ist, finden wir nun eine Dateien: siehe Anhang “TEST”
Wenn wir aber jetzt den Arduino nur mit Powerbank betreiben, wird folgendes gespeichert: siehe Anhang “TEST_POWERBANK”
Und da wir gerne später auch den Arduino nur mit einer Powerbank, also einem Akku betreiben wollten, wenn wir die Can-Bus Daten an einem realen Fahrzeug auslesen, wäre die Erkenntnis auch für uns wichtig, warum die gespeicherten Daten auf der SD Karte unterschiedlich sind, obwohl der selbe Code noch drauf ist und sieselben Daten immernoch gesendet werden?
Das gespeicherte dort sieht aber nicht so aus, wie wir es gerne hätten, wir würden nämlich gerne sowas in der Art speichern: siehe Anhang “Ausgabe”

Die Ausgabe wie im Anhang unter “Ausgabe” haben wir mit folgendem Code dann im seriellen Monitor erhalten:

void loop()
{
      unsigned char len = 0;
      unsigned char buf[8];

      if(CAN_MSGAVAIL == CAN.checkReceive())            // check if data coming
      {
          CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

          for(int i = 0; i<len; i++)    // print the data
          {
              Serial.print(buf[i], HEX);
              Serial.print("\t");
          }
          Serial.println();
      }
}

Und zu den Fragen, was das &len im write soll?:
Den &len Befehl haben wir eingesetzt, da wir für den Befehl CAN.readMsgBuf einen Zeiger zu Byte benötigen, wir benutzen &len also als Adresse, damit später die Anzahl der Bytes, die in len empfangen worden sind zur Ausgabe der Daten benutzt werden kann

TEST_POWERBANK.TXT (713 Bytes)

TEST.TXT (6.27 KB)

Hallo,

gibt diese Lesefunktion wirklich die Länge zurück?

CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

Ich an eurer Stelle würde ja erstmal die Kommunikation und die SD Karte einzeln testen.
Habt ihr schon geprüft was mit ‘len’ zurückkommt?
Stimmen die Werte vom CAN auf dem seriellen Monitor? Ohne SD Karte.

Die SD Karte kannste zum Bsp. im setup mit “Datenmüll” beschreiben und im setup wieder auslesen.
Legt euch meinetwegen selbst ein Array an und lasst es auf die Karte schreiben und lesen. Ohne CAN.
Mit allen zusammen wisst ihr nicht wo der Fehler steckt.
Baut euch zum debuggen meinetwegen auch im Code Zeilen ein mit Serial.print und lasst euch Zwischenwerte von irgendwelchen Variablen ausgeben zum überprüfen.

Edit:
das hier ist auch etwas Unsinn.

for(int i = 0; i<len; i++)
{
 myFile.write(buf, &len);
 ...
}

i wird nicht verwendet. Und wenn i verwendet würde, dann bräuchte write nur einen Parameter. Also entweder write mit 2 Parameter ohne for oder mit for und ein Parameter.

Vielleicht so …

for(int i = 0; i<len; i++)
{
 myFile.write(buf[i]);
}

Doc_Arduino:
gibt diese Lesefunktion wirklich die Länge zurück?

CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

Ja.
Ich hab mir das example mal angesehen.

hybridbagger:
Den Code haben wir jetzt auch angepasst, danke für die Korrektur!, sodass es mit folgendem zwar besser klappt, aber immernoch nicht optimal, so wie wir es gerne hätten:

Das liegt wohl daran, das ihr soviel um das example herum schon gebaut habt, das es unübersichtlich wurde. Zudem mischt ihr Konventionen
Als Beispiel:

const int SPI_CS_PIN = 9;
char  sd_cspin = 4;

Ich hätte wie folgt angefangen:
Ungetestet!

/*
   erweitertes DF-Can-Example
*/

#include <SPI.h>
#include "df_can.h"
#include <SD.h>

const int SD_CS_PIN = 4;
const int SPI_CS_PIN = 10;
MCPCAN CAN(SPI_CS_PIN);                                    // Set CS pin

unsigned char flagRecv = 0;
unsigned char len = 0;
unsigned char buf[8];
char str[20];

File myFile;

void setup()
  {
  Serial.begin(115200);
  if (!SD.begin(SD_CS_PIN))
    {
    Serial.println("initialization failed!");
    while (1);
    }
  int count = 50;                                     // the max numbers of initializint the CAN-BUS, if initialize failed first!.
  do
    {
    CAN.init();   //must initialize the Can interface here!
    if (CAN_OK == CAN.begin(CAN_500KBPS))                  // init can bus : baudrate = 500k
      {
      Serial.println("DFROBOT's CAN BUS Shield init ok!");
      break;
      }
    else
      {
      Serial.println("DFROBOT's CAN BUS Shield init fail");
      Serial.println("Please Init CAN BUS Shield again");
      delay(100);
      if (count <= 1)
        Serial.println("Please give up trying!, trying is useless!");
      }
    }
  while (count--);
  attachInterrupt(0, MCP2515_ISR, FALLING); // start interrupt
  }

void MCP2515_ISR()
  {
  flagRecv = 1;
  }

void loop()
  {
  if (flagRecv)
    {
    // check if get data
    myFile = SD.open("Node0x60.txt", FILE_WRITE);
    flagRecv = 0;                   // clear flag
    // iterate over all pending messages
    // If either the bus is saturated or the MCU is busy,
    // both RX buffers may be in use and after having read a single
    // message, MCU does  clear the corresponding IRQ conditon.
    while (CAN_MSGAVAIL == CAN.checkReceive())
      {
      // read data,  len: data length, buf: data buf
      CAN.readMsgBuf(&len, buf);
      // print the data
      for (int i = 0; i < len; i++)
        {
        Serial.write(buf[i]); Serial.print("\t");
        myfile.write(buf[i]); myfile.write("\t");
        }
      Serial.println();
      myfile.close();
      }
    }
  }