- PVSM.RU - https://www.pvsm.ru -

Эта история началась с того, что я допиливал свой пет-проект по обработке данных. В ходе работы мне попался старый, но надежный мультиметр. Я долго им пользовался ранее, изучая электронику. Сам прибор неплохой, но софт под него сильно устарел, да и заточен он только под Windows. Так я занялся реверс-инжинирингом, отладкой обмена сообщениями и сборкой библиотек для телефона. Подробности — под катом!
Используете Terraform? Помогите нам сделать новый сервис лучше. Пройдите [1] короткий опрос — мы выберем самые интересные кейсы и пригласим вас на онлайн-интервью. За участие — плюшевый Тирекс или бонусы на услуги Selectel.
Для подобного проекта можно использовать любую платформу, для которой получится собрать Qt-приложение.
Используйте навигацию, если не хотите читать текст целиком:
→ Предыстория [2]
→ О подключении по USB [3]
→ Немного кода [4]
→ Автоматизация [5]
→ Заключение [6]
На первых курсах вуза мне сильно хотелось иметь профессиональное оборудование для работы с электроникой, но стоимость для студента была заоблачной. К счастью, я мог использовать различные приборы на кафедре, но их не унести домой. Так я наткнулся на компактные измерительные устройства Hantek. Они представляли собой плату в металлическом корпусе, с одного торца которого были разъемы для подключения щупов, а с другого — USB-интерфейс.
Блоками Hantek365 и DSO-6204 я надолго оснастил свою домашнюю лабораторию. Оба устройства были все время подключены в ПК, а измерительные щупы находились в стакане для канцелярии. Это позволяло в любой момент открыть софт и провести нужные измерения. Во избежание помех в первый год пользования приобрел USB-изоляторы.
Забавный факт: китайские производители даже предлагали свой аналог NI PXI rack, но большого распространения они не получили.
Источник [7].
После переезда на Linux Mint в начале магистратуры мультиметр я практически забросил, т. к. не было софта под него. Заменил его обычным ручным UNI-T.
Спустя лет семь на проекте понадобилось записать изменение тока на электроприводе и я вспомнил про имеющийся прибор. Снял необходимые характеристики и отправил девайс дальше пылиться на полке. И тут я подумал: а почему бы не написать к нему оболочку? С этого и началось веселое приключение на 20 минут.


По запросу «hantek 365 source» буквально первая ссылка в поисковой выдаче привела меня в нужный репозиторий на GitHub [8].
При подключении по USB девайс работает по крайне простому протоколу. Как и USB/CDC-устройства, мастер посылает запрос на чтение и ожидает ответ от прибора. Если ответ получен в течение отведенного времени, его можно обработать. Ответ от устройства в виде набора байт выглядит следующим образом:
Анализируя формат сообщения в найденном примере кода, заметил, что выполняется занятная операция вывода:
for (i = 0; i < 4; i++)
{
fprintf(stdout, "%c", databuff[i + 2]);
if ((dpos - 0x30) >> i == 1)
{
fprintf(stdout, ".");
}
}
То есть устройство содержит готовый ответ измерения в виде ASCII-кода, а разделительную точку — в виде позиции бита седьмого байта. Также биты множителя (10) и единицы измерения (11) хранят информацию в виде позиции бита. Например, так выглядят единицы измерения:
А как получены эти значения? Одно из предположений — кто-то из разработчиков прибора выкладывал исходные коды. Или же просто проанализировали трафик интерфейса через Wireshark. Я нередко использовал такой метод поиска необходимых данных при доработке библиотеки для CDC. Таким же методом определил значение последних 4 байт.

Все байты как на ладони: +0,718 мВ (4=0b100 — три символа после запятой, 0x40 — милли, 0x80 Вольт).
Думаю, читателям интересно устройство прибора изнутри. Корпус представляет собой экструдированный алюминиевый каркас с полозьями для печатной платы, закрытый с торцов информационными пластинами с разъемами.

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

