Skip to content

Commit d06c0d4

Browse files
committed
Add support for generic analog handbrakes
1 parent 21db3c6 commit d06c0d4

File tree

4 files changed

+293
-0
lines changed

4 files changed

+293
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Project Sim Racing Library for Arduino
3+
* @author David Madison
4+
* @link github.com/dmadison/Sim-Racing-Arduino
5+
* @license LGPLv3 - Copyright (c) 2022 David Madison
6+
*
7+
* This file is part of the Sim Racing Library for Arduino.
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*/
22+
23+
/**
24+
* @brief Emulates the handbrake as a joystick over USB.
25+
* @example HandbrakeJoystick.ino
26+
*/
27+
28+
// This example requires the Arduino Joystick Library
29+
// Download Here: https://github.com/MHeironimus/ArduinoJoystickLibrary
30+
31+
#include <SimRacing.h>
32+
#include <Joystick.h>
33+
34+
const int Pin_Handbrake = A2;
35+
36+
SimRacing::Handbrake handbrake(Pin_Handbrake);
37+
38+
Joystick_ Joystick(
39+
JOYSTICK_DEFAULT_REPORT_ID, // default report (no additional pages)
40+
JOYSTICK_TYPE_JOYSTICK, // so that this shows up in Windows joystick manager
41+
0, // number of buttons (none)
42+
0, // number of hat switches (none)
43+
false, false, // no X and Y axes
44+
true, // include Z axis for the handbrake
45+
false, false, false, false, false, false, false, false); // no other axes
46+
47+
const int ADC_Max = 1023; // max value of the analog inputs, 10-bit on AVR boards
48+
49+
50+
void setup() {
51+
handbrake.begin(); // initialize handbrake pins
52+
53+
// if you have one, your calibration line should go here
54+
55+
Joystick.begin(false); // 'false' to disable auto-send
56+
57+
Joystick.setZAxisRange(0, ADC_Max);
58+
}
59+
60+
void loop() {
61+
handbrake.update();
62+
63+
int pos = handbrake.getPosition(0, ADC_Max);
64+
Joystick.setZAxis(pos);
65+
66+
Joystick.sendState();
67+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Project Sim Racing Library for Arduino
3+
* @author David Madison
4+
* @link github.com/dmadison/Sim-Racing-Arduino
5+
* @license LGPLv3 - Copyright (c) 2022 David Madison
6+
*
7+
* This file is part of the Sim Racing Library for Arduino.
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*/
22+
23+
/**
24+
* @brief Prints handbrake position percentage over Serial.
25+
* @example HandbrakePrint.ino
26+
*/
27+
28+
#include <SimRacing.h>
29+
30+
const int Pin_Handbrake = A2;
31+
32+
SimRacing::Handbrake handbrake(Pin_Handbrake);
33+
34+
35+
void setup() {
36+
handbrake.begin(); // initialize handbrake pins
37+
38+
// if you have one, your calibration line should go here
39+
40+
Serial.begin(115200);
41+
while (!Serial); // wait for connection to open
42+
43+
Serial.println("Starting...");
44+
}
45+
46+
void loop() {
47+
// send some serial data to run conversational calibration
48+
if (Serial.read() != -1) {
49+
handbrake.serialCalibration();
50+
delay(2000);
51+
}
52+
53+
handbrake.update();
54+
55+
Serial.print("Handbrake: ");
56+
57+
int pos = handbrake.getPosition();
58+
Serial.print(pos);
59+
Serial.print("%");
60+
Serial.println();
61+
62+
delay(100);
63+
}

src/SimRacing.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,4 +902,106 @@ LogitechShifter::LogitechShifter(uint8_t pinX, uint8_t pinY, uint8_t pinRev, uin
902902
this->setCalibration({ 490, 440 }, { 253, 799 }, { 262, 86 }, { 460, 826 }, { 470, 76 }, { 664, 841 }, { 677, 77 }, 0.70, 0.50, 0.70);
903903
}
904904

905+
//#########################################################
906+
// Handbrake #
907+
//#########################################################
908+
909+
Handbrake::Handbrake(uint8_t pinAx, uint8_t detectPin)
910+
: analogAxis(pinAx), detector(detectPin)
911+
{}
912+
913+
void Handbrake::begin() {
914+
update(); // set initial handbrake position
915+
}
916+
917+
bool Handbrake::update() {
918+
bool changed = false;
919+
920+
detector.poll();
921+
if (detector.getState() == DeviceConnection::Connected) {
922+
changed = analogAxis.read();
923+
}
924+
else if (detector.getState() == DeviceConnection::Unplug) {
925+
analogAxis.setPosition(analogAxis.getMin());
926+
}
927+
928+
return changed;
929+
}
930+
931+
long Handbrake::getPosition(long rMin, long rMax) const {
932+
return analogAxis.getPosition(rMin, rMax);
933+
}
934+
935+
int Handbrake::getPositionRaw() const {
936+
return analogAxis.getPositionRaw();
937+
}
938+
939+
void Handbrake::setCalibration(AnalogInput::Calibration newCal) {
940+
analogAxis.setCalibration(newCal);
941+
analogAxis.setPosition(analogAxis.getMin()); // reset to min
942+
}
943+
944+
void Handbrake::serialCalibration(Stream& iface) {
945+
if (isConnected() == false) {
946+
iface.print(F("Error! Cannot perform calibration, "));
947+
iface.print(F("handbrake"));
948+
iface.println(F(" is not connected."));
949+
return;
950+
}
951+
952+
const char* separator = "------------------------------------";
953+
954+
iface.println();
955+
iface.println(F("Sim Racing Library Handbrake Calibration"));
956+
iface.println(separator);
957+
iface.println();
958+
959+
AnalogInput::Calibration newCal;
960+
961+
// read minimum
962+
iface.println(F("Keep your hand off of the handbrake to record its resting position"));
963+
iface.println(F("Send any character to continue."));
964+
waitClient(iface);
965+
966+
analogAxis.read();
967+
newCal.min = analogAxis.getPositionRaw();
968+
iface.println();
969+
970+
// read maximum
971+
iface.println(F("Now pull on the handbrake and hold it at the end of its range"));
972+
iface.println(F("Send any character to continue."));
973+
waitClient(iface);
974+
975+
analogAxis.read();
976+
newCal.max = analogAxis.getPositionRaw();
977+
iface.println();
978+
979+
// set new calibration
980+
this->setCalibration(newCal);
981+
982+
// print finished calibration
983+
iface.println(F("Here is your calibration:"));
984+
iface.println(separator);
985+
iface.println();
986+
987+
iface.print(F("handbrake.setCalibration("));
988+
iface.print('{');
989+
iface.print(newCal.min);
990+
iface.print(F(", "));
991+
iface.print(newCal.max);
992+
iface.print("});");
993+
iface.println();
994+
995+
iface.println();
996+
iface.println(separator);
997+
iface.println();
998+
999+
iface.print(F("Paste this line into the setup() function. The "));
1000+
iface.print(F("handbrake"));
1001+
iface.print(F(" will be calibrated with these values on startup."));
1002+
iface.println(F("\nCalibration complete! :)\n\n"));
1003+
1004+
flushClient(iface);
1005+
}
1006+
9051007
}; // end SimRacing namespace

