Kommunikation mit Serial Port

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Pommes9485
User
Beiträge: 4
Registriert: Freitag 5. Dezember 2014, 13:33

Guten Tag,

ich habe ein seltsames Problem und dabei den folgenden Code:

Code: Alles auswählen

import serial
ser = serial.Serial('/dev/ttyACM0', 9600, timeout=2)
ser.open()
ser.write("C255;0;0")
ser.close()
Wenn ich das ganze so als Skript speichere und mit
python2 serial2.py ausführe, läuft es nicht, es passiert einfach nichts.
Wenn ich jedoch den obigen Code in den Interpreter eingebe, also nachdem ich python2 ausgeführt habe, funktioniert alles ohne Probleme. Dabei habe ich den Code mehrfach verglichen und auch schon mit root Rechten ausprobiert. Nichts hilft.

Was mache ich falsch?
BlackJack

@Pommes9485: Was heisst wenn Du es *so* als Skript speicherst? Ohne She-Bang-Zeile? Startest Du es dann wenigstens explizit mit Python?

Das `open()` ist übrigens nicht notwendig und um das `close()` zu garantieren würde man die ``with``-Anweisung verwenden.

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from serial import Serial


def main():
    with Serial('/dev/ttyACM0', 9600, timeout=2) as serial:
        serial.write('C255;0;0')


if __name__ == '__main__':
    main()
Pommes9485
User
Beiträge: 4
Registriert: Freitag 5. Dezember 2014, 13:33

Bisher habe ich das nicht gemacht, nein. Habe es jetzt aber mal übernommen, ändert nur leider nichts. Ich hatte es davor auch immer explicit mit Python gestartet, ausgaben haben auch funktioniert.

Der Arduino lässt kurz die L Led blinken, was eigentlich nichts damit zu tun hat. Das Skript auf dem Arduino habe ich aber auch mit Python getestet, das läuft normal.
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Dein Programm beendet sich einfach zu schnell. Du musst erst alles über den Port schreiben.

Benutzt 'serial.flush()'.
Pommes9485
User
Beiträge: 4
Registriert: Freitag 5. Dezember 2014, 13:33

Folgender Code funktioniert leider immer noch nicht:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from serial import Serial
import time

def main():
	ser = Serial("/dev/ttyACM0", 9600)
	ser.write('C255;0;0')
	ser.flush()
	time.sleep(2)
if __name__ == '__main__':
    main()
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

machst du in deinem Arduino Code `delay` oder bremst die Hauptschleife in irgend einer Art?

hilft es dir folgenden code zu schreiben?

Code: Alles auswählen

def main():
   ser = Serial("/dev/ttyACM0", 9600)
   ser.write("")
   ser.flush()
   ser.write('C255;0;0')
   ser.flush()
if __name__ == '__main__':
    main()
oder so:

Code: Alles auswählen

def main():
   ser = Serial("/dev/ttyACM0", 9600)
   time.sleep(1)
   ser.write('C255;0;0')
   ser.flush()
if __name__ == '__main__':
    main()
Du setzt eine RGB Farbe / RGB LED? Hast du eine Art Reset Code bei deinem Serial Protokoll? Sowas würde ich als erstes senden.
BlackJack

Je nach Arduino-Modell kann es nötig sein nach dem herstellen der seriellen Verbindung ein wenig zu warten bevor man Daten sendet, weil einige Modelle direkt nach dem die serielle Verbindung steht, eine kurze Zeit ein neues Programm übertragen bekommen können. Näheres sollte in der Dokumentation des Arduino stehen.
Pommes9485
User
Beiträge: 4
Registriert: Freitag 5. Dezember 2014, 13:33

Code: Alles auswählen

def main():
   ser = Serial("/dev/ttyACM0", 9600)
   time.sleep(2)
   ser.write('C255;0;0')
   ser.flush()
if __name__ == '__main__':
    main()
funktioniert, nur mit einer Wartezeit von 2 Sekunden.

Ich hatte schon gelesen, dass ich den Arduino Uno daran hindern kann, in den Stand By zu gehen, wenn ich einen Kapazitator zwischen Ground und 5V stecke. Das probiere ich dann mal.

Ich hatte mich davor über Bluetooth verbunden, da hat es ohne Probleme funktioniert :D

Danke schon mal!
BlackJack

@Pommes9485: Nicht „stand by“ sondern „auto reset“ jedes mal wenn eine serielle Verbindung über USB hergestellt wird. Das kann man ”dauerhaft” durch das Trennen der mit „RESET-EN“ beschrifteten Leiterbahn erreichen, oder mit einem 110Ω Widerstand zwischen 5V und Reset-Leitung. Steht jedenfalls so in der Anleitung. Anleitungen sind was tolles. Sollten mehr gelesen werden. ;-)
inter
User
Beiträge: 13
Registriert: Dienstag 2. Februar 2016, 17:27

