Forum: PC-Programmierung Serielle Kommunikation zwischen Python und µC


von Max M. (maxmicr)


Lesenswert?

Hallo,

ich versuche über Python die serielle Schnittstelle anzusprechen und 
versende da das Byte "1". Im µC überprüfe ich dann, ob "1" angekommen 
ist und gebe dann einen Text aus, wenn die angekommenen Daten nicht "1" 
entsprechen, gebe ich die angekommenen Daten aus. Seltsamerweise kommt 
bei meinem Python-Programm immer 49\n an.

Könnte mir da jemand erläutern, warum dass so ist und wieso anscheinend 
beim Mikrocontroller nicht "1" ankommt? Muss ich den gelesenen Text noch 
irgendwie umwandeln?

Python:
1
import serial
2
3
ser = serial.Serial(port='COM4', baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, timeout=2)
4
try:
5
    ser.isOpen()
6
except:
7
    print("Error")
8
    exit()
9
10
if(ser.isOpen()):
11
    try:
12
        while(1):
13
            if(ser.inWaiting() > 0):
14
                print(ser.readline())
15
            else:
16
                ser.write(b"1")
17
    except Exception as e:
18
        print(e)

µC:
1
#define F_CPU 16000000L //16 MHz
2
3
#define BAUD 9600UL
4
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)
5
6
#include <avr/io.h>
7
#include <stdlib.h>
8
#include <util/setbaud.h>
9
#include <util/delay.h>
10
#include <string.h>
11
12
uint8_t uart_getc(){
13
  while(!(UCSR0A & (1<<RXC0))){
14
  }
15
  return UDR0;
16
}
17
18
void uart_puts (char *s)
19
{
20
  while (*s)
21
  if(UCSR0A & (1<<UDRE0))
22
  UDR0 = *s++;
23
}
24
25
void uart_init(void)
26
{
27
  UBRR0 = UBRR_VAL;
28
  UCSR0B |= (1<<TXEN0);
29
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
30
}
31
32
int main(void)
33
{
34
  char s[7] = {"Hello"};
35
  uart_init();
36
  while (1) {
37
    uint8_t val = uart_getc();
38
    if(val == 1){
39
      uart_puts(s);
40
      _delay_ms(500);
41
    } else {
42
      char result[4];
43
      itoa(val,result,10);
44
      strcat(result,"\n");
45
      uart_puts(result);
46
    }
47
  }
48
}

von Decius (Gast)


Lesenswert?

Wenn ich mich recht erinnere, werden die Bits über eine RS232 invertiert 
übertragen. Kommt aber natürlich auf das Gesamtsystem an.

von Peter II (Gast)


Lesenswert?

Max M. schrieb:

