Skip to content
  • Категории
  • Главная
  • Документация
  • Последние
  • Пользователи
Collapse
  1. Главная
  2. Блоги сообщества Raspberry Pi
  3. Удаленное управление. QML.

Удаленное управление. QML.

Запланировано Прикреплена Закрыта Перенесена Блоги сообщества Raspberry Pi
1 Сообщения 1 Posters 47 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • piP Не в сети
    piP Не в сети
    pi
    написал в отредактировано pi
    #1

    И всем снова привет.

    Тут вспомнил еще про один проект, который построен на базе 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
    

    Этот код можно немного причесать и получить что-то типа такого

    4ac693ad-449a-4a4e-ab19-27805a042bcd-image.png

    Сам QML файл лежит тут. Он соединяется, разъединяется, показывает ошибку, если возникла.

    Можно сделать что-то поинтереснее. Например валяется у меня Grove LCD RGB.

    Как известно из даташита на это устройство, устройства в этой железке 2. Первое отвечает за текст, второе за цвет экранчика. Оба рулятся по i2c. Все должно быть просто.

    Кстати сам экранчик выглядит вот так

    cd9db198-3c16-4d0b-bd57-b05b2767402f-image.png

    Это он подключен не к малинке, если что. Просто такой же девайсик с линуксом на борту и агентом 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: есть еще примеры работы с удаленным исполнением консольной команды, с листингом директории.

    b4adddea-5e20-43c0-9b72-31bcebdeabb5-image.png

    • Есть работа с пинами 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
               }
    
           }
    

    Пока все. Спасибо за внимание 😃

    1 ответ Последний ответ
    0

    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Login or register to search.
    • Первое сообщение
      Последнее сообщение
    0
    • Категории
    • Главная
    • Документация
    • Последние
    • Пользователи