ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [MATLAB] serialport사용하기
    항법연구실/MATLAB 2026. 6. 23. 11:12

    지난 시간, Linux socat에 대해서 다루어보았다. 

     

    그런데 이게 끝이 아니다. 데이터는 Matlab에서 다루고 있기 때문에, matlab에서 소켓에서 전달받은 데이터를 어떻게 처리할 지에 대해서, 그리고 패킷을 보낼 때 헤더를 설정하는 방법과 페이로드를 주고 받는 바이트스트림 등 .. 알아야 할 개념이 참 많다. 내가 학부 때 공부를 안해서 그런 것이다.

     

    하나씩 차근차근하다보면 또 다 정리되겠지? 

     

    그럼 이번 포스팅에서는 matlab에서 serialport를 사용해서 패킷을 생성하고 전송하는 방식에 대해서 알아보자. 


    MATLAB Serialport 

    matlab의 serialport객체는 직렬(Serial) 인터페이스 규칙에 맞춰 데이터를 쓰고 읽는 전용 직렬 클라이언트 역할을 수행한다. 

     

    사용하는 방법은 여기 다 나와있다. 

     

    serialport - 직렬 포트 연결 - MATLAB

    serialport 객체는 직렬 포트와 통신하기 위한 직렬 클라이언트를 나타냅니다.

    kr.mathworks.com

     

     

    그러니 실제 코드에서 어떻게 구현했는지를 위주로 살펴보자 ! 

     


    서버에서 serialport를 사용하여 데이터 송/수신하기 

    데이터 송신부 
    # [1] 시리얼 포트 객체 생성 
    port_sender = "/tmp/vtty0";
    baud_rate = 115200;
    sender = serialport(port_sender, baud_rate);
    
    # [2] 전송할 데이터 준비
    fname_in = '/home/ejl/test2.mat';
    fid = fopen(fname_in, 'r');
    file_bytes = fread(fid, inf, 'uint8=>uint8');
    fclose(fid);
    
    # [3] 헤더 구성(전체 파일 bytes수)
    total_bytes = length(file_bytes);
    
    # [4] 데이터 전송 
    write(sender, total_bytes, 'uint32');
    pause(0.01);
    write(sender, file_bytes, 'uint8');
    
    fprintf("File transmission complete\n");
    clear sender;

     

    현재 헤더는 간단하게 작성해보았다. 4bytes로 구성하고 패킷의 크기만 작성한 간단한 헤더이다. 

     

     

    데이터 수신부 
    # [1] 수신측 시리얼 포트 객체 생성 
    port_receiver = '/tmp/vtty1';
    baud_rate = 115200;
    receiver = serialport(port_receiver, baud_rate);
    
    # [2] 헤더 수신 (4bytes)
    while receiver.NumBytesAvailable < 4
        pause(0.001);
    end
    
    # [3] 헤더 열어서 총 바이트 수를 확인
    total_bytes = read(receiver, 1, "uint32");
    fprintf("Incoming file size: %d bytes \n", total_bytes);
    
    # [4] 버퍼에 있는 바이트만큼 모두 읽어오기 
    while receiver.NumBytesAvailable < total_bytes
        pause(0.001);
    end
    received_fbytes = read(receiver, total_bytes, "uint8");
    
    # [5] 데이터 내보내기 
    fname_out = 'received_data.mat';
    fid = fopen(fname_out, 'w');
    fwrite(fid, received_fbytes,'uint8');
    fclose(fid);
    
    fprintf("file saved completely\n");
    loaded_data = load(fname_out);
    disp(loaded_data);
    
    clear receiver;

     

    데이터 송신부에서 write함수를, 데이터 수신부에서는 read함수를 사용하여 데이터를 송/수신한다. 


    Byte Stream에서 헤더(Header)와 패킷(Packet)

     

    시리얼 통신은 데이터가 한 줄로 끝없이 밀려오는 Byte Stream이다. 흔히 네트워크에서 배우는 패킷과 달리, 이 바이트스트림은 물리적으로 패킷처럼 쪼개져 있지 않다. 송신측에서 파일을 보내더라도, 통신선로 상에서는 경계선이 없는 덩어리일 뿐이다. 그러니 수신측 매트랩 버퍼 입장에서는 어디까지 읽어야 이 파일이 끝나는지 알 수 없다. 

     

    이 말이 조금 어려워서 택배 개념으로 풀어서 설명해보자면, 송신측에서 상자 포장도 안하고 물건들을 컨베이어 벨트(통신 선로)상에 다닥다닥 붙여서 연속으로 밀어 넣고 있는 상황으로 비유해볼 수 있다.

     

    그럼 수신측에서는 이렇게 생각할 것이다. 어 ?? 물건(byte)가 계속 들어오는데,, 어디서부터 어디까지가 test2.mat파일이고 어디서부터 다음파일이지? 구분할 수가 없잖아 ㅜㅜ 경계선이 없는데 데이터는 계속 들어오니 수신측은 정병이 생길 수 밖에 없다. 

     

    그래서 이를 해결하기 위해 헤더를 붙였다. 송장을 붙인다는 개념이라고 생각하면 편하다. 예를 들어 송신부에서 파일을 3개 연속으로 보낸다고 가정하면, 통신선(Stream) 내부에서는 패킷 상자 3개가 이렇게 연속적으로 밀려온다. 

     

    [송장1][알맹이1][송장2][알맹이2][송장3][알맹이3]...

     

    물리적인 경계선은 선로 어디에도 존재하지 않는다. 바이트들이 강물처럼 흘러간다. 

     

    그래서 구현한 방법이 NumbytesAvail을 통해 4바이트씩 읽고 페이로드의 길이를 구하는 것이다. 통신선에서는 경계선이 없지만, 수신측 매트랩 코드(read함수)가 스트림 위에서 다음과 같이 송장을 분리해서 어디까지가 파일인지 확인한다. 

     

    (1) 먼저 무조건 4bytes씩 잘라!

    read(receiver, 1, "uint32")

    흘러오는 스트림의 맨 앞 4바이트를 강제로 잘라서 확인한다. 이것이 Header, 송장이다. 

     

    (2) 송장에 적힌 길이만큼 읽어와! 

    read(receiver,total_bytes,'uint8')

    예를 들어 송장을 읽었더니 500이라고 적혀있다면, 수신 버퍼에 데이터가 500바이트가 찰 때까지 기다렸다가 그만큼을 통째로 긁어온다. 

     

     

    그럼 다음번엔 socat, serialport객체를 이용하여 어떻게 실제로 서로 다른 서버간 데이터를 송/수신하는지에 대해서 자세히!! 알아볼 예정이다. 깊게 들어가면 OSI 7 layer도 여기서 등장하고 .. 어렵긴한데 네트워크를 이해하는데 많은 도움이 되었다 

Designed by Tistory.