Skip to content

Instantly share code, notes, and snippets.

@S60Team
Last active April 12, 2021 06:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save S60Team/b759443008300798ccd690d611175614 to your computer and use it in GitHub Desktop.
Save S60Team/b759443008300798ccd690d611175614 to your computer and use it in GitHub Desktop.
Простой руль для ПК: Руль энкодер, Газ, Тормоз, Сцепление, Установка угла поворота руля - библиотека MHeironimus (ArduinoJoystickLibrary)
/* Простой руль для ПК:
Основан на: https://is.gd/gyverwheel и https://is.gd/udubon
Изменён: Гендель Владимир aka S60Team
Скачай и поставь библиотеки:
https://github.com/MHeironimus/ArduinoJoystickLibrary
https://github.com/S60Team/Rotary2
Пины: 3,4,5,6,7,8,9,10,11,12, 14,15,16 - кнопки.
*/
// Если у вас Arduino Pro Micro (0 для Arduino Leonardo):
#define ProMicro 1
// Если ось идёт вправо а руль крутится влево,
// меняем направление (0 или 1):
#define Direction 1
// Указываем Оси:
#define Axe_Throttle A0 // Газ
#define Axe_Brake A1 // Тормоз
#define Axe_Clutch A2 // Сцепление
#define Axe_HBrake A3 // Ручной тормоз
#define BTN_CAL 2 // Кнопка калибровки
// Количество подключенных кнопок:
int8_t Btnz = 16;
// Подключаем энкодер:
/* Настройки переехали в файл библиотеки Rotary2.h
Тут только подсказки:
#define DWF // Включить быструю версию DigitalWrite.
#define ENABLE_PULLUPS // Включить встроенный резистор подтяжки.
#define ENCODER2 // Включить другой тип энкодера.
#define HALF_STEP // Включить другой шаг энкодера.
*/
#include <Rotary2.h>
#define outA 0 // пин 0 (PD2)
#define outB 1 // пин 1 (PD3)
Rotary2 rotary = Rotary2(outA, outB);
volatile int16_t counter = 0;
int16_t encVal = 0;
// Подключаем геймпад (джойстик):
// Библиотека поддерживает 128 кнопок, 2 крестовины, 7 осей (X,Y,Z,Rz,Rx,Ry,Throttle)
#include <Joystick.h>
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
//JOYSTICK_TYPE_GAMEPAD,
//JOYSTICK_TYPE_MULTI_AXIS,
JOYSTICK_TYPE_JOYSTICK,
Btnz, 0, // Кол-во кнопок, кол-во крестовин
true, false, true, // X, Y, Z
true, true, true, // Rx, Ry, Rz
false, false, // Rudder(руль высоты), Throttle(Газ)
false, false, false); // Accelerator(Ускорение), brake(Тормоз), steering(Руль)
//const bool testAutoSendMode = true;
const bool testAutoSendMode = false;
// Переменные значений осей:
int16_t Axis0_ = 0, Axis1_ = 0, Axis2_ = 0, Axis3_ = 0, Axis4_ = 0;
// Переменные фильтра:
int16_t Filtr = 0, filt = 0;
// Переменные ВКЛ определения осей:
bool handbrake_present = true, throttle_present = true;
bool brake_present = true, clutch_present = true;
// Переменная режима угла поворота руля:
bool racing_mode = false;
void setup() {
pinMode(13, OUTPUT); // Встроенный светодиод
digitalWrite(13, LOW); // Встроенный светодиод ВЫКЛ
// Настраиваем энкодер:
pinMode(outA, INPUT_PULLUP); // пин энкодера
pinMode(outB, INPUT_PULLUP); // пин энкодера
//CHANGE для срабатывания прерывания если пин меняет значение:
attachInterrupt(digitalPinToInterrupt(outA),rotate,CHANGE);
attachInterrupt(digitalPinToInterrupt(outB),rotate,CHANGE);
// Кнопка для сброса позиции энкодера/установки значения поворота руля:
pinMode(BTN_CAL, INPUT_PULLUP);
// Проверяем положение кнопки при включении USB:
// (по-умолчанию режим выключен - 900°)
// при зажатой кнопке (при подсоединении к ПК): ВКЛ (270°)
if(digitalRead(BTN_CAL) == HIGH){
digitalWrite(13, LOW);
racing_mode = false;} // Кнопка сброса ВЫКЛ. Режим симулятора
else{
digitalWrite(13, HIGH);
racing_mode = true;} // Кнопка сброса ВКЛ. Гоночный режим
// Циклы для указания цифровых пинов как входы для обработки нажатия кнопок:
for (int8_t thisPin = 2; thisPin < 13; thisPin++) { // Leonardo:
pinMode(thisPin, INPUT_PULLUP); } // 3,4,5,6,7,8,9,10,11,12
for (int8_t thisPin = 14; thisPin < 17; thisPin++) {
pinMode(thisPin, INPUT_PULLUP); // 14, 15, 16
}
// Указываем угол поворота руля:
// По-умолчанию: 900°
if(racing_mode == false){
Joystick.setXAxisRange(-450,450);}
// Если зажата кн. калибровки и кн. на пин 10: 600°
else if(racing_mode == true && digitalRead(10) == LOW){
Joystick.setXAxisRange(-300,300);}
// Если зажата кн. калибровки и кн. на пин 11: 180°
else if(racing_mode == true && digitalRead(11) == LOW){
Joystick.setXAxisRange(-90,90);}
// Если зажата кн. калибровки: 270°
else{Joystick.setXAxisRange(-135,135);}
// Указываем границы осей:
Joystick.setZAxisRange(-128,127);
Joystick.setRzAxisRange(-128,127);
Joystick.setRyAxisRange(-128,127);
Joystick.setRxAxisRange(-128,127);
// Инициализация библиотеки джойстика:
if (testAutoSendMode){Joystick.begin();}
else{Joystick.begin(false);}
// Определяем, подключены ли педали и ручник к аналоговым входам:
int16_t a1 = analogRead(Axe_Throttle),
a2 = analogRead(Axe_Brake),
a3 = analogRead(Axe_Clutch),
a4 = analogRead(Axe_HBrake);
delay(10);
if(abs(a1-analogRead(Axe_Throttle)) > 10){ throttle_present = false; }
if(abs(a2-analogRead(Axe_Brake)) > 10){ brake_present = false; }
if(abs(a3-analogRead(Axe_Clutch)) > 10){ clutch_present = false; }
if(abs(a4-analogRead(Axe_HBrake)) > 10){ handbrake_present = false; }
}
void loop() {
// Сбрасываем значение осей: (на всякий случай)
Axis0_ = 0, Axis1_ = 0, Axis2_ = 0, Axis3_ = 0, Axis4_ = 0;
// Руль энкодер (360ppr/1140ppr):
if(!digitalRead(BTN_CAL)){ counter = 0;Joystick.setXAxis(0); }
encVal = counter;
Joystick.setXAxis(counter);
// Педаль Газа:
if (throttle_present == true) {
Axis1_ = analogRead(Axe_Throttle);
Axis1_ = map(Axis1_,0,1023,-128,127);
Joystick.setRxAxis(Axis1_);}
else{Joystick.setRxAxis(0);}
// Педаль Тормоза:
if (brake_present == true) {
Axis2_ = analogRead(Axe_Brake);
Axis2_ = constrain(Axis2_,235,800);
Axis2_ = map(Axis2_,235,800,-128,127);
Joystick.setZAxis(Axis2_);}
else{Joystick.setZAxis(0);}
// Педаль Сцепления:
if (clutch_present == true) {
Axis3_ = analogRead(Axe_Clutch);
Axis3_ = map(Axis3_,0,1023,-128,127);
Joystick.setRzAxis(Axis3_);}
else{Joystick.setRzAxis(0);}
// Ручной тормоз:
if (handbrake_present == true) {
Axis4_ = analogRead(Axe_HBrake);
Axis4_ = map(Axis4_,0,1023,-128,127);
Joystick.setRyAxis(Axis4_);}
else{Joystick.setRyAxis(0);}
// Перебираем оставшиеся пины и задаём их как кнопки:
#if (ProMicro == 0)
// Leonardo:
// Пины: 3,4,5,6, 7,8,9,10, 11,12
for (int thisPind = 3; thisPind < 13; thisPind++) {
if(!digitalRead(thisPind)){ Joystick.pressButton(thisPind-2); }
else{ Joystick.pressButton(thisPind-2); } }
#else
// Pro Micro:
// Пины: 3,4,5,6, 7,8,9,10
for (int thisPind = 3; thisPind < 11; thisPind++) {
if(!digitalRead(thisPind)){ Joystick.pressButton(thisPind-2); }
else{ Joystick.pressButton(thisPind-2); } }
#endif
// Пины: 14, 15, 16 (ещё кнопки!)
for (int thisPindd = 14; thisPindd < 17; thisPindd++) {
if(!digitalRead(thisPindd)){ Joystick.pressButton(thisPindd-2); }
else{ Joystick.pressButton(thisPindd-2); } }
// Отправляем значения в ПК:
if (testAutoSendMode == false){
Joystick.sendState();
delay(10);
}
}
// Функция для считывания показаний энкодера:
void rotate() {
unsigned char result = rotary.process();
#if (Direction == 0)
if (result == DIR_CW){counter++;}
else if (result == DIR_CCW){counter--;}
#else
if (result == DIR_CW){counter--;}
else if (result == DIR_CCW){counter++;}
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment