2015년 9월 23일 수요일

[C/C++] Qt QSerialPort를 활용한 PC와 아두이노 간의 시리얼 통신












시험도 치르고, 오랜만에 포스팅해봅니다.. 블로그 통계를 확인하면 사실 저 스스로를

위한 피드백 공간에 가깝지만.. 오늘 소개할 도구는 Qt 프레임워크의 'QSerialPort' 입니다.

이전에는 Ada 언어에서 'GNAT Serial Communications'를 활용해 시리얼 통신 입출력을

테스트해보았습니다. 사실 마이크로소프트 Visual Studio에서 시리얼 통신을 하든

리눅스에서 Ada 언어로 구동하든 '열고', '읽고/쓰고', '닫고' 하는 시리얼 통신의

큰 흐름은 변하지 않습니다.



Qt 프레임워크는 윈도우즈, 우분투와 같은 리눅스, 심지어 안드로이드 앱도 일관된

인터페이스로 구현 가능하게 해줍니다. 경험은 없지만 애플의 Mac도 지원한다고

합니다.











Qt 홈페이지 : http://www.qt.io/



GUI, DB 등 방대한 라이브러리를 지원하는 Qt 프레임워크에서 실습하기에, 이번에는

Qt의 GUI 위에서 아두이노와의 시리얼 통신을 구현했습니다.


구성은 Ada에서 했던 것과 같습니다. 아두이노와 PC가 USB를 '팔'로써, UART를 '손가락'

으로써 통신하게 됩니다. 회로 구조는 아래처럼 단순합니다.



회로에서 VCC의 +전압은 보통 빨강색 선, 그라운드는 검은색 선을 사용합니다.

개인적으로 더 큰 모듈이 주체가 되어 송신은 노란색 선, 수신은 하얀색 선을 사용합니다.

아두이노와 시리얼 모듈 모두 PC로부터 전압을 받고있기에 그라운드와 신호선만 연결해도

됩니다. 여기서는 아두이노보다 PC가 더 큰 주체이기에, PC 입장에서 송수신 점퍼선 색을

결정했습니다. 아두이노 모델들은 시리얼 통신을 지원하기에 어떤 제품을 사용하든 크게

상관없을 것입니다. 여기서는 'Arduino Nano'를 활용했습니다.








아두이노의 소스코드는 아래와 같이 됩니다. Qt 프로그램으로부터 수신한 데이터에

아스키 코드 10진수 값 65에 해당하는 'A'가 있으면 'Apple', 82에 해당하는 'R'이

있으면 'Ramen'을 Qt 프로그램에 돌려줍니다.


  



그럼 이제 Qt 프레임워크 위에서 프로그래밍을 진행하겠습니다. Windows 위에서

위젯 환경으로 프로젝트를 생성했습니다.





가장 먼저 해야할 것은 Qt 프로젝트 파일에 serialport 라이브러리를 활용하겠다고

알려주는 것입니다. 프로젝트 파일에는 컴파일을 위한 환경이 기입되어 있습니다.





최종적으로 구현할 GUI 인터페이스는 위와 같습니다. 보낼 메시지를 'QLineEdit'에

입력하고 'Send message : "라는 'QPushButton'을 클릭하면 입력된 메시지를 전송합니다.

'Received message' 아래의 '.. Waiting renewal'이라는 'QLabel'이 아두이노의 응답

'Apple', 'Ramen' 등을 받아 나타냅니다.






헤더파일에 "QSerialPort'를 추가해주고, 사용할 데이터타입과 'slots' 함수를 추가해줍니다.

조건과 그에 따른 행동을 1:N, N:1, M:N으로 정할 수 있는 직관적인 도구입니다. 

시그널과 슬롯을 사용하려면 'QObject'의 'connect' 함수를 사용해 조건에 따른 

슬롯 함수를 매칭시켜주어야 합니다.





이제 메인 위젯의 생성자에 인터페이스를 하나하나 추가합니다. 위젯을 생성하고

레이아웃을 'GridLayout'으로 정하고 버튼과 라벨 등을 하나씩 추가합니다.





그리고 시리얼 포트 환경설정 후 'open()'합니다. Baudrate는 아두이노와 같이

