Serielle Kommunikation zwischen Python und µC

OP #4204690
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
}
Gast #4204709
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?
Gast #4204718
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.
Gast #4204739
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.
OP #4204815
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)
#4204893
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.
OP #4204901
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.
Gast #4205009
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.
#4205163
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.
OP #4207197
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
ËËÕÿÿÿÿÿÿ¥¥¥¥¥¥¥¶øË
OP #4207207
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 ^^
Gast #4207579
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.
Gast #4208116
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
Gast #4208180
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
OP #4210813
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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren