Forum: Mikrocontroller und Digitale Elektronik Raspberry Pi C-Programmierung: RS232 Read Problem


von Autor (Gast)


Lesenswert?

Hallo,

momentan habe ich ein Problem beim Lesen von der RS232 Schnittstelle. 
Mit meinem Raspberry Pi habe ich einen TCP Server erstellt, welcher die 
Daten von einem C# Programm per Ethernet empfängt und diese anschließend 
über die RS232 Schnittstelle (MAX232 dazwischen) an ein Funkgerät 
(Elecraft K3) weiterreicht.!
Ziel ist es das Funkgerät von dem Internet fernsteuern zu können. 
Befehle zum Funkgerät senden funktioniert.
Mit dem String "IF;" sollten sich die eingestellten Parameter 
zurücklesen lassen, dies funktioniert nicht, obwohl die Daten auf der 
Leitung zurückgesendet werden (mit Oszi geschaut).

Zusätzlich stürzt mein Programm nachdem uart_read benutzt wurde ab - es 
zeigt sich keinerlei Reaktion mehr und das C# Programm erkennt, dass die 
Socket Verbindung nicht mehr vorhanden ist.
1
/* A simple server in the internet domain using TCP
2
   The port number is passed as an argument 
3
   This version runs forever, forking off a separate 
4
   process for each connection
5
*/
6
#include <stdio.h>
7
#include <unistd.h>
8
#include <stdlib.h>
9
#include <string.h>
10
#include <sys/types.h> 
11
#include <sys/socket.h>
12
#include <netinet/in.h>
13
14
//UART Includes
15
#include <unistd.h>  
16
#include <fcntl.h>
17
#include <termios.h>
18
#include <poll.h>      //UART POLLING?
19
20
21
22
23
void dostuff(int, int); /* function prototype */
24
void error(const char *msg)
25
{
26
    printf("Error:%s",msg);
27
    exit(1);
28
}
29
30
int uart_init(void);
31
int uart_write(int, int, char*);
32
char* uart_read(int, int);
33
void uart_close(int);
34
35
36
37
38
39
int main(int argc, char *argv[])
40
{
41
     int sockfd, newsockfd, uart_filestream, portno, pid;
42
     socklen_t clilen;
43
     struct sockaddr_in serv_addr, cli_addr;
44
45
     portno = 50000;
46
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
47
     if (sockfd < 0) 
48
     {
49
        error("ERROR opening socket");
50
   }
51
     bzero((char *) &serv_addr, sizeof(serv_addr));
52
     serv_addr.sin_family = AF_INET;
53
     serv_addr.sin_addr.s_addr = INADDR_ANY;
54
     serv_addr.sin_port = htons(portno);
55
     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
56
     {
57
    error("ERROR on binding");
58
   }
59
     listen(sockfd,5);
60
     clilen = sizeof(cli_addr);
61
     
62
     
63
     
64
     
65
     uart_filestream = uart_init();    //UART INIT
66
     
67
     while (1) 
68
     {
69
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
70
        
71
        if (newsockfd < 0) 
72
        {
73
      error("ERROR on accept");
74
    }
75
    
76
        pid = fork();
77
        
78
        if (pid < 0)
79
        {
80
            error("ERROR on fork");
81
    }
82
    
83
        if (pid == 0)                                  //Wenn PID == 0 dann im Kindprozess
84
        {
85
            //close(sockfd);
86
            dostuff(newsockfd, uart_filestream);
87
            exit(0);
88
        }
89
        else if (pid > 0)
90
        {
91
      close(newsockfd);
92
    }
93
     } 
94
     
95
     close(sockfd);                                  //CLOSE SOCKET
96
     uart_close(uart_filestream);                          //CLOSE UART
97
     return 0;
98
}
99
100
101
102
103
104
/******** DOSTUFF() *********************
105
 There is a separate instance of this function 
106
 for each connection.  It handles all communication
107
 once a connnection has been established.
108
 *****************************************/
