Пингователь на libcurl+Qt+Raspberry Pi

Или как быстро начать разрабатывать на QT для Raspberry Pi и перестать беспокоиться

Kirill Avdeev
Oct 4, 2019 · 12 min read

В один прекрасный день, совершенно внезапно выяснилось, что необходимо было давным давно закончить проект для одной международной горнодобывающей компании. Её рабочие регулярно жаловались на качество этих ваших интернетов в номерах жилого корпуса предприятия. Понятно, что факторов, из которых складывается качество работы с интернетом очень много, и зачастую источником проблем может являться старенький смартфон, а не IT-отдел или провайдер.

Заказчику пришла в голову идея в каждой комнате повесить экранчик, который красиво и разноцветно показывал бы пинги до известных ресурсов через WiFi в роли средства мониторинга качества интернета.

В широком смысле статья предназначена для тех, кто хочет влиться в разработку ПО на Qt под Raspberry Pi.

Оборудование

Мы рассматривали три варианта модулей для основы проекта: ESP32, RaspberryPi (малинка) и STM32.

STM32 отмели сразу: слишком низкий уровень разработки решения, большое количество времени на разработку, сложность в поддержке решения (предприятие заказчика находится в примерно 400 км от города Бишкек, на высоте около 4000 метров, туда можно забираться далеко).

ESP32 не показался надёжным и имел те же проблемы с поддержкой решения.

Мы решили пальнуть из пушки по воробьям и остановились на одноплатном компьютере Raspberry Pi.

Встраиваемый Linux на Raspberry Pi, решал очень много проблем в пуске и поддержке, но требовал немного специфического подхода к разработке.

Мы выбрали Raspberry Pi 3B+. Это превосходный одноплатник размером с банковскую карту, имеющий на борту 4 USB-порта, WiFi-модуль, 1Гб ОЗУ, процессор BCM2835 с четырьмя ядрами Cortex-M53, каждое частотой до 1.4 ГГц. HDMI и композитным выходом мы решили не пользоваться: чем меньше проводов, тем лучше. Не заметят и порвут. Или наоборот, порвут и не заметят. Горняки — народ суровый.

Raspberry Pi 3 B+. Изображение взято с сайта https://www.raspberrypi.org

В дополнение к одноплатнику были приобретены резистивные тач-дисплеи Adafruit PiTFT. Дисплей сразу понравился. Простой, лаконичный, без излишеств. Для подключения к одноплатнику достаточно просто плотно насадить его на плату.

Так выглядит заказанный экран. Изображение взято с сайта https://www.adafruit.com

Ну и вещи, про которые принято забывать: питание и флэшка для операционной системы (ОС).

Сначала я запитывал одноплатник от ноутбука, но что-то шло не так и одноплатник просто не находился в сети. Посмотрев на то, как моргают светодиоды (повторялась одна и та же короткая последовательность), я предположил, что он постоянно перезагружается. Наскоро ограбив коллегу на зарядку от сотового телефона, я запитал одноплатник полноценными 2.5А тока. Стало понятно, что одноплатник немилосерден и прожорлив к питанию.

В проекте она нужна для того, чтобы записать на неё образ ОС. Четыре года назад я делал сайд-проект на Raspberry Pi 2 b+ и сдуру взял десяток флэшек от неизвестого китайского производителя. Флэшки прекрасно работали на смартфоне и ноутбуке, но на Raspberry Pi не шли ни в какую. Однако, с взятыми двумя днями позже флэшками от AData, одноплатник заработал сразу и без проблем.

Вся система в сборе в процессе отладки программного обеспечения

Программное обеспечение

В качестве фреймворка для приложения мы решили использовать Qt.

Итак, для реализации проекта с точки зрения программного обеспечения нужно было сделать следующие шаги:

  1. Установка ОС на одноплатник и её первичная настройка
  2. Включение экрана
  3. Сборка и установка Qt
  4. Установка curl
  5. Настройка среды разработки
  6. Непосредственно написание программы curler
  7. Настройка ОС таким образом, чтобы curler запускался при старте ОС и перезапускался в случае своего падения

