Forum: Mikrocontroller und Digitale Elektronik Arduino Uno: Problem mit Data-Logger und GPS-Tracker


von Elvir È. (elvir)


Lesenswert?

Hallo liebe Community

Ich arbeite derzeit in einer Gruppe an einem und wir haben einen Fehler, 
auf den wir nicht kommen.

Unser Projekt besteht aus einem Arduino Uno, einem pH-Sensor, einem 
Temperatursensor, einem GPS-Tracker und einem Data Logger.

Irgendwie vertragen sich der Data-Logger und der GPS Tracker nicht, das 
Programm lässt sich zwar kompilieren und übertragen, jedoch kommt der 
Arduino irgendwie durcheinander

Das Programm:
1
#include "Arduino.h"
2
#include "Adafruit_GPS.h"
3
#include "SoftwareSerial.h"
4
#include "temperature.h"
5
#include "ph.h"
6
#include "SD.h"
7
8
SoftwareSerial mySerial_gps(6, 5); //Simulierte Serielle
9
10
Adafruit_GPS GPS(&mySerial_gps);
11
12
// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
13
// Set to 'true' if you want to debug and listen to the raw GPS sentences.
14
#define GPSECHO  false
15
16
// this keeps track of whether we're using the interrupt
17
// off by default!
18
boolean usingInterrupt = false;
19
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy
20
21
float temp;
22
23
24
//SD
25
//=====================================================================================
26
#define ECHO_TO_SERIAL   1 // echo data to serial port
27
#define WAIT_TO_START    0 // Wait for serial input in setup()
28
#define redLEDpin 7
29
#define greenLEDpin 8
30
const int chipSelect = 10;
31
File logfile;
32
//=====================================================================================
33
34
void error(char *str)
35
{
36
  //Fehlertext-Ausgabe
37
  Serial.print("Fehler: ");
38
  Serial.println(str);
39
  digitalWrite(redLEDpin, HIGH);      //Rote LED leuchtet permanent
40
  while(1);    //bleibt hier hängen im Falle eines Fehlers
41
}
42
43
44
void setup()
45
{
46
47
  Serial.begin(115200);
48
  Serial.println("GPS Ausgabe starten...");
49
  pinMode(tempVcc, OUTPUT);
50
  pinMode(redLEDpin, OUTPUT);
51
  pinMode(greenLEDpin, OUTPUT);
52
  // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
53
  GPS.begin(9600);
54
55
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
56
57
  // Set the update rate
58
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate
59
60
  // Request updates on antenna status, comment out to keep quiet
61
  // GPS.sendCommand(PGCMD_ANTENNA);
62
63
  useInterrupt(true); // Jede 1 millisekunden - GPS read
64
65
66
  //SD
67
//=====================================================================================
68
  //Check ob Speicherkarte vorhanden ist
69
  if (!SD.begin(chipSelect)) {
70
    error("Zugriff auf SD-Speicherkarte nicht moeglich");
71
  }
72
73
  Serial.println("SD-Speicherkarte initialisiert.");
74
75
  //Logging-File erstellen
76
  char filename[] = "LOGGER00.CSV";
77
  for (uint8_t i = 0; i < 100; i++) {
78
    filename[6] = i/10 + '0';
79
    filename[7] = i%10 + '0';
80
    if (! SD.exists(filename)) {
81
      // only open a new file if it doesn't exist
82
      logfile = SD.open(filename, FILE_WRITE);
83
      break;  // leave the loop!
84
    }
85
  }
86
87
  if (! logfile) {
88
    error("Datei konnte nicht erstellt werden.");
89
  }
90
  //=====================================================================================
91
92
93
  delay(1000);
94
95
}
96
97
98
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
99
SIGNAL(TIMER0_COMPA_vect) {
100
  char c = GPS.read();
101
  // if you want to debug, this is a good time to do it!
102
#ifdef UDR0
103
  if (GPSECHO)
104
    if (c) UDR0 = c;
105
    // writing direct to UDR0 is much much faster than Serial.print
106
    // but only one character can be written at a time.
107
#endif
108
}
109
110
void useInterrupt(boolean v) {
111
  if (v) {
112
    // Timer0 is already used for millis() - we'll just interrupt somewhere
113
    // in the middle and call the "Compare A" function above
114
    OCR0A = 0xAF;
115
    TIMSK0 |= _BV(OCIE0A);
116
    usingInterrupt = true;
117
  } else {
118
    // do not call the interrupt function COMPA anymore
119
    TIMSK0 &= ~_BV(OCIE0A);
120
    usingInterrupt = false;
121
  }
122
}
123
124
uint32_t timer = millis();
125
void loop()                     // run over and over again
126
{
127
  temp = read_temp();
128
  ph = read_ph();
129
130
  // in case you are not using the interrupt above, you'll
131
  // need to 'hand query' the GPS, not suggested :(
132
  if (! usingInterrupt) {
133
    // read data from the GPS in the 'main loop'
134
    char c = GPS.read();
135
    // if you want to debug, this is a good time to do it!
136
    if (GPSECHO)
137
      if (c) Serial.print(c);
138
  }
139
140
141
  // if a sentence is received, we can check the checksum, parse it...
142
   if (GPS.newNMEAreceived()) {
143
144
    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
145
      return;  // we can fail to parse a sentence in which case we should just wait for another
146
  }
147
148
  // if millis() or timer wraps around, we'll just reset it
149
  if (timer > millis())  timer = millis();
150
151
  //approximately every 2 seconds or so, print out the current stats
152
  if (millis() - timer > 2000) {
153
    timer = millis(); // reset the timer
154
    delay(1000);
155
    Serial.print("\nZeit: ");
156
    Serial.print(GPS.hour+1, DEC); Serial.print(':'); // +1 wegen GMT+1
157
    Serial.print(GPS.minute, DEC); Serial.print(':');
158
    Serial.print(GPS.seconds, DEC); Serial.print('.');
159
    Serial.println(GPS.milliseconds);
160
    Serial.print("Datum: ");
161
    Serial.print(GPS.day, DEC); Serial.print('/');
162
    Serial.print(GPS.month, DEC); Serial.print("/20");
163
    Serial.println(GPS.year, DEC);
164
    Serial.print("Fix: "); Serial.print((int)GPS.fix);
165
    Serial.print(" Quali: "); Serial.println((int)GPS.fixquality);
166
    Serial.println("Temperatur: ");
167
    Serial.println(temp);
168
    Serial.println("pH-Wert: ");
169
    Serial.println(ph);
170
    if (GPS.fix) {
171
      Serial.print("Position: ");
172
      Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
173
      Serial.print(", ");
174
      Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);
175
      Serial.print("Position (in degrees, works with Google Maps): ");
176
      Serial.print(GPS.latitudeDegrees, 4);
177
      Serial.print(", ");
178
      Serial.println(GPS.longitudeDegrees, 4);
179
180
      Serial.print("Geschwindigkeit (Knoten): "); Serial.println(GPS.speed);
181
      Serial.print("Angle: "); Serial.println(GPS.angle);
182
      Serial.print("Altitude: "); Serial.println(GPS.altitude);
183
      Serial.print("Satelliten: "); Serial.println((int)GPS.satellites);
184
    }
185
  }