109
void dostuff (int sock, int uart_filestream)
110
{
111
   int rxtxlength;
112
   char tcpip_buffer[256];
113
   char *rs232_buffer;
114
    
115
   bzero(tcpip_buffer,256);
116
117
   printf("Connection established\n\n\n");
118
   
119
   while(1)
120
   {
121
    rxtxlength = read(sock,tcpip_buffer,255);
122
    if (rxtxlength >= 0)
123
    {
124
      printf("Nachricht von TCP/IP Schnittstelle: %s\n",tcpip_buffer);
125
      uart_write(uart_filestream, rxtxlength, tcpip_buffer);
126
      
127
    }
128
    else
129
    {
130
      error("ERROR Reading from Socket");
131
    }
132
    
133
    
134
    if(tcpip_buffer[0] == 'I' && tcpip_buffer[1] == 'F' && tcpip_buffer[2] == ';')    //If GetDataCommand (IF;) then READ back Data
135
    {
136
      rs232_buffer = uart_read(uart_filestream, 30);                  //Read Rs232
137
//      printf("Nachricht von RS232 Schnittstelle: %s\n", rs232_buffer);
138
      //write(sock,rs232_buffer,255);                          //Write Rs232 Message to TCPIP
139
    }
140
    
141
    bzero(tcpip_buffer,256);
142
    rs232_buffer = NULL;
143
  }
144
}
145
146
147
148
149
150
151
152
153
154
int uart_init(void)
155
{
156
  int uart0_filestream = -1;
157
  
158
  
159
  //-------------------------
160
  //----- SETUP USART 0 -----
161
  //-------------------------
162
  //At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively
163
  //OPEN THE UART
164
  //The flags (defined in fcntl.h):
165
  //  Access modes (use 1 of these):
166
  //    O_RDONLY - Open for reading only.
167
  //    O_RDWR - Open for reading and writing.
168
  //    O_WRONLY - Open for writing only.
169
  //
170
  //  O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
171
  //                      if there is no input immediately available (instead of blocking). Likewise, write requests can also return
172
  //                      immediately with a failure status if the output can't be written immediately.
173
  //
174
  //  O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.
175
  
176
  
177
  system("sudo chmod a+rw /dev/ttyAMA0");
178
  
179
  uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY);    //Open in non blocking read/write mode
180
//  uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY);    //Open in non blocking read/write mode
181
  if (uart0_filestream == -1)
182
  {
183
    printf("Error - Unable to open UART.  Ensure it is not in use by another application\n");
184
  }
185
  
186
  //CONFIGURE THE UART
187
  //The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
188
  //  Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
189
  //  CSIZE:- CS5, CS6, CS7, CS8
190
  //  CLOCAL - Ignore modem status lines
191
  //  CREAD - Enable receiver
192
  //  IGNPAR = Ignore characters with parity errors
193
  //  ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
194
  //  PARENB - Parity enable
195
  //  PARODD - Odd parity (else even)
196
  
197
  
198
  struct termios options;
199
  tcgetattr(uart0_filestream, &options);
200
  options.c_cflag = B38400 | CS8 | CLOCAL | CREAD;    //<Set baud rate
201
  options.c_iflag = IGNPAR;
202
  options.c_oflag = 0;
203
  options.c_lflag = 0;
204
  tcflush(uart0_filestream, TCIFLUSH);
205
  tcsetattr(uart0_filestream, TCSANOW, &options);
206
207
  return uart0_filestream;
