Доброго времени суток! Сегодня я поделюсь опытом разработки программы для контроллера ROBO TX от немецкой фирмы Fischertechnik. Возможно, на данный момент он не является топовой моделью, однако базовые принципы, которые я собираюсь описать далее, могут быть полезны юным разработчикам, которые столкнутся с контроллерами этого производителя.

Контроллер ROBO TX
image

Данный контроллер в основном предназначен для конструирования простых роботов с целью обучения. В комплекте идет графическая среда программирования, которая помогает заложить в него простые алгоритмы. Также ребята из русского представительства Fischertechnik подсказали, что имеется динамическая библиотека c API от создателей, которая предоставляет полный контроль и позволяет решать требуемые задачи более гибко уже путем разработки собственного софта, что как раз и произошло в моем случае.

Раньше, когда учился, я работал в ЦТПО «Интеллектуальные роботы» на базе института МГУПИ. Центр занимался подготовкой абитуриентов и школьников старших классов по специальностям робототехники. И Андрей Назарович Будняк, который возглавлял его в то время, предложил создать для этого контроллера программу, которая позволит управлять роботом в реальном времени. Также был интересен вариант управления этим контроллером через Bluetooth, чтобы получить своего рода пульт управления, как у радио-машинки.

Для решения задачи потребовалось изучить документацию, которая шла с библиотекой. К счастью, она достаточно информативная.

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

Нас интересует второй.
Он делится на несколько этапов:
  1. Поиск доступных COM-портов
  2. Подключение через определенный COM-порт
  3. Запуск Transfer Area (протокол общения контроллера с компьютером)
  4. Передача требуемых команд
  5. Завершение Transfer Area
  6. Закрытие COM-порта

Как оказалось, в случае с соединением через Bluetooth этот механизм тот же самый и на программном уровне не отличается вовсе. Единственная задача заранее поднять COM-порт через Bluetooth.

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

Весь исходный код с комментариями выложен на Гитхабе в виде проекта на QT. Здесь опишу ключевые моменты по нему.

Постановка задачи


У контроллера 8 контактов для подключения 4 моторов (M1-M4). Наша задача при нажатии/отжимании клавиши подавать сигнал в контроллер, который включит/отключит определенный мотор.

image

Нам понадобится список клавиш, в котором мы сохраним код клавиши и соответствующий номер мотора. Но это не все. Контакты у ROBO TX способны выдавать последовательность импульсов с широтно-импульсной модуляцией (ШИМ) для регулировки скорости вращения моторов. Диапазон регулировки от 1 до 512. Это означает, что чем меньше цифра, тем реже подается напряжение, которое всегда имеет одинаковое значение (12В). Еще есть направление вращения. И вдобавок контроллеры способны объединяться в группы с помощью шлейфов до 9 за раз (1 мастер и 8 дополнительных).

Резюмируем и для быстрого понимания одновременно смотрим на финальный интерфейс формы ввода параметров клавиши:
  • Код клавиши
  • Номер контроллера
  • Номер мотора
  • Направление вращения
  • Скорость

image

Имея список, мы сможешь при нажатии клавиши с определенным кодом найти ее, определить, какие параметры передаем контроллеру, и отослать их. Вот и все в целом.

Реализация


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

struct Button {
    QString name;
    int code;
    int controller;
    int motor;
    bool direction;
    int speed;
};

Метод из динамической библиотеки ftMscLib.dll, который передает параметры на конкретный мотор называется setOutPwmValues.

Принимает 4 параметра:
  1. Идентификатор соединения через COM-порт
  2. Номер контроллера (0 — основной, 1-8 подключаемые)
  3. Номер контакта (1-8), но в нашем случае это номер мотора (1-4) * 2 + направление (0-1)
  4. Плотность импульсов (0-512)

Весь функционал программы разбил на следующие компоненты, реализованные отдельными классами:
  1. LibLoader — отвечает за подключение динамической библиотеки ftMscLib.dll.
  2. Сonnector — отвечает за подключение к контроллеру.
  3. Сontroller — отвечает за управление моторами контроллера в зависимости от списка клавиш и инкапсулирует его.
  4. FileManager — отвечает за выгрузку/загрузку списка клавиш в файл/из файла.

Этапы взаимодействия с контроллером.


Возвращаясь к этапам, которые были перечислены в начале.

1. Поиск доступных COM-портов
Connector::Connector() { //устанавливаем флаги в начальные значения
    found = false;
    connected = false;
    started = false;
}

//поиск доступных портов
bool Connector::searchCom()
{
    ports.clear();
    char *portName = new char[256];
    for (int i=0;i<libLoader.getAvailableComPorts(0);i++) {
        libLoader.enumComPorts(i,portName,256);
        ports.push_back(QString::fromLocal8Bit(portName));
    }
    return found = ports.size()>0 || debug;
}

