2015년 10월 8일 목요일

[C/C++] Qt QTcpSocket을 활용한 Android 스마트폰으로 WiFi 모듈 컨트롤하기 (Arduino, ESP8266)




Download link :


ESP8266 firmware bin file Google drive

ESP8266 firmware downloader

ESP8266 AT command set docs

Qt Android application

Arduino ESP8266 code




  이번에는 Qt 프레임워크에서 소켓통신을 활용한 선풍기 풍량 제어, 온도 데이터 습득

등을 구현해보겠습니다. 단순히 소켓통신을 보여드리려면 'VMware', 'Virtualbox'와 같은

가상머신에 다른 OS를 설치하고, IP와 포트번호를 거쳐 통신하는 것을 예로 볼 수 있습니다.

여기에 양념을 약간? 더해서 'ESP8266'이라는 WiFi 모듈을 아두이노에 연결해 스마트폰에

올린 Qt 안드로이드 앱으로 선풍기를 컨트롤하고, 온도센서로부터 온도를 읽어오는 앱을

만들어보았습니다.



  Qt가 리눅스, 윈도우즈, 맥을 넘어서 안드로이드 어플리케이션 제작까지 할 수 있는

장점을 들여다 볼 수 있습니다. 만든 프로그램이 기본 PC 컴파일러와 매칭되면

그림판과 같은 PC 실행 프로그램이 되고, 소스코드의 수정없이 안드로이드 컴파일러에

묶어주면 안드로이드 어플리케이션이 됩니다. OS를 대륙이라고 한다면, Qt라는 대양

항해선의 가치를 실감하실 수 있습니다.


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











                                 - 왼쪽부터 'SM130' RFID 모듈, 'GT511' 지문인식기



  시중의 RFID 태그 인식기(지하철 요금계산), 지문인식기, WiFi 모듈 등은 UART, SPI

통신 프로토콜을 활용해 PC 또는 아두이노로 제어할 수 있습니다. 이때,


'내가 10cm 거리 안에 갖다댄 교통카드의 요금을 읽어라.'    ex) 0x55

'주변에 널린 와이파이 SSID를 알려줘.'                            ex) AT+CWLAP


'0x55', 'AT+CWLAP'와 같은 명령어는 이미 모듈이 공장에서 만들어질때 들어간

펌웨어에 들어가있는 규칙입니다. 아두이노는 모듈에 명령어를 전송하고 결과를 받도록

전원과 통신선을 연결해서, 사용자가 명령어를 쓰는 프로그램을 만들어서 사용하게

해주는 중개인이 됩니다.

  물론, USB - Serial 컨버터를 통해 PC 프로그램으로도 명령할 수 있습니다. 모듈을

이미 특정 기능을 위해 만들어진 아두이노로 볼 수 있습니다. 이들을 조합하는 것은

만화영화의 로봇 합체와 같습니다. 각 로봇이 독립적인 인격(프로세서)이 있죠.






  오늘 활용할 모듈은 'ESP8266 - 01'이라는 WiFi 모듈입니다. 옥션 같은 인터넷 마켓에서

6000원 가량에 판매되고 있습니다. 이 모듈은 형제도 많은데, 문제는 펌웨어도 다양하다는

것이죠. 펌웨어 업데이트는 USB-Serial 컨버터가 필요합니다. 최신버전의 펌웨어 바이너리

파일은 위의 링크 사이트에 들어가서 받으실 수 있습니다. 저는 0.9.5.0 버전을 받았습니다.

업데이트 프로그램은 여러 곳에서 구할 수 있는데, 제가 사용하는 다운로더를 직접 첨부해

놓았습니다.







  다음으로 다운로더를 실행해서, USB-Serial 컨버터가 연결된 컴포트 입력과 바이너리

펌웨어 파일을 선택해줍니다.





  아래와 같이 컨버터와 ESP8266을 연결하고 'Download' 버튼을 누르면 깜빡 거리면서

최신 펌웨어를 업데이트 합니다. 펌웨어 업데이트는 ESP8266의 'GPIO 0' 핀에 그라운드를

