Skip to content

Instantly share code, notes, and snippets.

@S60Team
Last active April 12, 2021 06:10
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/819715b891fd1a562d7a92a1865ddf41 to your computer and use it in GitHub Desktop.
Save S60Team/819715b891fd1a562d7a92a1865ddf41 to your computer and use it in GitHub Desktop.
Простой руль для ПК: Руль энкодер, Газ, Тормоз, Сцепление, Установка угла поворота руля - библиотека NicoHood (HID)
/* Простой руль для ПК:
Основан на: https://is.gd/gyverwheel и https://is.gd/udubon
Изменён: Гендель Владимир aka S60Team
Скачай и поставь библиотеки:
https://github.com/S60Team/Rotary2
https://github.com/NicoHood/HID
Пины: 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
// Подключаем геймпад (джойстик):
// Библиотека поддерживает 32 кнопки, 1 крестовину, 6 осей (X,Y,Z,Rz,Rx,Ry)
#include "HID-Project.h"
// Указываем Оси:
#define Axe_Throttle A0 // Газ
#define Axe_Brake A1 // Тормоз
#define Axe_Clutch A2 // Сцепление
#define Axe_HBrake A3 // Ручной тормоз
#define BTN_CAL 2 // Кнопка сброса
// Подключаем энкодер:
/* Настройки переехали в файл библиотеки 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;
// Переменные значений осей:
int16_t Axis1_ = 0, Axis2_ = 0, Axis3_ = 0, Axis4_ = 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); // Встроенный светодиод ВЫКЛ
//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 (int thisPin = 3; thisPin < 13; thisPin++) { // Leonardo:
pinMode(thisPin, INPUT_PULLUP); } // 3,4,5,6,7,8,9,10,11,12
for (int thisPin = 14; thisPin < 17; thisPin++) {
pinMode(thisPin, INPUT_PULLUP); // 14, 15, 16
}
// Инициализация джойстика:
Gamepad.begin();
// Определяем, подключены ли педали и ручник к аналоговым входам:
int16_t ap1 = analogRead(Axe_Throttle),
ap2 = analogRead(Axe_Brake),
ap3 = analogRead(Axe_Clutch),
ap4 = analogRead(Axe_HBrake);
delay(10);
if(abs(ap1-analogRead(Axe_Throttle)) > 10){ throttle_present = false; }
if(abs(ap2-analogRead(Axe_Brake)) > 10){ brake_present = false; }
if(abs(ap3-analogRead(Axe_Clutch)) > 10){ clutch_present = false; }
if(abs(ap4-analogRead(Axe_HBrake)) > 10){ handbrake_present = false; }
}
//
void loop() {
// Сбрасываем значение осей: (на всякий случай)
Axis1_ = 0, Axis2_ = 0, Axis3_ = 0, Axis4_ = 0;
// Руль энкодер (360ppr/1140ppr):
// Сброс позиции энкодера кнопкой на пине 2:
if(!digitalRead(BTN_CAL)){ counter = 0;Gamepad.xAxis(0); }
encVal = counter;
// Если не зажата кнопка сброса (пин 2): 900°
if(racing_mode == false){
encVal = constrain(encVal,-450,450);
encVal = map(encVal,-450,450,-32768,32767);}
// Если зажата кнопка сброса: 270°
else {
encVal = constrain(encVal,-135,135);
encVal = map(encVal,-135,135,-32768,32767);}
Gamepad.xAxis(encVal); // Записываем значения в ось.
// Педаль Газа:
if (throttle_present == true) {
Axis1_ = analogRead(Axe_Throttle);
Axis1_ = map(Axis1_,0,1023,-32768,32767);
Gamepad.rxAxis(Axis1_);}
else{Gamepad.rxAxis(0);}
// Педаль Тормоза:
if (brake_present == true) {
Axis2_ = analogRead(Axe_Brake);
Axis2_ = map(Axis2_,0,1023,-128,127);
Gamepad.zAxis(Axis2_);}
else{Gamepad.zAxis(0);}
// Педаль Сцепления:
if (clutch_present == true) {
Axis3_ = analogRead(Axe_Clutch);
Axis3_ = map(Axis3_,0,1023,-128,127);
Gamepad.rzAxis(Axis3_);}
else{Gamepad.rzAxis(0);}
// Ручной тормоз:
if (handbrake_present == true) {
Axis4_ = analogRead(Axe_HBrake);
Axis4_ = map(Axis4_,0,1023,-32768,32767);
Gamepad.ryAxis(Axis4_);}
else{Gamepad.ryAxis(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)){ Gamepad.press(thisPind - 2); }
else{ Gamepad.release(thisPind - 2); } }
#else
// Pro Micro:
// Пины: 3,4,5,6, 7,8,9,10
for (int thisPind = 3; thisPind < 11; thisPind++) {
if(!digitalRead(thisPind)){ Gamepad.press(thisPind - 2); }
else{ Gamepad.release(thisPind - 2); } }
#endif
// Пины: 14, 15, 16 (ещё кнопки!)
for (int thisPindd = 14; thisPindd < 17; thisPindd++) {
if(!digitalRead(thisPindd)){ Gamepad.press(thisPindd - 3); }
else{ Gamepad.release(thisPindd - 3); } }
// Отправляем значения в ПК:
Gamepad.write();
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