@@ -1285,6 +1285,271 @@ bool LogitechShifterG27::readReverseButton() {
12851285}
12861286
12871287
1288+ /*
1289+ * Static calibration constants
1290+ * These values are arbitrary - just what worked well with my own shifter.
1291+ */
1292+ const float LogitechShifterG25::CalEngagementPoint = 0.70 ;
1293+ const float LogitechShifterG25::CalReleasePoint = 0.50 ;
1294+
1295+ LogitechShifterG25::LogitechShifterG25 (
1296+ PinNum pinX, PinNum pinY,
1297+ PinNum pinLatch, PinNum pinClock, PinNum pinData,
1298+ PinNum pinDetect,
1299+ PinNum pinLed
1300+ ) :
1301+ LogitechShifterG27(
1302+ pinX, pinY,
1303+ pinLatch, pinClock, pinData,
1304+ pinDetect,
1305+ pinLed
1306+ ),
1307+
1308+ sequentialProcess(false ), // not in sequential mode
1309+ sequentialState(0 ) // no sequential buttons pressed
1310+ {
1311+ // using the values from my own shifter
1312+ this ->setCalibrationSequential (425 , 619 , 257 , 0.70 , 0.50 );
1313+ }
1314+
1315+ void LogitechShifterG25::begin () {
1316+ this ->sequentialProcess = false ; // clear process flag
1317+ this ->sequentialState = 0 ; // clear any pressed buttons
1318+
1319+ this ->LogitechShifterG27 ::begin (); // call base class begin()
1320+ }
1321+
1322+ bool LogitechShifterG25::updateState (bool connected) {
1323+ // call the base class to update the state of the
1324+ // buttons and the H-pattern shifter
1325+ bool changed = this ->LogitechShifterG27 ::updateState (connected);
1326+
1327+ // if we're connected and in sequential mode...
1328+ if (connected && this ->inSequentialMode ()) {
1329+
1330+ // clear 'changed', because this will falsely report a change
1331+ // if we've "shifted" into 2nd/4th in the process of sequential
1332+ // shifting
1333+ changed = false ;
1334+
1335+ // force neutral gear, ignoring the H-pattern selection
1336+ this ->setGear (0 );
1337+
1338+ // edge case: if we've not just switched into sequential mode,
1339+ // we need to ignore the H-pattern gear change (to 2/4, and then
1340+ // set by us to neutral). We can do that, hackily, by setting to
1341+ // neutral again to clear the cached gear for comparison.
1342+ if (this ->sequentialProcess ) {
1343+ this ->setGear (0 );
1344+ }
1345+
1346+ // read the raw y axis value, ignoring the H-pattern calibration
1347+ const int y = this ->getPositionRaw (Axis::Y);
1348+
1349+ // save the previous state for reference
1350+ const int8_t prevState = this ->sequentialState ;
1351+
1352+ // if we're neutral, check for up/down shift
1353+ if (this ->sequentialState == 0 ) {
1354+ if (y >= this ->seqCalibration .upTrigger ) this ->sequentialState = 1 ;
1355+ else if (y <= this ->seqCalibration .downTrigger ) this ->sequentialState = -1 ;
1356+ }
1357+
1358+ // if we're in up-shift mode, check for release
1359+ else if ((this ->sequentialState == 1 ) && (y < this ->seqCalibration .upRelease )) {
1360+ this ->sequentialState = 0 ;
1361+ }
1362+
1363+ // if we're in down-shift mode, check for release
1364+ else if ((this ->sequentialState == -1 ) && (y > this ->seqCalibration .downRelease )) {
1365+ this ->sequentialState = 0 ;
1366+ }
1367+
1368+ // set the 'changed' flag if the sequential state changed
1369+ if (prevState != this ->sequentialState ) {
1370+ changed = true ;
1371+ }
1372+ // otherwise, set 'changed' based on the buttons *only*
1373+ else {
1374+ changed = this ->buttonsChanged ();
1375+ }
1376+
1377+ // set 'process' flag to handle edge case on subsequent updates
1378+ this ->sequentialProcess = true ;
1379+ }
1380+
1381+ // if we're not connected or if the sequential mode has been disabled,
1382+ // clear the sequential flags if they have been set
1383+ else {
1384+ if (this ->sequentialProcess ) {
1385+ this ->sequentialProcess = false ; // not in sequential mode
1386+ this ->sequentialState = 0 ; // no sequential buttons pressed
1387+ changed = true ;
1388+ }
1389+ }
1390+
1391+ return changed;
1392+ }
1393+
1394+ bool LogitechShifterG25::inSequentialMode () const {
1395+ return this ->getButton (BUTTON_SEQUENTIAL);
1396+ }
1397+
1398+ bool LogitechShifterG25::getShiftUp () const {
1399+ return this ->sequentialState == 1 ;
1400+ }
1401+
1402+ bool LogitechShifterG25::getShiftDown () const {
1403+ return this ->sequentialState == -1 ;
1404+ }
1405+
1406+ void LogitechShifterG25::setCalibrationSequential (int neutral, int up, int down, float engagePoint, float releasePoint) {
1407+ // limit percentage thresholds
1408+ engagePoint = floatPercent (engagePoint);
1409+ releasePoint = floatPercent (releasePoint);
1410+
1411+ // prevent release point from being higher than engage
1412+ // (which will prevent the shifter from working at all)
1413+ if (releasePoint > engagePoint) {
1414+ releasePoint = engagePoint;
1415+ }
1416+
1417+ // calculate ranges
1418+ const int upRange = up - neutral;
1419+ const int downRange = neutral - down;
1420+
1421+ // calculate calibration points
1422+ this ->seqCalibration .upTrigger = neutral + (upRange * engagePoint);
1423+ this ->seqCalibration .upRelease = neutral + (upRange * releasePoint);
1424+
1425+ this ->seqCalibration .downTrigger = neutral - (downRange * engagePoint);
1426+ this ->seqCalibration .downRelease = neutral - (downRange * releasePoint);
1427+ }
1428+
1429+ void LogitechShifterG25::serialCalibrationSequential (Stream& iface) {
1430+ // err if not connected
1431+ if (this ->isConnected () == false ) {
1432+ iface.print (F (" Error! Cannot perform calibration, " ));
1433+ iface.print (F (" shifter" ));
1434+ iface.println (F (" is not connected." ));
1435+ return ;
1436+ }
1437+
1438+ const char * separator = " ------------------------------------" ;
1439+
1440+ iface.println ();
1441+ iface.println (F (" Sim Racing Library G25 Sequential Shifter Calibration" ));
1442+ iface.println (separator);
1443+ iface.println ();
1444+
1445+ while (this ->inSequentialMode () == false ) {
1446+ iface.print (F (" Please press down on the shifter and move the dial counter-clockwise to put the shifter into sequential mode" ));
1447+ iface.print (F (" . Send any character to continue." ));
1448+ iface.println (F (" Send 'q' to quit." ));
1449+ iface.println ();
1450+
1451+ waitClient (iface);
1452+ this ->update ();
1453+
1454+ // quit if user sends 'q'
1455+ if (iface.read () == ' q' ) {
1456+ iface.println (F (" Quitting sequential calibration! Goodbye <3" ));
1457+ iface.println ();
1458+ return ;
1459+ }
1460+
1461+ // send an error if we're still not there
1462+ if (this ->inSequentialMode () == false ) {
1463+ iface.println (F (" Error: The shifter is not in sequential mode" ));
1464+ iface.println ();
1465+ }
1466+ }
1467+
1468+ float engagementPoint = LogitechShifterG25::CalEngagementPoint;
1469+ float releasePoint = LogitechShifterG25::CalReleasePoint;
1470+
1471+ const uint8_t NumPoints = 3 ;
1472+ const char * directions[2 ] = {
1473+ " up" ,
1474+ " down" ,
1475+ };
1476+ int data[NumPoints];
1477+
1478+ int & neutral = data[0 ];
1479+ int & yMax = data[1 ];
1480+ int & yMin = data[2 ];
1481+
1482+ for (uint8_t i = 0 ; i < NumPoints; ++i) {
1483+ if (i == 0 ) {
1484+ iface.print (F (" Leave the gear shifter in neutral" ));
1485+ }
1486+ else {
1487+ iface.print (F (" Please move the gear shifter to sequentially shift " ));
1488+ iface.print (directions[i - 1 ]);
1489+ iface.print (F (" and hold it there" ));
1490+ }
1491+ iface.println (F (" . Send any character to continue." ));
1492+ waitClient (iface);
1493+
1494+ this ->update ();
1495+ data[i] = this ->getPositionRaw (Axis::Y);
1496+ iface.println (); // spacing
1497+ }
1498+
1499+ iface.println (F (" These settings are optional. Send 'y' to customize. Send any other character to continue with the default values." ));
1500+
1501+ iface.print (F (" * Shift Engagement Point: \t " ));
1502+ iface.println (engagementPoint);
1503+
1504+ iface.print (F (" * Shift Release Point: \t " ));
1505+ iface.println (releasePoint);
1506+
1507+ iface.println ();
1508+
1509+ waitClient (iface);
1510+
1511+ if (iface.read () == ' y' ) {
1512+ 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." ));
1513+ readFloat (engagementPoint, iface);
1514+ iface.println ();
1515+
1516+ 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." ));
1517+ readFloat (releasePoint, iface);
1518+ iface.println ();
1519+ }
1520+
1521+ flushClient (iface);
1522+
1523+ // apply and print
1524+ this ->setCalibrationSequential (neutral, yMax, yMin, engagementPoint, releasePoint);
1525+
1526+ iface.println (F (" Here is your calibration:" ));
1527+ iface.println (separator);
1528+ iface.println ();
1529+
1530+ iface.print (F (" shifter.setCalibrationSequential( " ));
1531+
1532+ iface.print (neutral);
1533+ iface.print (" , " );
1534+ iface.print (yMax);
1535+ iface.print (" , " );
1536+ iface.print (yMin);
1537+ iface.print (" , " );
1538+
1539+ iface.print (engagementPoint);
1540+ iface.print (" , " );
1541+ iface.print (releasePoint);
1542+ iface.print (" );" );
1543+ iface.println ();
1544+
1545+ iface.println ();
1546+ iface.println (separator);
1547+ iface.println ();
1548+
1549+ iface.println (F (" Paste this line into the setup() function to calibrate on startup." ));
1550+ iface.println (F (" \n\n Calibration complete! :)\n " ));
1551+ }
1552+
12881553// #########################################################
12891554// Handbrake #
12901555// #########################################################
0 commit comments