Programmoptimierung Arduino Kommunikation

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Guysbert
User
Beiträge: 2
Registriert: Freitag 19. Oktober 2012, 09:28

Hallo,

ich bin ziemlicher Anfänger mit Python. Ich habe bisher hauptsächlich unter Windows programmiert (Java, C#). Jetzt habe ich gerade ein kleines Projekt umgesetzt und dabei hauptsächlich Codesnippets zusammengepastet. Ich verstehe was mein Programm macht, würde es aber gerne optimieren. Dafür fehlt mir allerdings die Python Erfahrung. Vielleicht könnt ihr mir hier ja weiterhelfen.

Folgendes Szenario:

Ich betreibe ein embedded Debian auf einer SheevaPlug. Daran angebunden ist ein Arduino Uno der bisher nichts weiter tut, als über einen Sensor die Raumtemperatur auf den Serial Port zu schreiben.

Mein Python Programm liest diesen Sensor aus und schreibt jede Minute die aktuelle Zeit + Temperatur in eine Datei. Diese Datei lese ich über meinen Webserver aus. Mit einem Javascript generiere ich mir dann eine hübsche Kurve. Das funktioniert auch soweit, aber wie gesagt, alles nur ein bisschen zusammengeschustert. Ich würde das jetzt gerne "schön" machen.

Hier das aktuelle Python Script:

Code: Alles auswählen

import datetime
import threading
import serial
import time


class Arduino(threading.Thread):
        def run(self):
                f = open('/var/www/values', 'a')
                now = datetime.datetime.now()
                self.ser = serial.Serial('/dev/ttyACM0', 9600, timeout=15)
                while 1:
                        self.ser.flushInput()
                        data = self.ser.readline().strip()
                        if data:
                                timestamp = time.strftime("%m, %d, %Y %H:%M:%S", time.localtime())
                                f.write(timestamp + ";" + data.strip() + "\n")
                        f.flush()
                        time.sleep(60)

def main():
        ard = Arduino()
        ard.start()


main()
Folgendes möchte ich erreichen:

1. Wenn ich den Arduino von der Plug abziehe, soll das Programm nicht stoppen. Stattdessen soll das Programm so lange die Schnittstelle abfragen, bis der Arduino wieder steckt. (Vielleicht über Exception Handling?)
2. Wie man sieht, startet das Programm einen Thread. Wenn ich das Programm über "python readserial.py" ausführe, kann ich keine weitere Eingabe in der Konsole vornehmen. Schließe ich mein SSH Fenster, wird das Programm beendet. Ich umgehe das bisher, indem ich das Programm in einer Screen Session ausführe. Das fühlt sich aber irgendwie "falsch" an. Gibt es da einen eleganteren Weg?

Das sind erst einmal die beiden main issues. Sollten euch weitere Verbesserungsvorschläge einfallen, bin ich dem gegenüber nicht abgeneigt. In jedem Fall schon einmal danke fürs durchlesen :)
Zuletzt geändert von Anonymous am Freitag 19. Oktober 2012, 10:37, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Hi und willkommen im Forum,

zu deinem Code gibt es ein bisschen was anzumerken.

- Benutze immer vier Leerzeichen zum einrücken
- Der thread ist unnötig, kannst alles auch ohne Thread und Klasse machen
- Bei entsprechender Python Version die Datei mit "with" öffnen
- Wenn du es nicht mit "with" machen kannst, dann die Datei wieder schließen.

Um zu deinen Fragen zu kommen :D
Zu 1:
Das kannst du, wie du vermutet hast, mit Exception handling machen. Suche dir einfach die Exception raus, die kommt wenn keine Verbindung besteht und baue entsprechenden Code um das "ser.readline()"
Zu 2: Kannst das script zum Beispiel als Dienst starten lassen : http://wiki.ubuntuusers.de/Dienste

Das war es ersteinmal von meiner Seite :) Hoffe es hat dir geholfen.

Edit:
now = datetime.datetime.now() wird nicht benutzt und kann raus ;) Damit aber dann auch den datetime import rauslöschen.
BlackJack

@Guysbert: Bezüglich der schon erwähnten Formatierung/Einrückung und anderer Konventionen gibt es PEP 8 -- Style Guide for Python Code.

Namen sollte man nicht abkürzen. `f` ist für meinen Geschmack zu kurz und `ard` und `ser` zu unverständlich.

