PC-Kommunikation seriell, Abbruch wenn kein Signal

Hey Leute, hab mal versucht, mein Problem möglichst konkret im Topictitel zusammenzufassen.
Es geht um folgendes:
Ich will mit nem UNO eine automatische Bierbrausteuerung entwickeln bzw. die Hardware für die Kommunikation mit der Brausoftware an einem PC.
Dieser wird dann mittels USB und der virtuellen COM Schnittstelle regelmäßig (z.B. aller 30 Sekunden) verschiedene Kommandos an den Arduino senden, wie z.B. für das Auslesen der Temperatur
und den Status von Heizplatte und Rührwerk und das An/ausschalten der beiden letztgenannten. Allerdings kann es ja sein, dass die USB Verbindung mal abbricht (Kabel aus versehen rausgezogen oder ähnliches)
In diesem Fall soll natürlich nicht alles weiterlaufen, da sonst die Heizplatte ewig weiterheizt, sondern es soll ausgeschaltet werden, bis wieder einmal pro 30s ein Signal kommt.

Wie löse ich dieses Problem mit Arduino?

Vielen Dank für eure Hilfe

fran83

Du kannst über deine serielle Verbindung einen "Ping" laufen lassen. Also z.B: alle 5 Sekunden ein "Hallo" schicken.
Sobald der Arduino von deiner Software innerhalb von 10 Sek. kein Signal mehr bekommt muss er dann das Abschalten deiner Anlage etc. einleiten.

Würde da dieser Quellcode hier gehen oder würdest du das irgendwie besser /anders schreiben?

#include <Metro.h> // Include the Metro library

Metro serialMetro = Metro(30000);  // Instantiate an instance

int incomingByte = 0;   // for incoming serial data

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

void loop() {

        // send data only when you receive data:
        if (Serial.available() > 0) {
                // mein Code
        } else if (serialMetro.check() == 1){
                // Abbruch 
        }
}

Die Metro Library kenne ich nicht. Auf die schnelle konnte ich auch nichts darüber finden.

Ich würde das über die Millies() Funktion und eine Long Variable lösen.

Also im Endeffekt so:
Wenn du den Text "Hallo" empfängst speicherst du dir den Empfangszeitpunkt (long SerTimerout) mit millies() ab.
Sobald die Zeit von 10 Sek abgelaufen(millies() - SerTimerout > 10000) ist, behandelst du die Abbruchroutine.

Mardetuino:
Die Metro Library kenne ich nicht. Auf die schnelle konnte ich auch nichts darüber finden.

Ich würde das über die Millies() Funktion und eine Long Variable lösen.

Also im Endeffekt so:
Wenn du den Text "Hallo" empfängst speicherst du dir den Empfangszeitpunkt (long SerTimerout) mit millies() ab.
Sobald die Zeit von 10 Sek abgelaufen(millies() - SerTimerout > 10000) ist, behandelst du die Abbruchroutine.

Hmm ich merke grade mein Quellcode ist unlogisch. (btw: hier ein Link Arduino Playground - Metro)
Beispiel: Ich sende regelmäßig vom PC aus, und der Arduino liest das ab. D.H. serial.available() > 0 trifft zu.
Jetzt sende ich ja aber nur aller meinetwegen 5 Sekunden. Daher kann es sein, dass serialMetro.check() == 1 und gleichzeitig gerade nichts gesendet wird.
In dem Fall wird die Abbruchroutine eingeleitet, obwohl erst vor 5 Sekunden etwas gesendet wurde.

Also hier der gefixte Code:

#include <Metro.h> // Include the Metro library
#define SERTIMEOUTABORT 30000

Metro serialMetro = Metro(SERTIMEOUTABORT);  

int incomingByte = 0;   // for incoming serial data


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

void loop() {
    // send data only when you receive data:
    if (Serial.available() > 0) {
        // mein Code
        SerialMetro.reset()
    } else if (serialMetro.check() == 1){
        // Abbruch 
    }
}

Mit Millies()

#define SERTIMEOUTABORT 30000

int incomingByte = 0;   // for incoming serial data
long SerialTimeout = 0;

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

void loop() {
    // send data only when you receive data:
    if (Serial.available() > 0) {
        // mein Code
        SerialTimeout = millies()
    } else if (millies() - SerialTimeout > SERTIMEOUTABORT){
        // Abbruch 
    }
}

Richtig so?

Beide Codes sollten funktionieren. So kannst du das mal auf den Arduino laden und testen. :grin:
Ob du jetzt die Metro oder die millies nimmst bleibt dir überlassen. Am besten das, mit dem du am besten zurecht kommst.

fran83:
Wie löse ich dieses Problem mit Arduino?

