Удаленное управление. QML.
-
И всем снова привет.
Тут вспомнил еще про один проект, который построен на базе ferro_remote клиента.
Это QML клиент. Тем, кто не знает, что это такое можно прочитать тут + много русскоязычных источников. QML это простой язык описания визуального интерфейса со встроенным JS. Была когда-то идея купить себе Jolla, интерфейс, которой построен именно на QML.
Проект, который входит в дерево ferro_remote. Пока я его подзабросил, потому как телефон Jolla я так и не купил.
Вот про этот клиент я и напишу сейчас.
gui_client, а именно так называется этот клиент (с названиями у меня туго, да) представляет собой обычный бинарник, который принимает в командную строку путь к QML файлу, экспортирует в машину некоторые интерфейсы клиента и исполняет файл. Все просто. Все очень похоже на работу lua_client, который я тут описывал не так давно. Есть, однако, пара моментов.
Клиентов в одном экземпляре QML может быть несколько, каждый может быть соединен со своим агентом
Соединяться нужно из скрипта, то есть вызывать методы клиента и реагировать на его события (сигналы)
Тут еще стоит отметить, что собранного бинарника для той же винды у меня нет, потому как под рукой нет самой винды с QtCreator.И так. Запуск приложения выглядит вот так
$ ./gui_client test.qml
где test.qml это как раз само окно приложения со всем, что необходимо.
Самый простой пример:
import QtQuick 2.0 Rectangle { id: mainWindow }
Теперь, если это запустить, мы получим просто белое окошко, которое можно свернуть, развернуть, или закрыть
Но нужно ж соединиться с малиной, например. Добавим.
import QtQuick 2.0 import Fr.Client 1.0 /// импорт клиента и некоторых интерфейсов Rectangle { id: mainWindow FrClient { // клиент id: generalClient Component.onCompleted: connect( "192.168.3.1:12345" ) // соединимся с малиной } }
Все. Вызов: (Далее просто можно закрыть)
$ ./gui_client test.qml
В логах агента на стороне малины будет:
2000-Jan-11 02:38:19.472011 [INF] [listener] New connection: ep: tcp://0.0.0.0:12345 client: tcp://192.168.3.10:52849 total: 1 2000-Jan-11 02:38:26.854824 [INF] [listener] tcp://0.0.0.0:12345 Close connection: tcp://192.168.3.10:52849; count: 0
Этот код можно немного причесать и получить что-то типа такого
Сам QML файл лежит тут. Он соединяется, разъединяется, показывает ошибку, если возникла.
Можно сделать что-то поинтереснее. Например валяется у меня Grove LCD RGB.
Как известно из даташита на это устройство, устройства в этой железке 2. Первое отвечает за текст, второе за цвет экранчика. Оба рулятся по i2c. Все должно быть просто.
Кстати сам экранчик выглядит вот так
Это он подключен не к малинке, если что. Просто такой же девайсик с линуксом на борту и агентом ferro_remote.
Для того, чтоб уметь работать с устройством, добавлю 2 компонента FrClientI2C:
Rectangle { // это будет общий компонент-девайс, который мы сможет потом,
// например, вынести в отдельный файл
id: lcdDevice
width: 0
height: 0
FrClientI2c {
id: txtDev // этот для текста
client: generalClient // использовать клиента с id generalClient
busId: 1 // шина 1
slaveAddress: 0x3E // адрес
}FrClientI2c { id: rgbDev // этот для цвета client: generalClient busId: 1 slaveAddress: 0x62 }
}
Что тут происходит:
Компоненты будут готовы к работе, как только клиент будет соединен с агентом. У каждого компонента (если я нигде не забыл это воткнуть), есть свойство ready и соответственно сигнал "onReadyChanged", в которой передается значение ready (true or false).
Поэтому прям в компонент lcdDevice можно добавить свойство, которое принесет с собой и сигналы, на которые можно реагировать:
Rectangle { id: lcdDevice width: 0 height: 0 property bool ready: txtDev.ready && rgbDev.ready // привяжем состояние 2 устройств к 1 значению
........
onReadyChanged: { // устройства изменили состояния
if( ready ) {
..... // оба устройства готовы.
}
}
}
Добавим в код пару функций. Первая будет менять цвет, вторая очищать экран.Rectangle { id: lcdDevice ...... function set_color( r, g, b ) { // установка цвета. за это отвечают 3 регистра. rgbDev.writeBytes( { 0x04: r, 0x03: g, 0x02: b } ) } function clear( ) { // очистка экрана - установка регистра 0x80 в 1 txtDev.writeBytes( { 0x80: 0x01 } ) } }
и теперь можно при готовности устройств очистить их и сбросить цвет на 0
onReadyChanged: { if( ready ) { clear( ) set_color( 0, 0, 0 ) } }
Так при подключении программка будет убирать текст с экрана и устанавливать цвет в 0x000000 то есть он будет выключен.
Добавлю функцию для установки текста. Я сделаю функцию с 2 параметрами, так как строк у экранчика 2.
/// собственно функция формирует массив из объектов, которые выглядят так: {registry: value} /// после этого все это пишется в устройство. /// тут, при использовании массива будет гарантия того, /// что значение регистров будет установлено ровно в той последовательности, /// в какой они поступят в массив. /// 0x40 - регистр, который пишет свое значение в следующее знакоместо. function set_text( txt, txt2 ) { /// сначала очистка экрана и установка курсора с положение 1:1 txtDev.writeBytes( [{ 0x80: 0x01 }, { 0x80: 0x08 | 0x4 }, { 0x80: 0x28 }] ) var txt_value = [] /// значение регистров для записи текста
for( var i = 0; i < txt.length; i++ ) { txt_value = txt_value.concat( [{ 0x40: txt.charCodeAt(i) }] ) } txt_value = txt_value.concat({ 0x80: 0xC0 }) /// переход на 2 строку. for( i = 0; i < txt2.length; i++ ) { txt_value = txt_value.concat( [{ 0x40: txt2.charCodeAt(i) }] ) } txtDev.writeBytes(txt_value) /// Пишем. }
Если все правильно, то после вызова функции мы увидим на экранчике текст, переданный в параметрах.
onReadyChanged: { if( ready ) { //clear( ) set_color( 100, 100, 100 ) set_text( "Hola,", "raspberrypi.ru" ) } }
Запустив и подключив, мы увидим светлый экран и надпись на нем "Hola,\nraspberrypi.ru"
Дальше - больше.
Я добавил ColorDialog из "QtQuick.Dialogs 1.0" и повесил событие смены текущего цвета на смену цвета экрана.
Скрипт можно взять тут. Скрипты немного корявенькие, потому как это вообще первый мой проект, как с QML, так и с Qt вообще.
PS: есть еще примеры работы с удаленным исполнением консольной команды, с листингом директории.
- Есть работа с пинами GPIO и, недавно начал прикручивать, SPI.
Так же при помощи QML можно легко и непринужденно смотреть видео с железки
Rectangle { width: 600 height: 200 Video { anchors.fill: parent source: "rtsp://192.168.3.1:554/video" enabled: true visible: true focus: true autoPlay: true } }
Пока все. Спасибо за внимание