연결해주는 점만 추가됩니다.









  이제 아두이노 회로를 구성할 차례입니다. 가능한 다양한 동작을 보이기 위해 조금 복잡한

회로를 구성했습니다. 필요한 소자만 장착해서 쓰시는게 좋을 것 같습니다.




  우선 아두이노와 안드로이드 폰 사이를 WiFi 중계해주는 ESP8266 모듈이 있습니다.

아두이노는 스마트폰으로부터 메시지가 올 때마다 LM35 온도 센서가 읽은 현재

섭씨 온도를 전송해줍니다. 2개의 Push button 또한 아두이노가 스위치의 눌러짐을

판단해, 안드로이드에 전송해주는 기능을 수행합니다. 2개의 LED는 반대로 스마트폰이

지시한 대로 1초간 불이 밝힙니다. 아두이노는 500Hz의 PWM 신호를 내보낼 수

있습니다. 하지만 아두이노 우노의 디지털 핀출력은 수십 mA의 낮은 전류만 출력하는

한계가 있습니다. 그래서 외부전원을 스위칭해주는 MOSFET으로 미니 선풍기의 전원을

제어했습니다. MOSFET(금속-산화층-반도체 전계효과 트랜지스터)은 BJT(양극성 접합

트랜지스터)보다 반응속도가 빠르고 전원제어에 효율적이라고 합니다. 여기서 모스펫은

아두이노 PWM 3번 핀의 작은 전류를 컴퓨터 USB 포트의 높은 전원으로 스위칭해 줍니다.



  여느 포스팅처럼 프로그램의 소스코드를 상단에 링크해두었습니다. 여기서는 Qt

안드로이드 앱과 아두이노 소스코드에서 눈길을 둬야할 영역만 정리해보겠습니다.

혹시 의문점이 있으시다면 댓글로 달아주세요. 먼저 아두이노 소스코드를 살펴보겠습니다.

ESP8266에 명령할 수 있는 AT 커맨드 문서는 위의 다운로드 링크에 나와있습니다.






  'init_wifi()' 함수는 WiFi 모듈을 공유기와 같은 서버모드로 설정합니다. 'SoftwareSerial'인

'mySerial'은 ESP8266 모듈에 연결된 시리얼 객체입니다. 여기에 'write()'를 사용해서

해당 메시지를 ESP8266 모듈에 전송합니다. '0x0A'는 아스키코드의 'Line Feed', '0x0D'는

아스키코드의 'Carriage Return'에 해당합니다. 각각 다음 라인으로 이동, 줄의 첫줄로

이동하는 명령으로, 즉 키보드로 Enter 키를 칠 때의 그 효과입니다. 실제 ESP8266 모듈은

메시지를 Enter 키와 함께 받아야 제대로 인식하기에, 메시지의 끝마다 Enter 키 효과를

주고 있습니다. 이 메시지가 ESP8266에 내리는 AT Command 명령을 살펴보면, 스마트폰에

뜰 SSID 명은 'LSS', 비밀번호 10자리는 '0123456789', 채널은 '11', 비밀번호 비활성화 '0'를

하고, 서버를 오픈했을 때 '3002'번 포트를 개방하도록 하고 있습니다. 안드로이드 스마트폰

앱에서는 소켓 프로그램을 짤 때 저 3002번 포트에 연결하도록 해야합니다.







  아두이노 루프문의 시작입니다. 'SoftwareSerial'인 'mySerial'은 ESP8266 모듈과 연결되어

스마트폰으로부터 들어오는 바이트 신호를 감지합니다. 이 신호가 들어왔는데 '*' 기호로

시작한다면, 바로 다음 바이트를 'parsingbuf'에 담습니다. 그리고 스마트폰에 응답할

메시지를 조립하는 'send_state()' 함수를 호출합니다. '*' 기호는 선풍기의 풍량을 제어할

PWM 값을 바꾸겠다는 의미이고, 그 다음 바이트는 선풍기의 풍량 세기입니다. 수신 메시지

중에 'X' 또는 'Y'가 있다면 해당 LED 중 하나를 1초간 점등하고 분기를 'send_state()' 함수에

