ARDUINO Arduino SEGA game pad nrf24l01 для RC моделей.

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Пульт управления для радиоуправляемых моделей на основе геймпада от SEGA MD2 консоли.
IMG_20210902_232209.jpg
IMG_20210927_002029.jpg
IMG_20210927_002154.jpg
 
Изменено:

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Схемы пока нет, попробую нарисовать попозже.
Чип на плате геймпада сохранён, ардуино считывает сигналы с платы геймпада и передаёт
сигнал через nrf24l01 на приёмник.
Все кнопки задействованы.
Платка передатчика nrf24l01 с усилителем внутри, антенна выведена наружу.
На передней панели ползунок заменён на ползунковый переключатель (вкл/выкл) питание.
Все остальные детали и ардуино про мини внутри, для батареи места не нашлось.
 

bort707

★★★★★★✩
21 Сен 2020
1,760
541
я правильно понял, что от оригинального пульта остались лишь кнопки и корпус?
Если для батареи места не нашлось - как оно питается? Я вмжу там DC-DC - нафига он там, если нет питания?
 

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Избыточное цитирование
я правильно понял, что от оригинального пульта остались лишь кнопки и корпус?
Если для батареи места не нашлось - как оно питается? Я вмжу там DC-DC - нафига он там, если нет питания?
В том то и дело что плата геймпада целая, это полноценный беспроводной геймпад от сега (в данном случае китайский) геймпад.
Ардуина считывает кнопки с чипа на плате геймпада, примерно также как это далала бы сама консоль SEGA.
Батарея да не поместилась. Пока проводки выведены наружу для батареи. С батареей колхоз, ну что поделать:rolleyes:
DC-DC нужен как раз для платы геймпада, ниже 4,5в не работает.
 
Изменено:

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Надеюсь я не накосячил со схемой:unsure:.
SEGA gamepad он же в простонародье "сеговский джойстик" имеет 9 контактов.
2 из них это земля и питание +5 вольт, остальные сигнальные.
SYNG или SEL этот контакт используется ардуиной для выбора режима считывания. (это важно)
Чтобы считывать состояние кнопок, необходимо в нужной последовательности подавать высокий и низкий уровень сигнала (HIGH/LOW).
C++:
void segaRead() {
  digitalWrite(SEL, HIGH);
  sega_up = (digitalRead(UP_OR_Z) == LOW);
  sega_left = (digitalRead(LEFT_OR_X) == LOW);
  sega_right = (digitalRead(RIGHT_OR_MODE) == LOW);
  sega_down = (digitalRead(DOWN_OR_Y) == LOW);
  sega_c = (digitalRead(C_OR_START) == LOW);
  sega_b = (digitalRead(B_OR_A) == LOW);
  digitalWrite(SEL, LOW);
  sega_a = (digitalRead(B_OR_A) == LOW);
  sega_start = (digitalRead(C_OR_START) == LOW);
  digitalWrite(SEL, HIGH);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
  sega_x = (digitalRead(LEFT_OR_X) == LOW);
  sega_y = (digitalRead(DOWN_OR_Y) == LOW);
  sega_z = (digitalRead(UP_OR_Z) == LOW);
  sega_mode = (digitalRead(RIGHT_OR_MODE) == LOW);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
}
Таким образом мы сможем считать 12 кнопок на геймпаде.
Подробнее о работе геймпада сега можно прочитать вот тут
 