src/SimRacing.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,67 @@ namespace SimRacing {
662662
/// @} Shifters
663663

664664

665+
/**
666+
* @brief Interface with analog handbrakes that use hall effect sensors
667+
*/
668+
class Handbrake : public Peripheral {
669+
public:
670+
/**
671+
* Class constructor
672+
*
673+
* @param pinAx analog pin number for the handbrake axis
674+
* @param detectPin the digital pin for device detection (high is detected)
675+
*/
676+
Handbrake(uint8_t pinAx, uint8_t detectPin = NOT_A_PIN);
677+
678+
/**
679+
* Initializes the pin for reading from the handbrake.
680+
*/
681+
virtual void begin();
682+
683+
/**
684+
* Polls the handbrake to update its position.
685+
*
686+
* @return 'true' if the gear has changed, 'false' otherwise
687+
*/
688+
virtual bool update();
689+
690+
/**
691+
* Retrieves the buffered position for the handbrake axis, rescaled to a
692+
* nominal range using the calibration values.
693+
*
694+
* By default this is rescaled to an integer percentage (0 - 100)
695+
*
696+
* @param rMin the minimum output value
697+
* @param rMax the maximum output value
698+
*
699+
* @return the handbrake position, buffered and rescaled
700+
*/
701+
long getPosition(long rMin = 0, long rMax = 100) const;
702+
703+
/**
704+
* Retrieves the buffered position for the handbrake, ignoring the
705+
* calibration data.
706+
*
707+
* @return the handbrake position, buffered
708+
*/
709+
int getPositionRaw() const;
710+
711+
/// @copydoc AnalogInput::setCalibration()
712+
void setCalibration(AnalogInput::Calibration newCal);
713+
714+
/// @copydoc AnalogShifter::serialCalibration()
715+
void serialCalibration(Stream& iface = Serial);
716+
717+
/** @copydoc Peripheral::isConnected() */
718+
bool isConnected() const { return detector.isConnected(); }
719+
720+
private:
721+
AnalogInput analogAxis; ///< axis data for the handbrake's position
722+
DeviceConnection detector; ///< detector instance for checking if the handbrake is connected
723+
};
724+
725+
665726
/**
666727
* @brief Interface with the Logitech pedals (Gas, Brake, and Clutch)
667728
* @ingroup Pedals

0 commit comments

Comments
 (0)