186
}

temperature.h:
1
#include "Arduino.h"
2
3
#define tempVcc 4
4
5
float tempReading;
6
7
8
float read_temp(void){
9
  float temp;
10
  digitalWrite(A0, LOW);   //Pull-up festlegen
11
  digitalWrite(tempVcc, HIGH); //Temperatur-Sensor einschalten
12
  delay(200);   //Bisschen warten damit die Temperatur stabilisiert wird
13
  tempReading = analogRead(0); //Spannung des Sensors auslesen
14
  digitalWrite(tempVcc, LOW);  //Sensor wieder ausschalten
15
  //Berechnungen um von der vom Sensor gelieferten Spannung auf den Temperaturwert in Celsius zu kommen
16
  tempReading*=.0048;
17
  tempReading*=1000;
18
  temp=0.0512 * tempReading -18.5128;
19
  return temp;  //die Temperatur als Rückgabewert liefern
20
}

ph.h:
1
#include "Arduino.h"
2
#include <SoftwareSerial.h>      //we have to include the SoftwareSerial library, or else we can't use it.
3
4
#define rx 2                     //define what pin rx is going to be.
5
#define tx 3                     //define what pin Tx is going to be.
6
7
SoftwareSerial myserial(rx, tx); //define how the soft serial port is going to work.
8
9
char ph_data[20];                  //we make a 20 byte character array to hold incoming data from the pH.
10
char computerdata[20];             //we make a 20 byte character array to hold incoming data from a pc/mac/other.
11
byte received_from_computer=0;     //we need to know how many characters have been received.
12
byte received_from_sensor=0;       //we need to know how many characters have been received.
13
byte arduino_only=1;               //if you would like to operate the pH Circuit with the Arduino only and not use the serial monitor to send it commands set this to 1. The data will still come out on the serial monitor, so you can see it working.
14
byte startup=0;                    //used to make sure the Arduino takes over control of the pH Circuit properly.
15
float ph=0;                        //used to hold a floating point number that is the pH.
16
byte string_received=0;            //used to identify when we have received a string from the pH circuit.
17
18
19
float Arduino_Control(void){
20
  if(startup==0){                //if the Arduino just booted up, we need to set some things up first.
21
  myserial.print("c,0\r");   //take the pH Circuit out of continues mode.
22
  delay(50);                 //on start up sometimes the first command is missed.
23
  myserial.print("c,0\r");   //so, let’s send it twice.
24
  delay(50);                 //a short delay after the pH Circuit was taken out of continues mode is used to make sure we don’t over load it with commands.
25
  startup=1;                 //startup is completed, let's not do this again during normal operation.
26
  }
27
28
  delay(200);                         //we will take a reading ever 800ms. You can make this much longer or shorter if you like.
29
  myserial.print("R\r");             //send it the command to take a single reading.
30
  if(string_received==1){            //did we get data back from the ph Circuit?
31
    ph=atof(ph_data);                //many people ask us "how do I convert a sting into a float?" This is how...
32
    return ph;
33
    string_received=0;}             //reset the string received flag.
34
}
35
36
float read_ph(void){
37
  myserial.begin(38400);
38
  if(myserial.available() > 0){        //if we see that the pH Circuit has sent a character.
39
    received_from_sensor=myserial.readBytesUntil(13,ph_data,20); //we read the data sent from pH Circuit until we see a <CR>. We also count how many character have been received.
40
    ph_data[received_from_sensor]=0;  //we add a 0 to the spot in the array just after the last character we received. This will stop us from transmitting incorrect data that may have been left in the buffer.
41
    string_received=1;     //a flag used when the Arduino is controlling the pH Circuit to let us know that a complete string has been received.
42
    }
43
44
  if(arduino_only==1){
45
    ph = Arduino_Control();
46
    return ph;
47
  } //If the var arduino_only is set to one we will call this function. Letting the Arduino take over control of the pH Circuit
48
}