При всём этом надо было исходить из того, что придётся делать почти пять десятков комплектов оборудования, то есть в уме надо держать необходимость быстрого дублирования флэшки.

В качестве рабочей машины использовался Asus ROG GL753VD с ОС Kubuntu 18.04

Здесь всё просто - как античность. Скачиваем образ Raspbian с официального сайта, распаковываем его. Копируем образ с помощью утилиты dd на флэшку. Всё, образ готов к запуску на одноплатнике. Вставляем флэшку, включаем. Одноплатник радостно моргает светодиодами.

Что дальше-то? WiFi нашего он не понимает, ip-адрес его нам неизвестен, да и всё равно по ssh не пускает даже при кабельном соединении. Просто по умолчанию так.

Снова вставляем флэшку в компьютер. Она подключится двумя разделами: загрузочным и системным. Загрузочный — маленький, в файловой системе FAT32, системный — большой, в файловой системе ext4.

Чтобы одноплатник пускал нас по ssh, в загрузочном разделе создаём файл с названием ssh. Увидев этот файл, малинка разрешит нам входить по ssh, но только до перезагрузки. После каждой загрузки этот файл удаляется.

Чтобы одноплатник увидел WiFi нужно немного другое колдовство. Заходим в системный раздел в директорию /etc/wpa_supplicant. Открываем файл wpa_supplicant.conf. Он описывает настройки подключения к беспроводным сетям. Одна сеть там описана. Добавляем описание своей WiFi-сети с реквизитами доступа дополнительным разделом network. Обязательно проследим, чтобы в конце файла не было пустой строки (один раз одноплатник не загружался именно из-за этого).

Для того, чтобы узнать ip-адрес нашей малинки мы можем использовать утилиту nmap таким образом:

sudo nmap -sP 192.168.20.0/24

Конечно, в эту команду вместо моей сети нужно будет подставить свою.

Мы увидим список всех хостов в сети, и, если малинка подключилась к WiFi, в списке найдём что-то типа такого:

Nmap scan report for 192.168.20.49
Host is up (0.0013s latency).
MAC Address: B8:27:EB:A0:50:9E (Raspberry Pi Foundation)

Ну, теперь мы можем подключиться к малинке по ssh.

ssh pi@192.168.20.49

Одноплатник спросит пароль. По умолчанию в качестве пароля используется raspberry. Настоятельно рекомендуется его сразу поменять.

Далее, чтобы закончить настройку, нужно разрешить доступ по ssh на постоянной основе и расширить файловую систему на всю флэшку утилитой raspi-config. Qt весит очень много и на 4Гб у меня просто не влазил.

Выполняем команду

sudo raspi-config

Появляется нехитрое приложение. Стрелками на клавиатуре выбираем пункт “Interfacing Options”, подпункт “Ssh”. Отвечаем утвердительно. Всё, у нас есть ssh.

Далее заходим в пункт “Advanced Options”, подпункт “Expand Filesystem”, отвечаем утвердительно. Ждём. Перезагружаемся командой

sudo reboot -h now

Имеет смысл также обновить всё, что есть на малинке. Для этого есть пара команд:

sudo apt updatesudo apt upgrade

Подождём окончания выполнения этих команд и малинка готова к использованию.

Экран просто насаживается на одноплатник. Но плотно, всё-таки электроника — это наука о контактах.

Собственно, шаги по софтверному включению экрана описаны здесь, но имеет смысл коротко привести их и тут.

Выполним следующие команды:

cd ~wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/adafruit-pitft.shchmod +x adafruit-pitft.shsudo ./adafruit-pitft.sh

Запускается скрипт, который предлагает нам настройки для драйвера экрана.

Первый диалог предлагает нам выбрать тип экрана. У меня экран 2.8" с резистивным тачем.

Второй диалог предлагает выбрать поворот экрана. Для начала можно выбрать просто что-нибудь. А потом-то уже подобрать поворот, который нужен.

В третьем диалоге я указал, что HDMI мне не нужен.

Далее скрипт что-то скачивает, устанавливает, думает и просит перезагрузки. Нужно понимать, что ещё раз запустить скрипт adafruit-pitft.sh можно в любое время, это вызовет просто перенастройку экрана.

