728x90

시리얼 통신이 무엇인가?

다른 말로는 uart통신이라고도 부른다. 

엄밀히 말하면 둘은 동일하지 않지만 보통 시리얼통신 이러면 아 uart통신!이라고 생각한다.

그 uart통신을 자바에서 한번 굴려보도록 하자.

먼저 시리얼 통신을 사용하기 위해서는 라이브러리가 필요하다.


http://rxtx.qbang.org/wiki/index.php/Main_Page


위의 주소에서 다운로드 받으면 되는데 Download에 들어가서 다운받아주면된다.


여기서 stable한 버전의 binary를 설치하도록하자.


만약 메이븐레포지터리에서 다운받고 싶다면(혹은 메이븐으로 추가하고싶다면) 메이븐 레포지터리를 확인해도 무방하다.


https://mvnrepository.com/search?q=rxtx


사용횟수가 그리 많지는 않다. 아무래도 자바로 시리얼통신을 하는 경우가 흔치는 않은것 같다.

대신에 사용하는 방법이 그리 어렵진 않다... 남이 짠 코드만 있으면...(바람직한 태도는 아니다.)


이제 설치하는법은 삼척동자도  할 수 있을 정도로 쉽지만 프로젝트에 넣는건 살짝 힘들다.

여기서는 이클립스에서 프로젝트에 넣는 방법에 대해서 짤막히 설명하고 넘어가겠다.



설치를 하면 파일 내부는 안에처럼 되어있다. 보통의 경우 다운로드를 완료하면 안에있는 jar파일만 사용하면된다.

그러나 시리얼통신은 그렇지 않은데 그 이유는 native한 소스가 필요하기 때문이다.

즉 순수 자바가아니라 실행 파일이 섞인 자바가 필요하다는것이다.

그럼 사용하려면 어떻게 해야하는가? 터미널에서는 조금 코드가 길어지면서 골때리게 되지만 이클립스에서는 간단하다.

이클립스로 사용하는 예제에 대해서 보여주겠다. 일단 당연히 jar파일과 자신의 운영체제에 맞는 라이브러리를 뽑아서 프로젝트에 넣는다.



이렇게 이쁘게 넣어주면 준비는 끝났는데 설정만 하면된다.

프로젝트를 우클릭한후 properties를 눌러준다.


이렇게 java build Path에 jar파일을 추가한다.

그리고 jar파일에 화살표를 클릭을 하자.


Native library Location을 선택해준다. 그리고 Edit을 누른다.


그 다음 폴더를 선택하면되는데 만약 워크스페이스내에있다면 Workspace를, 아니라면 External Folder를 선택한다.


보다시피 필자는 lib에 넣었으므로 lib를 선택하겠다.

다른데 넣었다면 여러분은 다른 파일을 선택해라.


다 끝났으면 apply를 누른다.

이제 준비는 끝났고 코드를 치는 일만 남았다.

그런데... 물론 코드를 다 이해하는건 중요하지만 남이 이미 만들어 놓은 코드가 있다.

우리는 일일히 코드를 치지않고 분석하는 선에서 끝내도록 하자.

코드는 크게 세부분으로 나뉜다. 먼저 시리얼을 읽는 SerialReader이다.


SerialReader

import java.io.IOException;
import java.io.InputStream;

public class SerialReader implements Runnable {
InputStream in;

public SerialReader(InputStream in) {
this.in = in;
}

public void run() {
byte[] buffer = new byte[1024];
int len = -1;
try {
while ((len = this.in.read(buffer)) > -1) {
System.out.print(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

보면 알겠지만 코드가 생각보다 매우 쉽다.

외부에 특정 InputStream을 지정해주면 걔로 부터 데이터를 받는다.

SerialReader는 쓰레드(Runnable)로 되어서 계속해서 실행되서 끊임 없이 값을 받는다.

보통 계속해서 신호를 받기때문에 사실상 while문을 빠져나가는 상황은 코드가 꽂혀있는한 없다.

만약 코드가 뽑힌다면 -1이 반환되어 while문은 종료가 되게 될것이다.

게다가 uart통신 특성상 1바이트씩만 받기에 사실 byte는 배열이 아니라도 크게 상관은 없다.

결론은 위의 코드는 화면에 시리얼에서 받은 값을 뿌려주는 역활을 하게 된다.


SerialWriter

import java.io.IOException;
import java.io.OutputStream;

public class SerialWriter implements Runnable {
OutputStream out;

public SerialWriter(OutputStream out) {
this.out = out;
}

public void run() {
try {
int c = 0;
while ((c = System.in.read()) > -1) {
this.out.write(c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

이번에는 보내는쪽의 코드이다. 이역시 OutputStream을 받아온다.

System.in.read는 우리의 표준입력을 날것으로 받아들인다.

받을때도 바이트로 받았는데 보낼때도 바이트로 받는다.

따라서 저기 c는 byte라도 사실 크게 문제는 없다.

while문이 도는동안 사용자의 입력을 계속받게 코드가 짜져있다.

대신 System.in.read는 블로킹 함수이므로 SerialReader마냥 무한대로 돌면서 사용자의 입력을 받지는 않는다.


Serial


import java.io.InputStream;
import java.io.OutputStream;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;

public class Serial {
public Serial() {
super();
}

void connect(String portName) throws Exception {
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName)
;
if (portIdentifier.isCurrentlyOwned()) {
System.
out.println("Error: Port is currently in use");
} else {
CommPort commPort = portIdentifier.open(
this.getClass().getName(), 2000);

if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort
;
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);

InputStream in = serialPort.getInputStream();
OutputStream out = serialPort.getOutputStream();

(new Thread(new SerialReader(in))).start();
(new Thread(new SerialWriter(out))).start();

} else {
System.
out.println("Error: Only serial ports are handled by this example.");
}
}
}
}

이제 두 코드를 종합 취합해서 사용할것이다.

전혀 어렵지 않으니 코드의 길이를보고 쫄지 말길 바란다.

먼저 CommPortIdentifier 포트가 실제로 존재하는지, 또한 사용할 수 있는 상태인지확인한다.

아래의 if문은 만약 누군가 사용하고 있다면 연결은 실패한다(포트를 둘이상 점유하는건 불가능하다.)

만약 선점되어 있지않다면 우리가 여는데(open) 2000밀리초의 시간만큼 타임아웃을 가진다.

만약 받은게 시리얼포트라면(instanceof) 보드레이트9600으로 데이터 교환 8비트에 스탑비트1비트,그리고 패리티비트는 없이 보낸다.

모르겠으면 그냥 저대로 사용하면된다. 보드레이트만 맞춰주면된다.

마지막으로 각각의 스트림을 만들어주면된다.

그 다음 쓰레드를 스타트 시키면된다.


Main

public class Main {
public static void main(String[] args) {
try {
(new Serial()).connect("COM11");
} catch (Exception e) {
e.printStackTrace();
}
}
}

이제 마지막으로 포트를 정하고 연결시켜주면된다.

윈도우에서는 무조근 COM으로 시작하지만 리눅스나 맥은 /dev밑에 serial이라는 이름으로 보통 되어 있다.

한번 테스트 해보자.


현재 매 클럭마다 hi라는 값을 보내는 아두이노와 연결되어있는 예제이다.

+ Recent posts