Die Bauteile sowie libraries haben wir von hier:
GPS-Tracker: http://www.adafruit.com/product/746
Data-Logger: http://www.adafruit.com/product/1141

Die Sensoren funktionieren einwandfrei, jedoch entweder nur mit dem 
Data-Logger ODER der GPS-Tracker, alles zusammen will nicht.

Wenn ich alles was mit dem Data.Logger zu tun hat (markiert durch 
Kommentare mit "SD" im Hauptprgramm), dann kommt folgendes dabei heraus:

GPS Ausgabe starten...
Zeit: 1:0:0.0
Datum: 0/0/200
Fix: 0 Quali: 0
Temperatur:
25.23
pH-Wert:
7.17

Zur Zeit haben wir keinen GPS-Empfang, deshalb kommt überall 0 raus

Wenn ich alls unkommentiert lasse, dann kommt der Arduino durcheinander 
und führt offensichtlich nur mehr als 1 mal die setup() Methode aus:

GPS Ausgabe starten...
GPS Ausgabe starten...
GPS Ausgabe starten...
GPS Ausgabe starten...
GPS Ausgabe starten...
GPS Ausgabe starten...
GPS Ausgabe starten...

Ab und zu hört er dann auf und gibt folgendes aus:

GPS Ausgabe starten...
GPS Ausgabe starten...
GPS Ausgabe starten...
Fehler: Zugriff auf SD-Speicherkarte nicht moeglich