Вообще очень сильно порадовал тот факт, что экран завёлся с полпинка и неплохо работает даже без калибровки.

После перезагрузки мы видим, что экран радостно показывает нам консоль, а затем и графическую оболочку.

Ну а теперь приступаем к самой мякотке процесса. Почему вообще Qt? Потому что он:

  1. Позволяет собирать очень приличные интерфейсы пользователя (GUI)
  2. Собирается под широкий спектр ОС и архитектур компьютеров
  3. Разработку можно вести на С++ и ни о чём не думать

Сборка Qt состоит из четырёх частей: подготовки окружения, конфигурирования, сборки и установки.

Подготовка окружения для меня лично состоит из создания директории с исходниками Qt, кросс-компилятором, копией системного раздела малинки и увязывания всего этого в скриптах, облегчающих жизнь.

Пути я вынес в отдельный скрипт с переменными окружения.

Скачиваем архив *.tar.xz с исходниками Qt отсюда. Распаковываем куда-нибудь.

Скачиваем компилятор gcc под ABI arm-linux-generic-elf-32bit. Я пользуюсь проверенным в бою gcc-linaro-5.4.1–2017.05-x86_64_arm-linux-gnueabihf, но попробовать можно любой, подходящий под требования Qt.

Теперь нам нужен клон рабочей флэшки от малинки. Сделать его можно командой dd. Втыкаем флэшку в компьютер и выполняем

lsblk

Получаем дерево блочных устройств, находим как называется флэшка. Ну и пишем что-то типа

dd if=/dev/mmcblk01 of=~/images/rpi_working.img bs=100M status=progress

Путь в параметре of можете использовать свой. Это вопрос личного удобства.

Я предпочитаю иметь полный клон файловой системы для разработки на одноплатниках на каждый значительный шаг при настройке. Неглобальные синхронизации можно проводить утилитой rsync.

Итак, мы имеем образ. Нужно его замаунтить в какую-нибудь директорию. Для этого есть такой вот скрипт:

mountImage.sh

Волшебное число 98304 было получено утилитой fdisk. Это смещение системного раздела, указанное в блоках файловой системы в образе флэшки, который мы сняли.

Итак, среда для сборки Qt сформирована.

Конфигурирование сводится к вызову скрипта configure в директории с исходниками Qt с нужными нам параметрами. Параметры подбираются исходя из требований к разрабатываемому ПО и путей к разным нужным штукам на конкретной машине разработчика. Оно нужно в первую очередь для того, чтобы выявить недостающие библиотеки для сборки модулей Qt.

Для конфигурирования Qt я использую вот такой вот скрипт:

buildQt.sh

Возможно, будет смысл назначить другое значение переменной BUILDPATH. В ней указана директория для теневой сборки.

А это наши переменные build_variables.sh:

buildVariables.sh

Нашего внимания касаются только четыре переменные:

PATH_TO_CC — путь к компилятору

RPI_ROOT — директория с примонтированной файловой системой малинки

PATH_TO_QT_SOURCES — директория с исходниками Qt

PATH_TO_QT_RPI — путь, в который будет установлен Qt на малинке.

Задаём их, запускаем скрипт и…

И я вот уверен на сто процентов, что Qt не сконфигурируется. Конфигурирование будет проваливаться из-за отсутствующих библиотек, даже если они будут на месте. Что делать?

После недолгого разглядывания библиотек до меня дошло, что:

  1. Просто отсутствуют символьные ссылки.
  2. Библиотеки есть, но лежат реально по совершенно другим путям
  3. Некоторые библиотеки не видны из-за того, что в качестве имени библиотеки используется имя символьной ссылки с абсолютным путём. Путь существует в рамках корневой файловой системы одноплатника, но его нет в корне нашей файловой системы.

В первом случае всё просто: просматриваем логи configure, находим несуществующие либы. Если в файловой системе малинки есть нужные либы с более или менее подробным указанием версии, то не хватает символьной ссылки, поэтому создаём её вручную командой ln.

