Dicas de desenvolvimento em Qt no Raspberry

Vou tomar como base o Raspberry Pi, porque tenho 22 anos de experiência com Linux e isso facilita muito as coisas, de modo que tomo meus próprios caminhos na dissolução de problemas e eventualmente preciso pesquisar sobre alguma questão específica. De qualquer modo, é praticamente impossível não precisar fazer pesquisas no desenvolvimento em sistemas embarcados, conforme explicarei no decorrer do artigo sobre Qt no Raspberry.

Atualização do Raspbian

Uso o Raspbian por ser uma compilação de Debian para ARM e em servidores é o sistema que tenho utilizado desde 2006, aproximadamente.

Nem sempre é uma boa ideia atualizar o sistema, pois a base de diversos recursos de sistema podem mudar. Símbolos, comportamentos, recursos que se modificam. Um bom exemplo de um problema que eu não tinha (e que já esperava, devido à experiência anterior) é em relação ao programa que estou desenvolvendo em Qt com QML e QtQuick. Já programo há anos em Qt, mas sempre fui resistente ao modelo MVC (tentarei discorrer a respeito mais adiante).

Se o desenvolvimento já estiver em andamento e alguma necessidade pontual aparecer, pode ser melhor tentar resolver a dependência através do gerenciador de pacotes, usando repositório de software adicional ou compilando o recurso do Qt no Raspberry manualmente. Como eu não tinha o compromisso de prazo, não me incomodei em atualizar o Raspbian de Stretch para Buster. E achei até fundamental, porque a versão padrão do Qt no Stretch é a 5.7.1, enquanto no Buster é a 5.11.3. Como estou tentando fazer um software utilizando os recursos do sistema para simplificar em um artigo (exceto queira realmente fazer cross compile ou compilação nativa) peguei um outro cartão para tentar utilizar o Qt nativo e ver o que ele oferecia. Essa diferença de versão entre uma versão e outra do Raspbian é gritante.

Faça o update

Primeiro passo para uma atualização com sucesso (considere que pode não funcionar em todos os casos) é atualizar os pacotes já instalados. Para tal:

sudo apt-get update
sudo apt-get upgrade

Após atualizar o sistema, reinicie. Ao reiniciar, edite o arquivo de repositórios do apt e modifique stretch para buster. Deve ficar assim:

deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi

Agora, um novo update é necessário e após isso, iniciamos o upgrade do sistema:

sudo apt-get update
sudo apt-get dist-upgrade

Se estiver fazendo a atualização por ssh, pode ocorrer a perda de comunicação por um período variável, tenha paciência.

Alguns serviços de sistema terão seus arquivos de configuração substituídos, mas isso é questionado durante a instalação dos pacotes. Se houver personalização nos respectivos serviços, mantenha a versão já instalada.

Quando terminar a atualização, reinicie. Provavelmente o sistema ainda estará funcionando.

Depuração de funcionalidades

Vou primeiro ao problema que ocorreu.

A atualização de sistema trouxe benefícios e malefícios. Tendo a sugerir o início do desenvolvimento no sistema previamente atualizado, mas alguns recursos importantes não estavam disponíveis na versão padrão do Qt no Raspberry com Raspbian Stretch.

Avalie se os recursos necessários são funcionais

Após a atualização, recursos do programa deixaram de funcionar. A maioria deles, relecionado ao código C++ do programa. O programa sequer iniciava, de cara dava falha de segmentação. E para descobrir?

O primeiro passo foi criar uma nova aplicação com o básico de QML e compilar. Como era um projeto só com a “carcaça”, já serviria para saber se haveria de funcionar o EGLFS, que é o backend que utilizo para rodar o programa sem servidor gráfico. Funcionou, portanto o problema era relacionado a alguma particularidade do meu código – acho.

Use alguma forma de depuração do Qt no Raspberry

