Forum: Mikrocontroller und Digitale Elektronik Seltsames Programmverhalten


von Coder (Gast)


Lesenswert?

Hallo!
Ich arbeite gerade an einem Programm für einen Arduino Nano welches über 
einen SoftwareSerial-Port die NMEA-Sätze von einem u-blox neo 6m 
einliest und auf einem SPI-OLED ein paar der Daten anzeigt. Zum parsen 
nehme ich die TinyGPS++ Lib. Das ganze funktioniert auch ganz gut. 
Allerdings habe ich jetzt ein Stück Code eingefügt welches wenn für 5 
Sekunden keine neuen Daten kommen eine Meldung auf das OLED schreibt. 
Wenn jetzt allerdings wieder Daten kommen ollen die natürlich wieder 
angezeigt werden. das funktioniert aber nicht (das programm scheint in 
der "No Fix" nachricht zu hängen. Wenn Ich den Arduino resette 
funktioniert das ganze wieder und die Daten werden angezeigt. Hier der 
Code:
1
#include <SPI.h>
2
#include <Wire.h>
3
#include <Adafruit_GFX.h>
4
#include <Adafruit_SSD1306.h>
5
#include <TinyGPS++.h>
6
#include <SoftwareSerial.h>
7
8
static const int RXPin = 4, TXPin = 3;
9
static const uint32_t GPSBaud = 9600;
10
11
// The TinyGPS++ object
12
TinyGPSPlus gps;
13
14
// The serial connection to the GPS device
15
SoftwareSerial ss(RXPin, TXPin);
16
17
// If using software SPI (the default case):
18
#define OLED_MOSI   9
19
#define OLED_CLK   10
20
#define OLED_DC    11
21
#define OLED_CS    12
22
#define OLED_RESET 8
23
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
24
25
/* Uncomment this block to use hardware SPI
26
 #define OLED_DC     6
27
 #define OLED_CS     7
28
 #define OLED_RESET  8
29
 Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
30
 */
31
32
33
34
#if (SSD1306_LCDHEIGHT != 64)
35
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
36
#endif
37
38
int lastUpdated;
39
40
41
void setup()   {                
42
  Serial.begin(9600);
43
  ss.begin(GPSBaud);
44
  pinMode(13,OUTPUT);
45
46
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
47
  display.begin(SSD1306_SWITCHCAPVCC);
48
  // init done
49
50
  // Show image buffer on the display hardware.
51
  // Since the buffer is intialized with an Adafruit splashscreen
52
  // internally, this will display the splashscreen.
53
  //display.display();
54
  //delay(1000);
55
56
  // Clear the buffer.
57
  display.clearDisplay();
58
  display.display();
59
}
60
61
62
void loop() {
63
  while (ss.available() > 0){
64
    gps.encode(ss.read());
65
    if (gps.location.isUpdated()){
66
      digitalWrite(13,HIGH);
67
      lastUpdated = millis();
68
      display.clearDisplay();
69
      display.setCursor(0,0);
70
      display.setTextColor(WHITE);
71
      display.setTextSize(1);
72
      display.setTextWrap(0);
73
      display.print("Lat.:  ");
74
      display.println(gps.location.lat(), 6);   
75
      display.print("Long.: ");
76
      display.println(gps.location.lng(), 6);   
77
      display.print("Speed: ");
78
      display.print(gps.speed.kmph()); 
79
      display.println(" km/h");
80
      display.print("Heading: ");
81
      display.println(gps.course.deg()); 
82
      display.print("Time:");
83
      display.print(gps.time.hour()); 
84
      display.print(":"); 
85
      display.print(gps.time.minute()); 
86
      display.print("."); 
87
      display.print(gps.time.second()); 
88
      display.display();
89
    }
90
  
91
    if(millis() - lastUpdated > 5000){
92
      digitalWrite(13,LOW);
93
      display.clearDisplay();
94
      display.setCursor(10,10);
95
      display.setTextColor(WHITE);
96
      display.setTextSize(3);
97
      display.setTextWrap(0);
98
      display.println("No Fix");
99
      display.setTextSize(1);
100
      display.setCursor(35,35);
101
      display.print("for ");
102
      display.print((millis() - lastUpdated) / 1000);
103
      display.println("s");
104
      display.print(" Press Reset to retry");
105
      display.display();
106
    }
107
  }
108
}

von Stefan F. (Gast)


Lesenswert?

lastUpdate hat den falschen Typ. Schau mal nach, was millis zurück 
liefert. Den selben Typ muss die Variable haben.

Dann musst du nach Erkennung des Fehlers die Variable lastUpdate 
aktualisieren, denn sonst wird der Fehler beim nächsten 
Schleifendurchlauf gleich wieder auftreten (endlos oft).

Dann hast du die Kontrolle innerhalb deine while Schleife, die nur 
ausgeführt wird, solange ss.available() > 0 ist. Ich glaube nicht, dass 
das so richtig ist. Du willst doch melden, wenn zu lange nichts 
verfügbar ist.

Insgesamt ist dein Lösungsansatz suboptimal. Hier geht es noch, aber 
wenn dein Programm noch etwas komplexer wird, kommst du mit dieser 
Struktur ganz schnell in eine Sackgasse.

Es sollte besser ein "endlicher Automat" sein. Dieser hätte dann 2 
Tasks:

1) Die empfangenen Daten anzeigen
2) Timeouts melden