Hallo,
hätte dazu auch ne Frage. Wie stelle ich 7n1 mit parity ein bei serial.serial ("ttyUsb0", 9600, timeout 1)ein ?

Und wie kann ich erreichen, dass einmal der gesamte Inhalt eingelesen wird? Mache es bisher mit Inhalt=ser.read (200)

Wenn ich mehr einlese, kann ich ja erkennen was die letzten Zeichen sind. b' ........... \n!\x8d\n
und dann fängt es wieder von vorne an.

Desweiteren würde ich gerne die Daten in einen string schreiben ohne \x543\x57z\n..........
Wie kann ich dies umwandeln in 543 57 ?
BlackJack

@inter: Was ist denn bitte „7n1 mit parity“? Wofür denkst Du das das 'n' steht‽

Ansonsten schau Dir doch mal die Dokumentation vom `serial`-Modul an was man für Argumente übergeben kann.

Was ist denn „der gesamte Inhalt“? Das wäre ein `read()` ohne Argument bei einer Verbindung ohne `timeout`, aber dann schreibst Du etwas davon das sich Daten wiederholen und es klingt ein wenig als wenn der Arduino immer wieder Daten sendet und nicht alles sendet und dann sein Ende der Verbindung schliesst. Also woran erkennst Du das alles gelesen wurde das Dich interessiert?

b'\x543\x57z\n' kannst Du nicht in '543 57...' umwandeln, beziehungsweise willst Du das sicher so nicht, denn da steht nicht die Zahl 543 sondern ein Byte mit dem Hexadezimalwert 54 (dezimal 84) gefolgt von einem Byte das in ASCII für die Ziffer 3 steht, also den Hexadezimalwert 33 hat (dezimal 51). Du bekommst da offensichtlich keinen Text sondern Binärdaten. Also musst Du wissen wie die aufgebaut sind, um sie dann beispielsweise mit dem `struct`-Modul entsprechend umzuwandeln. Kann natürlich auch sein das die Daten Müll sind weil die Parameter bei der seriellen Verbindung nicht stimmen.
inter
User
Beiträge: 13
Registriert: Dienstag 2. Februar 2016, 17:27

Danke für die Antwort. Lese einen Zähler aus, bin mir aber nicht sicher ob ich die Schnittstelle richtig eingestellt habe.
Das kommt an [Codebox=text file=Unbenannt.txt]b'\xaf\xc5MH5----eHZ-\xc500\xb2\xb8\xc5\x8d\n\x8d\n\xb1-0:0.0.0\xaa\xb255(\xb20\xb836\xb2\xb7\xb8\xa9\x8d\n\xb1-0:\xb1.\xb8.\xb1\xaa\xb255(0\xb10\xb760.\xb73\xb2\xb2\xa9\x8d\n\xb1-0:\xb2.\xb8.\xb1\xaa\xb255(0\xb239\xb4\xb2.\xb196\xb7\xa9\x8d\n\xb1-0:96.5.5\xaa\xb255(\xb8\xb2\xa9\x8d\n0-0:96.\xb1.\xb255\xaa\xb255(000\xb2\xb4\xb7\xb765\xb2\xa9\x8d\n!\x8d\n'
[/Codebox]

und mit ser.read liest er die gleichen Daten erneut ein. Ich möchte eigentlich nur das \x entfernen, da die Werte in den Bytes korrekt sind.
BlackJack

@inter: Das kann nicht sein das die Werte korrekt sind. Du hast ganz offensichtlich die Darstellung von Bytes hier nicht richtig verstanden. Es muss doch irgendeine Protokollbeschreibung geben. Nach der musst Du Dich richten.
inter
User
Beiträge: 13
Registriert: Dienstag 2. Februar 2016, 17:27

Es handelt sich um das sml protokoll. Z.B. Der Zählerstand ist hier versteckt 10\xb760.\xb73\xb2\ enspricht aktuell 10.760,73 kWh auf meiner Anzeige. Kann ich die bytes in einen string wandeln und dann mir "formatieren". Habe es mit replace schon versucht, aber ohne Erfolg. \n kann ich entfernen, aber\x meckert python. Ne idee?
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Hast du den Arduino Code geschrieben? Kannst du den zeigen?

Denk mal darüber nach:

Code: Alles auswählen

In [1]: chr(65)
Out[1]: 'A'

In [2]: hex(65)
Out[2]: '0x41'

In [3]: print "\x41"
A
*edit*
Das lässt sich auch mixen dann wird es dir vll klarer:

Code: Alles auswählen

In [1]: print "H\x41LLO"
HALLO
BlackJack

@inter: Sorry aber das glaube ich nicht das jemand ein Protokoll entwirft bei dem Zahlen eine Mischung aus BCD-kodierten *und* ASCII-Ziffern besteht. Auf welchen Drogen müsste man den sein um so einen Schwachsinn zu machen‽