> Im µC überprüfe ich dann, ob "1" angekommen ist
> if(val == 1){

dann sollte man auch auf '1' abfragen und nicht auf 1

von Lars R. (lrs)


Lesenswert?

ord('1')
chr(49)

von Felix Adam (Gast)


Lesenswert?

Vermutlich sagst du Python mit "1", dass es sich um das ASCII-Zeichen 1 
handelt. Das kleine b davor soll vermutlich aussagen, dass die 1 binär 
anzusehen ist. Vielleicht geht es ohne Anführungszeichen?

von Max M. (maxmicr)


Lesenswert?

Peter II schrieb:
> dann sollte man auch auf '1' abfragen und nicht auf 1

Danke!

von ascii (Gast)


Lesenswert?


von Max M. (maxmicr)


Lesenswert?

ascii schrieb:
> http://www.torsten-horn.de/techdocs/ascii.htm

Danke, so gehts einfacher:
1
uint8_t val = uart_getc();
2
char result[3] = {val};
3
strcat(result,"\n");
4
uart_puts(result);

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Max M. schrieb:
> Danke, so gehts einfacher:
> uint8_t val = uart_getc();
> char result[3] = {val};
> strcat(result,"\n");
> uart_puts(result);

was soll denn der Unsinn.

damit sendest du Binär im Zusammenhang mit \n macht das aber wenig sinn. 
Was ist wenn du mal eine 13 zurück sendest? Oder noch spannenden wenn 
mal eine 0 gesendet werden soll.

von Max M. (maxmicr)


Lesenswert?

Peter II schrieb:
> damit sendest du Binär im Zusammenhang mit \n macht das aber wenig sinn.

Somit erkennt Python, wann der gesendete Text zu Ende ist.

Peter II schrieb:
> Was ist wenn du mal eine 13 zurück sendest?

Dann kommt das dabei raus :(
1
\x8a\n

Wie löst man das?

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Max M. schrieb:
> Somit erkennt Python, wann der gesendete Text zu Ende ist.

genau das führt aber zu Problemen, wenn du 13 oder eine 0 sendest. Du 
kannst nicht einfach Binär und Ascii mischen und hoffen das geht 
irgendwie.

Sende zahlen als Ascii also eine 1 als 49 (oder '1') und schon kannst du 
sauber mit \n arbeiten.

von Max M. (maxmicr)


Lesenswert?

Peter II schrieb:
> Sende zahlen als Ascii also eine 1 als 49 (oder '1') und schon kannst du
> sauber mit \n arbeiten.

Da sagt mir Python dann, dass es einen int erwartet (wenn ich einen Int 
übergebe, wird mir gesagt: 'int' object is not iterable).

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Max M. schrieb:
> Da sagt mir Python dann, dass es einen int erwartet (wenn ich einen Int
> übergebe, wird mir gesagt, dass Integer nicht iterable ist).

ser.write("1") tut es nicht?

von Max M. (maxmicr)


Lesenswert?

Peter II schrieb:
> ser.write("1") tut es nicht?

Leider nicht :(
1
an integer is required

von Peter II (Gast)


Lesenswert?

Max M. schrieb:
> Leider nicht :(

andere können es scheinbar

http://stackoverflow.com/questions/676172/full-examples-of-using-pyserial-package


warum 2 Programmiersprachen (die du scheinbar beide nicht sehr gut 
kannst) für ein Projekt?

von Max M. (maxmicr)


Lesenswert?

Peter II schrieb:
> andere können es scheinbar

Hm, bei mir kommt da immer die von mir gepostete Fehlermeldung.

von Sven B. (scummos)


Lesenswert?

Zeig mal den Code, das kann nicht sein.

von Max M. (maxmicr)


Lesenswert?

Sven B. schrieb:
> Zeig mal den Code, das kann nicht sein.
1
import serial
2
3
ser = serial.Serial(port='COM4', baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, timeout=2)
4
try:
5
    ser.isOpen()
6
except:
7
    print("Error")
8
    exit()
9
if(ser.isOpen()):
10
    try:
11
        while(1):
12
            if(ser.inWaiting()>0):
13
                print(ser.readline())
14
            else:
15
                ser.write(str(chr(13)))
16
    except Exception as e:
17
        print(e)

von Sven B. (scummos)


Lesenswert?

Und jetzt noch die komplette Fehlermeldung dazu, samt Traceback?

von Wolfgang S. (ws01)


Lesenswert?

Felix Adam schrieb:
> Vermutlich sagst du Python mit "1", dass es sich um das ASCII-Zeichen 1
> handelt. Das kleine b davor soll vermutlich aussagen, dass die 1 binär
> anzusehen ist. Vielleicht geht es ohne Anführungszeichen?

Nein. In nicht zu alten Python-Versionen denotiert der Prefix b in 
Literals, daß ein Bytestring gemeint ist, nicht ein Textstring. PySerial 
sollte beides können. Und dass ser.write(b'1') oder ser.write('1') "an 
integer is required" produziert, glaube ich nicht.

von Max M. (maxmicr)


Lesenswert?

Sven B. schrieb:
> Und jetzt noch die komplette Fehlermeldung dazu, samt Traceback?
1
Traceback (most recent call last):
2
  File "Python Projects/pythonTest.py/exmpl.py", line 8, in <module>
3
    ser.write("asd")
4
  File "C:\Python34\lib\site-packages\serial\serialwin32.py", line 283, in write
5
    data = to_bytes(data)
6
  File "C:\Python34\lib\site-packages\serial\serialutil.py", line 76, in to_bytes
7
    b.append(item)  # this one handles int and str for our emulation and ints for Python 3.x
8
TypeError: an integer is required

Nach dem Trace hat das wohl was mit der Python-Version zu tun.

von Sven B. (scummos)


Lesenswert?

mach mal
 ser.write(bytes([13]))

von Kaj (Gast)


Lesenswert?

Das "Problem", dass hier auftritt, beruht auf den unterschieden zwischen 
Python 2.x und Python 3.x. In Python 3 geht das aber auch voll 
entspannt, wenn man das Zeichen vor dem versenden in den richtigen Typ 
castet.

von Max M. (maxmicr)


Lesenswert?

Kaj schrieb:
> In Python 3 geht das aber auch voll
> entspannt, wenn man das Zeichen vor dem versenden in den richtigen Typ
> castet.

Magst du da ein Beispiel geben, wie ich die
1
13
 casten muss?

Sven B. schrieb:
> mach mal
>  ser.write(bytes([13]))

Läuft! Danke

: Bearbeitet durch User
von Sven B. (scummos)


Lesenswert?

Ich würde in Python das Wort "cast" nicht verwenden, das passt 
eigentlich nicht.

Du musst die 13 in ein Bytearray mit einem Element umwandeln. Das machst 
du genau so wie ich es geschrieben habe, du machst eine Liste mit einem 
Element, also [13], und konvertierst die dann in ein Byte Array, also 
bytes([13]). Jeder Eintrag der Liste wird zu einem Byte.

von Max M. (maxmicr)


Lesenswert?

Noch mal ein weiteres Problem mit UART. Nun versuche ich den analogen 
Wert eines Temperatursensors (LM35DZ) auszulesen und danach über UART 
auszugeben:
1
#define F_CPU 16000000L //16 MHz
2
3
#include <avr/io.h>
4
#include <stdint.h>
5
#include <stdlib.h>
6
#include <util/delay.h>
7
#include <string.h>
8
9
#define ADC_PIN      0
10
11
#define  LED_PIN      PORTB5
12
13
#define ADC_THRESHOLD  512
14
15
void uart_puts (char *s)
16
{
17
  while (*s)
18
  if(UCSR0A & (1<<UDRE0))
19
  UDR0 = *s++;
20
}
21
22
uint16_t adc_read(uint8_t adcx) {
23
  ADMUX  &=  0xf0;
24
  ADMUX  |=  adcx;
25
26
  /* This starts the conversion. */
27
  ADCSRA |= _BV(ADSC);
28
29
  while ( (ADCSRA & _BV(ADSC)) );
30
31
  return ADC;
32
}
33
34
int main(void) {
35
36
  ADCSRA |= _BV(ADEN);
37
38
  DDRB  |= _BV(LED_PIN);
39
40
  while(true){
41
    uint16_t adcValue = adc_read(ADC_PIN);
42
    if (adcValue > ADC_THRESHOLD)
43
    PORTB |= (1<<LED_PIN);
44
    else
45
    PORTB &= ~(1<<LED_PIN);
46
    char result[30];
47
    itoa(adcValue,result,16);
48
    strcat(result,"\n\r");
49
    uart_puts(result);
50
    _delay_ms(500);
51
  }
52
}

allerdings kommt im Terminal-Window von Atmel Studio nur komisches 
Kauderwelsch an:
1
ËËÕÿÿÿÿÿÿ¥¥¥¥¥¥¥¶øË

von Peter II (Gast)


Lesenswert?

Max M. schrieb:
> allerdings kommt im Terminal-Window von Atmel Studio nur komisches
> Kauderwelsch an:
> ËËÕÿÿÿÿÿÿ¥¥¥¥¥¥¥¶øË

dann werden wohl die Schnittstellen Parameter nicht stimmen.

Baudrate, Bitzahl, Parität, Stoppits

von Max M. (maxmicr)


Lesenswert?

Peter II schrieb:
> dann werden wohl die Schnittstellen Parameter nicht stimmen.

Danke, das war der nötige Hinweis, dass ich die uart_init() - Funktion 
vergessen habe, wie dumm von mir:
1
#define F_CPU 16000000L //16 MHz
2
3
#define BAUD 9600UL
4
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)
5
6
#include <avr/io.h>
7
#include <stdint.h>
8
#include <stdlib.h>
9
#include <util/delay.h>
10
#include <string.h>
11
#include <util/setbaud.h>
12
13
#define ADC_PIN      0
14
15
#define  LED_PIN      PORTB5
16
17
#define ADC_THRESHOLD  512
18
19
void uart_puts (char *s)
20
{
21
  while (*s)
22
  if(UCSR0A & (1<<UDRE0))
23
  UDR0 = *s++;
24
}
25
26
void uart_init(void)
27
{
28
  UBRR0 = UBRR_VAL;
29
  UCSR0B |= (1<<TXEN0);
30
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
31
}
32
33
uint16_t adc_read(uint8_t adcx) {
34
  ADMUX  &=  0xf0;
35
  ADMUX  |=  adcx;
36
37
  /* This starts the conversion. */
38
  ADCSRA |= _BV(ADSC);
39
40
  while ( (ADCSRA & _BV(ADSC)) );
41
42
  return ADC;
43
}
44
45
int main(void) {
46
47
  ADCSRA |= _BV(ADEN);
48
49
  DDRB  |= _BV(LED_PIN);
50
51
  uart_init();
52
53
  while(true){
54
    uint16_t adcValue = adc_read(ADC_PIN);
55
    if (adcValue > ADC_THRESHOLD)
56
      PORTB |= (1<<LED_PIN);
57
    else
58
      PORTB &= ~(1<<LED_PIN);
59
    char result[30];
60
    itoa(adcValue,result,10);
61
    strcat(result,"\n");
62
    uart_puts(result);
63
    _delay_ms(500);
64
  }
65
}

Jetzt bekomm ich als Wert 1023. Aber wenn ich  meinen Finger an den 
Temperatursensor halte, ändert sich das nicht?

Okay, wenn ich den Strom abstecke, ändert sich auch nichts ^^

: Bearbeitet durch User
von fb (Gast)


Lesenswert?

Wie sieht denn die Beschaltung des AREF-Pins aus?
Hängt da nur der übliche Kondensator dran oder auch eine 
Referenzspannung (z.B. Vcc)?
Wenn keine extra Referencspannung angeschlossen ist, mußt Du noch die 
REFSx Bits im ADMUX Register korrekt setzen.

von Max M. (maxmicr)


Lesenswert?

fb schrieb:
> AREF-Pins

Ähm, damit kann ich leider gar nichts anfangen. Auch im Datenblatt finde 
ich nichts dazu.

fb schrieb:
> Wenn keine extra Referencspannung angeschlossen ist,

Meinst du am Temperatursensor? Der ist mit GND und 5V verbunden.

von Kaj (Gast)


Lesenswert?

Max M. schrieb:
> Peter II schrieb:
>> ser.write("1") tut es nicht?
>
> Leider nicht :(
> an integer is required

Fuer Strings kann man es so machen:
1
#!/usr/bin/env python3
2
3
import serial
4
5
ser = serial.Serial('/dev/ttyUSB0', 9600)
6
7
ser.write('Hallo\n'.encode())
8
#oder
9
#ser.write(bytes(b'Hallo\n'))
10
11
print(ser.readline())

Fuer deine 13 kannst du wie oben schon geschrieben wurde
1
ser.write(bytes([13]))
schreiben, oder du schreibst einfach:
1
ser.write('\r'.encode())

Gruesse

von Karl Käfer (Gast)


Lesenswert?

Hi Max,

Max M. schrieb:
>
1
> import serial
2
> 
3
> ser = serial.Serial(port='COM4', baudrate=9600, 
4
> bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, timeout=2)
5
> try:
6
>     ser.isOpen()
7
> except:
8
>     print("Error")
9
>     exit()
10
> 
11
> if(ser.isOpen()):
12
>     try:
13
>         while(1):
14
>             if(ser.inWaiting() > 0):
15
>                 print(ser.readline())
16
>             else:
17
>                 ser.write(b"1")
18
>     except Exception as e:
19
>         print(e)
20
>

Besser:
1
try:
2
  ser = serial.Serial(...)
3
except Exception as e:
4
  print(e) # oder traceback.print_exc()
5
  sys.exit()
6
while(ser.isOpen()):
7
  try:
8
    if(ser.inWaiting() > 0):
9
      print(ser.readline())
10
    else:
11
      ser.write(b"1")
12
  except Exception as e:
13
    print(e)
14
    break

Der Konstruktor der Serial-Klasse wirft bereits eine Exception, wenn er 
die entsprechende Schnettstille nicht öffnen kann. Wenn der Konstruktor 
erfolgreich war, also keine Exception geworfen hat, ist es nicht nötig, 
das erfolgreiche Öffnen mit isOpen() zu überprüfen -- zumal Dein "try: 
ser.isOpen()" das Ergebnis ins Nirvana schreibt und deswegen genau gar 
nichts tut. Richtig müßte es heißen "try: assert ser.isOpen() == True".

Auch die Überprüfung vor der while()-Schleife ist unsinnig: da prüfst Du 
einmal, ob das Ding geöffnet wurde, und loopst dann ewig darauf herum. 
Wenn die Schnittstelle während der Schleife entfernt wird, zum Beispiel 
weil der USB-Serial-Adapter abgezogen wurde, nutzt Dir das an der Stelle 
überhaupt gar nichts. Eine alternative könnte sein, ser.isOpen() als 
Schleifenbedingung zu benutzen oder ein "assert ser.isOpen() == True" 
innerhalb der Schleife zu benutzen -- TIMTOWTDI.

Und um nebenbei noch Deine Frage zu beantworten: "49" ist der ASCII-Code 
für die Ziffer "1". Um eine "1" zu schicken, möchtest Du keinen binären 
String (b"1"), sondern einfach die Zahl 1 (int(1)) benutzen.

Liebe Grüße,
Karl

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Karl Käfer schrieb:
> Um eine "1" zu schicken, möchtest Du keinen binären
> String (b"1"), sondern einfach die Zahl 1 (int(1)) benutzen.
das 'b' steht nicht fuer binaer, sondern fuer byte.
http://stackoverflow.com/questions/6269765/what-does-the-b-character-do-in-front-of-a-string-literal

von Sven B. (scummos)


Lesenswert?

Karl Käfer schrieb:
>
1
> try:
2
>   ser = serial.Serial(...)
3
> except Exception as e:
4
>   print(e) # oder traceback.print_exc()
5
>   sys.exit()
6
[...]
7
>

Was soll dieser Code? Was würde denn bitte ohne den try-catch-Block 
passieren? Richtig, genau dasselbe. 4 Zeilen komischer Code ohne jeden 
Sinn.

von Max M. (maxmicr)


Lesenswert?

Danke für eure Hilfe! Jetzt funktioniert es so wie geplant :)

von Max M. (maxmicr)


Lesenswert?

Ich habe einen LM35DZ an den Arduino angeschlossen (Port PC0 bzw. A0) 
und versucht, mit diesem Code auszulesen:
1
#define F_CPU 16000000L //16 MHz
2
3
#define BAUD 9600UL
4
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)
5
6
#include <avr/io.h>
7
#include <stdint.h>
8
#include <stdlib.h>
9
#include <util/delay.h>
10
#include <string.h>
11
#include <util/setbaud.h>
12
13
#define ADC_PIN      0
14
#define  LED_PIN      PORTB5
15
16
void uart_puts (char *s)
17
{
18
  while (*s)
19
  if(UCSR0A & (1<<UDRE0))
20
  UDR0 = *s++;
21
}
22
23
void uart_init(void)
24
{
25
  UBRR0 = UBRR_VAL;
26
  UCSR0B |= (1<<TXEN0);
27
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
28
}
29
30
uint16_t adc_read(uint8_t adcx) {
31
  ADMUX  &=  0xf0;
32
  ADMUX  |=  adcx;
33
  ADCSRA |= _BV(ADSC);
34
35
  while ( (ADCSRA & _BV(ADSC)) );
36
37
  return ADC;
38
}
39
40
int main(void) {
41
42
  ADCSRA |= _BV(ADEN);
43
44
  DDRB  |= _BV(LED_PIN);
45
46
  uart_init();
47
48
  while(true){
49
    uint16_t adcValue = adc_read(ADC_PIN);
50
    float mv = (adcValue/1024.0) * 5000;
51
    float cel = mv/10.0;
52
    char result[30];
53
    itoa(cel,result,10);
54
    strcat(result,"\n");
55
    uart_puts(result);
56
    _delay_ms(500);
57
  }
58
}

Allerdings kommt bei meinem PC immer der selbe Wert (499) an und das 
sogar, wenn ich den Sensor vom Strom trenne?

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.