КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Робот "Микроша", управляемый с помощью геймпада DualShock 2

Общие сведения:

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

Изменяя положение левого стика (джойстика), робот будет менять направление движения, а нажатие на кнопки "КРУГ" или "КВАДРАТ" в правой части геймпада будет включать или выключать светодиодные модули.

Видео:

Нам понадобится:

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

    • PsxNewLib— для работы с геймпадом;
    • DigitalIO - зависимость библиотеки PsxNewLib

    О том, как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.

    Описание работы робота:

    После подачи питания робот готов к работе.

    Нажимая на рукоятку джойстика вы выбираете направление движения робота.

    Нажимая кнопки на правой части геймпада, вы включаете/выключаете светодиоды.

    Схема сборки:

    Arduino / Piranha UNO:

    Batery Shield:

    Установите Battery Shield на Arduino / Piranha UNO:
    Во время установки Battery Shield должен быть в выключенном состоянии.

    Motor Shield:

    На Battery Shield установите Motor Shield

    Светодиоды:

    Подключите светодиоды к Motor Shield

    Мотор-редукторы:

    Подключите мотор-редукторы к Motor Shield

    Крепёж 1:

    Закрепите на ПВХ-пластине шаровую опору и мотор-редукторы

    Крепёж 2:

    Закрепите на ПВХ-конструкторе светодиоды и приёмник геймпада (с помощью стяжки)

    Геймпад:

    Подключите приёмник геймпада к Motor Shield

    Вывод приёмникаВывод Arduino
    GNDGND
    VCC
    DAT (MISO)12
    CMD (MOSI)
    11
    CLK (SCK)13
    ATT (CS)
    10

    Код программы (скетч):

    #include <PsxControllerHwSpi.h>
    
    const uint8_t  PS2_attention      = 10;                                                                           // Вывод Arduino, к которому подключен вывод приёмника геймпада ATT
    PsxControllerHwSpi<PS2_attention> psx;                                                                            // Создаём объект "ps2x"
    
    uint8_t  RAW_UP_OR_DOWN     = 0;                                                                                  // переменная "сырых" значений с джойстика по оси Оу
    uint8_t  RAW_LEFT_OR_RIGHT  = 0;                                                                                  // переменная "сырых" значений с джойстика по оси Ох
    uint8_t  GO_LEFT            = 0;                                                                                  // переменная для хранения отсортированных значений по оси Ох для движения влево
    uint8_t  GO_RIGHT           = 0;                                                                                  // переменная для хранения отсортированных значений по оси Ох для движения вправо
    uint8_t  GO_UP              = 0;                                                                                  // переменная для хранения отсортированных значений по оси Оу для движения вверх
    uint8_t  GO_DOWN            = 0;                                                                                  // переменная для хранения отсортированных значений по оси Оу для движения вниз
    
    uint8_t  pinShield_H2       = 4;                                                                                  // Вывод, подключенный к драйверу, для задания направления вращения левым мотором
    uint8_t  pinShield_E2       = 5;                                                                                  // Вывод ШИМ, подключенный к драйверу, для задания скорости левого мотора
    uint8_t  pinShield_E1       = 6;                                                                                  // Вывод ШИМ, подключенный к драйверу, для задания скорости правого мотора
    uint8_t  pinShield_H1       = 7;                                                                                  // Вывод, подключенный к драйверу, для задания направления вращения правым мотором
    uint8_t  valSpeed           = 0;                                                                                  // Максимальная скорость ШИМ (число от 0 до 255)
    uint8_t  led_right          = 2;
    uint8_t  led_left           = 3;
    bool     arrRoute[2]        = {0, 0};                                                                             // Направление движения для каждого мотора ([0]- правый мотор, [1] - левый мотор)
    uint16_t arrSpeed[2];                                                                                             // Скорость для каждого мотора ([0]- правый мотор, [1] - левый мотор)
    uint8_t  flg;                                                                                                     // Флаг направлений движения машинки
    bool     flg_led_right = false;                                                                                   // Флаг работы светодиода красного цвета
    bool     flg_led_left  = false;                                                                                   // Флаг работы светодиода синего цвета
    
    // ГЕЙМПАД
    
    // Функция конфигурирования контроллера 
    void configureGamepad(bool& configured)                                                                           
    {
            if (psx.begin()) {
                    // входим врежим конфигурирования
                    if (!psx.enterConfigMode())
                            return;
                    // если удалосль войти
                    else {
                            // включаем аналоговые джойстики
                            psx.enableAnalogSticks();
                            // выходим из режима конфигурации
                            psx.exitConfigMode();
                            // устанавливаем флаг конфигурации
                            configured = true;
                    }
            }
    }
    
    // Чтение контроллера
    void readGamepad(bool& configured)
    {
            // если не удалось прочитать - сбрасываем флаг конфигурации
            if (!psx.read())
                    configured = false;
    }
    
    // Обработка контроллера
    void handleGamepad()
    {
            static bool configured = false;
            // если не сконфигурирован - конфигурируем, передавая флаг
            if (!configured) {
                    configureGamepad(configured);
            }
            // если сконфигурирован - читаем, передавая флаг
            else {
                    readGamepad(configured);
            }
    }
    
    void setup() 
    {
      // МОТОРЫ
      pinMode(pinShield_H2, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_H2 как выход (направление вращения левого мотора)
      pinMode(pinShield_E2, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_E2 как выход (скорость вращения левого мотора, ШИМ)
      pinMode(pinShield_E1, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_E1 как выход (скорость вращения правого мотора, ШИМ)
      pinMode(pinShield_H1, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_H1 как выход (направление вращения правого мотора)
      // СВЕТОДИОДЫ
      pinMode(led_right,OUTPUT);
      pinMode(led_left, OUTPUT);
    }
    
    void loop() 
    {
            // обработка геймпада
            handleGamepad();
    
            // Считываем "сырое" аналоговое значение с левого джойстика
            psx.getLeftAnalog(RAW_LEFT_OR_RIGHT, RAW_UP_OR_DOWN); 
            // Если была нажата кнопка "КРУГ", то включаем красный светодоид
            if (psx.buttonPressed(PSB_CIRCLE)) {
                    flg_led_right = !flg_led_right; 
                    digitalWrite(led_right, flg_led_right);
            }       
            // Если была нажата кнопка "КВАДРАТ", то включаем синий светодоид
            if (psx.buttonPressed(PSB_SQUARE)) {
                    flg_led_left  = !flg_led_left;  
                    digitalWrite(led_left,  flg_led_left );
            }       
    
            if (RAW_UP_OR_DOWN <=120)   {GO_UP     = map(RAW_UP_OR_DOWN,       0,   120, 255,   0); GO_DOWN  = 0;} else             // Если значение по оси Оу меньше 120, значит джойстик сдвинут вверх, переопределяем диапазон значений с джойстика (переворачиваем его) 
                    if(RAW_UP_OR_DOWN >=135)   {GO_DOWN   = map(RAW_UP_OR_DOWN,     135,   255,   0, 255); GO_UP    = 0;} else      // Если значение по оси Оу больше 135, значит джойстик сдвинут вниз, переопределяем диапазон значений с джойстика
                    {GO_DOWN = 0; GO_UP = 0;}                                                                                       // Если значение находится в диапазоне от 120 до 135 - значит джойстик находится в центральном положении
            if (RAW_LEFT_OR_RIGHT <=120){GO_LEFT   = map(RAW_LEFT_OR_RIGHT,    0,   120, 255,   0); GO_RIGHT = 0;} else             // Если значение по оси Ох меньше 120, значит джойстик сдвинут влево, переопределяем диапазон значений с джойстика (переворачиваем его) 
                    if(RAW_LEFT_OR_RIGHT >=135){GO_RIGHT  = map(RAW_LEFT_OR_RIGHT,  135,   255,   0, 255); GO_LEFT  = 0;} else      // Если значение по оси Оу больше 135, значит джойстик сдвинут вправо, переопределяем диапазон значений с джойстика
                    {GO_LEFT = 0; GO_RIGHT = 0;}                                                                                    // Если значение находится в диапазоне от 120 до 135 - значит джойстик находится в центральном положении
            if (GO_UP){                                                                                                             // Если джойстик был сдвинут вверх, то
                    if(!GO_LEFT || !GO_RIGHT) {flg = 2; valSpeed = GO_UP;}    else                                                  // проверяем, сдвинут ли он вправо/влево, и если нет, то устанавливаем флаг движения на север
                    if(GO_LEFT)               {flg = 1; valSpeed = GO_UP;}    else                                                  // если джойстик сдвинут влево, то устанавливаем флаг движения на северо-запад
                    if(GO_RIGHT)              {flg = 3; valSpeed = GO_UP;}}   else                                                  // если джойстик сдвинут вправо, то устанавливаем флаг движения на северо-восток
                    if(GO_DOWN){                                                                                                    // если джойстик сдвинут вниз, то
                    if(!GO_LEFT || !GO_RIGHT) {flg = 8; valSpeed = GO_DOWN;}  else                                                  // проверяем, сдвинут ли он вправо/влево, и если нет, то устанавливаем флаг движения на юг
                    if(GO_LEFT)               {flg = 7; valSpeed = GO_DOWN;}  else                                                  // если джойстик сдвинут влево, то устанавливаем флаг движения на юго-запад
                    if(GO_RIGHT)              {flg = 9; valSpeed = GO_DOWN;}} else                                                  // если джойстик сдвинут вправо, то устанавливаем флаг движения на юго-восток
                    if(GO_LEFT)                 {flg = 4; valSpeed = GO_LEFT;}  else                                                // Проверяем, сдвинут ли джойстик только влево, и если да, то устанавливаем флаг движения на запад
                    if(GO_RIGHT)                {flg = 6; valSpeed = GO_RIGHT;} else                                                // Проверяем, сдвинут ли джойстик только вправо, и если да, то устанавливаем флаг движения на восток
                                                                                    {flg = 5;}                                      // Если джойстик не был сдвинут ни в одну из указанных выше сторон, то устанавливаем флаг остановки
            switch (flg) {// Направ. лев. м.     Направ. прав. м.        Скорость лев. м.                 Скорость прав. м.
                    case 1:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = (valSpeed / 2);    arrSpeed[0] = valSpeed;         break;      // С-З
                    case 2:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = valSpeed;          arrSpeed[0] = valSpeed;         break;      // С
                    case 3:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = valSpeed;          arrSpeed[0] = (valSpeed / 2);   break;      // С-В
                    case 4:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = 0;                 arrSpeed[0] = valSpeed;         break;      // З
                    case 5:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = 0;                 arrSpeed[0] = 0;                break;      // Стоп
                    case 6:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = valSpeed;          arrSpeed[0] = 0;                break;      // В
                    case 7:       arrRoute[1] = 1;     arrRoute[0] = 1;        arrSpeed[1] = (valSpeed / 2);    arrSpeed[0] = valSpeed;         break;      // Ю-З
                    case 8:       arrRoute[1] = 1;     arrRoute[0] = 1;        arrSpeed[1] = valSpeed;          arrSpeed[0] = valSpeed;         break;      // Ю
                    case 9:       arrRoute[1] = 1;     arrRoute[0] = 1;        arrSpeed[1] = valSpeed;          arrSpeed[0] = (valSpeed / 2);   break;      // Ю-В
            }
            digitalWrite(pinShield_H2, arrRoute[1]);                                                                         // тогда задаем направление вращения правого мотора
            digitalWrite(pinShield_H1, arrRoute[0]);                                                                         // и левого мотора
            analogWrite(pinShield_E2, arrSpeed[1]);                                                                          // Задаём скорость вращения для правого мотора
            analogWrite(pinShield_E1, arrSpeed[0]);                                                                          // и для левого мотора
            delay(50);                                                                                                       // Задержка 50мс
    }
    

    Алгоритм работы скетча:

    До кода void setup() определяются переменные, подключаются необходимые библиотеки.

    В коде void setup() инициализируется геймпад, настраивается режим работы выводов для моторов и светодиодов.

    Код void loop() делится на несколько частей:

    • Опрашивается геймпад
      • Считывается аналоговое значение с джойстика по оси Оу
      • Считывается аналоговое значение с джойстика по оси Ох
      • Опрашиваются кнопки "Квадрат" и "Круг"
        • Если они были нажаты, то состояние на выходах меняется на противоположное
    • Если на джойстиках были изменения положения, то:
    • Проверяем оси:
      • Ось Y:
        • Проверяем, было ли изменение в пределах от 0 до 120 - если да, то тогда робот движется вперёд;
        • Проверяем, было ли изменение в пределах от 135 до 255 - если да, то тогда робот движется назад;
      • Ось X:
        • Проверяем, было ли изменение в пределах от 0 до 120 - если да, то тогда робот движется влево;
        • Проверяем, было ли изменение в пределах от 135 до 255 - если да, то тогда робот движется вправо;

  • Проверяем, какое направление движения было определено и согласно ему задаём значение для флага flg;
  • В зависимости от флага flg устанавливаем направление вращения и скорость вращения колёс;
  • Устанавливаем на выводах Piranha Uno аналоговое значение для скорости вращения и цифровое значение для направления вращения.
  • Ставим задержку 50мс для того, чтобы геймпад опрашивался с некоторой задержкой.
  • Ссылки:




    Обсуждение

    Гарантии и возврат Используя сайт Вы соглашаетесь с условями