В качестве конвертора интерфейсов на обратной стороне платы располагается STM32F103c8T6 (практически bluepill), который связан с микросхемой-мультиметром FS9922-DMM4 (даташит [9] на эту микросхему) через оптопары. Контакты STM выведены как на интерфейс USB-B, так и на разъем USBXI.
В целом, там всего четыре контакта. При большом желании можно развести собственную плату для рэка из набора разъемов PCIe 1x [10]. Однако несмотря на схожесть разъема и назначения, интерфейс PCIe и USBXI несовместимы! В лучшем случае — сожжете порт на STM32.
Основная логика работы, как и ранее, сделана на Qt C++ и доступна в репозитории [11]. Помимо мультиметра думаю добавить интерфейс для аналогичного осциллографа, например DSO-6022 [12].
В первую очередь убеждаемся, что есть хотя бы одно оборудование из перечня знакомых, и выставляем соответствующее значение.
QtHantek* QtHantek::createDevice(QObject *parent) {
libusb_context *context = NULL;
libusb_device **list = NULL;
int rc = 0;
size_t count = 0;
rc = libusb_init(&context);
if (rc != 0)
{
qWarning () << "USB not available!";
return new QtHantek(0,parent);
}
count = libusb_get_device_list(context, &list);
if (count < 1){
qWarning () << "Devices not found!";
return new QtHantek(0,parent);
}
if (count>0) {
for (size_t idx = 0; idx < count; ++idx)
{
libusb_device *device = list[idx];
libusb_device_descriptor desc = {0};
rc = libusb_get_device_descriptor(device, &desc);
if (rc != 0)
continue; // skip if error
if (desc.idVendor == availableDevices[0][0] && desc.idProduct == availableDevices[0][1]){
libusb_exit(context);
return new QtHantek365(parent);
}
}
}
libusb_free_device_list(list, 1);
libusb_exit(context);
return new QtHantek(0,parent);
}
Вся работа с мультиметром по USB заключаются в выборе режима работы и считывания буфера с обратным значением.
Так, если все ок, производим стандартную процедуру инициализации интерфейса, выбираем текущий режим (по умолчанию — 60V DC) и направляем его устройству для инициализации работы и старта обработки данных.
int init()
{
libusb_device **list = NULL;
m_husb = libusb_open_device_with_vid_pid(m_ctx, 1155, 22306);
if (m_husb == nullptr)
{
qDebug() << "Cannot open device!";
return -2;
}
if (libusb_claim_interface(m_husb, 0) < 0)
return -3;
initTimer();
emit exceptionSignal(0); // Успешная инициализация
return 0;
}
Как и в случае с CDC, запускаем таймер. Каждый тик (100 мс) проверяем значение интерфейса и, если первый байт 0xA0, выставляем сигнал, что данные готовы. Далее этот сигнал можно обрабатывать UI-интерфейсом. В связи с тем, что данные приходят сразу в готовом виде, я использую две переменные для хранения результата.
if (databuff[0] == 0xA0 && actual == 15)
{
int value = 0;
int afterPoint = 0;
uint8_t dpos = databuff[7]; // десятичная точка
uint8_t mult = databuff[10]; // Множитель
uint8_t unit = databuff[11]; // Единица измерения
// преобразование в число
for (int i = 0; i < 4; i++)
{
afterPoint = afterPoint * 10 + (databuff[i + 2] - 0x30);
if (dpos & 1 << (4 - i))
{
value = afterPoint;
afterPoint = 0;
}
}
if (databuff[1] == 0x2D)
value *= -1;
emit messageAvailable(value, afterPoint, getMultiplierString(mult), getUnitString(unit));
Для «связывания» интерфейса пользователя и подпрограммы обработки данных приборов используем setContextProperty.
На целевой странице с прибором добавляем обработку сигналов от прибора. Основной из них — onDataAvailable — сообщает о наличии новых данных. При срабатывании сигнала приложение определяет текущие максимальное и минимальное значение, форматирует и выводит на экран.
Connections {
target: hantekDevice
onMessageAvailable:{
valueDisplay.text = value+"."+afterPoint;
dimensionLabel.text = mult + unit;
if(modeSelector.sIndex>8){
if (acdcSwitch.checked)
{dimensionLabel.text += " AC";}
else {dimensionLabel.text += " DC";}
}
}
}
Пообщавшись со знакомыми разработчиками, мы создали публичный репозиторий с элементами QML. Актуальную версию селектора можно скачать оттуда [13].
Для реализации привычного интерфейса мультиметра решил сделать собственный элемент — селектор режимов. Он состоит из окружности, вокруг которой расположены части дуги с цветовыми метками режимов и названия каждого возможного режима работы.

Физическое и программное воплощение одного устройства.
Из интересного — добавил обратную связь через QtFeedback. Примеры применения можно найти в документации Ubuntu Phone [14]. В целом, применение очень простое: создаете компонент и по необходимости его вызываете. Под капотом данный элемент передает необходимый запрос через DBus интерфейсу com.nokia.NonGraphicFeedback. Следующий пример по нажатию кнопки вызывает вибрацию на 100 мс:
import QtQuick 2.0
import Sailfish.Silica 1.0
import QtFeedback 5.0
Page {
objectName: "mainPage"
allowedOrientations: Orientation.Portrait
// HapticsEffect для вибрации
HapticsEffect {
id: rumbleEffect
duration: 100
}
Button {
text: "DRILL!!!"
width: parent.width
height: parent.height
onClicked: rumbleEffect.start(); // plays a rumble effect
}
}
Интересный нюанс: если выключена вибрация клавиатуры, то и здесь она не сработает.
Для отображения графика на планшете в этот раз использовал ChartJs2QML [15] из примеров ОМП [16]. Этот вариант удобнее в плане интерфейса для разработки, но отображаются артефакты при обновлении графиков. Чтобы интерфейс с графиком отображался только на планшетах, создал новую qml-страницу и прописал условие вызова в главном файле:
initialPage: getWidthBasedPage()
…
function getWidthBasedPage() {
if (Screen.width > 720) {return Qt.resolvedUrl("pages/TabletMainPage.qml");
} else { // Для маленьких экранов
return Qt.resolvedUrl("pages/MainPage.qml");
}
Инсайдерская информация! Компания ОМП готовит значительное обновление системы. В частности — компонентов управления USB-устройствами и доступам к ним. В скором времени при разработке ПО пропадет необходимость подключать статическую библиотеку libusb и прописывать ручками права на устройство в консоли. Вместо этого, начиная с Аврора ОС 5.2, появится возможность запросить разрешения на интерфейс в ОС после соответствующей настройки в проекте.
После публикации предыдущего проекта я получил пару замечаний от таких же энтузиастов, как и я. Оказалось, что в моем телефоне отключена валидация пакетов, которая не дает установить приложение со включенными динамическими библиотеками.

Эту проблему решил пересборкой библиотек статически. Чтобы не пересобирать их каждый раз, сделал пайплайн [17], который автоматически собирает libusb. В общем виде пайплайн скачивает исходный код библиотеки, конфигурирует проект с указанием «собирать статические библиотеки» и выполняет сборку проекта для каждой необходимой архитектуры. Все архитектуры прописаны в «матрице», переменной arch:
jobs:
build:
name: Сборка библиотеки
runs-on: psdk
strategy:
matrix:
arch: [aarch64, armv7hl]
steps:
…
- name: Сборка для каждой архитектуры
run: |
cd libusb
${{ env.psdkPath }}/sdk-chroot sb2 -R -t ${{ env.aurora_tag }}-${{ matrix.arch }}.default make
…
Собранный пакет автоматически пушится на следующем этапе в общий публичный репозиторий пакетов и доступен всем по ссылке [18]. При дальнейшей сборке достаточно выбрать нужную библиотеку, стянуть архив и распаковать решение для целевой архитектуры приложения.

Так, за пару вечеров с помощью более современных инструментов я дал вторую жизнь устаревшему прибору. Теперь устройством можно снова пользоваться для быстрого анализа и оценки различных показателей. В дальнейшем планирую развить приложение, подключив аналогичный осциллограф и некоторые другие приборы (например, логический анализатор) и собрать вариант приложения для других используемых ОС.
Автор: VRyabchevsky
Источник [19]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/diy/417688
Ссылки в тексте:
[1] Пройдите: https://forms.selectel.ru/s/cm99r3fdr470ovp01bfglc6ej?utm_source=habr.com&utm_medium=referral&utm_campaign=terraform_article_moblab_230425_banner_077_03_ord
[2] Предыстория: #1
[3] О подключении по USB: #2
[4] Немного кода: #3
[5] Автоматизация: #4
[6] Заключение: #5
[7] Источник: https://www.hantek.com/news/detail?id=51
[8] репозиторий на GitHub: https://github.com/mp035/hantek-365
[9] даташит: https://www.ic-fortune.com/upload/Download/FS9922-DMM4-DS-13_EN.pdf
[10] PCIe 1x: https://www.chipdip.ru/product/10039755-10000tlf
[11] в репозитории: https://gitverse.ru/VORyabchevsky/EasyHantek
[12] DSO-6022: https://habr.com/ru/articles/243937/
[13] скачать оттуда: https://gitverse.ru/aurora_developers/qml_lib
[14] Ubuntu Phone: https://phone.docs.ubuntu.com/en/apps/api-qml-current/QtFeedback.HapticsEffect
[15] ChartJs2QML: https://github.com/MichaelVoelkel/ChartJs2QML
[16] примеров ОМП: https://gitlab.com/omprussia/demos/ChartsJs
[17] пайплайн: https://gitverse.ru/aurora_developers/aurora_libusb
[18] по ссылке: https://gitverse.ru/aurora_developers/-/packages/generic/aurora_libusb/1.0.28?tab=packagesOrg
[19] Источник: https://habr.com/ru/companies/selectel/articles/903458/?utm_source=habrahabr&utm_medium=rss&utm_campaign=903458
Нажмите здесь для печати.