von Gpser (Gast)


Lesenswert?

ss.available bleibt aber TRUE, selbst wenn keine Daten mehr kommen da 
der GPS-Empfänger dann weiter auf der Seriellen Schnittstelle sendet. 
Warum ich lastUpdate aktualisieren soll verstehe ich nicht. Eigentlich 
wird doch zuerst geschaut ob neue Daten da sind und wenn ja wird 
lastUpdate aktualisiert. Nur wenn keine neuen Daten da sind kommt die 
Fehlermeldung.
Was ist denn das Problem an dem Lösungsansatz

von Stefan F. (Gast)


Lesenswert?

> und wenn ja wird lastUpdate aktualisiert

Ich sehe überhaupt keinen Schreibzugriff auf diese Variable.

> ss.available bleibt aber TRUE, selbst wenn keine Daten mehr kommen

Das glaube ich Dir nicht. Wenn keine Daten mehr empfangen werden, gibt 
es nichts zu lesen. Stecke den Empfänger doch einfach mal ab, dann hängt 
dein ganzes Programm für immer.

> Was ist denn das Problem an dem Lösungsansatz

Habe ich doch geschrieben: Dass du die Variable lastUpdate nicht 
aktualisierst.

Und falls du auf meinen Kommentar zum endlichen Automaten anspielst: 
Liest erstmal nach, was das ist und wozu er gut ist. Ich werde Dir hier 
keine persönliche Zusammenfassung der zahlreichen verfügbaren 
Beschreibungen erstellen.

Noch was zum Nachdenken:
> ss.available bleibt aber TRUE, selbst wenn keine Daten mehr kommen
> Nur wenn keine neuen Daten da sind kommt die Fehlermeldung.

Das widerspricht sich.

von Coder (Gast)


Lesenswert?

Der GPS-Empfänger sendet die ganze Zeit NMEA-Sätze, aber die enthalten 
nur Daten wenn er einen Fix hat. Wenn der Sender kein Signal hat aber 
angesteckt ist sendet er auf dem seriellen Port.
Deswegen prüfe ich

Stefan U. schrieb:
> Ich sehe überhaupt keinen Schreibzugriff auf diese Variable.

Im programm ist doch
lastUpdated = millis();

Stefan U. schrieb:
> Das widerspricht sich.

Nein, das GPS sendet weiter auf der Seriellen schnittstelle aber ohne 
sinnvolle Daten, und wenn keine sinnvollen daten kommen kommt die 
Fehlermeldung.