208
}
209
210
211
212
213
214
215
int uart_write(int uart0_filestream, int bytes_to_sent, char* tx_buffer)
216
{
217
  int count;
218
  
219
  tx_buffer[bytes_to_sent] = '0';
220
  
221
  count = write(uart0_filestream, tx_buffer, bytes_to_sent);
222
  
223
    if (count < 0)
224
    {
225
      error("UART: Tx failed\n");
226
      return count;
227
    }
228
    else
229
    {
230
      return 1;
231
    }  
232
}
233
234
235
236
237
238
239
240
241
242
char* uart_read(int uart0_filestream, int read_length)
243
{
244
  int rx_length;
245
  static char rx_buffer[256];
246
  
247
  
248
  // Read up to 255 characters from the port if they are there
249
  rx_length = read(uart0_filestream, rx_buffer, read_length);    //Filestream, buffer to store in, number of bytes to read (max)
250
      
251
  if (rx_length < 0)
252
  {
253
    error("UART Read: Error -1\n");   //An error occured (will occur if there are no bytes)
254
  }
255
  else if (rx_length == 0)
256
  {
257
    error("UART Read: No Data Waiting\n");    //No data waiting
258
  }
259
  else
260
  {
261
    //Bytes received
262
    rx_buffer[rx_length] = '\0';
263
    printf("%i bytes read : %s\n", rx_length, rx_buffer);
264
  }
265
  
266
  return rx_buffer;
267
}
268
269
270
271
272
273
274
void uart_close(int uart0_filestream)
275
{
276
  close(uart0_filestream);   //Close UART
277
}

Der Fehler scheint momentan in der dostuff Funktion zu liegen 
(Uart_read).

Sowohl Lesen mittels O_NDELAY als auch ohne, führt zu keinem Ergebnis.
Ich weiß nicht mehr weiter :(.

von Karl H. (kbuchegg)


Lesenswert?

lass dir hier
1
printf("Nachricht von TCP/IP Schnittstelle: %s\n",tcpip_buffer);

den String in tcpip_buffer mal so ausgeben
1
printf("Nachricht von TCP/IP Schnittstelle: #%s#\n",tcpip_buffer);

Hintergrund ist der, dass man sich vor bzw. nach dem eigentlichen String 
ein Sonderzeichen einfügt, damit man im String Dinge wie Leerzeichen 
bzw. Zeilenvorschübe sehen kann.
Die Ausgabe muss dann
1
   ...lle: #IF;#
sein. D.h. zwischen dem eigentlichen String und den Sonderzeichen dürfen 
keine Leerzeichen sein.
1
   ...lle: # IF;#

 So etwas
1
   ...lle: #
2
IF;#
würde darauf hindeuten, dass sich ein \n eingeschmuggelt hat. Genauso 
wie bei
1
   ...lle: #IF;
2
#

All das sind Dinge, die man ansonsten bei einer Text-Ausgabe nur 
allzuleicht übersieht. Daher Begrenzungen in die Ausgabe einfügen, so 
dass man exakt sieht, wo der String beginnt und wo er endet. Auch die 
zusätzliche Ausgabe der Stringlänge ist manchmal hilfreich, weil es auch 
Zeichen gibt, die zwar im String enthalten sind, von einer normalen 
Terminalausgabe aber nicht angezeigt werden.




1
    if(tcpip_buffer[0] == 'I' && tcpip_buffer[1] == 'F' && tcpip_buffer[2] == ';')    //If GetDataCommand (IF
====>
1
    if( strncmp( tcpip_buffer, "IF;", 3 ) == 0 )
2
    {
3
       ....

: Bearbeitet durch User
von Autor (Gast)


Lesenswert?

Achja, der Fehler, welcher ausgegeben wird lautet:

error("UART Read: Error -1\n");   //An error occured (will occur if 
there are no bytes)

Erscheint also wenn die read Funktion einen Wert <0 zurückgibt...

Aber warum stürzt das Programm anschließend ab?

von Autor (Gast)


Lesenswert?

Schonmal vielen Dank, Karl Heinz. Werde ich mich gleich darum kümmern. 
Die Steuerkommandos wie z.B. IF kommen aber am Funkgerät an, da dieses 
daraufhin mir die Daten zurücksendet. Die eine Richtung (senden) 
funktioniert einwandfrei - lesen leider nicht :/.

von Karl H. (kbuchegg)


Lesenswert?

Autor schrieb:
> Schonmal vielen Dank, Karl Heinz. Werde ich mich gleich darum kümmern.

Vergiss es.

Ich liebe es, wenn die wensentlichen Informationen ganz am Ende eines 
Postings kommen und erst mal am Anfang eine falsche Fährte gelegt wird. 
Das macht das Fehlersuchen interessanter.

von Karl H. (kbuchegg)


Lesenswert?

Autor schrieb:
> Achja, der Fehler, welcher ausgegeben wird lautet:
>
> error("UART Read: Error -1\n");   //An error occured (will occur if
> there are no bytes)
>
> Erscheint also wenn die read Funktion einen Wert <0 zurückgibt...

