Управление роботом через беспроводной UART

Описание:

В данном примере мы сделаем радио-управляемого робота при помощи радио модулей HC-12, которые будет передавать и принимать состояния кнопок на пульте управления. Робот будет управляться в режиме трактора: каждая гусеница управляется отдельно. Сделаем мы это при помощи  Trema-модулей Сенсорных кнопок и радио-модуля HC-12. Так же мы применим Trema Motor Shield и два мотора 1:200, 50rpm. Корпус робота мы распечатаем на 3D принтере на основе популярной модели робота SMARS (Screwless/Screwed Modular Assemblable Robotic System), файлы стерео-литографии Вы можете найти в разделе Ссылки.

Видео:

Редактируется...

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

Подключение:

Пульт:

Для удобства подключения мы воспользуемся Trema Set Shield. Так же нам понадобятся Battery Shield, Piranha Ultra, пять Trema-модулей Сенсорных кнопок и радио модуль HC-12.

Для начала установим Battery Shield на Piranha Ultra:

Затем установим Trema Set Shield:

Теперь можно установить пять сенсорных кнопок:

И финальной нотой будет радио-модуль HC-12:

Робот:

Для робота мы будем использовать Trema Motor Shield, Battery Shield, Piranha Ultra, Trema-модуль Светодиод и радио модуль HC-12.

Как и при сборке пульта первым делом мы установим Battery Shield:

Затем установим Trema Motor Shild:

Подключим радио модуль HC-12 и модуль светодиода к Trema Motor Shield по следующей таблице:

Trema ShieldHC-12Светодиод
9RX-
8TX-
3S-
2-S

И последним шагом подключим моторы:

Сборка робота:

Для сборки робота нам понадобятся заранее напечатанные детали на 3D принтере. Файлы для печати можно скачать по этой ссылке. Помимо распечатанных файлов, ещё нам понадобятся несколько винтов М3х6 для крепления Trema-модулей к держателю и сборки конструкции держателя.  На рисунке ниже показаны все детали, которые необходимо распечатать для сборки робота.

Все детали распечатаны, приступим к сборке. Для начала закрепим ведомые колёса и моторы на корпусе:

Затем устанавливаем ведущие колёса на валы моторов и сборку держателя Trema-модулей в корпус. 

И нам остаётся только установить платы управления, собрать и одеть гусеницы. Робот готов!

Скетчи проекта:

Пульт:

#define FRAME_HEADER        0xAA                // Определяем заголовок пакета
#define FRAME_FOOTER        '\n'                // Определяем конец пакета
#define button_left_up      6                   // Определяем номер вывода верхней левой кнопки пульта
#define button_left_down    A0                  // Определяем номер вывода нижней левой кнопки пульта
#define button_right_up     11                  // Определяем номер вывода верхней правой кнопки пульта
#define button_right_down   A3                  // Определяем номер вывода нижней правой кнопки пульта
#define radio_pin           3                   // Определяем номер вывода S модуля
#define button_led_k        2                   // Определяем номер вывода K кнопки переключения светодиода
#define button_led_t        A2                  // Определяем номер вывода T кнопки переключения светодиода
                                                //
byte data[5];                                   // Создаём массив данных для хранения и отправки значений через UART
                                                //
void setup() {                                  //
  Serial1.begin(9600);                          // Инициируем второй аппаратный последовательный порт Piranha Ultra на скорости 9600 бит/сек
  pinMode(button_led_t, OUTPUT );               // Конфигурируем вывод к которому подключен вывод Т кнопки управления светодиодом как выход
  digitalWrite(button_led_t, HIGH);             // Конфигурируем модуль работать в режиме переключателя
}                                               //
                                                //
void loop() {                                   //
  if (digitalRead(button_left_up)) {            // Если зажата верхняя левая кнопка 
    data[0] = 1;                                // Записываем единицу в первый элемент массива
  }                                             //
  else if (digitalRead(button_left_down)) {     // Если зажата нижняя левая кнопка
    data[0] = -1;                               // Записываем 255 в первый элемент массива
  }                                             // Если ни одна из правых кнопок не зажата
  else data[0] = 0;                             // Записываем ноль в первый элемент массива 
                                                //
  if (digitalRead(button_right_up)) {           // Если зажата верхняя правая кнопка 
    data[1] = 1;                                // Записываем единицу во второй элемент массив
  }                                             //                                           
  else if (digitalRead(button_right_down)) {    // Если зажата нижняя правая кнопка
    data[1] = -1;                               // Записываем 255 во второй элемент массива
  }                                             // Если ни одна из правых кнопок не зажата
  else data[1] = 0;                             // Записываем ноль во второй элемент массива 
                                                //
  if (digitalRead(button_led)) {                // Если переключатель светодиода в положении вкл
    data[2] = 1;                                // Записываем единицу в третий элемент массива
  }                                             //
  else data[2] = 0;                             // Иначе записываем ноль
                                                //
  Serial1.write(FRAME_HEADER);                  // Выводим заголовок пакета в последовательный порт
  for (int i = 0; i < sizeof(data); i++) {      // Побайтово выводим данные
    Serial1.write(data[i]);                     // в последовательный порт
  }                                             //
  Serial1.write(FRAME_FOOTER);                  // Выводим байт конца пакета в последовательный порт
  delay(100);                                   // Ждём сто миллисекунд
}                                               //