Со вторым случаем всё посложнее. В параметрах configure есть такой: -device linux-rasp-pi3-g+. Он указывает на параметры сборки для конкретного типа компьютера. Можно создавать свои параметры, под кастомное железо. Они хранятся в директории с исходниками Qt, в поддиректории qtbase/mkspecs/devices. Там, в директории qtbase/mkspecs вообще много интересного. Находим файл с таким именем, изучаем, правим как надо.

А вот с последним случаем всё непросто. Переделывать все ссылки вручную — в крайней степени неразумно, поэтому пара добрых людей написала свои скрипты для этого хитрого дела и поделилась ими с сообществом.

Первый скрипт лежит здесь. На него легко наткнуться и на нём легко обломаться. Он не подходит уже как года три. Не работает адекватно.

Второй скрипт лежит тут. Он работает как швейцарские армейские командирские часы.

Запустили с указанием корневой директории малинки (из-под sudo, конечно же). Он отработал. Пожалуйста, кушать подано. Конфигуратор выкатывает все фичи Qt, которые будут в билде. Если нас всё устраивает, то переходим к сборке.

Для сборки переходим в директорию BUILDPATH и командуем:

make -j4

Цифра 4 зависит от количества ядер в процессоре вашего компьютера. Но сколько-нибудь процессора себе оставить надо, для порядка. Идём, завариваем кофе, чай, или наливаем, например, чайного гриба. Выпиваем. Перекусываем. Поговорим с кем-нибудь для души. Возвращаемся. Если у нас возникли ошибки компиляции, имеет смысл запустить сборку ещё раз. Бывает, помогает.

Если ошибки продолжают возникать в тех же местах, то проблема может быть в чём угодно, от компилятора, до отсутствующих или старых хедеров на малинке. Тут подход строго индивидуальный к каждой ошибке сборки.

Если у нас всё собралось, можем приступать к установке.

Установка производится из той же директории BUILDPATH командой

sudo make install

Ждём окончания выполнения команды. Происходит копирование собранных либ и создание символьных ссылок к ним.

После окончания установки образ готов к использованию. Синхронизируем его содержимое с флэшкой или через dd или через rsync.

Если после rsync вы вдруг не можете зайти по ssh с ошибкой “Неправильный пользователь или пароль”, то скорее всего что-то случилось с файлом /etc/shadow. Необходимо копировать его с примонтированного на компьютере раздела флэшки на саму флэшку.

Заходим на малинку, выполняем команду

sudo apt install libcurl4-gnutls-dev

Она устанавливает curl со всем необходимым для разработки в качестве библиотеки.

Теперь мы хотим как-то начать писать код приложения. Для этого мы должны настроить среду разработки QtCreator. Её можно скачать и установить отдельно. Однако, мы поставим весь комплект Qt для того, чтобы основную часть разработки и отладки вести в комфортных условиях — на компьютере. Снова скачиваем Qt, но на этот раз уже не архив, а полноценный *.run файл. Запускаем его, устанавливаем Qt.

Открываем QtCreator. Пробуем запустить какой-нибудь пример. Если всё хорошо, то всё хорошо. Однако чаще всего не хватает установленного gcc и библиотеки libgl1-mesa-dev. Устанавливаем их обычным для Ubuntu способом.

Запускаем какой-нибудь пример. Всё хорошо и красиво запускается и мы рады этому.

Идём в Tools > Options > Kits. Мы видим настройки всех пакетов Qt (А именно пока одного).

Открываем вкладку Debuggers. Добавляем отладчик arm-linux-gnueabihf-gdb из директории с нашим кросс-компилятором, называем его arm-gdb.

Добавление отладчика

Открываем вкладку Compilers, добавляем компилятор arm-linux-gnueabihf-gcc из директории с нашим кросс-компилятором, называем его arm-gcc.

Добавление компилятора

Открываем вкладку Qt versions, добавляем новую версию Qt из смонтированного образа (помните параметр PATH_TO_QT_RPI, который мы видели при сборке? Вот в этой директории на образе, в поддиректории bin лежит файл qmake, указываем его), называем её arm-qt.

Добавление версии Qt

Применяем все изменения, идём во вкладку Kits. Добавляем новый комплект. Называем его rpi-qt. Указываем в нём наши отладчик, компилятор и версию Qt.

