Qt 5.12.12 Arm 타겟 빌드 (Win32 호스트)

참조사항

Qt 버전별 툴체인 권장버전: https://wiki.qt.io/MinGW
MinGW-w64의 스레드 모델, 예외 처리 모델의 자세한 내용: http://blog.tcltk.co.kr/?p=3836

필요환경

Perl: https://strawberryperl.com/releases.html
Python: https://www.python.org/downloads/
MinGW: https://sourceforge.net/projects/mingw-w64/files/
GCC (Raspberry Pi): https://gnutoolchains.com/raspberry/

Perl이나 Python이 이미 시스템에 설치되어 있다면 환경변수에 경로가 설정되어 있는지 확인하여 그대로 사용하면 되며, 시스템에 설치하지 않고 Qt 빌드에 잠깐 사용을 원한다면 Perl은 Portable Edition, Python 3는 Embeddable Package로 받으면 된다.

이 글에서 압축을 푸는 위치는 D:\OpenSource를 기준으로 한다. 단 Python은 압축 파일 내 폴더없이 압축되어 있으므로 압축 해제 시 ‘여기에 압축 풀기’가 아닌 ‘python-3.9.9-embed-amd64에 압축풀기’와 같은 방법으로 해제한다.

환경변수

set PATH=D:\OpenSource\mingw64\bin;D:\OpenSource\strawberry-perl-5.32.1.1-64bit-portable\perl\bin;D:\OpenSource\python-3.9.9-embed-amd64;%PATH%

Perl 5.32.1.1 64bit Portable Edition, Python 3.9.9 Embeddable Package, MinGW 는 참조사항의 링크 내용을 따라서 버전을 선택하면 되며 현재 글에서 사용한 컴파일러 posix보다는 더 나은 퍼포먼스를 위해 x86_64-win32-seh 사용, GCC (Raspberry Pi)는 대상 OS에 맞는 버전으로 사용.

참고: 윈 10 최근 버전은 python 명령이 스토어에 연결된 단축 명령 형식으로 재공되므로 원하지 않는 오류가 발생할 수 있다. 이러한 경우가 발생하는것을 원하지 않는다면 시작 – 설정 실행, 앱 항목에서 ‘앱 및 기능’ 레이블의 하단 ‘앱 실행 별칭’ 링크로 이동하여 ‘앱 설치 관리자’항목으로 지정된 python.exe와 python3.exe 항목을 ‘끔’으로 변경하면 별칭과의 충돌을 피할 수 있다.

빌드설정

..\qt-everywhere-src-5.12.12\configure -opensource -confirm-license -platform win32-g++ -device linux-rasp-pi3-g++ -device-option CROSS_COMPILE=C:\SysGCC\raspberry\bin\arm-linux-gnueabihf- -sysroot C:/SysGCC/raspberry/arm-linux-gnueabihf/sysroot -prefix /opt/Qt5.12.12 -extprefix D:\OpenSource\Qt5.12.12.gcc-arm -hostprefix D:\OpenSource\Qt5.12.12.gcc-arm -release -eglfs -opengl es2 -no-use-gold-linker -nomake examples -nomake tests -skip qtscript

shadow 빌드를 기준으로 한다. (설정이 잘못되거나 처음부터 다시 빌드해야 할 경우 빌드 폴더를 지우고 다시 폴더를 만들어서 진행하면 make distclean보다 훨씬 깔끔하게 다시 빌드할 수 있기때문이다.)

Qt 소스경로: D:\OpenSource\qt-everywhere-src-5.12
Qt빌드경로: D:\OpenSource\Qt5.12.12.gcc-arm.build
Qt설치경로: D:\OpenSource\Qt5.12.12.gcc-arm

gold 링커가 더 성능 좋다고하지만 MinGW 쉘 환경이 아니라면 링커가 실행되지 않으므로 -no-use-gold-linker 옵션을 추가, device 값은 linux-rasp-pi3-vc4-g++도 있으나 손봐야 하는 환경도 있고 타겟 보드에 Qt를 빌드하여 넣는 것이 아닌 윈도에서 타겟 보드에서 실행할 프로그램을 크로스 빌드 하기위한 Qt 이므로 별 의미 없다.