Das scheint nicht nur so, das ist auch so.

> Aber warum stürzt das Programm anschließend ab?

Sorg erst mal dafür, dass der Aufrufer einen ordentlichen String 
zurückbekommt, wenn ein Fehler auftritt. SChliesslich kümmert sich dein 
aufrufender Code einen Scheissdreck darum, dass irgendwas schief 
gelaufen ist.

Und für den Rest hast du einen Debugger. Lerne damit umzugehen.

: Bearbeitet durch User
von Autor (Gast)


Lesenswert?

Sorry, ich hatte oben lediglich geschrieben, dass er nach Uart_Read 
abstürzt.

Trotzdem war dein Tipp ganz gut, dadurch konnte ich zumindest schon 
einmal ausschließen, dass nach IF; noch unnötige Zeichen gesendet werden 
und dadurch vielleicht das Lesen blockiert wird. Dies scheint aber nicht 
der Fall zu sein.

Die Antwort des Funkgerätes habe ich am aufgeknüpften Kabel feststellen 
können - jetzt vermute ich aber langsam, dass das Problem hinter dem 
Max232 liegen könnte. Also dass dieser das Signal nicht mehr 
entsprechend weiterreicht.

von Karl H. (kbuchegg)


Lesenswert?

Sagtest du nicht ganz am Anfang, dass deine RS232 getestet wäre?
Scheint wohl doch nicht so gut getestet zu sein.

Edit: Oh, sehe gerade im Eröffnungsposting, dass nur vom Senden zur 
Funke sprichst. Was ist mit der Gegenrichtung? Hast du die erst mal 
ebenfalls unabhängig von irgendwelchen TCP-IP Sachen getestet?

Ernsthaft: Schreib nie zu viel Code, zu viele Module auf einmal!
Du hängst eine Funke per RS232 an deinen Pi. Gut. Dann teste das auch. 
Und zwar unabhängig von allem anderen. Denn was du auf keinen Fall haben 
willst, das ist, dass du in deinem Programm 5 ungetestete 
Module/Funktionalitäten hast, .. nichts funktioniert, ... und du hast 
keinen Schimmer, welches der 5 Module dafür verantwortlich ist. Im 
Idealfall hantelt man sich von einer funktinierenden Programmversion zur 
nächsten hoch, indem man mit einem Trivialprogramm anfängt und dann 
sukzessive Schritt für Schritt neue Funktionalität ergänzt. So behält 
man die Kontrolle über Änderungen und welche Auswirkungen diese 
Änderungen haben. Das kann auch heißen, eine gewisse Teilfunktionalität 
erst mal unabhängig für sich alleine (mit einem eigenen Programm) zu 
testen, ehe sie dann ins Programm eingebaut wird.

Also: Klär erst mal ab, ob du ein Hardware Problem hast. Dazu brauchst 
du kein TCP/IP und keinen fork. Das sind alles nur Dinge, die die Sache 
erst mal komplizierter machen als minimal notwendig, wenn es um die 
Frage geht: kann ich mit meinem Funkgerät per RS232 kommunizeren, ja 
oder nein?

: Bearbeitet durch User
von Autor (Gast)


Lesenswert?

Hinter dem Pegelwandler kommt auf der RXD Seite fast nur noch Peaks auf 
der Leitung an.  Vor dem Wandler ist das Signal noch in Ordnung. D.h. 
für mich ich muss den Wandler nochmal neu aufbauen ... Ich hoffe dannach 
funktioniert das ganze, ansonsten melde ich mich nochmal.

von Autor (Gast)


Lesenswert?

Wer misst, misst Mist....