Робот:

#define FRAME_HEADER 0xAA                                       // Определяем заголовок пакета
#define FRAME_FOOTER '\n'                                       // Определяем конец пакета
#define pinH1         7                                         // Определяем номер вывода H1 MotorShield (он управляет направлением 1 мотора)
#define pinE1         6                                         // Определяем номер вывода E1 MotorShield (он управляет скоростью    1 мотора)
#define pinE2         5                                         // Определяем номер вывода E2 MotorShield (он управляет скоростью    2 мотора)
#define pinH2         4                                         // Определяем номер вывода H2 MotorShield (он управляет направлением 2 мотора)
#define radio_pin     3                                         // Определяем номер вывода S радио модуля
#define led_pin       2                                         // Определяем номер вывода S модуля светодиода
                                                                //
uint8_t mSpeed    =   0;                                        // Создаём переменную для хранения скорости моторов
bool    mDirect   =   HIGH;                                     // Создаём переменную для хранения направления моторов
byte    b         =   0;                                        // Создаём переменную для хранения текущего байта последовательного порта
byte    data[5];                                                // Создаём массив для хранения данных пакета
                                                                //
void setup(){                                                   //
  Serial1.begin(9600);                                          // Инициируем второй аппаратный последовательный порт Piranha Ultra на скорости 9600 бит/сек
  pinMode(led_pin, OUTPUT);                                     // Конфигурируем вывод S модуля светодиода как выход
  pinMode(pinH1, OUTPUT);                                       // Конфигурируем вывод pinH1 как выход и       
  digitalWrite(pinH1, LOW);                                     // устанавливаем на нём уровень логического нуля
  pinMode(pinE1, OUTPUT);                                       // Конфигурируем вывод pinE1 как выход и
  digitalWrite(pinE1, LOW);                                     // устанавливаем на нём уровень логического нуля
  pinMode(pinE2, OUTPUT);                                       // Конфигурируем вывод pinE2 как выход и
  digitalWrite(pinE2, LOW);                                     // устанавливаем на нём уровень логического нуля
  pinMode(pinH2, OUTPUT);                                       // Конфигурируем вывод pinH2 как выход и
  digitalWrite(pinH2, LOW);                                     // устанавливаем на нём уровень логического нуля
}                                                               //
void loop(){                                                    //
  memset(data, 0, sizeof(data));                                // Обнуляем массив данных
  if (Serial1.available() && Serial1.read() == 0xAA){           // Если в буфере последовательного порта есть данные
    for (int i = 0; (b = Serial1.read()) != FRAME_FOOTER; ++i){ // Читаем байт до тех пор пока он не равен символу конца пакета
    data[i] = b;                                                // Записываем байт в массив
    }                                                           //
    switch (data[0]) {                                          // Проверяем значение первого элемента массива
      case 0x01:                                                // Еcли в элементе 1
      digitalWrite(pinH1, mDirect);                             // Устанавливаем направление
      digitalWrite(pinE1, HIGH);                                // Устанавливаем максимальную скорость вращения
      break;                                                    //
      case 0xFF:                                                // Еcли в элементе -1
      digitalWrite(pinH1, !mDirect);                            // Устанавливаем противоположное направление
      digitalWrite(pinE1, HIGH);                                // Устанавливаем максимальную скорость вращения
      break;                                                    //
      default: digitalWrite(pinE1, 0);                          // Если в элементе не 1 и не -1, останавливаем мотор
    }                                                           //
    switch (data[1]) {                                          // Проверяем значение второго элемента массива
      case 0x01:                                                // Еcли в элементе 1
      digitalWrite(pinH2, mDirect);                             // Устанавливаем направление
      digitalWrite(pinE2, HIGH);                                // Устанавливаем максимальную скорость вращения
      break;                                                    //
      case 0xFF:                                                // Еcли в элементе -1
      digitalWrite(pinH2, !mDirect);                            // Устанавливаем противоположное направление
      digitalWrite(pinE2, HIGH);                                // Устанавливаем максимальную скорость вращения
      break;                                                    //
      default: digitalWrite(pinE2, 0);                          // Если в элементе не 1 и не -1, останавливаем мотор
    }                                                           //
    if (data[2]) digitalWrite(led_pin, HIGH);                   // Если в третьем элементе массива 1, включаем светодиод
    else digitalWrite(led_pin, LOW);                            // Иначе выключаем cветодиод
  }                                                             //
  delay(50);                                                    // Ждём 50 миллисекунд в основном цикле скетча
}                                                               //

Ссылки:

Обсуждение