examples는 필요한 예제를 나중에 따로 빌드해도 되고 꽤나 시간을 많이 잡아먹기때문에 nomake 처리하였고, 크로스 빌드에선 tests를 빌드하는지 모르겠지만 테스트도 보통 시간을 많이 잡아먹기 때문에 일단 nomake에 추가.

sysroot 옵션은 VideoCore의 그래픽 가속 라이브러리 링크를 위해서이다. 이 옵션이 없으면 OpenGL ES 인터페이스가 비활성화된다. X Window를 사용하지않고 eglfs를 사용한다면 필수.

sysroot 옵션을 지정할 경우 install 경로가 $sysroot/$prefix 으로 되므로 prefix는 임의로 /opt/Qt5.12.12 로 지정하였다. 실제 설치위치는 extprefix와 hostprefix로 지정한다. (extprefix는 라이브러리, hostprefix는 qmake등 호스트 레벨의 실행파일의 설치 위치)

device 옵션으로 pi3를 지정하면 이곳의 글 6번 항목 내용처럼 qtscript 모듈에서 빌드 오류가 뜬다. 쓰지않으니 skip 처리하였다.

빌드 및 바이너리 설치

아래와같이 명령어를 입력하면 된다. j옵션의 숫자는 CPU 스레드 수 또는 +1 정도면 적당하다.

mingw32-make -j6
mingw32-make install

DLL 복사

G++로 빌드된 바이너리는 libstdc++이 필요하다. 해당 런타임은 D:\OpenSource\mingw64\bin 폴더에서 libgcc_s_seh-1.dll, libstdc++-6.dll 두 파일을 D:\OpenSource\Qt5.12.12.gcc-arm\bin에 복사하면 된다. posix 모델을 사용하는 MinGW를 사용하였으면 libwinpthread-1.dll 파일도 필요할것으로 보인다.

Continue reading Qt 5.12.12 Arm 타겟 빌드 (Win32 호스트)

IR 송수신 모듈 (YS-IRTM)

우리나라 쇼핑몰에서는 S148 IR이라고 붙어있는곳도 있다.

IR 신호의 인코딩/디코딩을 해주기때문에 편리할거같아서 샀지만 NEC 포맷을 지키지않는 제품이 의외로 많은거같다. LG 에어컨, 선풍기리모컨이 그러한 경우. 덕분에 신호를 디코딩하지 못한다(…). NEC 포맷와 일치하지않아도 8비트 체계로 데이터가 들어오면 포맷이 맞지않아도 출력해준다면 좋았을텐데 아쉽다.

본래는 집 밖에서 에어컨을 키고 들어가기위해 뭐 좀 만들어보고 싶었는데 아무레도 송신모듈, 수신모듈, 아두이노를 써야할지도 모르겠다는 생각이 든다.

결론은 리모컨을 편하게 만들어보려했지만 실패.

Qt5 (5.11.2 기준) MySQL 함께 빌드

1. MySQL 홈페이지에서 MySQL 서버 다운로드
https://dev.mysql.com/downloads/windows/installer/8.0.html
Connector/C++은 Qt의 SQL 플러그인에서 지원안하며, Connector/C는 서버 패키지에 포함되어있다… 다른건 다 개별로 배포하면서 C 라이브러리는 서버 패키지에서 설치하도록 변경되었는지는 이유를 모르겠다.

2. Connector/C 설치
Custom 설치로 선택 후 아래와같이 트리뷰를 펼치면 x64, x86 두가지 아키텍처가 있으며 이 중 한가지만 설치가능하다(…)

필요한 아키텍처를 선탁하여 설치하면 각각 아래 경로에 설치된다. (8버전 기준)
x86: C:\Program Files (x86)\MySQL\MySQL Connector C 6.1
x64: C:\Program Files\MySQL\MySQL Connector C 6.1
폴더가 서로 다른데 왜 둘 다 설치 못하는지는 미지수.