Como pegar traços do que pode estar ocorrendo no sistema? Bem, o arquivo main.qml pode ser um ponto de partida. Coloquei “prints” em alguns pontos. Percebi que ele era carregado adequadamente, portanto o problema não era relacionado ao view ou ao model, mas ao controller.

Como meu código C++ tem acesso aos elementos da interface, desabilitei todas as chamadas e posteriormente fui reabilitando. Isso foi fácil porque costumo programar de forma modular, bastando desabilitar uma recurso e o restante do código não é afetado.

Ao desabilitar as chamadas aos objetos da interface, o programa iniciou. Ótimo! É muito mais fácil resolver um problema identificado do que um problema incerto. Mas o touchscreen deixou de funcionar. E agora? É driver? É sistema? É programa?

Para resolver essa questão, é fundamental saber que recursos do sistema o programa utiliza e conhecer esses recurso do sistema. No caso, eu sabia que estava utilizando os eventos gerados pelo sistema em /dev/input/*. O touchscreen é o dispositivo event0 no meu sistema. O dispositivo existia. Mas estava funcionando? Fácil saber:

Desculpe pela transparência no terminal, mas eu gosto.

Repare que utilizei o comando cat nativo do sistema, apontando para o dispositivo /dev/input/event0. Ele fica travado porque é um dispositivo de caractere, o que significa que não tem “final de arquivo”. Os dispositivos em Linux são arquivos descritores, se não tiver esses conceitos, é bom dar uma estudada.

Com esse comando, comprovei que o touchscreen estava funcionando no sistema. Ótimo, meu programa só precisa estar lendo esses eventos. Como saber se o programa está interagindo com os eventos da tela? Para isso, utilizei o comando strace:

strace ./swipe

Como tenho um relógio na tela atualizando segundo a segundo, já sei que a tela não está travada. Sempre coloque um evento visual para ter feedback da aplicação, ainda que seja aplicação final. Nada como olhar para a tela e ter um diagnóstico inicial.

Agora, ao tocar na tela, os eventos de interação do Qt com o sistema deve aparecer na tela. O programa estava recebendo eventos do display, menos mal.

A documentação também diz que é necessário ter as bibliotecas de desenvolvimento da libinput ou tslib, conforme o que for utilizado.

Para adiantar o processo, invés de caçar um a um os problemas, tentei resolver instalando o máximo possível dos pacotes relacionados aos eventos. No caso, a documentação diz que é utilizado o evdev e a libinput, como citei anteriormente. Portanto, fui instalando pacotes supostamente relacionados. Em caso de sistemas que venham a ser produto, claramente é fundamental saber a origem do problema de forma pontual, mas na informalidade de um passatempo, a preguiça fala mais alto. Não precisa reproduzir essa parte, estou apenas mostrando o que fiz:

sudo apt-get install libts-dev libinput-dev

Para tentar usar o evdev, configurei a variável de ambiente que desabilita a libinput:

export QT_QPA_EGLFS_NO_LIBINPUT=1
strace ./swipe

Nada, só funcionou com a libinput, mas isso já ajuda  a perceber que realmente os eventos ocorrem com a libinput. O problema não pode estar relacionado ao touchscreen. Voltei a variável de ambiente para o valor 0 e segui adiante.

Esses testes devem consumir 20 minutos ou um pouco mais. Após as tentativas frustradas, ainda restava a possibilidade de usar um programa dos exemplos. Ao menos esse deveria funcionar e assim garantir que o problema era na minha aplicação. Utilizei o Qt/Examples/Qt-5.13.0/widgets/touch/fingerpaint/, que também não funcionou. Onde estará o problema?

Variáveis de ambiente

Pra saber como está configurado o Qt no Raspberry, podemos usar o comando qtdiag:

libEGL warning: DRI3: failed to query the version
libEGL warning: DRI2: failed to authenticate
Unable to query physical screen size, defaulting to 100 dpi.
To override, set QT_QPA_EGLFS_PHYSICAL_WIDTH and QT_QPA_EGLFS_PHYSICAL_HEIGHT (in millimeters).
Running on a software rasterizer (LLVMpipe), expect limited performance.
This plugin does not support createPlatformVulkanInstance
QVulkanInstance: Failed to initialize Vulkan
Qt 5.11.3 (arm-little_endian-ilp32-eabi-hardfloat shared (dynamic) release build; by GCC 8.3.0) on "eglfs" 
OS: Raspbian GNU/Linux 10 (buster) [linux version 4.19.66-v7+]

Architecture: arm; features: Neon

Environment:
  QT_LOGGING_RULE="qt.qpa.*=true"
  QT_QPA_FB_DRM="0"
  QT_QPA_FB_HIDECURSOR="1"
  QT_QPA_PLATFORM="eglfs:size=800x480"

Features: QT_NO_EXCEPTIONS

Library info:
  PrefixPath: /usr
  DocumentationPath: /usr/share/qt5/doc
  HeadersPath: /usr/include/arm-linux-gnueabihf/qt5
  LibrariesPath: /usr/lib/arm-linux-gnueabihf
  LibraryExecutablesPath: /usr/lib/arm-linux-gnueabihf/qt5/libexec
  BinariesPath: /usr/lib/qt5/bin
  PluginsPath: /usr/lib/arm-linux-gnueabihf/qt5/plugins
  ImportsPath: /usr/lib/arm-linux-gnueabihf/qt5/imports
  Qml2ImportsPath: /usr/lib/arm-linux-gnueabihf/qt5/qml
  ArchDataPath: /usr/lib/arm-linux-gnueabihf/qt5
  DataPath: /usr/share/qt5
  TranslationsPath: /usr/share/qt5/translations
  ExamplesPath: /usr/lib/arm-linux-gnueabihf/qt5/examples
  TestsPath: /usr/tests
  SettingsPath: /etc/xdg

Standard paths [*...* denote writable entry]:
  DesktopLocation: "Desktop" */home/pi/Desktop*
  DocumentsLocation: "Documents" */home/pi/Documents*
  FontsLocation: "Fonts" */home/pi/.local/share/fonts* /home/pi/.fonts
  ApplicationsLocation: "Applications" */home/pi/.local/share/applications* /usr/local/share/applications /usr/share/applications
  MusicLocation: "Music" */home/pi/Music*
  MoviesLocation: "Movies" */home/pi/Videos*
  PicturesLocation: "Pictures" */home/pi/Pictures*
  TempLocation: "Temporary Directory" */tmp*
  HomeLocation: "Home" */home/pi*
  AppLocalDataLocation: "Application Data" */home/pi/.local/share/QtProject/qtdiag* /usr/local/share/QtProject/qtdiag /usr/share/QtProject/qtdiag
  CacheLocation: "Cache" */home/pi/.cache/QtProject/qtdiag*
  GenericDataLocation: "Shared Data" */home/pi/.local/share* /usr/local/share /usr/share
  RuntimeLocation: "Runtime" */run/user/1000*
  ConfigLocation: "Configuration" */home/pi/.config* /etc/xdg
  DownloadLocation: "Download" */home/pi/Downloads*
  GenericCacheLocation: "Shared Cache" */home/pi/.cache*
  GenericConfigLocation: "Shared Configuration" */home/pi/.config* /etc/xdg
  AppDataLocation: "Application Data" */home/pi/.local/share/QtProject/qtdiag* /usr/local/share/QtProject/qtdiag /usr/share/QtProject/qtdiag
  AppConfigLocation: "Application Configuration" */home/pi/.config/QtProject/qtdiag* /etc/xdg/QtProject/qtdiag