Was ist sml? Spezifikation?
inter
User
Beiträge: 13
Registriert: Dienstag 2. Februar 2016, 17:27

Hallo,

ok das in Hex umwandeln hat geklappt. Hätte aber noch ne Frage zu z.B. \xaf\x10. Wenn ich die Bytes in einen Datei schreibe, so werden ja auch die \x immer mitgeschrieben. Kann ich diese irgendwie decodieren bzw. encodieren wenn ich die Bytes in eine Datei schreibe, damit sie lesbar sind.

Sollte dies nicht gehen, dann würde ich gerne alles in hex umwandeln. Dann muss ich aber die Hex_Bytes irgendiwe maskieren. Als Beispiel immer 8 Bytes der Zahlenkette als variable spiechern, um diese weiterverarbeiten zu können. Bei Bits kann man ja einfach maskieren z.B. 0101 0001 01100 mit und Verknüpfen 1111 1111 0000. Dann wäre das Ergebnis wäre ja dann variable1=1111 1111.

Vielen Dank für euer Bemüchen im voraus.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@inter: Was versuchst Du wie in eine Datei zu schreiben?
inter
User
Beiträge: 13
Registriert: Dienstag 2. Februar 2016, 17:27

Also hier mein vorlaäufiges Programm:

Code: Alles auswählen

#!/usr/bin/python
#tutorialspoint.com
import serial
from time import *
import os,sys
import time
from struct import *




#Pfad definieren
pfad = "/home/pi"
#Gibt den aktuellen Pfad zurck
retval = os.getcwd()

print ("ok")

#Pfad wechseln
os.chdir(pfad)

#Funktionier der Pfad?
retval = os.getcwd()




neuerpfad="/home/pi/test"
if not os.path.exists(neuerpfad):
    os.mkdir(neuerpfad, "0755");
    retval=os.getcwd()
    print ("Neuer Ordner angelegt" + retval)
else:
    retval=os.getcwd()
    print ("Pfad vorhanden" + retval)

#Pfad wechseln
os.chdir(neuerpfad)

i=0

while True:

    z = time.strftime("%S")
    if (int(z) == 30 or int(z)==0 and i==0):
        Datum=strftime("%d.%m.%Y")
        Uhrzeit=strftime("%H:%M:%S")

    

       
        ser = serial.Serial("/dev/ttyUSB0", 9600, timeout=1)
        ser.open()
        print ("seropen")

        Input = ""
        Zeichen=""
        zeich=""
       
                   
        Zeichen=ser.read(200)
        
        

        ser.close()
        print ("serclose")
        #print (Zeichen)
                
        Input = str(Zeichen) 
        
        #Textdatei erzeugen
        #fobj_out=open("ehz.csv","w")
        #   wenn Datei vorhanden
        fobj_out=open("ehz.txt","a")

        #Daten in Datei schreiben
        fobj_out.write(Datum + " " + Uhrzeit + " " + Input +"\n")
        #Datei schliessen
        fobj_out.close()
        i = 1
        print ("Daten in Datei geschrieben")
    if ((int(z)==31) or (int(z)==1)):
        i = 0
Zuletzt geändert von Anonymous am Mittwoch 9. März 2016, 19:59, insgesamt 1-mal geändert.
Grund: Quelltext in Code-Tags gesetzt.
BlackJack

@inter: Es gibt keine '\x', wenn die wirklich in Deinen Daten stehen als zwei Zeichen dann hast Du etwas falsch gemacht Wenn Du den Wert b'\xaf\x10' in eine Datei schreibst, dann sind in der Datei weder Backslashes noch 'x'e (und auch kein b oder einfache Anführungszeichen), sondern genau *zwei* Bytes mit den Hexwerten AF und 10. Schreib einfach die Bytes in eine Datei. Du musst Dir den Unterschied zwischen den Bytes im Speicher, die Du *nicht* sehen kannst, und der Zeichenkettendarstellung eines Objekts vom Typ `bytes` klar machen, welche Dir die Bytes sicht- und lesbar in einer eindeutigen Form präsentieren. Das ist für Dich als Programmierer zum angucken. In dieser Form willst Du die weder in eine Datei schreiben, noch sonst irgendwie weiterverarbeiten. Falls Du die Bytes in eine Hexdarstellung umwandeln möchtest, schau Dir das `binascii`-Modul in der Standardbibliothek an. Damit kannst Du die Bytes in eine Zeichenkette mit zwei Zeichen pro Byte umwandeln. Da würde ich dann aber auch gleich hinterfragen warum Du das machen wollen würdest.

Code: Alles auswählen

In [3]: len(b'\xaf\x10')
Out[3]: 2

In [4]: import binascii

In [5]: binascii.b2a_hex(b'\xaf\x10')
Out[5]: 'af10'

In [6]: len(binascii.b2a_hex(b'\xaf\x10'))
Out[6]: 4
Antworten