Python hat einen eigenen Datentyp für Wahrheitswerte und man sollte deshalb nicht 0 und 1 schreiben wenn man eigentlich `False` oder `True` meint.

Zeichenketten und Werte mit ``+`` zusammen setzen ist eher BASIC (oder (altes) Java) als Python. In Python gibt es dafür den ``%``-Operator oder die `format()`-Methode auf Zeichenketten.

Ohne den unnötigen Thread kann man das zum Beispiel so schreiben:

Code: Alles auswählen

#!/usr/bin/env python
from datetime import datetime as DateTime
from time import sleep
from serial import Serial


def main():
    connection = Serial('/dev/ttyACM0', 9600, timeout=15)
    with open('/var/www/values', 'a') as log_file:
        while True:
            connection.flushInput()
            line = next(connection).strip()
            if line:
                log_file.write(
                    '%s;%s\n' % (
                        DateTime.now().strftime('%m, %d, %Y %H:%M:%S'),
                        line
                    )
                )
                log_file.flush()
            sleep(60)


if __name__ == '__main__':
    main()
Zu 2.: ``screen`` oder ``nohup`` finde ich für so etwas zum entwickeln und testen okay. Eigentlich handelt es sich ja um einen Dienst und den könnte man entsprechend als Dienst/Daemon in das System einbinden. Also zum Beispiel über entsprechende Skripte in ``/etc/init.d/`` oder über einen Daemon der Daemons verwaltet, wie `supervisord`.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

@BlackJack wie kommst du auf "line = next(connection).strip()"?
Das "connection" objekt ist nicht iterierbar... Und wenn, würde next nicht eine Exception geben, wenn eine Weile keine Daten zurück kommen?
Guysbert
User
Beiträge: 2
Registriert: Freitag 19. Oktober 2012, 09:28

Cool! Schon mal vielen Dank für die Anregungen. Ich bin nächste Woche verreist, daher komm ich wahrscheinlich nicht so bald dazu diese umzusetzen. Ich werde aber definitiv berichten, sobald ich Erfolg hatte :) Für weitere Anregungen bin ich natürlich immer noch dankbar :)

Eine weiterführende Frage hätte ich schonmal...

Ich habe bisher immer mit Nano unter Linux dateien editiert, weil ich zu faul war, mich mit vi anzufreunden. Bei dem Python Programm habe ich jetzt aber mal damit angefangen. Nach ein bisschen Einarbeitung kommt man ja doch ganz gut damit klar. Habt ihr eventuell empfohlene Einstellungen für den Editor? Ein Syntaxhighlighting z.B. fände ich großartig oder halt, dass man standardmäßig mit 4 Leerzeichen einrückt.
BlackJack

@JonasR: Wieso sollte `connection` nicht iterierbar sein? Das mit der Zeitüberschreitung ist in der Tat ein Problem an das ich nicht gedacht hatte, denn dann löst `next()` eine `StopIteration`-Ausnahme aus. Hier ist die Implementierung von `next()`:

Code: Alles auswählen

In [18]: serial.SerialBase.next??
Type:           instancemethod
Base Class:     <type 'instancemethod'>
String Form:    <unbound method SerialBase.next>
Namespace:      Interactive
File:           /usr/lib/python2.6/dist-packages/serial/serialutil.py
Definition:     serial.SerialBase.next(self)
Source:
    def next(self):                                                             
        line = self.readline()
        if not line: raise StopIteration
        return line
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

@Guysbert Sorry da kann ich dir nicht weiterhelfen...
@BlackJack Hmmm ich weiß nicht was ich vorhin getestet habe, aber jetzt kann ich tatsächlich über "connection" iterieren... Dann sorry dafür :)
Übrigens es geht ja auch da rum, dass die Verbindung nicht mehr geöffnet werden kann, weil der Arduino nicht mehr angeschlossen ist. Es kommt also zu der "StopIteration"-Exception noch eine "PySerial"-Exception hinzu ;)
BlackJack

@Guysbert: Spätestens wenn man das Wiederverbinden einbaut würde ich anfangen das ganze in Funktionen aufzubrechen und auch das Auslesen vom Weiterverarbeiten/Wegschreiben zu trennen. Dazu eignen sich Generatorfunktionen ganz gut — Stichwort ``yield``-Anweisung.
Antworten