File selectors (increasing order of precedence):
  en_US unix linux raspbian

Network:
  Using "OpenSSL 1.1.1c  28 May 2019", version: 0x1010103f

Platform capabilities: ThreadedPixmaps OpenGL ThreadedOpenGL NonFullScreenWindows NativeWidgets RasterGLSurface

Style hints:
  mouseDoubleClickInterval: 400
  mousePressAndHoldInterval: 800
  startDragDistance: 10
  startDragTime: 500
  startDragVelocity: 0
  keyboardInputInterval: 400
  keyboardAutoRepeatRate: 30
  cursorFlashTime: 1000
  showIsFullScreen: 0
  showIsMaximized: 0
  passwordMaskDelay: 0
  passwordMaskCharacter: U+25CF
  fontSmoothingGamma: 1.7
  useRtlExtensions: 0
  setFocusOnTouchRelease: 0
  tabFocusBehavior: Qt::TabFocusBehavior(TabFocusAllControls) 
  singleClickActivation: 0

Additional style hints (QPlatformIntegration):
  ReplayMousePressOutsidePopup: 1

Theme:
  Platforms requested : 
            available : gtk3,qt5ct
  Styles requested    : 
         available    : qt5ct-style,Windows,Fusion
Fonts:
  General font : "DejaVu Sans" 12
  Fixed font   : "DejaVu Sans" 12
  Title font   : "DejaVu Sans" 12
  Smallest font: "DejaVu Sans" 12