von Stefan F. (Gast)


Lesenswert?

> Im programm ist doch
> lastUpdated = millis();

Aaaaaah, habe ich übersehen.


> Nur wenn keine neuen Daten da sind kommt die Fehlermeldung.
> wenn keine sinnvollen Daten kommen ...

Nun kommen wie der Sache näher. Du musst es schon korrekt beschrieben, 
sonst halten wir uns an Abweichungen zwischen Programm und Text auf, die 
so gewollt sind.

Dein Programm tut nicht, was du willst. Also bleibt mir nichts anderes 
übrig, als deine geschriebenen Anforderungen mit dem Quelltext zu 
vergleichen.

Du vermutest jetzt also, dass das Programm irgendwo in diesme Block 
hängen bleibt:
1
    if(millis() - lastUpdated > 5000){
2
      digitalWrite(13,LOW);
3
      ...
4
    }

Richtig? Dann schalte doch mal am Anfang zusätzlich eine weitere LED aus 
und am Ende wieder an. Dann sehen wir, ob deine Annahme richtig ist.
1
if(millis() - lastUpdated > 5000){
2
      digitalWrite(13,LOW);
3
      digitalWrite(5,LOW);
4
      ...
5
      digitalWrite(5,HIGH);
6
    }

Wenn bei Dir Pin 5 nicht frei ist, nimm halt einen anderen.

von Stefan F. (Gast)


Lesenswert?

Hast du jetzt den Typ der lastUpdate Variable korrigiert?

von Coder (Gast)


Lesenswert?

Vielen Dank schonmal, Ich habe jetzt auch ein
lastUpdated = millis();
in den Block geschrieben der für die Fehlermeldung verantwortlich ist 
und so funktioniert es auch aber ich verstehe nicht warum das so ist.
Das programm scheint nicht in dem Block hängen zu bleiben sondern 
einfach den oberen Block nicht auszuführen (wenn ich in den unteren 
Fehleranzeigeblock ein SerialPrint mache printet er das ganze in 
Dauerschleife) -> gps.location.isUpdated() wird nicht True obwohl der 
GPS-fix wieder hergestellt ist. Wenn Ich in den Fehleranzeigeblock aber 
lastUpdated aktualisiere geht es. Sehr komisch.

von Coder (Gast)


Lesenswert?

Stefan U. schrieb:
> Hast du jetzt den Typ der lastUpdate Variable korrigiert?

Ja, ich hab da jetzt unsigned long.

von Stefan F. (Gast)


Lesenswert?

Ich vermute, dass die wiederholte Display-Ausgabe so viel Rechenzeit in 
Anspruch nimmt, dass der Empfang der GPS Daten gestört wird. Der 
soft-serial Port kann ja überhaupt nichts empfangen, solange die CPU mit 
anderen Dingen beschäftigt ist.

Durch dein zusätzliches Update sorgst du dafür, dass die Anzeige der 
Fehlermeldung nur einmal stattfindet und dann erst wieder in 5 Sekunden. 
Dadurch gewinnst du 5 Sekunden Zeit, wo deine soft-serial Schnittstelle 
vernünftig funktionieren/empfangen kann.

von Coder (Gast)


Lesenswert?

Ich habe mal weitergetestet, wenn ich in dem Fehlerblock lastUpdated 
aktualisiere geht es, mein workaround wäre jetzt eine zweite Variable 
für den Counter seit wann die Verbindung unterbrochen ist zu machen aber 
ganz elegant ist das nicht und ich würde gerne den Fehler verstehen...

von Coder (Gast)


Lesenswert?

Stimmt, das könnte natürlich sein. Dann mach Ich mal eine displayChanged 
flag und aktualisiere das Display nur wenn sich was ändert. Mal schauen 
obs dann geht.

von Coder (Gast)


Lesenswert?

Nochmal vielen Dank, die Hilfe hat mir sehr geholfen!

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.