본문 바로가기

라즈베리파이

라즈베리파이 2대를 사용한 UART 채팅 프로그램 C, Python 사용

반응형


라즈베리파이 2대를 사용한 UART 채팅 프로그램 C언어 사용 


연결은 라즈베리파이 GPIO 번호 8번(TXD), 9번(RXD) 핀에 MAX232 통신 모듈이나, USB to Serial 포트 등을 양쪽으로 연결하여 테스트 해야 하지만 여기서는 간단하게 라즈베리파이 1과 라즈베리파이2 의 8, 9번 핀\을 크로스로 연결하여 테스트 한다. Python 코드는 아래 C언어 예제 코드 뒤에 나온다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <wiringPi.h>
#include <wiringSerial.h>
 
 
#define STX 0x02
#define ETX 0x03
#define ACK 0x06
#define NCK 0x15
 
#define RED  "\033[31m"
#define DEFAULT  "\033[0m"
#define BLUE "\033[34m"
#define GREEN "\033[32m"
 
bool Rcv_End = false;
unsigned char Last_Packet[256];
int NAK_CNT  = 0;
int ser;
 
unsigned char make_LRC(const char *packet)
{
    unsigned char LRC = 0x00;
    int x;
    for( x = 0; packet[x] != 0x00; x++)
    {
        LRC = ((LRC + (unsigned char)packet[x]) & 0xFF);
    }
    LRC = (~LRC + 1& 0xFF;
    return LRC;
}
 
void send(const char *packet)
{
    int x, len;
    unsigned char LRC;
 
    Last_Packet[0= STX;
    strcpy((char *)Last_Packet + 1, packet);
    len = strlen(Last_Packet);
    Last_Packet[len ] = ETX;
    Last_Packet[len + 1= 0x00;
    LRC = make_LRC(Last_Packet + 1);
    Last_Packet[len + 1= LRC;
    Last_Packet[len + 2= 0x00;
     for( x = 0; Last_Packet[x] != 0x00; x++)
    {
        serialPutchar (ser, Last_Packet[x]) ;
    }
    printf"%sSend:%s%s\n", BLUE, packet, DEFAULT);
    return;
}
 
char *check_packet(char *val)
{
    static char buffer[256];
    char tmp[256];
    char lrc;
 
    strcpy(tmp, val + 1);    //remove STX
    tmp[strlen(tmp) - 1= 0x00;    //remove LRC
    lrc = make_LRC(tmp);
    if(lrc == val[strlen(val) -1])
    {
        strcpy(buffer, tmp);
        buffer[strlen(buffer) -1= 0x00;    //remove ETX
    }
    else
    {
        buffer[0= 0x00;
    }
    return buffer;
}
 
void *rs232_receive_thread(void *p)
{
    char val, *rcv_data;
    char data[256];
    int x, index = 0;
 
    memset(data, 0x00sizeof(data));
    while (1)
    {
        val = 0x00;
        if(serialDataAvail (ser)){    
            val = serialGetchar (ser);
        }
        if(0x00 == val){
            delay(2); 
            continue;
        }
        
        if(val ==  ACK){
//            printf ("\033[37mSend:OK ACK rcv \033[0m\n");
            continue;
        }
 
        if(val ==  NCK){    //packet currupted ->resend 3 times
            printf ("%s Packet Currupted ->NAK received %s", RED, DEFAULT);
            if(++NAK_CNT < 4){
                for( x = 0; Last_Packet[x] != 0x00; x++)
                {
                    serialPutchar (ser, Last_Packet[x]) ;
                }
            }
            else{    //drop packet
                NAK_CNT = 0;
            }
            continue;
        }
 
        if(val ==  ETX){
            data[index++= val;
            while (1){
                if(serialDataAvail (ser)){    
                    val = serialGetchar (ser);
                    if(0x00 == val){
                        delay(2); 
                        continue;
                    }
                    Rcv_End = true;
                    data[index++]= val;
                    break;
                }
            }
        }
 
        else{
            Rcv_End = false;
            data[index++= val;
        }
 
        if(true == Rcv_End){
            rcv_data = check_packet(data);
            if(0 == strlen(rcv_data)){
                printf("%s Invalid Packet Received ->NAK  DATA:%s %s\n", RED, data,  DEFAULT);
                serialPutchar (ser, NCK) ;
            }
            else{
                printf("%s RCV:%s %s\n", GREEN, rcv_data, DEFAULT);
                serialPutchar (ser, ACK) ;
            }
            index = 0;
            memset(data, 0x00sizeof(data));
        }
        delay(2); 
    }
}
 
int main()
{
    int  x, thr_id;
    char buf[256];
    pthread_t p_thread;
 
    if ((ser = serialOpen ("/dev/ttyAMA0"9600)) < 0)
    {
        fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
        return 1 ;
    }
 
    if (wiringPiSetup () == -1)
    {
        fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
        return 1 ;
    }
    thr_id = pthread_create(&p_thread, NULL, rs232_receive_thread, (void *)NULL);
    while(1){
        gets(buf);
        send(buf);
    }
    return 0;
}
 
 


라즈베리파이 2대를 사용한 UART 채팅 프로그램 Python 사용


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python
 
import serial
import sys, time 
#파이썬에서 쓰레드 기능 구현을 위해 필요
import threading
import commands
import binascii
import RPi.GPIO as GPIO
 
#중요한 제어 문자
NAK_CNT = 0
STX = 0x02
ETX = 0x03
ACK = 0x06
NCK = 0x15
 
Last_Packet= ""
Rcv_End = False
Debug = False
 
#출력 문자 색 제어
RED =  "\033[31m"
DEFAULT =  "\033[0m"
BLUE = "\033[34m"
GREEN = "\033[32m"
 
# ISO 1155 표준 LRC 계산식
def make_LRC(packet):
    LRC = 0x00
    for ch in packet:
        LRC = ((LRC + ord(ch[0])) & 0xFF)
    LRC = (~LRC + 1& 0xFF
    return LRC
 
# UART 패킷을 송신
def send(packet):
    global Last_Packet
    bytes = [STX]
    LRC = 0x10
    Last_Packet = "".join(map(chr, bytes)) 
    Last_Packet += packet
    bytes = [ETX]
    Last_Packet += "".join(map(chr, bytes))
    LRC = make_LRC(Last_Packet[1:len(Last_Packet)])
    bytes = [LRC]
    Last_Packet += "".join(map(chr, bytes)) 
    ser.write(Last_Packet)
    print "S:", binascii.hexlify(bytearray(Last_Packet))
    print "\033[34mSend:", packet, DEFAULT
    return
 
# LRC 체크가 성공여부 체크
def check_packet(val):
    lrc = make_LRC(val[1:len(val) - 1])
    if(lrc == ord(val[len(val) -1])):
        if(Debug == True):
            print BLUE, "LRC Check Success :", lrc, DEFAULT
            print "CHECK:", binascii.hexlify(bytearray(val))
        ret = val[1len(val) -2]
    else:
        if(Debug == True):
            print RED, "LRC Check Fail", lrc, "!= ",ord(val[len(val) -1]), DEFAULT
            print "CHECK:", binascii.hexlify(bytearray(val))
        ret = ""
        
    return ret
"""
UART 채널은 완전 이중화(Full Duplex)이기 때문에 별도 쓰레드에서 수신처리 가능
"""
def rs232_receive_thread(): 
    global  Last_Packet
    data = ""
    while 1
        val = ser.read(1)
        if(0 == len(val)):
            time.sleep(0.002)
            continue
        ival = ord(val[0])
        if(Debug == True):
            print "R:", binascii.hexlify(bytearray(val))
        if(ival ==  ACK):    
            if(Debug == True):
                print "\033[37mSend:OK ACK rcv \033[0m"
            continue
 
 
        if(ival ==  NCK):    #packet currupted ->resend 3 times
            print "\033[31m""Packet Currupted ->NAK received""\033[0m"
            if(++NAK_CNT < 4):
                ser.write(Last_Packet)
            else:    #drop packet
                NAK_CNT = 0
            continue;
 
        if(ival ==  ETX):
            data += val
            while True:
                val = ser.read(1)    #receive Last LRC
                if(len(val) == 1):
                    Rcv_End = True
                    data += val
                    break
 
        else:
            Rcv_End = False
            data += val
 
        if(True == Rcv_End):
            rcv_data = check_packet(data)
            if(0 == len(rcv_data)):
                print RED,  "Invalid Packet Received ->NAK  DATA:", data,  DEFAULT
                bytes = [NCK]
                ser.write("".join(map(chr, bytes)))
            else:
                print GREEN, "RCV:",  rcv_data, DEFAULT
                bytes = [ACK]
                ser.write("".join(map(chr, bytes)))
            data = ""
        time.sleep(0.002
 
# 수신 전용 쓰레드를 만들어 실행시킴
def rs232_receive_svc(): 
    print ('rs232 receive svc'
    th = threading.Thread(target=rs232_receive_thread) 
    th.start() 
 
# 여기에서부터 프로그램 시작!
print "RS232 Chatting Application"
 
#루프백 테스트에 사용할 UART용 가상 파일 ttyAMA0(ttyS0)를 연다.
if(GPIO.RPI_REVISION < 3):
  ser = serial.Serial(port = "/dev/ttyAMA0", baudrate=9600, timeout=2)
else:
  ser = serial.Serial(port = "/dev/ttyS0", baudrate=9600, timeout=2)
 
if (ser.isOpen() == False):
    ser.open()
#만약 ttyAMA0에 데이터가 남아있으면 비우고 새로 시작한다.
ser.flushInput()
ser.flushOutput()
 
#수신 쓰레드 생성
rs232_receive_svc()
 
try:
    while True:
        #송신할 채팅 문장을 입력받아서 송신
        packet = raw_input("")
        send(packet)
 
except KeyboardInterrupt:   
    print "RS232 Chatting Application End" 
 
finally:
    ser.close()
    print "Good by!"
 


이미지출처 : element114.com

반응형