Am einfachsten:
Wenn der Arduino seit über 60 Sekunden kein Kommando mehr erhalten hat, schaltet er in den Fail-Safe Modus (und schlägt Alarm).

Allerdings: Deine System-Grundkonzeption, eine kleine, autarke und wenig fehleranfällige Steuereinheit (Arduino) von einer großen, übergeordneten, fehlerträchtigen zweiten Steuereinheit (PC) steuern zu lassen, erhöt die Gesamtausfallwahrscheinlichkeit des Systems enorm. Hoffentlich ist das Konzept nur für eine Hobbyproduktion vorgesehen und nicht für eine Brauerei, die Personal in der Anlagenüberwachung sparen möchte.

Da fehlt bei dir noch das Semikolon:
SerialTimeout = millies()
sowie auch da
SerialMetro.reset()

Das wirst du aber beim kompilieren schon merken :wink:

jurs:

fran83:
Wie löse ich dieses Problem mit Arduino?

Am einfachsten:
Wenn der Arduino seit über 60 Sekunden kein Kommando mehr erhalten hat, schaltet er in den Fail-Safe Modus (und schlägt Alarm).

Allerdings: Deine System-Grundkonzeption, eine kleine, autarke und wenig fehleranfällige Steuereinheit (Arduino) von einer großen, übergeordneten, fehlerträchtigen zweiten Steuereinheit (PC) steuern zu lassen, erhöt die Gesamtausfallwahrscheinlichkeit des Systems enorm. Hoffentlich ist das Konzept nur für eine Hobbyproduktion vorgesehen und nicht für eine Brauerei, die Personal in der Anlagenüberwachung sparen möchte.

Ich wollte auch erst alles vom Arduino übernehmen lassen, aber da es eben schon eine PC-Steuerung gibt, will ich ja das Rad nicht neu erfinden :wink: Ist nur für Eigenversorgung vorgesehen.

Mardetuino:
Da fehlt bei dir noch das Semikolon:
SerialTimeout = millies()
sowie auch da
SerialMetro.reset()

Das wirst du aber beim kompilieren schon merken :wink:

Das kommt davon, wenn man in der letzten Zeit nur Sprachen wie Python genutzt hat :smiley:

Ich hab jetzt irgendwie einen anderen komischen Fehler:

#include <Metro.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define SERIALTIMEOUTABORT 30000 // Abbruchzeit nach Verbindungsverlust
#define ONE_WIRE_BUS 2 // Anschluss DS18B20

Metro SerialMetro = Metro(SERIALTIMEOUTABORT);  

OneWire oneWire(ONE_WIRE_BUS); 
DallasTemperature sensors(&oneWire); // Pass oneWire reference to Dallas Temp 

int incomingByte = 0;   // eingehende serielle Daten
int heatStatus = 0;
int mixerStatus = 0;

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

void loop() {
    if (Serial.available() > 0) {
        SerialMetro.reset(); // Verbindung vorhanden
        incomingByte = Serial.read();
        switch(incomingByte) {
            case 0x41:
                sensors.requestTemperatures();
                Serial.print(sensors.getTempCByIndex(0)); // 0 -> 1. IC an Kabel      
        }      
    } else if (SerialMetro.check() == 1){ // in SERTIMEOUTABORT ms kein Byte
        // Abbruch 
    }
}

Syntaktisch sollte eigentlich alles ok sein, aber der Compiler gibt immer folgende Fehlermeldung aus:

