32#if defined(__AVR_ATmega32U4__) || defined(SIM_RACING_DOXYGEN)
40 const PinNum Pin_Brake = A1;
41 const PinNum Pin_Clutch = A0;
42 const PinNum Pin_Detect = 10;
51 return CreateShieldObject<LogitechPedals, 1>();
60 const PinNum Pin_X_Wiper = A1;
61 const PinNum Pin_Y_Wiper = A0;
62 const PinNum Pin_DataOut = 14;
63 const PinNum Pin_Detect = A2;
65 return LogitechShifter(Pin_X_Wiper, Pin_Y_Wiper, Pin_DataOut, Pin_Detect);
72 return CreateShieldObject<LogitechShifter, 1>();
80 const PinNum Pin_X_Wiper = A1;
81 const PinNum Pin_Y_Wiper = A0;
82 const PinNum Pin_DataOut = 14;
84 const PinNum Pin_Latch = 10;
85 const PinNum Pin_Clock = 15;
88 const PinNum Pin_Detect = A2;
90 return LogitechShifterG27(Pin_X_Wiper, Pin_Y_Wiper, Pin_Latch, Pin_Clock, Pin_DataOut, Pin_LED, Pin_Detect);
98 const PinNum Pin_X_Wiper = A1;
99 const PinNum Pin_Y_Wiper = A0;
100 const PinNum Pin_DataOut = 14;
102 const PinNum Pin_Latch = 10;
103 const PinNum Pin_Clock = A2;
105 const PinNum Pin_LED = 16;
106 const PinNum Pin_Detect = 15;
108 return LogitechShifterG25(Pin_X_Wiper, Pin_Y_Wiper, Pin_Latch, Pin_Clock, Pin_DataOut, Pin_LED, Pin_Detect);
124static constexpr PinNum sanitizePin(PinNum pin) {
125 return pin < 0 ? UnusedPin : pin;
139static constexpr long invertAxis(
long value,
long min,
long max) {
140 return max - value + min;
161static long remap(
long value,
long inMin,
long inMax,
long outMin,
long outMax) {
164 const long temp = inMin;
168 value = invertAxis(value, inMin, inMax);
171 if (value <= inMin)
return outMin;
172 if (value >= inMax)
return outMax;
173 return map(value, inMin, inMax, outMin, outMax);
183static float floatPercent(
float pct) {
184 if (pct < 0.0) pct = 0.0;
185 else if (pct > 1.0) pct = 1.0;
197static void flushClient(Stream& client) {
198 while (client.read() != -1) { delay(2); }
206static void waitClient(Stream& client) {
208 while (client.peek() == -1) { delay(1); }
223static void readFloat(
float& value, Stream& client) {
224 client.print(
"(to skip this step and go with the default value of '");
226 client.print(
"', send 'n')");
230 if (client.peek() ==
'n')
return;
235 client.setTimeout(200);
236 input = client.parseFloat();
238 if (input >= 0.0 && input <= 1.0) {
239 client.print(F(
"Set the new value to '"));
244 client.print(F(
"Input '"));
246 client.print(F(
"' not within acceptable range (0.0 - 1.0). Please try again."));
262 pin(sanitizePin(pin)), inverted(activeLow), stablePeriod(detectTime),
282 lastChange(millis() - detectTime)
291 const bool newState = readPin();
296 if (pinState != newState) {
298 lastChange = millis();
301 if (pinState == HIGH) {
313 if (pinState == HIGH) {
314 const unsigned long now = millis();
315 if (now - lastChange >= stablePeriod) {
338 const unsigned long now = millis();
342 if (now - lastChange < stablePeriod) {
343 lastChange = now - stablePeriod;
348bool DeviceConnection::readPin()
const {
349 if (pin == UnusedPin)
return HIGH;
350 const bool state = digitalRead(pin);
351 return inverted ? !state : state;
362 if (pin != UnusedPin) {
368 bool changed =
false;
371 const int previous = this->position;
372 this->position = analogRead(pin);
375 if (previous != this->position) {
383 !(previous < rMin && this->position < rMin) &&
387 !(previous > rMax && this->position > rMax)
406 return this->position;
410 return (this->cal.
min > this->cal.max);
414 this->position = newPos;
435 if (this->detector) {
436 this->detector->
poll();
448 if (this->detector) {
462 if (this->detector) {
483 this->changed =
false;
488 changed |= pedalData[i].
read();
495 const int min = pedalData[i].
getMin();
504 return this->changed;
524 pedalData[pedal].
setPosition(pedalData[pedal].getMin());
534 case(PedalID::Brake):
537 case(PedalID::Clutch):
548 const char* separator =
"------------------------------------";
551 iface.println(F(
"Sim Racing Library Pedal Calibration"));
552 iface.println(separator);
556 iface.println(F(
"Take your feet off of the pedals so they move to their resting position."));
557 iface.println(F(
"Send any character to continue."));
560 const int MaxPedals = 3;
565 for (
int i = 0; (i <
getNumPedals()) && (i < MaxPedals); i++) {
569 iface.println(F(
"\nMinimum values for all pedals successfully recorded!\n"));
570 iface.println(separator);
573 iface.println(F(
"\nOne at a time, let's measure the maximum range of each pedal.\n"));
574 for (
int i = 0; (i <
getNumPedals()) && (i < MaxPedals); i++) {
576 iface.print(F(
"Push the "));
580 iface.print(F(
" pedal to the floor. "));
582 iface.println(F(
"Send any character to continue."));
590 iface.println(separator);
593 float DeadzoneMin = 0.01;
594 float DeadzoneMax = 0.025;
596 iface.println(F(
"These settings are optional. Send 'y' to customize. Send any other character to continue with the default values."));
598 iface.print(F(
" * Pedal Travel Deadzone, Start: \t"));
599 iface.print(DeadzoneMin);
600 iface.println(F(
" (Used to avoid the pedal always being slightly pressed)"));
602 iface.print(F(
" * Pedal Travel Deadzone, End: \t"));
603 iface.print(DeadzoneMax);
604 iface.println(F(
" (Used to guarantee that the pedal can be fully pressed)"));
610 if (iface.read() ==
'y') {
611 iface.println(F(
"Set the pedal travel starting deadzone as a floating point percentage."));
612 readFloat(DeadzoneMin, iface);
615 iface.println(F(
"Set the pedal travel ending deadzone as a floating point percentage."));
616 readFloat(DeadzoneMax, iface);
623 for (
int i = 0; (i <
getNumPedals()) && (i < MaxPedals); i++) {
624 auto &cMin = pedalCal[i].
min;
625 auto &cMax = pedalCal[i].
max;
627 const int range = abs(cMax - cMin);
628 const int dzMin = DeadzoneMin * (float)range;
629 const int dzMax = DeadzoneMax * (float)range;
644 iface.println(F(
"Here is your calibration:"));
645 iface.println(separator);
648 iface.print(F(
"pedals.setCalibration("));
650 for (
int i = 0; (i <
getNumPedals()) && (i < MaxPedals); i++) {
651 if(i > 0) iface.print(F(
", "));
654 iface.print(pedalCal[i].min);
655 iface.print(F(
", "));
656 iface.print(pedalCal[i].max);
666 iface.println(separator);
669 iface.print(F(
"Paste this line into the setup() function. The "));
670 iface.print(F(
"pedals"));
671 iface.print(F(
" will be calibrated with these values on startup."));
672 iface.println(F(
"\nCalibration complete! :)\n\n"));
679 :
Pedals(pedalData, NumPedals),
690 :
Pedals(pedalData, NumPedals),
704 detectObj(detectPin, false)
717 detectObj(detectPin, false)
730 MinGear(min), MaxGear(max)
732 this->currentGear = this->previousGear = 0;
737 if (gear < MinGear || gear > MaxGear) {
741 this->previousGear = this->currentGear;
742 this->currentGear = gear;
756 if (gear > 0 && gear <= 9)
778 if (gear < 0 || gear > 9) {
812const float AnalogShifter::CalEngagementPoint = 0.70;
813const float AnalogShifter::CalReleasePoint = 0.50;
814const float AnalogShifter::CalEdgeOffset = 0.60;
825 pinReverse(sanitizePin(pinRev)),
831 pinMode(pinReverse, INPUT);
841 analogAxis[Axis::X].
setPosition(calibration.neutralX);
842 analogAxis[Axis::Y].
setPosition(calibration.neutralY);
845 this->reverseState =
false;
855 analogAxis[Axis::X].
read();
856 analogAxis[Axis::Y].
read();
861 this->reverseState = this->readReverseButton();
865 const bool prevOdd = ((previousGear != -1) && (previousGear & 1));
866 const bool prevEven = (!prevOdd && previousGear != 0);
871 if ((prevOdd && y > calibration.oddRelease) || (prevEven && y < calibration.evenRelease)) {
872 newGear = previousGear;
878 if (y > calibration.oddTrigger) {
881 else if (y < calibration.evenTrigger) {
887 if (x > calibration.rightEdge) newGear += 4;
888 else if (x >= calibration.leftEdge) newGear += 2;
896 if (reverse && newGear == 5) {
903 else if ((reverse || previousGear == -1) && newGear == 6) {
916 if (ax != Axis::X && ax != Axis::Y)
return min;
925bool AnalogShifter::readReverseButton() {
928 if (pinReverse == UnusedPin) {
931 return digitalRead(pinReverse);
937 return this->reverseState;
943 float engagePoint,
float releasePoint,
float edgeOffset) {
946 engagePoint = floatPercent(engagePoint);
947 releasePoint = floatPercent(releasePoint);
948 edgeOffset = floatPercent(edgeOffset);
950 const int xLeft = (g1.
x + g2.
x) / 2;
951 const int xRight = (g5.
x + g6.
x) / 2;
953 const int yOdd = (g1.
y + g3.
y + g5.
y) / 3;
954 const int yEven = (g2.
y + g4.
y + g6.
y) / 3;
961 calibration.neutralX = neutral.
x;
962 calibration.neutralY = neutral.
y;
968 const Axis axes[2] = { Axis::X, Axis::Y };
969 int*
const neutralAxis[2] = { &neutral.
x, &neutral.
y };
970 for (
int i = 0; i < 2; i++) {
973 *neutralAxis[i] = analogAxis[axes[i]].
getPosition();
985 calibration.oddTrigger = neutral.
y + ((float)yOddDiff * engagePoint);
986 calibration.oddRelease = neutral.
y + ((float)yOddDiff * releasePoint);
988 calibration.evenTrigger = neutral.
y - ((float)yEvenDiff * engagePoint);
989 calibration.evenRelease = neutral.
y - ((float)yEvenDiff * releasePoint);
991 calibration.leftEdge = neutral.
x - ((float)leftDiff * edgeOffset);
992 calibration.rightEdge = neutral.
x + ((float)rightDiff * edgeOffset);
995 Serial.print(
"Odd Trigger: ");
996 Serial.println(calibration.oddTrigger);
997 Serial.print(
"Odd Release: ");
998 Serial.println(calibration.oddRelease);
999 Serial.print(
"Even Trigger: ");
1000 Serial.println(calibration.evenTrigger);
1001 Serial.print(
"Even Release: ");
1002 Serial.println(calibration.evenRelease);
1003 Serial.print(
"Left Edge: ");
1004 Serial.println(calibration.leftEdge);
1005 Serial.print(
"Right Edge: ");
1006 Serial.println(calibration.rightEdge);
1009 Serial.print(
"X Min: ");
1010 Serial.println(analogAxis[Axis::X].getMin());
1011 Serial.print(
"X Max: ");
1012 Serial.println(analogAxis[Axis::X].getMax());
1014 Serial.print(
"Y Min: ");
1015 Serial.println(analogAxis[Axis::Y].getMin());
1016 Serial.print(
"Y Max: ");
1017 Serial.println(analogAxis[Axis::Y].getMax());
1023 iface.print(F(
"Error! Cannot perform calibration, "));
1024 iface.print(F(
"shifter"));
1025 iface.println(F(
" is not connected."));
1029 const char* separator =
"------------------------------------";
1032 iface.println(F(
"Sim Racing Library Shifter Calibration"));
1033 iface.println(separator);
1037 float engagementPoint = CalEngagementPoint;
1038 float releasePoint = CalReleasePoint;
1039 float edgeOffset = CalEdgeOffset;
1041 for (
int i = 0; i <= 6; i++) {
1044 iface.print(F(
"Please move the gear shifter into "));
1045 iface.print(gearName);
1046 iface.println(F(
". Send any character to continue."));
1056 iface.print(
"Gear '");
1057 iface.print(gearName);
1058 iface.print(
"' position recorded as { ");
1059 iface.print(gears[i].x);
1061 iface.print(gears[i].y);
1062 iface.println(
" }");
1066 iface.println(separator);
1069 iface.println(F(
"These settings are optional. Send 'y' to customize. Send any other character to continue with the default values."));
1071 iface.print(F(
" * Gear Engagement Point: \t"));
1072 iface.println(engagementPoint);
1074 iface.print(F(
" * Gear Release Point: \t"));
1075 iface.println(releasePoint);
1077 iface.print(F(
" * Horizontal Gate Offset:\t"));
1078 iface.println(edgeOffset);
1084 if (iface.read() ==
'y') {
1085 iface.println(F(
"Set the engagement point as a floating point percentage. This is the percentage away from the neutral axis on Y to start engaging gears."));
1086 readFloat(engagementPoint, iface);
1089 iface.println(F(
"Set the release point as a floating point percentage. This is the percentage away from the neutral axis on Y to go back into neutral. It must be less than the engagement point."));
1090 readFloat(releasePoint, iface);
1093 iface.println(F(
"Set the gate offset as a floating point percentage. This is the percentage away from the neutral axis on X to select the side gears."));
1094 readFloat(edgeOffset, iface);
1100 this->
setCalibration(gears[0], gears[1], gears[2], gears[3], gears[4], gears[5], gears[6], engagementPoint, releasePoint, edgeOffset);
1102 iface.println(F(
"Here is your calibration:"));
1103 iface.println(separator);
1106 iface.print(F(
"shifter.setCalibration( "));
1108 for (
int i = 0; i < 7; i++) {
1111 iface.print(gears[i].x);
1113 iface.print(gears[i].y);
1118 iface.print(engagementPoint);
1120 iface.print(releasePoint);
1122 iface.print(edgeOffset);
1127 iface.println(separator);
1130 iface.println(F(
"Paste this line into the setup() function to calibrate on startup."));
1131 iface.println(F(
"\n\nCalibration complete! :)\n"));
1141 detectObj(detectPin, false)
1144 this->
setCalibration({ 490, 440 }, { 253, 799 }, { 262, 86 }, { 460, 826 }, { 470, 76 }, { 664, 841 }, { 677, 77 });
1156 pinLatch(sanitizePin(pinLatch)), pinClock(sanitizePin(pinClock)), pinData(sanitizePin(pinData)),
1157 pinLed(sanitizePin(pinLed))
1159 this->pinModesSet =
false;
1161 this->buttonStates = this->previousButtons = 0x0000;
1164 this->
setCalibration({ 453, 470 }, { 247, 828 }, { 258, 6 }, { 449, 878 }, { 472, 5 }, { 645, 880 }, { 651, 21 });
1167void LogitechShifterG27::cacheButtons(uint16_t newStates) {
1168 this->previousButtons = this->buttonStates;
1169 this->buttonStates = newStates;
1172void LogitechShifterG27::setPinModes(
bool enabled) {
1177 this->pinData == UnusedPin ||
1178 this->pinLatch == UnusedPin ||
1179 this->pinClock == UnusedPin)
1185 pinMode(this->pinData, INPUT);
1194 digitalWrite(this->pinLatch, HIGH);
1195 pinMode(this->pinLatch, OUTPUT);
1198 digitalWrite(this->pinClock, LOW);
1199 pinMode(this->pinClock, OUTPUT);
1203 if (this->pinLed != UnusedPin) {
1204 digitalWrite(this->pinLed, !(this->ledState));
1205 pinMode(this->pinLed, OUTPUT);
1216 pinMode(this->pinLatch, INPUT);
1217 digitalWrite(this->pinLatch, HIGH);
1220 pinMode(this->pinClock, INPUT);
1221 digitalWrite(this->pinClock, LOW);
1224 if (this->pinLed != UnusedPin) {
1225 pinMode(this->pinLed, INPUT);
1226 digitalWrite(this->pinLed, LOW);
1230 this->pinModesSet = enabled;
1234 this->ledState = state;
1237uint16_t LogitechShifterG27::readShiftRegisters() {
1239 if (!this->pinModesSet)
return 0x0000;
1241 uint16_t data = 0x0000;
1246 digitalWrite(this->pinLatch, LOW);
1247 delayMicroseconds(12);
1248 digitalWrite(this->pinLatch, HIGH);
1249 delayMicroseconds(12);
1253 for (
int i = 0; i < 16; ++i) {
1254 digitalWrite(this->pinClock, LOW);
1255 const bool state = digitalRead(this->pinData);
1256 if (state) data |= 1 << (15 - i);
1257 digitalWrite(this->pinClock, HIGH);
1258 delayMicroseconds(6);
1260 digitalWrite(this->pinClock, LOW);
1273 if (data == 0xFFFF) {
1284 this->setPinModes(0);
1292 bool changed =
false;
1297 if (!this->pinModesSet) {
1298 this->setPinModes(1);
1302 digitalWrite(this->pinLed, !(this->ledState));
1305 const uint16_t data = this->readShiftRegisters();
1306 this->cacheButtons(data);
1313 if (this->pinModesSet) {
1314 this->setPinModes(0);
1317 this->cacheButtons(0x0000);
1328 return this->buttonStates != this->previousButtons;
1332 return this->extractButton(button, this->buttonStates);
1336 return this->
getButton(button) != this->extractButton(button, this->previousButtons);
1348 uint8_t dpad = 0x00;
1349 for (uint8_t i = 0; i < 4; ++i) {
1350 dpad |= (this->
getButton(pads[i]) << i);
1363 const uint8_t hat_table[16] = {
1383 int16_t angle = hat_table[dpad & 0x0F] * 45;
1386 if (angle == 360) angle = -1;
1391bool LogitechShifterG27::readReverseButton() {
1403const float LogitechShifterG25::CalEngagementPoint = 0.70;
1404const float LogitechShifterG25::CalReleasePoint = 0.50;
1414 pinLatch, pinClock, pinData,
1419 sequentialProcess(false),
1423 this->
setCalibration({ 508, 435 }, { 310, 843 }, { 303, 8 }, { 516, 827 }, { 540, 14 }, { 713, 846 }, { 704, 17 });
1428 this->sequentialProcess =
false;
1429 this->sequentialState = 0;
1454 if (this->sequentialProcess) {
1462 const int8_t prevState = this->sequentialState;
1465 if (this->sequentialState == 0) {
1466 if (y >= this->seqCalibration.upTrigger) this->sequentialState = 1;
1467 else if (y <= this->seqCalibration.downTrigger) this->sequentialState = -1;
1471 else if ((this->sequentialState == 1) && (y < this->seqCalibration.upRelease)) {
1472 this->sequentialState = 0;
1476 else if ((this->sequentialState == -1) && (y > this->seqCalibration.downRelease)) {
1477 this->sequentialState = 0;
1481 if (prevState != this->sequentialState) {
1490 this->sequentialProcess =
true;
1496 if (this->sequentialProcess) {
1497 this->sequentialProcess =
false;
1498 this->sequentialState = 0;
1511 return this->sequentialState == 1;
1515 return this->sequentialState == -1;
1520 engagePoint = floatPercent(engagePoint);
1521 releasePoint = floatPercent(releasePoint);
1525 if (releasePoint > engagePoint) {
1526 releasePoint = engagePoint;
1530 const int upRange = up - neutral;
1531 const int downRange = neutral - down;
1534 this->seqCalibration.upTrigger = neutral + (upRange * engagePoint);
1535 this->seqCalibration.upRelease = neutral + (upRange * releasePoint);
1537 this->seqCalibration.downTrigger = neutral - (downRange * engagePoint);
1538 this->seqCalibration.downRelease = neutral - (downRange * releasePoint);
1544 iface.print(F(
"Error! Cannot perform calibration, "));
1545 iface.print(F(
"shifter"));
1546 iface.println(F(
" is not connected."));
1550 const char* separator =
"------------------------------------";
1553 iface.println(F(
"Sim Racing Library G25 Sequential Shifter Calibration"));
1554 iface.println(separator);
1558 iface.print(F(
"Please press down on the shifter and move the dial counter-clockwise to put the shifter into sequential mode"));
1559 iface.print(F(
". Send any character to continue."));
1560 iface.println(F(
" Send 'q' to quit."));
1567 if (iface.read() ==
'q') {
1568 iface.println(F(
"Quitting sequential calibration! Goodbye <3"));
1575 iface.println(F(
"Error: The shifter is not in sequential mode"));
1580 float engagementPoint = LogitechShifterG25::CalEngagementPoint;
1581 float releasePoint = LogitechShifterG25::CalReleasePoint;
1583 const uint8_t NumPoints = 3;
1584 const char* directions[2] = {
1588 int data[NumPoints];
1590 int& neutral = data[0];
1591 int& yMax = data[1];
1592 int& yMin = data[2];
1594 for (uint8_t i = 0; i < NumPoints; ++i) {
1596 iface.print(F(
"Leave the gear shifter in neutral"));
1599 iface.print(F(
"Please move the gear shifter to sequentially shift "));
1600 iface.print(directions[i - 1]);
1601 iface.print(F(
" and hold it there"));
1603 iface.println(F(
". Send any character to continue."));
1611 iface.println(F(
"These settings are optional. Send 'y' to customize. Send any other character to continue with the default values."));
1613 iface.print(F(
" * Shift Engagement Point: \t"));
1614 iface.println(engagementPoint);
1616 iface.print(F(
" * Shift Release Point: \t"));
1617 iface.println(releasePoint);
1623 if (iface.read() ==
'y') {
1624 iface.println(F(
"Set the engagement point as a floating point percentage. This is the percentage away from the neutral axis on Y to start shifting."));
1625 readFloat(engagementPoint, iface);
1628 iface.println(F(
"Set the release point as a floating point percentage. This is the percentage away from the neutral axis on Y to stop shifting. It must be less than the engagement point."));
1629 readFloat(releasePoint, iface);
1638 iface.println(F(
"Here is your calibration:"));
1639 iface.println(separator);
1642 iface.print(F(
"shifter.setCalibrationSequential( "));
1644 iface.print(neutral);
1651 iface.print(engagementPoint);
1653 iface.print(releasePoint);
1658 iface.println(separator);
1661 iface.println(F(
"Paste this line into the setup() function to calibrate on startup."));
1662 iface.println(F(
"\n\nCalibration complete! :)\n"));
1680 this->changed =
false;
1684 this->changed = this->analogAxis.
read();
1689 const int min = this->analogAxis.
getMin();
1694 this->changed =
true;
1698 return this->changed;
1716 iface.print(F(
"Error! Cannot perform calibration, "));
1717 iface.print(F(
"handbrake"));
1718 iface.println(F(
" is not connected."));
1722 const char* separator =
"------------------------------------";
1725 iface.println(F(
"Sim Racing Library Handbrake Calibration"));
1726 iface.println(separator);
1732 iface.println(F(
"Keep your hand off of the handbrake to record its resting position"));
1733 iface.println(F(
"Send any character to continue."));
1741 iface.println(F(
"Now pull on the handbrake and hold it at the end of its range"));
1742 iface.println(F(
"Send any character to continue."));
1753 iface.println(F(
"Here is your calibration:"));
1754 iface.println(separator);
1757 iface.print(F(
"handbrake.setCalibration("));
1759 iface.print(newCal.
min);
1760 iface.print(F(
", "));
1761 iface.print(newCal.
max);
1766 iface.println(separator);
1769 iface.print(F(
"Paste this line into the setup() function. The "));
1770 iface.print(F(
"handbrake"));
1771 iface.print(F(
" will be calibrated with these values on startup."));
1772 iface.println(F(
"\nCalibration complete! :)\n\n"));
Header file for the Sim Racing Library.
Axis
Enumeration for analog axis names, mapped to integers.
const PinNum UnusedPin
Dummy pin number signaling that a pin is unused and can be safely ignored.
int16_t PinNum
Type alias for pin numbers, using Arduino numbering.
Interface with shifters using two potentiometers for gear position.
void setCalibration(GearPosition neutral, GearPosition g1, GearPosition g2, GearPosition g3, GearPosition g4, GearPosition g5, GearPosition g6, float engagePoint=CalEngagementPoint, float releasePoint=CalReleasePoint, float edgeOffset=CalEdgeOffset)
Calibrate the gear shifter for more accurate shifting.
void serialCalibration(Stream &iface=Serial)
Runs an interactive calibration tool using the serial interface.
bool getReverseButton() const
Checks the current state of the "reverse" button at the bottom of the shift column.
AnalogShifter(Gear gearMin, Gear gearMax, PinNum pinX, PinNum pinY, PinNum pinRev=UnusedPin)
Class constructor.
int getPositionRaw(Axis ax) const
Retrieves the buffered position for the analog axis.
virtual bool updateState(bool connected)
Perform an internal poll of the hardware to refresh the class state.
virtual void begin()
Initializes the hardware pins for reading the gear states.
long getPosition(Axis ax, long rMin=AnalogInput::Min, long rMax=AnalogInput::Max) const
Retrieves the buffered position for the analog axis, rescaled to a nominal range using the calibratio...
Used for tracking whether a device is connected to a specific pin and stable.
void poll()
Checks if the pin detects a connection.
void setStablePeriod(unsigned long t)
Set how long the detection pin must be stable for before the device is considered to be 'connected'.
ConnectionState getState() const
Retrieves the current ConnectionState from the instance.
DeviceConnection(PinNum pin, bool activeLow=false, unsigned long detectTime=250)
Class constructor.
ConnectionState
The state of the connection, whether it is connected, disconnected, and everywhere in-between.
@ Unplug
Device was just removed (connection ends)
@ PlugIn
Device was just plugged in (connection starts), unstable.
@ Disconnected
No connection present.
@ Connected
Connection present and stable.
bool isConnected() const
Check if the device is physically connected to the board.
virtual void begin()
Initializes the pin for reading from the handbrake.
long getPosition(long rMin=0, long rMax=100) const
Retrieves the buffered position for the handbrake axis, rescaled to a nominal range using the calibra...
void setCalibration(AnalogInput::Calibration newCal)
Calibrate the axis' min/max values for rescaling.
int getPositionRaw() const
Retrieves the buffered position for the handbrake, ignoring the calibration data.
Handbrake(PinNum pinAx)
Class constructor.
virtual bool updateState(bool connected)
Perform an internal poll of the hardware to refresh the class state.
void serialCalibration(Stream &iface=Serial)
Runs an interactive calibration tool using the serial interface.
LogitechDrivingForceGT_Pedals(PinNum pinGas, PinNum pinBrake, PinNum pinDetect=UnusedPin)
Class constructor.
Interface with the Logitech pedals (Gas, Brake, and Clutch)
LogitechPedals(PinNum pinGas, PinNum pinBrake, PinNum pinClutch, PinNum pinDetect=UnusedPin)
Class constructor.
Interface with the Logitech G25 shifter.
bool getShiftDown() const
Check if the sequential shifter is shifted down.
LogitechShifterG25(PinNum pinX, PinNum pinY, PinNum pinLatch, PinNum pinClock, PinNum pinData, PinNum pinLed=UnusedPin, PinNum pinDetect=UnusedPin)
Class constructor.
void serialCalibrationSequential(Stream &iface=Serial)
Runs an interactive calibration tool using the serial interface.
virtual bool updateState(bool connected)
Perform an internal poll of the hardware to refresh the class state.
bool getShiftUp() const
Check if the sequential shifter is shifted up.
bool inSequentialMode() const
Check if the shifter is in sequential shifting mode.
void setCalibrationSequential(int neutral, int up, int down, float engagePoint=LogitechShifterG25::CalEngagementPoint, float releasePoint=LogitechShifterG25::CalReleasePoint)
Calibrate the sequential shifter for more accurate shifting.
virtual void begin()
Initializes the hardware pins for reading the gear states and the buttons.
Interface with the Logitech G27 shifter.
int getDpadAngle() const
Get the directional pad angle in degrees.
Button
Enumeration of button values.
@ BUTTON_REVERSE
Reverse button (press down on the shifter)
@ BUTTON_SEQUENTIAL
Sequential mode button (turn the dial counter-clockwise)
@ DPAD_DOWN
Down button of the directional pad.
@ DPAD_RIGHT
Right button of the directional pad.
@ DPAD_UP
Top button of the directional pad.
@ DPAD_LEFT
Left button of the directional pad.
bool getButton(Button button) const
Retrieve the state of a single button.
bool buttonsChanged() const
Checks if any of the buttons have changed since the last update.
LogitechShifterG27(PinNum pinX, PinNum pinY, PinNum pinLatch, PinNum pinClock, PinNum pinData, PinNum pinLed=UnusedPin, PinNum pinDetect=UnusedPin)
Class constructor.
bool getButtonChanged(Button button) const
Checks whether a button has changed between updates.
virtual void begin()
Initializes the hardware pins for reading the gear states and the buttons.
virtual bool updateState(bool connected)
Perform an internal poll of the hardware to refresh the class state.
void setPowerLED(bool state)
Sets the state of the shifter's power LED.
Interface with the Logitech Driving Force shifter.
LogitechShifter(PinNum pinX, PinNum pinY, PinNum pinRev=UnusedPin, PinNum pinDetect=UnusedPin)
Class constructor.
Base class for all pedals instances.
void serialCalibration(Stream &iface=Serial)
Runs an interactive calibration tool using the serial interface.
void setCalibration(PedalID pedal, AnalogInput::Calibration cal)
Calibrate a pedal's min/max values for rescaling.
Pedals(AnalogInput *dataPtr, uint8_t nPedals)
Class constructor.
static String getPedalName(PedalID pedal)
Utility function to get the string name for each pedal.
virtual bool updateState(bool connected)
Perform an internal poll of the hardware to refresh the class state.
bool hasPedal(PedalID pedal) const
Checks if a given pedal is present in the class.
int getPositionRaw(PedalID pedal) const
Retrieves the buffered position for the pedal, ignoring the calibration data.
virtual void begin()
Initialize the hardware (if necessary)
long getPosition(PedalID pedal, long rMin=0, long rMax=100) const
Retrieves the buffered position for the pedal, rescaled to a nominal range using the calibration valu...
int getNumPedals() const
Retrieves the number of pedals handled by the class.
bool isConnected() const
Check if the device is physically connected to the board.
void setDetectPtr(DeviceConnection *d)
Sets the pointer to the detector object.
bool update()
Perform a poll of the hardware to refresh the class state.
virtual bool updateState(bool connected)=0
Perform an internal poll of the hardware to refresh the class state.
void setStablePeriod(unsigned long t)
Set how long the detection pin must be stable for before the device is considered to be 'connected'.
Base class for all shifter instances.
String getGearString() const
Returns a String that represents the current gear.
void setGear(Gear gear)
Changes the currently set gear, internally.
Shifter(Gear min, Gear max)
Class constructor.
int8_t Gear
Type alias for gear numbers.
char getGearChar() const
Returns a character that represents the current gear.
Gear getGear() const
Returns the currently selected gear.
bool gearChanged() const
Checks whether the current gear has changed since the last update.
Pedal implementation for devices with gas, brake, and clutch.
ThreePedals(PinNum pinGas, PinNum pinBrake, PinNum pinClutch)
Class constructor.
void setCalibration(AnalogInput::Calibration gasCal, AnalogInput::Calibration brakeCal, AnalogInput::Calibration clutchCal)
Sets the calibration data (min/max) for the pedals.
Pedal implementation for devices with only gas and brake.
void setCalibration(AnalogInput::Calibration gasCal, AnalogInput::Calibration brakeCal)
Sets the calibration data (min/max) for the pedals.
TwoPedals(PinNum pinGas, PinNum pinBrake)
Class constructor.
Simple struct to store X/Y coordinates for the calibration function.
int y
Y coordinate of the gear position from the ADC.
int x
X coordinate of the gear position from the ADC.