Palette:
  WindowText: #ff000000
  Button: #ffefefef
  Light: #ffffffff
  Midlight: #ffcbcbcb
  Dark: #ff9f9f9f
  Mid: #ffb8b8b8
  Text: #ff000000
  BrightText: #ffffffff
  ButtonText: #ff000000
  Base: #ffffffff
  Window: #ffefefef
  Shadow: #ff767676
  Highlight: #ff308cc6
  HighlightedText: #ffffffff
  Link: #ff0000ff
  LinkVisited: #ffff00ff
  AlternateBase: #fff7f7f7
  NoRole: #ff000000
  ToolTipBase: #ffffffdc
  ToolTipText: #ff000000

Screens: 1, High DPI scaling: inactive
# 0 "" Depth: 32 Primary: yes
  Manufacturer: 
  Model: 
  Serial number: 
  Geometry: 2560x2160+0+0 Available: 2560x2160+0+0
  Physical size: 650.24x548.64 mm  Refresh: 60 Hz Power state: 0
  Physical DPI: 100,100 Logical DPI: 100,100 Subpixel_None
  DevicePixelRatio: 1 Pixel density: 1
  Primary orientation: 2 Orientation: 2 Native orientation: 0 OrientationUpdateMask: 0

Touch devices: 1
  TouchScreen "ADS7846 Touchscreen", max 1 touch points, capabilities: Position Area


LibGLES Vendor: VMware, Inc.
Renderer: llvmpipe (LLVM 7.0, 128 bits)
Version: OpenGL ES 3.0 Mesa 18.3.6
Shading language: OpenGL ES GLSL ES 3.00
Format: Version: 3.0 Profile: 0 Swap behavior: 0 Buffer size (RGB): 8,8,8

Unable to create a Vulkan instance, error code is0

Plugins instalados

Ainda analisando o ambiente, onde estão e quais são os plugins disponíveis no sistema? Para descobrir, usamos o comando:

qtpaths --plugin-dir

Daí uma listagem no diretório mostrará algo como:

qt modules

No diretório platforms temos todos os backends suportados, mas chamar o executável do programa com os parâmetros -platform ? retornará essa informação.

Diretório de binários

Utilizando o mesmo programa com o parâmetro –binaries-dir encontramos o diretório de binários usado no sistema, no qual se encontram diversas ferramentas que podem auxiliar em casos específicos:

qtpaths binaries

Instalando pacotes relacionados a tslib

Ainda restaram algumas possibilidades. Instalei alguns pacotes extras:

sudo apt-get install i2c-tools evtest libts-bin libts-dev

O programa evtest serve para confirmar o dispositivo. Após executá-lo, aponte o número para o dispositivo da lista. No caso, 0. Depois é só tocar na tela e ver o resultado. Mas já havia confirmado isso de outras formas anteriormente.

Mais um teste (são apenas opções, uma já é mais que o suficiente para diagnóstico):