Das Signal auf RXD Seite sieht nun nach sauberer Masseanzapfung doch in 
Ordnung aus. Die Funktiono read scheint nicht zu funktionieren. Jemand 
eine Idee? Der Rückgabewert der Funktion ist immer <1, egal ob ständig 
daten gesendet werden...

von ... (Gast)


Lesenswert?

Der MAX232 funktioniert nicht mit 3,3V Betriebsspannung. Wenn du ihn an 
die 5V-Schiene des RPi angeschlossen hast, hast du den RX-Eingang der 
seriellen Schnittstelle AMA0 geschossen. Der verträgt nämlich nur 
3,3V-Signale.

von Daniel T. (Gast)


Lesenswert?

Ich sehe keinen Unterschied zu meinen, funktionierenden, Code. Kann es 
daran liegen, dass das System immer noch (Raspberry-Default) einen 
Zugriff auf die serielle Schnittstelle hat?

In meinem "ProjektLog" steht dazu folgendes:

- enable serial interface for own use:
  - run
    nano /etc/inittab
  - auskommentieren
    T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
  - run
    init q

Ich bin alles andere als ein Linux-Experte, aber bei mir läuft die 
Schnittstelle in beiden Richtungen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Autor schrieb:
> Achja, der Fehler, welcher ausgegeben wird lautet:
>
> error("UART Read: Error -1\n");   //An error occured (will occur if
> there are no bytes)

Du solltest Dich mal mit errno bzw. perror() beschäftigen. Dann bekommst 
Du auch den Grund mitgeteilt, warum Du eine -1 zurückbekommst.

Erhältst Du auch den Error, wenn Du ohne O_NDELAY arbeitest?

von ... (Gast)


Lesenswert?

Disable Serial Port Login:
To enable the serial port for the RPi-extension you need to disable 
login on the port. There are two files that need to be edited.
The first and main one is /etc/inittab
This file has the command to enable the login prompt and this needs to 
be disabled. Edit the file and move to the end of the file. You will see 
a line similar to:
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
Disable it by adding a # character to the beginning.
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100</note>
Save the file.
Disable Bootup Info:
Disable it by editing the file /boot/cmdline.txt .
The contents of the file look like this:
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 
console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline 
rootwait
Remove all references to ttyAMA0 (which is the name of the serial port). 
The file will now look like this:
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 
elevator=deadline rootwait
Save the file.
Reboot
In order you enable the changes you have made, you will need to reboot 
the Raspberry Pi:
sudo shutdown -r now

von Autor (Gast)


Lesenswert?

Daniel T. schrieb:
> Ich sehe keinen Unterschied zu meinen, funktionierenden, Code. Kann es
> daran liegen, dass das System immer noch (Raspberry-Default) einen
> Zugriff auf die serielle Schnittstelle hat?
>
> In meinem "ProjektLog" steht dazu folgendes:
>
> - enable serial interface for own use:
>   - run
>     nano /etc/inittab
>   - auskommentieren
>     T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
>   - run
>     init q
>
> Ich bin alles andere als ein Linux-Experte, aber bei mir läuft die
> Schnittstelle in beiden Richtungen.

Das war der Fehler!

Vielen Dank! :)

Ich meine ich hatte in der Datei schon einen Teil auskommentiert, aber 
dort gibt es leider zwei dieser Anweisungen :).

Was mich dabei wundert, der Schreibvorgang hat trotzdem funktioniert....

von Autor (Gast)


Lesenswert?

... schrieb:
> Der MAX232 funktioniert nicht mit 3,3V Betriebsspannung. Wenn du ihn an
> die 5V-Schiene des RPi angeschlossen hast, hast du den RX-Eingang der
> seriellen Schnittstelle AMA0 geschossen. Der verträgt nämlich nur
> 3,3V-Signale.

Den Pegelwandler habe ich von meinem Betreuer bekommen, war wohl ein 
Max323, also schon der richtige. Mein Fehler - Sorry.

von ... (Gast)


Lesenswert?

nur die inittab editieren reicht nicht. Da muss auch die cmdline.txt 
editiert werden, sonst macht dein Funkgerät beim booten komische Fehler.

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.