3. 필수 유틸 설치
Python 2.7버전, 그리고 펄을 다운받아 설치한다. 설치 후 콘솔에서 python, perl 실행해보고 INCLUDE, LIB 변수 값 수정.

4. 환경변수 설정
명령 도구모음 x86 또는 x64를 실행한 후 아래와같이 실행하여 환경변수에 추가.
x86:
set INCLUDE=%INCLUDE%;C:\Program Files (x86)\MySQL\MySQL Connector C 6.1\include
set LIB=%LIB%;C:\Program Files (x86)\MySQL\MySQL Connector C 6.1\lib

x64:
set INCLUDE=%INCLUDE%;C:\Program Files\MySQL\MySQL Connector C 6.1\include
set LIB=%LIB%;C:\Program Files\MySQL\MySQL Connector C 6.1\lib

5. 빌드 준비
configure -opensource -confirm-license -prefix D:\OpenSource\Qt5.11.2.x86 -nomake examples -nomake tests -opengl dynamic -mediaplayer-backend wmf -force-debug-info
소스를 압축푼 후 해당 경로에서 위와같이 입력한다.
D:\OpenSource
– Qt5.11.2.x86.build
– qt-everywhere-src-5.11.2
이와같은 구조라면 ..\qt-everywhere-src-5.11.2\configure -opensource…. 와 같이 입력한다.
x86과 x64 타겟 둘 다 빌드하려면 이렇게 쉐도우빌드하는게 좋다. (이미 빌드, configure 완료 된 설정과 충돌이 발생하지 않기때문.) 또한 prefix를 지정하면 빌드 완료 후 nmake install하여 헤더와 바이너리파일을 소스와 오브젝트 파일 따로 둘 수 있다.

아래는 MySQL을 포함 한 configure 결과내용.

Configure summary:

Build type: win32-msvc (x86_64, CPU features: sse sse2)
Configuration: sse2 aesni sse3 ssse3 sse4_1 sse4_2 avx avx2 avx512f avx512bw avx512cd avx512dq avx512er avx512ifma avx512pf avx512vbmi avx512vl compile_examples f16c force_debug_info largefile precompile_header rdrnd shani x86SimdAlways shared debug_and_release release debug build_all c++11 c++14 c++1z concurrent dbus no-pkg-config release_tools stl
Build options:
  Mode ................................... debug and release (with debug info); default link: debug; optimized tools

... 중략 ...

Qt Gui:
  Accessibility .......................... yes
  FreeType ............................... yes
    Using system FreeType ................ no
  HarfBuzz ............................... yes
    Using system HarfBuzz ................ no
  Fontconfig ............................. no
  Image formats:
    GIF .................................. yes
    ICO .................................. yes
    JPEG ................................. yes
      Using system libjpeg ............... no
    PNG .................................. yes
      Using system libpng ................ no
  EGL .................................... yes
  OpenVG ................................. no
  OpenGL:
    ANGLE ................................ yes
    Combined ANGLE Library ............... no
    Desktop OpenGL ....................... no
    Dynamic OpenGL ....................... yes
    OpenGL ES 2.0 ........................ no
    OpenGL ES 3.0 ........................ no
    OpenGL ES 3.1 ........................ no
    OpenGL ES 3.2 ........................ no
  Vulkan ................................. no
  Session Management ..................... yes

... 중략 ...

Qt Sql:
  DB2 (IBM) .............................. no
  InterBase .............................. no
  MySql .................................. yes
  OCI (Oracle) ........................... no
  ODBC ................................... yes
  PostgreSQL ............................. no
  SQLite2 ................................ no
  SQLite ................................. yes
    Using system provided SQLite ......... no
  TDS (Sybase) ........................... no

... 중략 ...