sudo TSLIB_FBDEVICE=/dev/fb0 TSLIB_TSDEVICE=/dev/input/event0 ts_print

Compilar a tslib

Ok, até agora nada resolveu o problema. A tslib instalada pelo apt não tem todos os requerimentos necessários para funcionar, por isso resolvi compilar.

git clone git://github.com/kergoth/tslib.git
cd tslib
sudo apt-get install libtool
./autogen.sh
./configure
make
sudo make install
sudo cp -P /usr/local/lib/libts* /usr/lib/arm-linux-gnueabihf/

A compilação será um processo lindo e direto. Não é assim, normalmente.

Quando escrevo artigos, gosto de fazer experimentos primeiro e assim disponibilizar algo realmente funcional. Para chegar nessa compilação, tive diversos problemas que, se os colocasse no artigo, talvez você desistisse da leitura.

O display já estava funcional em relação ao sistema operacional, mas por alguma razão, não o estava no Qt. Compilar a tslib foi uma opção para tentar outra API. Após o processo acima, o primeiro passo foi testar a resposta do display, que virou event3:

evtest /dev/input/event3

 

E ao passar o dedo na tela, a resposta ocorreu normalmente. Esse teste tem uma interface feia, mas o que importa é saber se está funcionando, porque se não estiver no sistema, não estará no Qt:

Qt no Raspberry - tslib console

Os testes mostraram o display responsivo, mas o problema ainda não foi sanado. A tslib tem uma outra ferramenta para fazer o teste gráfico, então percebi que realmente havia um problema com a interface entre o sistema. Ao tocar na tela, o cross-hair sumiu. Os botões não respondiam, logo, outra coisa não estava funcionando adequadamente.

Qt no Raspberry - tslib

Mas será que o primeiro toque tem algum efeito? Bem, reinicie o ts_test e cliquei sobre o botão Drag. Daí apareceu o cursor, lá no final da tela, ou seja, a partir da atualização se fez necessário calibrar o display.

Utilizei o ts_calibrate para ajustar, depois rodei novamente o ts_test. Tudo aparentemente funcionando agora, exceto o programa em Qt.

Nessa hora que se destaca a importância de fazer sua própria compilação. Pelo visto, não tem suporte a libts na compilação padrão do Qt para Raspberry. A libinput está funcionando também, portanto, ainda há um longo trabalho pela frente.

Instalar o Qt no Raspberry

Para continuar o desenvolvimento de onde parei enquanto não resolvo essa questão, instalei o sistema em um novo cartão e em seguida instalei o Qt novamente, na versão 5.7, padrão do Raspbian Stretch. Após acessar o Raspbian:

apt-get install qtmultimedia5-examples qt5-style-plugins qt5-qmltooling-plugins qt5-qmake qt5-image-formats-plugins qt5-default qtwebchannel5-examples qml-module-qtwebsockets qml-module-qtwebsockets qml-module-qtwebkit qml-module-qtwebchannel qml-module-qtsensors qml-module-qttest qml-module-qtsensors qml-module-qtquick2 qml-module-qtquick-xmllistmodel qml-module-qtquick-window2 qml-module-qtquick-templates2 qml-module-qtquick-privatewidgets qml-module-qtquick-particles2 qml-module-qtquick-localstorage qml-module-qtquick-layouts qml-module-qtquick-extras qml-module-qtquick-dialogs qml-module-qtquick-controls2 qml-module-qtquick-controls qml-module-qtqml-models2 qml-module-qtmultimedia qml-module-qtgraphicaleffects qml-module-qtaudioengine qml-module-qt-websockets qml-module-qt-labs-settings qml-module-qt-labs-folderlistmodel qml-module-qt-labs-calendar libqtermwidget5-0 libqtermwidget5-0-dev libqt5xml5 libqt5widgets5 libqt5websockets5-dev libqt5websockets5 libqt5webkit5-dev libqt5webkit5 libqt5webengine-data libqt5webchannel5-dev libqt5webchannel5 libqt5test5 libqt5svg5-dev libqt5svg5 libqt5sql5-sqlite libqt5serialport5-dev libqt5serialport5 libqt5sensors5-dev libqt5sensors5 libqt5scripttools5 libqt5script5 libqt5quickwidgets5 libqt5quicktest5 libqt5quicktemplates2-5 libqt5quickcontrols2-5 libqt5quick5 libqt5qml5 libqt5opengl5-dev libqt5opengl5 libqt5network5 libqt5multimediawidgets5 libqt5multimediaquick-p5 libqt5multimedia5-plugins libqt5multimedia5 qml-module-qtgstreamer qtgstreamer-plugins-qt5 qtquickcontrols2-5-dev qtquickcontrols2-5-examples qtquickcontrols5-examples binfmt-support clang clang-3.8 libbotan-1.10-1 libclang-common-3.8-dev libclang1-3.8 libffi-dev libgc1c2 libllvm3.8 libobjc-6-dev libobjc4 libqbscore1.7 libqbsqtprofilesetup1.7 libqt5clucene5 libqt5designer5 \
  libqt5designercomponents5 libqt5help5 libtinfo-dev llvm-3.8 llvm-3.8-dev \
  llvm-3.8-runtime qmlscene qt-assistant-compat qt3d5-doc qt5-doc \
  qt5serialport-examples qtbase5-doc qtbase5-examples qtconnectivity5-doc \
  qtcreator qtcreator-data qtcreator-doc qtdeclarative5-dev-tools \
  qtdeclarative5-doc qtdeclarative5-examples qtdeclarative5-private-dev \
  qtgraphicaleffects5-doc qtlocation5-doc qtmultimedia5-doc \
  qtquickcontrols2-5-doc qtquickcontrols5-doc qtscript5-doc qtsensors5-doc \
  qtserialport5-doc qtsvg5-doc qttools5-dev qttools5-dev-tools qttools5-doc \
  qttools5-examples qttools5-private-dev qtwayland5-doc qtwebchannel5-doc \
  qtwebengine5-doc qtwebkit5-doc qtwebkit5-examples qtwebkit5-examples-doc \
  qtwebsockets5-doc qtx11extras5-doc qtxmlpatterns5-dev-tools \
  qtxmlpatterns5-doc

Compilar um módulo específico do Qt

Ainda há uma opção extra, caso deseje utilizar algum módulo que não esteja no repositório de pacotes, mas esteja disponível no branch da versão que estiver utilizando.

git clone git://code.qt.io/qt/<modulo>.git -b <versao_do_qt>
cd <modulo>
qmake
make
make install

Substitua a opção modulo pelo módulo que deseja utilizar, e a versão do Qt pode ser identificada com o qmake:

Qt no Raspberry - versão do qmake

Para saber o nome correto do módulo que deseja instalar, pode-se fazer uma consulta no “cardápio” disponível nesse link.

Apenas a título de exemplo, supondo que queira o QtMQTT:

git clone git://code.qt.io/qt/qtmqtt.git -b 5.12.1
cd qtmqtt
qmake
make
make install

Nem tente esse processo na versão 5.7.1, porque o módulo MQTT não está disponível nela.

Quando eu der continuidade à busca pela solução, escrevo outro artigo com os resultados e o processo para tal.

Vídeo

Se desejar dar uma conferida na interface que estou fazendo com Qt, QML e Quick, clique nesse link. A interface de 3 abas é essa cuja uma das abas é a imagem de destaque.

Aproveite para se inscrever no canal, clique no sininho para receber notificações, deixe seu like e se desejar tutoriais para produzir interfaces como essa, comente lá no vídeo ou em nossa página no facebook.

Display 5 polegadas 800×480

Esse display bacana é do nosso parceiro Baú da Eletrônica, cujo artigo relacionado você encontra nesse link. O tutorial mostra a maneira mais simples de configurá-lo, sem precisar instalar nada para ter o touch funcionando!

O link direto para o produto é esse.

Até a próxima!