9600으로 정했습니다.





이제 시그널과 슬롯을 매칭시키는 차례입니다. 'SIGNAL'은 어떤 슬롯 함수를 동작시킬지

정의하는 'Condition'입니다. 'SLOT'은 그에 따른 'Action'을 수행할 함수를 정의합니다.

메시지 전송을 위한 QPushButton의 시그널은 'clicked'가 되고, 메시지 수신을 감지하기

위한 시그널은 'readyRead()'가 됩니다. 빨강줄이 그어진 'connect' 함수에 담겨지는

매개변수들은 차례로



'시그널을 일으키는 "sender" ',                   - ex) QSerialPort 데이터 타입인 'port'에서

'시그널이 구동될 특정 동작',                     - ex) readyRead(), 수신되는 데이터가 있다면

'시그널에 영향을 받는 "receiver",               - ex) this, 현재 객체에서

'시그널이 동작시킬 함수'                         - ex) text_Reading(), 이 함수를 동작시킨다.



'If'문과 'Switch문' 등, C/C++의 기본 분기문이 조건과 그에 따른 행동을 결정하는 것처럼,

Qt 프레임워크의 'SIGNAL-SLOT' 개념도 융통성있게 행동을 결정하고 있습니다. 시그널과

슬롯은 사전에 정의되어 있는 것을 바로 쓰거나, 사용자가 직접 작성할 수 있습니다.





'readyRead()' 시그널이 포트로 들어오는 데이터가 있을 때 발동되는 사전 정의

시그널이라면, 그에 대한 행동인 'text_Reading()'은 사용자인 제가 정의한 슬롯에

해당합니다. 아두이노에서 들어오는 데이터를 읽어 텍스트로 출력하도록 했습니다.

'text_Sending()' 함수는 입력창의 처음 1바이트만 잘라내어 송신하도록 했습니다.





아두이노에 'A'와 'R'이라는 신호를 보내면,





아두이노와 약속한대로 해당 데이터에 대한 응답을 확인할 수 있습니다.