Qt Multimedia:
  ALSA ................................... no
  GStreamer 1.0 .......................... no
  GStreamer 0.10 ......................... no
  Video for Linux ........................ no
  OpenAL ................................. no
  PulseAudio ............................. no
  Resource Policy (libresourceqt5) ....... no
  Windows Audio Services ................. yes
  DirectShow ............................. yes
  Windows Media Foundation ............... yes
  Media player backend ................... Windows Media Foundation
Qt WebEngine:
  Embedded build ......................... no
  Pepper Plugins ......................... yes
  Printing and PDF ....................... yes
  Proprietary Codecs ..................... no
  Spellchecker ........................... yes
  Native Spellchecker .................... no
  WebRTC ................................. yes
  Use System Ninja ....................... no
  Geolocation ............................ yes
  Use v8 snapshot ........................ yes

Note: No wayland-egl support detected. Cross-toolkit compatibility disabled.

Qt is now configured for building. Just run 'nmake'.
Once everything is built, you must run 'nmake install'.
Qt will be installed into 'D:\OpenSource\Qt5.11.2.x64'.

Prior to reconfiguration, make sure you remove any leftovers from
the previous build.

만약 MySQL 부분이 활성화 안되어 환경설정 후 그 부분만 다시 체크되도록 하려면 config.cache 파일을 메모장에서 열어 아래와 같이 cache.mysql로 시작하는 라인을 전부 지우고 다시 configure하면 된다.

cache.mysql._KEYS_ = result msgs source sources.5.libs sources.5.includedir sources.5.cflags sources.5.version sources.5.export
cache.mysql.result = true
cache.mysql.msgs = "Trying source 0 (type mysqlConfig) of library mysql ..." "mysql_config not found." "  => source produced no result." "Trying source 1 (type mysqlConfig) of library mysql ..." "mysql_config not found." "  => source produced no result." "Trying source 2 (type mysqlConfig) of library mysql ..." "mysql_config not found." "  => source produced no result." "Trying source 3 (type mysqlConfig) of library mysql ..." "mysql_config not found." "  => source produced no result." "Trying source 4 (type inline) of library mysql ..." "  => source failed condition \'!config.win32\'." "Trying source 5 (type inline) of library mysql ..." "+ cd /d D:\\OpenSource\\Qt5.11.2.x86.build\\config.tests\\mysql && D:\\OpenSource\\Qt5.11.2.x86.build\\qtbase\\bin\\qmake.exe \"CONFIG -= qt debug_and_release app_bundle lib_bundle\" \"CONFIG += shared warn_off console single_arch\" \"LIBS += -llibmysql\" D:/OpenSource/Qt5.11.2.x86.build/config.tests/mysql" "+ cd /d D:\\OpenSource\\Qt5.11.2.x86.build\\config.tests\\mysql && set MAKEFLAGS=& nmake" "> Microsoft(R) Program Maintenance Utility ���� 14.16.27024.1" "> Copyright (c) Microsoft Corporation. All rights reserved." "> $$escape_expand(\\t)cl -c -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W0 -EHsc /Fdmysql.vc.pdb -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -I. -ID:\\OpenSource\\qt-everywhere-src-5.11.2\\qtbase\\mkspecs\\win32-msvc -Fo @C:\\Users\\star\\AppData\\Local\\Temp\\nm8248.tmp" "> main.cpp" "> $$escape_expand(\\t)link /NOLOGO /DYNAMICBASE /NXCOMPAT /DEBUG /SUBSYSTEM:CONSOLE \"/MANIFESTDEPENDENCY:type=\'win32\' name=\'Microsoft.Windows.Common-Controls\' version=\'6.0.0.0\' publicKeyToken=\'6595b64144ccf1df\' language=\'*\' processorArchitecture=\'*\'\" /MANIFEST:embed /OUT:mysql.exe @C:\\Users\\star\\AppData\\Local\\Temp\\nm869F.tmp" " => source accepted."
cache.mysql.source = 5
cache.mysql.sources.5.libs = -llibmysql
cache.mysql.sources.5.includedir = 
cache.mysql.sources.5.cflags = 
cache.mysql.sources.5.version = 
cache.mysql.sources.5.export = 