넘깁니다.





  아두이노 프로그램의 핵이 담긴 'send_state()' 함수입니다. 스마트폰으로부터 받은

선풍기의 PWM 값 parsingbuf에 2.58을 곱해 선풍기 제어선으로 보내고 있습니다.

2.58을 곱하는 이유는 스마트폰에서 슬라이더를 조작해 보내는 최대값이 99인데,

여기에 2.58을 곱해야 PWM 최댓값인 255 (255.42)가 맞춰집니다. 'tmp' 변수는 온도센서가

읽어들린 온도를 저장한 뒤 섭씨로 변환됩니다. 스마트폰에 메시지를 돌려줄 때는 아스키

코드 37에 해당하는 '%' 기호를 시작으로 주었습니다. 다음으로 온도데이터, 두 Push

button의 누름 상태를 배열에 담아 스마트폰에 응답으로 돌려줍니다. 이때 Push button이

눌리면 논리값이 1, 안 눌리면 0인데, 여기에 1을 더해준 이유는 숫자 0 자체가 아스키코드

'NULL'에 해당해 메시지를 깨뜨리기 때문입니다. 이때문에 스마트폰에서도 1과 2로써

버튼의 누름을 판단합니다. 모니터에 보이는 숫자 "0"은 아스키코드 48에 해당합니다.




  Qt 안드로이드로 제작할 앱의 인터페이스는 아래와 같습니다. 안드로이드 앱을 PC용

컴파일러로 실행했습니다. 안드로이드 개발환경으로, 안드로이드 키트를 통해 컴파일하면

모니터가 아닌, PC와 연결된 스마트폰에 어플리케이션이 업로드됩니다.




  위 인터페이스는 UI를 드래그해서 디자인 한 것이 아닌, 바둑판처럼 2차원 배열에 각각

자리를 주어 나타냈습니다. Qt 프로젝트를 받아보시면 '.ui' 파일은 텅 비어있는 것을 확인할

수 있습니다. 이유는 모르겠지만, Qt에서 UI 디자이너를 통해서 만들어진 객체는 시그널과

슬롯을 연결할 때 오류가 뜬 적이 있습니다. 그후로는 코드로 직접 인터페이스를 채워넣고

있습니다. UI 디자이너에서도 'Grid Layout'을 선택해 바둑판처럼 그릴 수 있긴 합니다.

'Grid Layout' 등의 레이아웃을 사용하면, 스마트폰마다 다른 해상도 때문에 인터페이스가

깨지는 문제를 예방할 수 있습니다.


  '연결'은 안드로이드 자체의 WiFi 연결을 활용해 아누이노와 연결된 ESP8266 모듈과

연결하는 버튼입니다. '연결해제'는 소켓을 닫아서 연결을 해제합니다. 연결 버튼 아래의

'QProgressBar'는 아두이노가 보낸 온도를 나타냅니다. 오른쪽의 'QSlider'는 사용자가

아두이노에 연결된 선풍기의 PWM 값을 조작할 수 있게 합니다. 마지막 'QPushButton'은

이름 그대로 문자 바이트 하나씩을 전송해 아두이노가 그에 맞는 처리를 수행하게 됩니다.

여기서는 LED 2개 각각을 1초간 점등하게 됩니다. 중앙의 'QLabel'은 아두이노에 Push

button이 눌러졌는지 여부를 전송받아 상태를 출력합니다.




  아래처럼 Qt Android의 옵션에서 JDK, SDK, NDK를 설치하고 경로를 잡아주면

프로젝트에서 안드로이드 키트를 사용할 수 있습니다. SDK는 다운로드 후 사용자의

스마트폰 안드로이드 버전에 맞는 API를 설치해주어야 합니다. 저의 경우는 '4.4.2'

버전이었습니다. SDK는 JAVA 언어를 사용하는 안드로이드 스튜디오와 같이 사용할 수

있습니다.













  가장 먼저 'QTcpSocket' 라이브러리를 사용하기 위해서, 프로젝트 파일에