Изменено:

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Код для проверки геймпада сеги.
Код взят с сайта который я привёл выше с моими небольшими дополнениями.
C++:
#include <OLED_I2C.h>
OLED oled(SDA, SCL);
extern uint8_t SmallFont[];
#define UP_OR_Z 2
#define DOWN_OR_Y 3
#define LEFT_OR_X 4
#define RIGHT_OR_MODE 5
#define B_OR_A 6
#define SEL 7
#define C_OR_START 8
int i;
bool sega_up = false;
bool sega_down = false;
bool sega_left = false;
bool sega_right = false;
bool sega_start = false;
bool sega_mode = false;
bool sega_a = false;
bool sega_b = false;
bool sega_c = false;
bool sega_x = false;
bool sega_y = false;
bool sega_z = false;
void segaRead() {
  digitalWrite(SEL, HIGH);
  sega_up = (digitalRead(UP_OR_Z) == LOW);
  sega_left = (digitalRead(LEFT_OR_X) == LOW);
  sega_right = (digitalRead(RIGHT_OR_MODE) == LOW);
  sega_down = (digitalRead(DOWN_OR_Y) == LOW);
  sega_c = (digitalRead(C_OR_START) == LOW);
  sega_b = (digitalRead(B_OR_A) == LOW);
  digitalWrite(SEL, LOW);
  sega_a = (digitalRead(B_OR_A) == LOW);
  sega_start = (digitalRead(C_OR_START) == LOW);
  digitalWrite(SEL, HIGH);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
  sega_x = (digitalRead(LEFT_OR_X) == LOW);
  sega_y = (digitalRead(DOWN_OR_Y) == LOW);
  sega_z = (digitalRead(UP_OR_Z) == LOW);
  sega_mode = (digitalRead(RIGHT_OR_MODE) == LOW);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
}
void setup() {
  oled.begin();
  oled.setFont(SmallFont);
  pinMode(SEL, OUTPUT);
  pinMode(UP_OR_Z, INPUT);
  pinMode(DOWN_OR_Y, INPUT);
  pinMode(LEFT_OR_X, INPUT);
  pinMode(RIGHT_OR_MODE, INPUT);
  pinMode(B_OR_A, INPUT);
  pinMode(C_OR_START, INPUT);
}
void loop() {
  segaRead();
  char temp[16];
  oled.clrScr();
  sprintf(temp, " %s   %s  %s%s%s",
          sega_up ? "U" : "u",
          sega_mode ? "M" : "m",
          sega_x ? "X" : "x",
          sega_y ? "Y" : "y",
          sega_z ? "Z" : "z");
  oled.print(temp, CENTER, 8 * 3);
  sprintf(temp, "%s%s%s  %s  %s%s%s",
          sega_left ? "L" : "l",
          sega_down ? "D" : "d",
          sega_right ? "R" : "r",
          sega_start ? "S" : "s",
          sega_a ? "A" : "a",
          sega_b ? "B" : "b",
          sega_c ? "C" : "c");
  oled.print(temp, CENTER, 8 * 4);
  oled.print("SEGA", CENTER, 52);
  oled.printNumI(i, LEFT, 52);
  {
    if (sega_up == 1) {
      oled.print("UP", LEFT, 0);
    } else if (sega_down == 1)
    { oled.print("DOWN", LEFT, 0);
    } else if (sega_left == 1)
    { oled.print("LEFT", LEFT, 0);
    } else if (sega_right == 1)
    { oled.print("RIGHT", LEFT, 0);
    } else if (sega_a == 1)
    { oled.print("A", RIGHT, 0);
    } else if (sega_b == 1)
    { oled.print("B", RIGHT, 0);
    } else if (sega_c == 1)
    { oled.print("C", RIGHT, 0);
    } else if (sega_mode == 1)
    { oled.print("MODE", CENTER, 0);
    } else if (sega_start == 1)
    { oled.print("START", CENTER, 0);
    } else if (sega_x == 1)
    { oled.print("X", CENTER, 8);
    } else if (sega_y == 1)
    { oled.print("Y", CENTER, 8);
    } else if (sega_z == 1)
    { oled.print("Z", CENTER, 8);
    }
  }
  oled.update();
}
 

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Пример считывания нажатия кнопок с геймпада сега.
В данном случае геймпад от 8bitDo M30, он сам по себе беспроводной на частоте 2,4 ГГц но сути не меняет, приёмник
этого геймпада также считывается как и любой другой проводной геймпад.
Оригинальные геймпады сега китайские или современные беспроводные работают по одному и тому же принципу.
 

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Тест самодельного пульта из геймпада сега.
Приёмник подключённый к компьютеру фиксирует нажатие кнопок и выводит в монитор порта.
Так же к приёмной части подключён сервопривод, которым я могу управлять нажимая на крестовину (кнопки LEFT/RIGHT)
 

te238s

✩✩✩✩✩✩✩
14 Ноя 2021
36
1
У меня подобная задумка в работе. Только джойстик Defender с усб разъемом. Зато,в отличие от Сеги,присутствуют 2 стика.
Там вместо одного вибромотора идеально помещается Pro mini и нрф24. Вместо второго аккумулятор.
Сейчас думаю как с интерфейсом усб общаться,пока в этом нет опыта.
 

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Геймпад USB это USB-HID устройство. Для работы с ним нужно получить его HID дескриптор, но перед этим надо опросить его как USB устройство и получить его входные и выходные ноды, затем опросить как HID получив дескриптор, построить по дискриптору формат пакетов. Затем обрабатывать каждый отправленный пакет с нужной ноды по заданному формату.
Есть шилд юсб для меги и уно работает с геймпадами PS3 и PS4 и XBOX360 вроди но это всё громоздкое и дорого выходит.
Если стики нужны купите PS2 беспроводной, геймпад переделывать не придётся к модулю приёмнику подключитесь и всё.
У геймпадов денди и сега протокол обращения простой.
 