댓글 8개:

  1. 감사합니다.

    다름이아니라 작성자님 코드를 이용해볼까하는데.. 자꾸 빌드에서 링크오류가 나네요 ㅠㅠ

    .pro에 serialport도 추가했는데.. 머가문제일까요..
    뭐 따로 환경설정같은거 해줘야하는게 있나요?

    QT는 5.7에 MSVC2013, 32bit사용중입니다.
    https://hyu-my.sharepoint.com/personal/bgyoo_hanyang_ac_kr/_layouts/15/guestaccess.aspx?guestaccesstoken=zdtepd36F9D%2bF6la1TUb6BnODYe13eOT%2bd7uF2%2fZsP4%3d&docid=1e122354b531e4ff6b105fce0ca961fae&rev=1

    제가 작성한 프로젝트입니다.. 소스내용은 똑같지만..

    답글삭제
    답글
    1. 안녕하세요~ 어떤 오류인지 알 수 있을까요? 사용하는 것은

      Qt 기본 클래스 밖에 없어서 추가 환경설정은 필요 없습니다.

      말씀하신 사항 중에 결정적인 차이는, 저는 MSVC2013 컴파일러가

      아닌 MinGW로 GCC 컴파일러를 사용하였습니다.


      MSVC는 마이크로소프트의 비주얼 스튜디오에 들어있는 컴파일러이고,

      MinGW는 Windows에서 리눅스에서 쓰이는 GCC 컴파일러를 사용하게

      해줍니다. C++ 언어는 하나의 표준이 있더라도, 사투리처럼

      마이크로소프트, 자유소프트웨어 재단의 GNU 프로젝트는 각자

      입맛에 맞게 변경한 차이점들이 있습니다. 저는 Qt면 GCC,

      비주얼 스튜디오면 MSVC만 묶어서 사용해봤습니다.


      소스코드 자체가 단순해서 이 둘의 차이로 인한 문제가 있을진

      모르겠네요.

      삭제
    2. 답변감사합니다.
      라인 바이 라인으로 해보니 QSerialPort 관련 구문을 넣으니 에러가 나네요.
      에러는 LNK2001과 LNK2019 이 두가지입니다.

      한가지 궁금한게 Qt가 크로스 컴파일 프레임워크인데, 컴파일 종류에따라 되는 문법이 있고 안되는 문법이 있는건가요?..

      삭제
    3. 안녕하세요, 'Byeonggil Yoo'님.

      말씀하신 대로 Qt는 여러 OS에서 사용할 수 있는

      통합개발환경입니다. #include 해서 사용할 수 있는

      Qt 클래스 재료들을 담아둔 냉장고입니다.

      이 재료들을 실제 바이너리 파일로 가공해주는 요리사는

      컴파일러입니다. 같은 C++ 요리사라도 차이점이 조금 있는데,

      예로 GCC는 main 함수에 'void' 반환형을 허락하지 않습니다.

      하지만 MSVC는 void로 해도 오류 없이 컴파일해줍니다.


      근데 저도 의문인 것이, Qt를 쓰는 이유가 아랫단인 API를

      보지 않고도 'Q~~'로 통일된 소스코드를 마음껏 사용하기

      위함입니다. 소스코드를 보아도 포장된 Qt 클래스만 사용하는데.


      아, 방금 링크해주신 프로젝트를 실행해보았습니다. 저는 문제없이

      컴파일, 실행되네요. Qt 버전은 5.5입니다. Qt 버전업 후

      'QSerialPort' 사용법에 변화가 생긴 걸까요?

      명확한 도움드릴만큼 경험이 풍부하지 못해서 죄송합니다.

      삭제
    4. 아닙니다 ㅎㅎ 덕분에 많이 배우고갑니다.
      해결은 하였는데, 답변해주시만큼 해결(?)했던 방법들을 남겨놓겠습니다.
      add-in으로 visual studio내에서 작업하니 정상적으로 돌아가네요... 그 뒤, qt creator로 진행해도 되네요..
      뭔가 컴퓨터 시스템이 꼬였던거 같기도하고..

      아무튼 답변 감사드려요~

      삭제
  2. 감사합니다. 혹시 윈도우말고 리눅스상에서 사용해보려고하는데 아두이노에서 먼저 키면 permissionError가 뜨고, qt를 먼저 실행시키면 아두이노가 연결이 안되더라고요
    계속 구글링을 해봤는데 리눅스에서는 시리얼 포트접근이 윈도우랑 다른건지 잘 모르겟네요 혹시 이부분에서 해결하는 방법을 아신다면 답변해주시면 감사하겠습니다!

    답글삭제
    답글
    1. 안녕하세요.

      리눅스에서는 윈도우의 COM포트가 TTY로 바뀌는 것 말고는 차이점이 없었던 걸로 기억합니다.

      permission 문제는 sudo 명령어로 해보셨나요? 해보셨겠죠..

      감이 잘 안오네요. 리눅스에서 시리얼 포트를 쓸 때 힘들었던 몇가지를 기억해보면,

      아두이노와 시리얼 컨버터의 드라이버가 제대로 설치되었는지 확인하는 것과,
      (저의 경우 시리얼 컨버터 드라이버를 직접 설치했었습니다.)

      COM1과 같은 포트명을 /dev/ttyS0 으로 변환하는 것이었습니다.

      qt 프레임워크에 포함된 기본 시리얼 통신 프로젝트로 확인해보시면 어느 부분 문제인지

      분명할 것 같습니다. 무엇보다 디버깅 모드로 단계별로 확인하시는게 가장 좋겠습니다.

      minicom 시리얼 통신 프로그램으로 아두이노와의 기본통신을 우선 점검해 볼 수 있습니다.

      삭제
  3. [아래로 _ ]: [C/C++] Qt Qserialport를 활용한 Pc와 아두이노 간의 시리얼 통신 >>>>> Download Now

    >>>>> Download Full

    [아래로 _ ]: [C/C++] Qt Qserialport를 활용한 Pc와 아두이노 간의 시리얼 통신 >>>>> Download LINK

    >>>>> Download Now

    [아래로 _ ]: [C/C++] Qt Qserialport를 활용한 Pc와 아두이노 간의 시리얼 통신 >>>>> Download Full

    >>>>> Download LINK

    답글삭제