Wobei der Zugriff eigentlich erfolgreich seien sollte.



Bitte um dringende Hilfe!

von Christian (Gast)


Lesenswert?

Kann es sein, dass ihr ein Problem mit der Stromversorgung habt? 
Versorgt ihr das ganze über USB oder über ein externes Netzteil? Was hat 
das für einen maximalen Ausgangsstrom?

von Stefan F. (Gast)


Lesenswert?

1
useInterrupt(true); // Jede 1 millisekunden - GPS read
2
3
SIGNAL(TIMER0_COMPA_vect) {
4
  char c = GPS.read();
5
}
6
7
GPS.begin(9600);

Wie lange dauert GPS.read()? Ich fürchte, dass es knapp länger als eine 
Millisekunde dauert, und das passt nicht zum 1ms Timer.

von PittyJ (Gast)


Lesenswert?

In so einem Programm haben delay()s nichts mehr zu suchen. Das macht 
man, wenn man mit nur einem Sensor etwas herum spielt.
Bei komplexeren Aufbauten verzichtet man auf Delays und baut besserer 
Algorithmen, weil es sonst zu Problemen kommt.

Und GPS verändert sich ja nicht so schnell ( außer in Rennwagen und 
Flugzeugen). Da reicht es oft, den nur alle Sekunde oder noch seltener 
zu lesen.

von Gerald B. (gerald_b)


Lesenswert?

Software Serial ist manchmal recht eigenwillig. Funktionierte bei mir 
z.B. nicht zusammen mit I2C (fürs LCD).
Versuche mal eine andere Lib. Gibt noch AltSoftSerial u.a.
Wenn du nicht über die echte serielle Schnittstelle am PC Daten auslesen 
willst, dann kannst die auch nutzen. Ist dann wesentlich entspannter. 
Kann dann aber sein, das du das nächste Mal das Programm per ISP flashen 
mußt, weil du über die serielle Schnittstelle keinen Zugriff mehr hast.
Ist mit dem USBasp z.B. auch kein Problem, da ich den Arduino lediglich 
zum Prototyping nutze und dann auf Lochraster aufbaue.

von Elvir È. (elvir)


Lesenswert?

Christian schrieb:
> Kann es sein, dass ihr ein Problem mit der Stromversorgung habt?
> Versorgt ihr das ganze über USB oder über ein externes Netzteil? Was hat
> das für einen maximalen Ausgangsstrom?

Das ist ein interessanter Einwand, wir werden gleich heute mal ein 
Netzgerät dranhängen, wir hatten es nur über USB versorgt.


Stefan us schrieb:
> Wie lange dauert GPS.read()? Ich fürchte, dass es knapp länger als eine
> Millisekunde dauert, und das passt nicht zum 1ms Timer.

Der Timer wird hier nicht benutzt, da die GPS Koordinaten in das char c 
im loop geupdatet wird, nicht im Interrupt.

PittyJ schrieb:
> In so einem Programm haben delay()s nichts mehr zu suchen. Das macht
> man, wenn man mit nur einem Sensor etwas herum spielt.
> Bei komplexeren Aufbauten verzichtet man auf Delays und baut besserer
> Algorithmen, weil es sonst zu Problemen kommt.
>
> Und GPS verändert sich ja nicht so schnell ( außer in Rennwagen und
> Flugzeugen). Da reicht es oft, den nur alle Sekunde oder noch seltener
> zu lesen.

Danke für den Tipp! Die Zeit werden wir vermutlich erhöhen auf 5 
Sekunden oder so.

Gerald B. schrieb:
> Software Serial ist manchmal recht eigenwillig. Funktionierte bei mir
> z.B. nicht zusammen mit I2C (fürs LCD).
> Versuche mal eine andere Lib. Gibt noch AltSoftSerial u.a.
> Wenn du nicht über die echte serielle Schnittstelle am PC Daten auslesen
> willst, dann kannst die auch nutzen. Ist dann wesentlich entspannter.
> Kann dann aber sein, das du das nächste Mal das Programm per ISP flashen
> mußt, weil du über die serielle Schnittstelle keinen Zugriff mehr hast.
> Ist mit dem USBasp z.B. auch kein Problem, da ich den Arduino lediglich
> zum Prototyping nutze und dann auf Lochraster aufbaue.