6. 빌드
qt에서 제공하는 jom을 쓴다면 jom, 아니라면 nmake 명령으로 빌드. 주위할 점으로 시스템 로케일을 영어(미국)으로 하는게 좋다. 웹킷은 경고도 에러로 처리하고 이 편이 훨씬 깔끔하게 한번에 빌드된다.

7. 기타
귀차니즘 덕분에 이정도로 마무리. 더 자세한 내용은 이곳 참조.

libmodbus 3.1.4 분석내용

보통의 plc 기반 modbus rtu는 2 wire RS-485 방식을 사용하는것으로 보인다.

RS-485가 병렬연결 방식이라 모든 장치에 메시지를 전송할 수 있다는것이 장점인데 modbus 프로토콜은 명확하게 구분되는 STX와 ETX가 없다. 이렇다보니 master가되는 서버가 1번 아이디의 장치로 데이터를 요청하여 1번 아이디의 장치가 응답을 하였을경우 2번 아이디의 장치의 시리얼에 수신되는 데이터는 1번 아이디의 장치 요청데이터, 1번 아이디 장치의 응답데이터이다.

즉, 1번 장치에 요청되고 응답 되었을경우 아래처럼 2번장치에 수신된다. (read holding registers 기준)
[SLAVE ID][FUNCTION CODE][START][QUANTITY][CHECK SUM][SLAVE ID][FUNCTION CODE][BYTES][UINT16 DATA 1][UINT16 DATA 2]…[UINT16 DATA n][CHECK SUM]

STX와 ETX가 없다보니 2번 장치에서 자신의 요청이 아닌 1번 응답을 무시하더라도 바로 뒤에 붙어오는 데이터를 읽게되면 ‘1번 장치의 holding register의 데이터 x주소에서부터 y만큼의 데이터 요청’으로 해석되어진다. 하지만 응답되는 데이터의 길이가 더 크기때문에 이후에 또 읽게되면 UINT16 DATA 부분이 읽혀지게되고 이후 파싱이 안된다.

libmodbus는 rtu일경우 기본적으로 요청 -> 응답 순으로 데이터가 수신되는 조건으로 만들어져있다. 즉, 처음 modbus_receive(3)를 호출하면 데이터 요청, 그다음 modbus_receive(3)을 호출하면 데이터 응답인것으로 수신한다. 따라서 처음 modbus_receive(3) 호출 시 [SLAVE ID][FUNCTION CODE][START][QUANTITY][CHECK SUM]으로 데이터를 읽고 자신에 대한 요청이 아닐경우 데이터 응답을 읽도록 상태가 변경되어 다음 modbus_receive(3) 호출 시 응답 데이터인 [SLAVE ID][FUNCTION CODE][BYTES][UINT16 DATA 1][UINT16 DATA 2]…[UINT16 DATA n][CHECK SUM] 형식으로 읽게되어 완전하게 읽은 후 데이터 요청을 읽도록 상태를 변경한 후 읽은 데이터는 버리도록 한다.

modbus_receive(3)을 호출하면 처음 [SLAVE ID][FUNCTION CODE]를 읽기까지 receive timeout값(기본 0.5초)만큼 버퍼의 데이터를 대기하고 이후 나머지 데이터는 byte timeout값(기본 0.5초)만큼 버퍼의 데이터를 대기한다. 만약 receive timeout값을 5초로 주어지고 slave id가 2번으로 설정되어있을 때 1번 장치에 대한 요청이 modbus_receive(3) 호출하여 읽혀지면 응답 데이터를 읽도록 상태가 변경되어있기때문에 5초 이내 master에서 2번, 또는 다른 장치에 대한 데이터 요청이 발생하면 데이터 응답이 수신되지 않기때문에 에러 즉, -1이 반환된다. (이후 다시 modbus_receive(3)을 호출하면 정상으로 다시 데이터 요청을 읽을지 테스트 하지는 않았다.)