te238s

✩✩✩✩✩✩✩
14 Ноя 2021
36
1
@Nick,вот из-за сложностей общения по УСБ и не хотелось бы в дебри лезть. Думаю банально физически сигналы с платы брать.
 

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
e52dd82e-d74f-461d-862c-fd1bde369c1d.jpg























c9f5ddfa-f75f-41ee-8eaa-814b2dd3ca5e.jpg
Привёл в порядок сам геймпад уменьшил количество проводов и запихнул батарею во внутрь.
Теперь это полностью автономное устройство.
42b23c77-34ef-4a8f-a455-641d3334e428.jpg
 
Изменено:

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Код gamepad SEGA c nrf24l01

C++:
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
RF24 radio(9, 10);
#define UP_OR_Z 2
#define DOWN_OR_Y 3
#define LEFT_OR_X 4
#define RIGHT_OR_MODE 5
#define B_OR_A 6
#define SEL 7
#define C_OR_START 8
int i;
bool sega_up = false;
bool sega_down = false;
bool sega_left = false;
bool sega_right = false;
bool sega_start = false;
bool sega_mode = false;
bool sega_a = false;
bool sega_b = false;
bool sega_c = false;
bool sega_x = false;
bool sega_y = false;
bool sega_z = false;
void segaRead() {
  digitalWrite(SEL, HIGH);
  sega_up = (digitalRead(UP_OR_Z) == LOW);
  sega_left = (digitalRead(LEFT_OR_X) == LOW);
  sega_right = (digitalRead(RIGHT_OR_MODE) == LOW);
  sega_down = (digitalRead(DOWN_OR_Y) == LOW);
  sega_c = (digitalRead(C_OR_START) == LOW);
  sega_b = (digitalRead(B_OR_A) == LOW);
  digitalWrite(SEL, LOW);
  sega_a = (digitalRead(B_OR_A) == LOW);
  sega_start = (digitalRead(C_OR_START) == LOW);
  digitalWrite(SEL, HIGH);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
  sega_x = (digitalRead(LEFT_OR_X) == LOW);
  sega_y = (digitalRead(DOWN_OR_Y) == LOW);
  sega_z = (digitalRead(UP_OR_Z) == LOW);
  sega_mode = (digitalRead(RIGHT_OR_MODE) == LOW);
  digitalWrite(SEL, LOW);
  digitalWrite(SEL, HIGH);
}
void setup() {
  radio.begin();
  radio.setChannel(0x70);
  radio.setPALevel (RF24_PA_MAX);
  radio.setDataRate (RF24_1MBPS);
  radio.openWritingPipe(0x0123456789LL);
  pinMode(SEL, OUTPUT);
  pinMode(UP_OR_Z, INPUT);
  pinMode(DOWN_OR_Y, INPUT);
  pinMode(LEFT_OR_X, INPUT);
  pinMode(RIGHT_OR_MODE, INPUT);
  pinMode(B_OR_A, INPUT);
  pinMode(C_OR_START, INPUT);
}
void loop() {
  segaRead();
  {
    if (sega_up == 1) {
      i = 1;
      radio.write(&i, sizeof(i));
    } else if (sega_down == 1)
    { i = 2;
      radio.write(&i, sizeof(i));
    } else if (sega_left == 1)
    { i = 3;
      radio.write(&i, sizeof(i));
    } else if (sega_right == 1)
    { i = 4;
      radio.write(&i, sizeof(i));
    } else if (sega_a == 1)
    { i = 5;
      radio.write(&i, sizeof(i));
    } else if (sega_b == 1)
    { i = 6;
      radio.write(&i, sizeof(i));
    } else if (sega_c == 1)
    { i = 7;
      radio.write(&i, sizeof(i));
    } else if (sega_mode == 1)
    { i = 8;
      radio.write(&i, sizeof(i));
    } else if (sega_start == 1)
    { i = 9;
      radio.write(&i, sizeof(i));
    } else if (sega_x == 1)
    { i = 10;
      radio.write(&i, sizeof(i));
    } else if (sega_y == 1)
    { i = 11;
      radio.write(&i, sizeof(i));
    } else if (sega_z == 1)
    { i = 12;
      radio.write(&i, sizeof(i));
    } else {
      i = 0;
      radio.write(&i, sizeof(i));
    }
  }
}
 