'network' 기능을 포함시켜야 합니다.





  생성자에서 헤더파일에 생성된 인터페이스 객체들을 설정하고 추가합니다. 첫번째로

메인 위젯을 중앙위젯으로 설정하고, Grid Layout을 위젯의 레이아웃으로 설정합니다.

그리고 이 레이아웃에 인터페이스를 하나씩 채워넣습니다.




  소켓 또한 동적으로 생성하고, Push button을 누를 때의 동작, 슬라이더를 변화시킬 때의

동작을 시그널-슬롯으로 묶습니다. 시그널-슬롯에 대한 개념은 저의 이전 포스팅에서

설명하고 있습니다.

                                               Qt QSerialPort를 활용한 PC와 아두이노 간의 시리얼 통신






  버튼을 누르거나, 슬라이더를 움직일 때의 동작입니다. 'X' 전송 버튼을 누르면 소켓에

X를 전송하고, 아두이노가 현재 온도를 알려주면 'bar_Moving()' 함수가 온도를 스마트폰

화면에 갱신합니다. 반대로 선풍기 풍량을 올리기 위해 슬라이더를 움직이면,

'slider_Moved()' 함수가 발동해 현재 슬라이더의 눈금을 '*' 기호와 함께 전송합니다.

아두이노에서 '*' 기호로 선풍기 PWM 값을 변화시킨다는 것을 보여드렸습니다.





  '연결', '연결해제' 버튼에 해당하는 동작입니다. 펌웨어를 특별히 수정하지 않으셨다면,

ESP8266의 IP는 서버로서, 항상 '192.168.4.1'이 됩니다. 포트번호 '3002'는 아두이노가

ESP8266 모듈에 명령할 때 이 포트번호로 서버모드가 되라고 제가 내려두었던 값입니다.






   아두이노가 보낸 응답을 처리하는 슬롯 함수입니다. 일단 소켓으로 전송받은 데이터를

'readAll()' 함수를 통해 'read_Data'라는 'QByteArray' 객체에 저장하고 있습니다.

qDebug()는 제가 디버깅을 위해 그 값을 Qt 실행창에서 확인하도록 넣은 처리입니다.

아두이노의 응답은 아스키코드 37에 해당하는 '%' 기호가 그 시작이었습니다.

'%' 기호를 읽었다면 그 다음 바이트가 온도 데이터이기에, 이를 'bar_Moving()' 함수에

넘겨주어 온도를 화면에서 갱신시키도록 했습니다. 아래에는 Push button을 통해

아두이노에 신호가 들어왔는지 갱신하고 있습니다.



  실제 동작하는 과정을 동영상으로 담았습니다.






<아두이노 소스코드 작성시 주의점>



  아두이노는 신호가 있는지, 없는지만 판단합니다. 따라서 ESP8266 모듈로부터 신호가

있다면 무조건 'readbuf'에 담게 됩니다. 위의 이미지처럼, ESP8266 모듈이 사용자가 보낸

메시지를 그대로 응답하는 등, 잡다한 메시지들이 보입니다. 만약 사용자가 'P'를

스마트폰으로부터 받은 메시지의 시작으로 정한다면, 의도치 않게 'IPD'라는 무조건 뜨는

메시지 때문에 프로그램이 엉뚱하게 반응하겠죠? 그래서 저는 '*' 같이 기본 메시지에

포함되지 않는 겉도는 문자를 사용했습니다.


  이런 점이 아두이노가 ESP8266 모듈을 통해, 일반 PC의 소켓통신을 흉내내는 데서

나오는 가장 큰 문제점입니다. 일반 PC의 소켓통신은 받을 것만 받고, 줄 것만 주기에

깔끔한 구현이 가능합니다. 라즈베리파이처럼 리눅스 정도의 OS가 돌아가는 모듈이

좀더 저렴해지고, 좀더 소형화된다면 제가 소개한 아두이노와 ESP8266 모듈의 조합은

불필요할 것입니다.

댓글 2개:

  1. Thanks a lot for your work. It's very helpful and instructive

    답글삭제
    답글
    1. Thank you for your comment ^_^.

      Your comment is the fuel that makes me write.

      삭제