이런 상태를 보면 modbus rtu는 master와 slave간의 요청 응답의 타임아웃을 서로 여유를 두고 잘 정해야 할것으로 보인다.

예전 클라이언트를 만들었을 때 로직은 시리얼 데이터를 읽는 스레드 내 데이터가 수신되면 마지막으로 버퍼에 데이터가 읽힌 시간을 확인하여 0.2초(예시값)이상 지났을경우 버퍼를 비우고 수신된 데이터를 버퍼에 쌓은 후 파싱하고 자신에 대한 요청이 아니면 무시, 자신에 대한 요청이면 응답하는 단순한 구조였다. 첫 바이트가 자신의 아이디와 동일하고 Function Code가 유효하더라도 Check Sum 값이 동일할 경우는 희박하고 데이터 요청 개수(quantity)의 최소, 최대가 이미 정해져있으므로 문제가 발생하지 않았다.

범용적인 상황에서는 libmodbus의 구현이 더 맞겠지만 요청 데이터의 address와 개수의 범위가 이미 정해진 상태에서는 직접 만들었던 방식으로 충분하다고 생각되기때문에 무엇이 더 좋은 방법인지는 모르겠지만 나중에 참고가 필요할 때 참고하기 위해 분석 내용을 기록한다.

여담이지만 그냥 modbus ascii 프로토콜은 STX, ETX가 명확해 보여서 이걸 쓰면 간단할텐데…(?) 아쉽게도 이건 잘 안쓰는거같다.

암튼 이쯤에서 끝!

Qt Multimedia의 QMediaPlayer가 동작 안할 때

여기에서 말하는 문제는 윈도에서의 Qt 문제.

먼저 Backends 별 사용할 수 있는 기능(?)의 표는 아래 링크에서 확인가능.
https://wiki.qt.io/Qt_Multimedia_Backends

Qt 홈페이지에서 받을 수 있는 바이너리 라이브러리는 Multimedia Backends가 DirectShow로 되어있는것으로 보인다. 구글링해도 뾰족한 답을 찾기 힘들었는데 결국 이것도 몇시간동안 삽질해본 결과 재생 안되면 적절한 코덱을 깔아야하고(…) 아니면 Media Foundation을 사용하는 것이였다.

Media Foundation을 사용하려면 Multimedia 라이브러리를 빌드할 때 Media Foundation을 사용하도록 해야한다. 아래는 내가 사용한 qt-everywhere-opensource 패키지의 configure 옵션.

configure -opensource -confirm-license -prefix D:\OpenSource\Qt5.9.0.x64 -nomake examples -nomake tests -force-debug-info -platform win32-msvc -mp -opengl dynamic -mediaplayer-backend wmf

핵심은 -mediaplayer-backend wmf 옵션이다. 이렇게 빌드된것과 기본인 DirectShow를 사용하도록 빌드했을 때 대략 아래와같이 차이가 난다.

– DirectShow

dsengine.dll은 211KB, wmfengine.dll은 42KB

– Media Foundation

dsengine.dll은 63KB, wmfengine.dll은 167KB (qtmedia_audioengined.pdb 파일이 약간 차이있지만 코드에서 backend 별로 메크로 차이가 있는지는 확인 안해보았다.)

Media Foundation은 범용적으로 사용하는 코덱을 지원하므로 프로그램이 윈 7 이상이라면 이쪽을 사용하도록하는것이 더 나아보인다. (자세한 정보는 https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd757927 이곳에서.) 물론 기존의 DirectShow가 더 다양한 코덱이 있을것으로 보기때문에 특정 포맷만 지원할것이 아니라면 DirectShow를 사용하는것이 더 나을 수 있다.

빌드 자체를 남기는 기록이 아니기때문에 여기까지. 만약 빌드 방법을 찾는다면 http://rette.iruis.net/2017/05/qt-5-8-0-webengine-%EB%AA%A8%EB%93%88-%EB%B9%8C%EB%93%9C/ 이 글을 참조.