te238s

✩✩✩✩✩✩✩
14 Ноя 2021
36
1
Отправка происходит только при нажатии чего-либо. Не лучше ли отправку совершать скопом в виде массива,к примеру? Допустим каждые 20мс.
 

Nick

★✩✩✩✩✩✩
1 Авг 2018
52
10
Это пока первый вариант, нужно машинку сделать, что бы понять как лучше.
У меня деталей пока не хватает.
Почему каждые 20мс?
Каждые 20мс, тогда задержка будет в +20мс.
В этом варианте у меня переменная i которая меняется от состояния кнопки.
Я так понимаю для каждой кнопки создать свою переменную (массив) и при нажатии отправлять всё это скопом.
 

Stamp

★✩✩✩✩✩✩
12 Янв 2021
44
22
@Nick, Создаётся структура, в которой каждой кнопке или джойстику задаётся своя переменная.
При нажатии на кнопку или изменении положения джойстика, соответствующая переменная в структуре обновляется и структура целиком передаётся управляемому устройству.
На другом конце структура анализируется и производится управление исполнительным устройством, чья переменная изменилась.
 
  • Лойс +1
Реакции: te238s

Lumenjer

★★★★★✩✩
10 Дек 2020
788
415
@Stamp, Проще передавать не структуру, а ключ:значение, первые 4 бита - код кнопки, + еще 1 бит на значение.
Но можно и структуру запаковать в 2 байта. По одному биту на каждую кнопку.
 

Stamp

★✩✩✩✩✩✩
12 Янв 2021
44
22
@Lumenjer,
Структура:
  //******************************************************************************
  //  Секция определения типов
  //******************************************************************************
  struct
  {
    int16_t Turn;                                                               // Управление поворотом
    int16_t Speed;                                                              // Управление ходом
    unsigned Bunker_1     :1;                                                   // Бункер 1
    unsigned Bunker_2     :1;                                                   // Бункер 2
    unsigned RunFires     :1;                                                   // Ходовые огни
    unsigned SearchLight  :1;                                                   // Прожектор
    unsigned Joystick     :1;                                                   // Инверсия джойстиков
    unsigned Free         :3;                                                   // Пока не занятые переменные
  } SendBuff = {0,0,0};
 

Lumenjer

★★★★★✩✩
10 Дек 2020
788
415
Turn и Speed это же для аналоговых стиков, их там нет.
Я вижу 12 кнопок на геймпаде, т.е. 12 бит + еще 4 резервные)
 

Stamp

★✩✩✩✩✩✩
12 Янв 2021
44
22
@Lumenjer, Я просто код привёл, как сделано было.
А так Вы правы, ему 2 байта за глаза.
Но передавать проще структуру.
Не придётся Разбор принятых данных проверять. Принял два байта и выковыривай из них данные.
 
  • Лойс +1
Реакции: Lumenjer

Геннадий П

★★★✩✩✩✩
14 Апр 2021
590
192
42
Создаётся структура, в которой каждой кнопке или джойстику задаётся своя переменная.
Берется байт, в котором каждому биту назначается определенная кнопка. В итоге на все кнопки хватит два байта информации.

Принял два байта и выковыривай из них данные.
простейшие логические операции.
 

Stamp

★✩✩✩✩✩✩
12 Янв 2021
44
22
@Геннадий П, Если делать без структуры, придётся или передавать побайтно или передавать массив.
Что влечёт за собой работу с циклами. Если передавать структуру, достаточно дать функции передачи адрес и размер. Да с массивом можно так же. Но почти. А если передавать нужно больше 2 байт, там уже неприятности возникают.
Если передавать массив, то нужно накладывать маски на байт, что бы понять, какой бит поднят. Кроме того при передаче смешанных данных нужно будет заботится об их парсинге. В структуре достаточно просто к ним обратится. А это удобнее.
Так не лучше ли написать правильно, а потом использовать это в других разработках, где двумя байтами не обойдёшься.
 
  • Лойс +1
Реакции: te238s

bort707

★★★★★★✩
21 Сен 2020
1,760
541
спор ни о чем, байт - это та же структура
И вы оба упускаете из виду необходимость контроля целостности передачи, которая является абсолютно необходимой, как только размер данных становится более 1 байта.
Так что в любом случае понадобится протокол передачи с заголовком, суффиксом и контрольной суммой.
А что там будет внутри - байты, структура или массив - почти без разницы.