Общие сведения:
В этом уроке мы создадим робота, который будем управлять с помощью удобного геймпада.
Изменяя положение левого стика (джойстика), робот будет менять направление движения, а нажатие на кнопки "КРУГ" или "КВАДРАТ" в правой части геймпада будет включать или выключать светодиодные модули.
Видео:
Нам понадобится:
- 1х Arduino / Piranha UNO;
- 1х Battery Shield;
- 1х Motor Shield;
- 2х Мотор-редуктора;
- 2х Крепеж для мотора;
- 2х Колесо;
- 1х Шаровая опора;
- 1х Светодиод красный;
- 1х Светодиод синий;
- 1х Геймпад DualShock 2;
- 1х ПВХ-пластина для крепления Piranha/Arduino UNO;
- 1х ПВХ-конструктор;
- 1х Комплект проводов;
Для реализации проекта нам необходимо установить библиотеки:
О том, как устанавливать библиотеки, Вы можете ознакомиться на странице 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 |
| GND | GND |
| VCC | 5В |
| 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 - если да, то тогда робот движется вправо;
- Ось Y:
flg;flg устанавливаем направление вращения и скорость вращения колёс;

Обсуждение