Добавление комплекта

В Device type указываем Generic Linux Device, у Device нажимаем Manage. Откроется диалог управления устройствами.

Диалог управления устройствами

Добавляем новое Generic Linux устройство называем его как-нибудь (здесь оно curler.raspberry). Прописываем его ip-адрес и имя пользователя, которым мы ходим по ssh. Жмём test. Видим окно, в котором выводится результат проверки. Применяем. Закрываем. Мы снова на формировании комплекта. У выбора имени справа есть маленькая картинка с компьютером. Нажимаем туда и выбираем значок. Меньше путаницы возникнет дальше.

Создаём новый проект, включаем в нём оба комплекта (и для компьютера, и для малинки).

Подключаем библиотеки в pro-файле:

LIBS += -lcurl

Ну и немного меняем правила деплоя:

target.path = /home/pi/qcurler/qcurler
INSTALLS += target

Это приведёт к тому, что проект будет деплоиться на малинку в домашнюю директорию. Меньше проблем с правами во время разработки.

Пишем код, как на обычном Qt. Отлаживаем всё на компьютере. Если всё нормально, пробуем запуститься на малинке. Появляется окно на дисплее с нашей программой.

Однако нам надо было, чтобы была доступна лишь наша программа и поэтому через утилиту raspi-config мы выключили графическую оболочку, а программу запускали с параметром

-platform linuxfb

И она запускалась поверх консоли. Для таких программ вообще имеет смысл использовать скрипты запуска с переменными её окружения и параметрами, но это зависит от способа разработки.

Скрипт запуска программы curler

Исходный код самого нашего приложения вы можете найти здесь.

В Raspbian за работу со службами отвечает systemd.

Для того, чтобы зарегистрировать службу, необходимо закинуть её описание в виде файла с расширением .service в директорию /etc/systemd/system и выполнить команду:

sudo systemctl enable servicename.service

Где enable — это команда, а servicename — имя закинутого файла.

Команд есть целая куча: start, stop, restart, disable. Лучше всего сверяться с документацией по systemd.

Для запуска службы нужно использовать команду start.

Наше описание службы выглядит следующим образом:

Описание службы curler’а

После того, как мы отправили первую партию устройств, в радиусе шаговой доступности от офиса кончились нужные флэш-карты. Взятая новая китайская флэшка, совершенно внезапно не подошла по объёму своей памяти и dd просто отказался её копировать. Возникла необходимость уменьшить образ для записи его на флэшку. Выручил нас вот этот вот скрипт:

Итог

В статье был рассмотрен мой личный способ разработки ПО под одноплатный компьютер Raspberry Pi, однако в общих чертах он подойдёт и под прочие одноплатники подобного класса. Он подойдёт в том числе и под кастомное железо (был случай использования Qt на контроллере бурового станка фирменной разработки), с той разницей, что загружаться моему личному рабочему контроллеру было удобнее через сетевую файловую систему NFS.

Если у вас есть замечания по теме или по тексту, You are welcome.

P.S. Ну и конечно, вы можете познакомиться со всей командой Mad Devs в нашем уютненьком бложике или инстаграме :3

P.P.S. Напоследок повторю ссылку на наш репозиторий с Curler — Репозиторий с curler — https://github.com/maddevsio/QCurler

Mad Devs — блог об IT

Engineering your growth. Mad Devs is the team behind large scalable projects, globally.

Mad Devs — блог об IT

Mad Devs is a Cambridge-headquartered IT company developing enterprise-level software solutions for finance, transportation & logistics, security, edtech, and advertising industries. For more information about us, please browse our website: https://maddevs.io/

Kirill Avdeev

Written by

Senior software developer at MadDevs. Embedded Linux, STM32, NodeMCU, Raspberry Pi, Beaglebone. Qt, C/C++, Golang, Delphi, PHP, Python…

Mad Devs — блог об IT

Mad Devs is a Cambridge-headquartered IT company developing enterprise-level software solutions for finance, transportation & logistics, security, edtech, and advertising industries. For more information about us, please browse our website: https://maddevs.io/

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store