2. Подключение через определенный COM-порт
//подключение по определенному порту
bool Connector::openCom(char* comPortName) {
    DWORD errCode = FTLIB_ERR_SUCCESS;
    fthdl = libLoader.ftxOpenComDevice(comPortName, 38400, &errCode);
    if (errCode==FTLIB_ERR_SUCCESS || debug) connected = true;
    return connected;
}

3. Запуск Transfer Area
//запуск протокола общения с контроллером (Transfer Area) [необходимо уже иметь подключение через порт]
bool Connector::startTA() {
    if (libLoader.ftxStartTransferArea(fthdl)==FTLIB_ERR_SUCCESS || debug) {
        started = true;
    }
    return started;
}

4. Передача требуемых команд
void Controller::exec(int buttonKey, bool pressed) //установить на контроллере состояние мотора в зависимости от конфигурации клавиши
{
    if (connector.getStarted()) {                  //если Трансфер Арея запущена
        for (int i=0;i<getButtons().size();i++) {  //перебираем список клавиш
            if (buttonKey==getButtons()[i].code) { //если текущая нажатая или отпущенная клавиша имеется в списке
                libLoader.setOutPwmValues(         //то посылаем конфигурацию мотора на контроллер, соответствующую этой клавиши и ее статусу нажатия (нажата или отпущена)
                    connector.fthdl,
                    getButtons()[i].controller,
                    getButtons()[i].motor * 2 + (getButtons()[i].direction ? 1 : 0),
                    getMemorySpeed(getButtons()[i], pressed)
                );
            }
        }
    }
}
Стоит сказать пару слов о методе getMemorySpeed. Это своего рода буфер зажатых клавиш. Добавлен был для того, чтобы не забывать очередность зажатых клавиш относящихся к одному и тому же мотору, но передающих разную скорость. Возвращает скорость последней зажатой клавиши для текущего мотора, если такая есть. В противном случае останавливает мотор, возвращая 0.

5. Завершение Transfer Area
//остановка протокола общения с контроллером (Transfer Area) [необходимо уже иметь подключение через порт]
bool Connector::stopTA() {
    if (libLoader.ftxStopTransferArea(fthdl)==FTLIB_ERR_SUCCESS || debug) {
        started = false;
    }
    return !started;
}

6. Закрытие COM-порта
//отключение открытого порта
bool Connector::closeCom() {
    if (libLoader.ftxCloseDevice(fthdl)==FTLIB_ERR_SUCCESS || debug) {
        started = false;
        connected = false;
    }
    return !connected;
}

Всю ключевую работу, в принципе, за нас делает библиотека ftMscLib.dll. Нам лишь важно это обернуть в удобную оболочку и приспособить для наших потребностей.

Данная разработка оказалась полезной на форуме роботов в 2012 году в соревновании «Трудная дорога». С помощью неё осуществлялся контроль роботом в реальном времени.


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

> Немецкий Сайт Fischertechnik
> Русский Сайт Fischertechnik
> Сайт русского комьюнити Fischertechnik
> Архив с библиотекой, документацией к ней и примерами с немецкого сайта Fischertechnik

Отдельное спасибо Андрею Назаровичу Будняку за помощь при составлении статьи.
Поделиться с друзьями
-->

Комментарии (9)


  1. Genoik
    03.03.2017 17:43

    Посмотрел код.
    Андрей, скажите, вы давно программируете?
    Я могу конечно ошибаться, но если судить по коду, мне показалось, что у вас используется не ООП подход, а больше «Си с классами».
    Если вам интересно, то можно обсудить сам код.


    1. Quardex
      03.03.2017 18:04
      +1

      Вас смущает отсутствие наследований?


      1. Genoik
        03.03.2017 18:11

        меня смущают глобальные переменные, отсутствие разделения ответственности, немного странные условия


        1. Quardex
          03.03.2017 18:15
          +1

          Дельные замечания. Скажу лишь, в целом так получилось, потому что с++ не мой основной язык, и это слишком простая задача, чтобы накручивать идеальный ООП. Я принял к сведению, на что обратить внимание в будущем, спасибо!


          1. Quardex
            03.03.2017 18:32

            идеальное*


  1. lingvo
    03.03.2017 18:54

    Немножко непонятно про "режим реального времени":
    имеется ввиду возможность управления с помощью компьютерных кнопок, как бы сказать в режиме live — т.е. нажали — мотор закрутился, отпустили — остановился. Или режим исполнения программы в реальном времени?
    По-моему тут первое, так?


    1. Quardex
      03.03.2017 18:59

      Угу, первое.


  1. kubikus
    03.03.2017 22:50

    Большая работа проведена однако. Помню еще первый вариант на borland c++ builder :)

    Контроллер ROBO TX, конечно хорошая машина для бортовой системы управления игрушечного робота, но, к сожалению, уже давно снят с произодства. Как насчет апдейта для управления выходами нового контроллера TXT?


    1. Quardex
      04.03.2017 01:59

      Я не против, но у меня нет доступа к этому контроллеру :)