Danke für den Tipp, SoftwareSerial hat schon manchmal Probleme bei uns 
gemacht.

von Elvir È. (elvir)


Lesenswert?

An dem zu geringem Strom hat es nicht gelegen.

Mittlerweile haben wir den Übeltäter auf diesen Block eingeschränkt.
1
    if (! SD.exists(filename)) {
2
      // only open a new file if it doesn't exist
3
      logfile = SD.open(filename, FILE_WRITE);
4
      break;  // leave the loop!
5
    }

Wir haben uns die Klassen im cpp file angesehen, aber wissen immer noch 
nicht inwiefern das dem Ablauf des Programms im Weg steht.

Bitte um weitere Hinweise! Danke!

von Gerald B. (gerald_b)


Lesenswert?

Vielleicht reicht das RAM nicht. Es wird ja immer der jeweilige Sektor, 
der bearbeitet werden soll, im RAM vorgehalten.
Wenn dem so ist, hast du 2 Möglichkeiten. Den Code optimieren, oder wenn 
das nicht ausreicht, dann größeren Controller nehmen.
Wenn du "zu Fuß" mit den einzelnen Bauelementen aufbaust, bietet sich 
der ATMEGA 1284 an. Wenn du einen fertigen Arduino bevorzugst, dann 
läuft es auf den Mega hinaus.

von Jürgen S. (jurs)


Lesenswert?

Elvir Ègalo schrieb:
> Wenn ich alls unkommentiert lasse, dann kommt der Arduino durcheinander
> und führt offensichtlich nur mehr als 1 mal die setup() Methode aus:
>
> GPS Ausgabe starten...
> GPS Ausgabe starten...
> GPS Ausgabe starten...
> GPS Ausgabe starten...
> GPS Ausgabe starten...
> GPS Ausgabe starten...
> GPS Ausgabe starten...

Die ständigen Neustarts des Programms deuten darauf hin, dass ihr beim 
Programmieren mehr RAM verbraucht als der Controller eingebaut hat.

Und wenn ich mir ansehe, wie schweinemäßig ihr den wenigen RAM-Speicher 
verschwendet mit solchen Zeilen wie:
> Serial.println("Temperatur: ");
dann wundert mich das auch gar nicht.

Die absolute Grundregel bei der Ausgabe von Textkonstanten, die keinen 
RAM-Speicher verbrauchen sollen, ist: "print" zusammen mit 
Textkonstanten immer mit dem F-Makro verwenden.

Also als erstes mal alle print-Ausgaben mit Textkonstanten auf diese 
Form umschreiben:
> Serial.println(F("Temperatur: "));

Ob das in Eurem Fall ausreicht, um dadurch genügend RAM-Speicher 
einzusparen, wenn man konsequent Textkonstanten mit F-Makro ausgibt, 
müßte man mal testen.

Wenn es dann immer noch nicht läuft, würde ich mal eine Funktion zur 
Ermittlung des freien RAM-Speichers mit einbinden:
1
int freeRam () {
2
  extern int __heap_start, *__brkval; 
3
  int v; 
4
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
5
}
und mir mit dieser Funktion am Ende der setup-Funktion, nach der 
Initialisierung der Serial-, GPS- und SD-Library ausgeben lassen, ob und 
wieviel RAM-Speicher dann überhaupt noch frei bleibt, um die Funktionen 
in der loop laufen zu lassen.

Und dann ggf. einen Controller mit mehr RAM verwenden, z.B. MEGA statt 
UNO.

Oder weniger vollfette, full-featured Third-Party-Libraries verwenden, 
die Euch den RAM-Speicher für die Bereitstellung von Funktionen 
wegknabbern, die ihr gar nicht braucht, und stattdessen die notwendige 
Funktionalität mit minimalem RAM-Speicherverbrauch selbst programmieren.

von Elvir È. (elvir)


Lesenswert?

Mit dem Mega funktioniert es! Es lag tatsächlich am RAM.

Vielen Dank für die Hilfe & Tipps!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.