(Pfad zur IDE)/arduino-1.0.3/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../../avr/lib/avr5/crtm328p.o:(.init9+0x0): undefined reference to `main'

Hab keine Ahnung, woran das liegt. Andere einfache Sketches werden problemlos compiliert.

Bei mir geht dein geposteter Sketch.

Der Fehler sieht für mich nach etwas "internem" aus. Evtl. Arduino nochmals im extra Verzeichnis entpacken und testen...

Du musst nach sensors.requestTemperatures() mind. 750ms Zeit verplämpern. Der DS braucht bei 11 bit ein wenig Zeit.

Oder so:
sensors.setWaitForConversion(true);

Mardetuino:
Bei mir geht dein geposteter Sketch.

Der Fehler sieht für mich nach etwas "internem" aus. Evtl. Arduino nochmals im extra Verzeichnis entpacken und testen...

Habs, da muss man aber erst mal darauf kommen ...
Bei mir hieß der Sketch main.ino, deswegen gings nicht. Komische IDE ... :smiley:

Mardetuino:
Du musst nach sensors.requestTemperatures() mind. 750ms Zeit verplämpern. Der DS braucht bei 11 bit ein wenig Zeit.

Oder so:
sensors.setWaitForConversion(true);

hmm du hast natürlich Recht. Ich fand das so im Beispiel Sketch von Dallastemperature.h

void loop(void)
{ 
  // call sensors.requestTemperatures() to issue a global temperature 
  // request to all devices on the bus
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
  
  Serial.print("Temperature for the device 1 (index 0) is: ");
  Serial.println(sensors.getTempCByIndex(0));  
}

Wahrscheinlich geht das so, weil die Loop() eh nichts anderes macht und da kein Delay vonnöten ist (nach einer bestimmten Ausführungszeit ist eh die Loop() zuende)

Da aber Metro.h und vor allem die serielle Kommunikation mit delay() nicht richtig funktioniert, habe ich wieder millis() benutzt. Das garantiert, dass ich in der Zwischenzeit weiter Mitteilungen empfangen und senden und z.B. auch ein LCD ansteuern kann.

Bei einer Sache bin ich mir aber noch nicht sicher. Im Beispiel "WaitForConversion.pde" von Dallas Temperature sieht der loop so aus:

...
  // Request temperature conversion (traditional)
  Serial.println("Before blocking requestForConversion");
  unsigned long start = millis();    

  sensors.requestTemperatures();

  unsigned long stop = millis();
  Serial.println("After blocking requestForConversion");
  Serial.print("Time used: ");
  Serial.println(stop - start);
  
  // get temperature
  Serial.print("Temperature: ");
  Serial.println(sensors.getTempCByIndex(0));  
  Serial.println("\n");
  
  // Request temperature conversion - non-blocking / async
  Serial.println("Before NON-blocking/async requestForConversion");
  start = millis();       
  sensors.setWaitForConversion(false);  // makes it async
  sensors.requestTemperatures();
  sensors.setWaitForConversion(true);
  stop = millis();
  Serial.println("After NON-blocking/async requestForConversion");
  Serial.print("Time used: ");
  Serial.println(stop - start);       
  ...

Also praktisch 2 Möglichkeiten des Auslesens:
Die erste soll "synchron" sein, heißt, dass beim requestTemperatures() immer so lange gewartet wird, wie für die gewählte Resolution nötig ist.
Bei der zweiten wird dann weiter unten im Programmcode noch ein Delay passend für die Resolution eingefügt. Richtig so?

Auf dieser Basis habe ich meinen Code überarbeitet.
Am Anfang lese ich in setup() ein einziges Mal synchron aus, was garantiert, dass zum ersten Aufruf der Schleife schon eine Temperatur in der Variablen gespeichert ist.
Link: http://pastebin.com/Zj2aTUzX

Sind in dem Code noch irgendwo Fehler bzw. seht Ihr Verbesserungsvorschläge vor allem bezüglich der Performance und der Programmgröße?

/edit: noch eine Frage: Kann ich die Variablen HEATING_PIN und MIXER_PIN als byte deklarieren ohne Funktionsverlust? Und geht dasselbe auch mit der Variable incomingByte, in die Serial.Read() eingelesen wird?

Also praktisch 2 Möglichkeiten des Auslesens:
Die erste soll "synchron" sein, heißt, dass beim requestTemperatures() immer so lange gewartet wird, wie für die gewählte Resolution nötig ist.
Bei der zweiten wird dann weiter unten im Programmcode noch ein Delay passend für die Resolution eingefügt. Richtig so?

Wobei die Bezeichnung für zweite Möglichkeit mit delay nicht ganz richtig ist. So wie du das gemacht hast passt das ganz gut.

Sind in dem Code noch irgendwo Fehler bzw. seht Ihr Verbesserungsvorschläge vor allem bezüglich der Performance und der Programmgröße?

Dein Sketch ist so klein, dass Größe und Performance kaum eine Rolle spielen. Der Arduino ist eher unterfordert.

noch eine Frage: Kann ich die Variablen HEATING_PIN und MIXER_PIN als byte deklarieren ohne Funktionsverlust? Und geht dasselbe auch mit der Variable incomingByte, in die Serial.Read() eingelesen wird?

Du kannst für HEATING_PIN und MIXER_PIN auch das #define nehmen.
Bzgl. incomingByte habe ich keine Ahnung. In der Reference ist das incomingByte tatsächlich auch ein int.

Mardetuino:
Du kannst für HEATING_PIN und MIXER_PIN auch das #define nehmen.
Bzgl. incomingByte habe ich keine Ahnung. In der Reference ist das incomingByte tatsächlich auch ein int.

hmm, hab gerade gemerkt, dass das ja eh Konstanten sind -> kein Einfluss auf Sketch Größe.

Hab den Code jetzt noch mal verändert, da ich will, dass man den Sensor während des laufenden Betriebs an- und abstecken kann.
http://pastebin.com/t7LxjMJ7
Hast du noch irgendwo Verbesserungsvorschläge? (bezügl. Performance: will später noch LCD und SD Karte anschließen, daher will ich jetzt schon "sparen" :D)