Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unused pin rework #17

Merged
merged 4 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 45 additions & 26 deletions src/SimRacing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@

namespace SimRacing {

/**
* Take a pin number as an input and sanitize it to a known working value
*
* In an ideal world this would check against the available pins on the micro,
* but as far as I know the Arduino API does not have a "valid pin" function.
* Instead, we'll just accept any positive number as a pin and reject any
* negative number as invalid ("Unused").
*
* @param pin the pin number to sanitize
* @returns the pin number, or UnusedPin
*/
static constexpr PinNum sanitizePin(PinNum pin) {
return pin < 0 ? UnusedPin : pin;
}


/**
* Invert an input value so it's at the same relative position
Expand Down Expand Up @@ -161,9 +176,9 @@ static void readFloat(float& value, Stream& client) {
// DeviceConnection #
//#########################################################

DeviceConnection::DeviceConnection(uint8_t pin, bool invert, unsigned long detectTime)
DeviceConnection::DeviceConnection(PinNum pin, bool invert, unsigned long detectTime)
:
Pin(pin), Inverted(invert), stablePeriod(detectTime), // constants(ish)
pin(sanitizePin(pin)), inverted(invert), stablePeriod(detectTime), // constants(ish)

/* Assume we're connected on first call
*/
Expand All @@ -177,7 +192,7 @@ DeviceConnection::DeviceConnection(uint8_t pin, bool invert, unsigned long detec
* the device to be read as connected as soon as the board turns on, without
* having to wait an arbitrary amount.
*/
pinState(!Inverted),
pinState(!inverted),

/* Set the last pin change to right now minus the stable period so it's
* read as being already stable. Again, this will make the class return
Expand All @@ -186,7 +201,9 @@ DeviceConnection::DeviceConnection(uint8_t pin, bool invert, unsigned long detec
lastChange(millis() - detectTime)

{
pinMode(Pin, INPUT); // set pin as input, *no* pull-up
if (pin != UnusedPin) {
pinMode(pin, INPUT); // set pin as input, *no* pull-up
}
}

void DeviceConnection::poll() {
Expand Down Expand Up @@ -248,30 +265,30 @@ void DeviceConnection::setStablePeriod(unsigned long t) {
}

bool DeviceConnection::readPin() const {
if (Pin == NOT_A_PIN) return HIGH; // if no pin is set, we're always connected
const bool state = digitalRead(Pin);
return Inverted ? !state : state;
if (pin == UnusedPin) return HIGH; // if no pin is set, we're always connected
const bool state = digitalRead(pin);
return inverted ? !state : state;
}

//#########################################################
// AnalogInput #
//#########################################################


AnalogInput::AnalogInput(uint8_t p)
: Pin(p), position(AnalogInput::Min), cal({AnalogInput::Min, AnalogInput::Max})
AnalogInput::AnalogInput(PinNum pin)
: pin(sanitizePin(pin)), position(AnalogInput::Min), cal({AnalogInput::Min, AnalogInput::Max})
{
if (Pin != NOT_A_PIN) {
pinMode(Pin, INPUT);
if (pin != UnusedPin) {
pinMode(pin, INPUT);
}
}

bool AnalogInput::read() {
bool changed = false;

if (Pin != NOT_A_PIN) {
if (pin != UnusedPin) {
const int previous = this->position;
this->position = analogRead(Pin);
this->position = analogRead(pin);

// check if value is different for 'changed' flag
if (previous != this->position) {
Expand Down Expand Up @@ -333,7 +350,7 @@ void AnalogInput::setCalibration(AnalogInput::Calibration newCal) {
// Pedals #
//#########################################################

Pedals::Pedals(AnalogInput* dataPtr, uint8_t nPedals, uint8_t detectPin)
Pedals::Pedals(AnalogInput* dataPtr, uint8_t nPedals, PinNum detectPin)
:
pedalData(dataPtr),
NumPedals(nPedals),
Expand Down Expand Up @@ -538,7 +555,7 @@ void Pedals::serialCalibration(Stream& iface) {
}


TwoPedals::TwoPedals(uint8_t gasPin, uint8_t brakePin, uint8_t detectPin)
TwoPedals::TwoPedals(PinNum gasPin, PinNum brakePin, PinNum detectPin)
: Pedals(pedalData, NumPedals, detectPin),
pedalData{ AnalogInput(gasPin), AnalogInput(brakePin) }
{}
Expand All @@ -549,7 +566,7 @@ void TwoPedals::setCalibration(AnalogInput::Calibration gasCal, AnalogInput::Cal
}


ThreePedals::ThreePedals(uint8_t gasPin, uint8_t brakePin, uint8_t clutchPin, uint8_t detectPin)
ThreePedals::ThreePedals(PinNum gasPin, PinNum brakePin, PinNum clutchPin, PinNum detectPin)
: Pedals(pedalData, NumPedals, detectPin),
pedalData{ AnalogInput(gasPin), AnalogInput(brakePin), AnalogInput(clutchPin) }
{}
Expand All @@ -562,7 +579,7 @@ void ThreePedals::setCalibration(AnalogInput::Calibration gasCal, AnalogInput::C



LogitechPedals::LogitechPedals(uint8_t gasPin, uint8_t brakePin, uint8_t clutchPin, uint8_t detectPin)
LogitechPedals::LogitechPedals(PinNum gasPin, PinNum brakePin, PinNum clutchPin, PinNum detectPin)
: ThreePedals(gasPin, brakePin, clutchPin, detectPin)
{
// taken from calibrating my own pedals. the springs are pretty stiff so while
Expand All @@ -571,7 +588,7 @@ LogitechPedals::LogitechPedals(uint8_t gasPin, uint8_t brakePin, uint8_t clutchP
this->setCalibration({ 904, 48 }, { 944, 286 }, { 881, 59 });
}

LogitechDrivingForceGT_Pedals::LogitechDrivingForceGT_Pedals(uint8_t gasPin, uint8_t brakePin, uint8_t detectPin)
LogitechDrivingForceGT_Pedals::LogitechDrivingForceGT_Pedals(PinNum gasPin, PinNum brakePin, PinNum detectPin)
: TwoPedals(gasPin, brakePin, detectPin)
{
this->setCalibration({ 646, 0 }, { 473, 1023 }); // taken from calibrating my own pedals
Expand Down Expand Up @@ -657,22 +674,24 @@ const float AnalogShifter::CalEngagementPoint = 0.70;
const float AnalogShifter::CalReleasePoint = 0.50;
const float AnalogShifter::CalEdgeOffset = 0.60;

AnalogShifter::AnalogShifter(uint8_t pinX, uint8_t pinY, uint8_t pinRev, uint8_t detectPin)
AnalogShifter::AnalogShifter(PinNum pinX, PinNum pinY, PinNum pinRev, PinNum detectPin)
:
/* In initializing the Shifter, the lowest gear is going to be '-1' if a pin
* exists for reverse, otherwise it's going to be '0' (neutral).
*/
Shifter(pinRev != NOT_A_PIN ? -1 : 0, 6),
Shifter(sanitizePin(pinRev) != UnusedPin ? -1 : 0, 6),

/* Two axes, X and Y */
analogAxis{ AnalogInput(pinX), AnalogInput(pinY) },

PinReverse(pinRev),
pinReverse(sanitizePin(pinRev)),
detector(detectPin, false) // not inverted
{}

void AnalogShifter::begin() {
pinMode(PinReverse, INPUT);
if (this->pinReverse != UnusedPin) {
pinMode(pinReverse, INPUT);
}
update(); // set initial gear position
}

Expand Down Expand Up @@ -777,10 +796,10 @@ int AnalogShifter::getPositionRaw(Axis ax) const {
bool AnalogShifter::getReverseButton() const {
// if the reverse pin is not set *or* if the device is not currently
// connected, avoid reading the floating input and just return 'false'
if (PinReverse == NOT_A_PIN || detector.getState() != DeviceConnection::Connected) {
if (pinReverse == UnusedPin || detector.getState() != DeviceConnection::Connected) {
return false;
}
return digitalRead(PinReverse);
return digitalRead(pinReverse);
}

void AnalogShifter::setCalibration(
Expand Down Expand Up @@ -977,7 +996,7 @@ void AnalogShifter::serialCalibration(Stream& iface) {
iface.println(F("\n\nCalibration complete! :)\n"));
}

LogitechShifter::LogitechShifter(uint8_t pinX, uint8_t pinY, uint8_t pinRev, uint8_t detectPin)
LogitechShifter::LogitechShifter(PinNum pinX, PinNum pinY, PinNum pinRev, PinNum detectPin)
: AnalogShifter(pinX, pinY, pinRev, detectPin)
{
this->setCalibration({ 490, 440 }, { 253, 799 }, { 262, 86 }, { 460, 826 }, { 470, 76 }, { 664, 841 }, { 677, 77 });
Expand All @@ -987,7 +1006,7 @@ LogitechShifter::LogitechShifter(uint8_t pinX, uint8_t pinY, uint8_t pinRev, uin
// Handbrake #
//#########################################################

Handbrake::Handbrake(uint8_t pinAx, uint8_t detectPin)
Handbrake::Handbrake(PinNum pinAx, PinNum detectPin)
:
analogAxis(pinAx),
detector(detectPin),
Expand Down
72 changes: 42 additions & 30 deletions src/SimRacing.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@
*/

namespace SimRacing {
/**
* Type alias for pin numbers, using Arduino numbering
*/
using PinNum = int16_t;

/**
* Dummy pin number signaling that a pin is unused
* and can be safely ignored
*/
const PinNum UnusedPin = -1;


/**
* Enumeration for analog axis names, mapped to integers
*/
Expand Down Expand Up @@ -63,12 +75,12 @@ namespace SimRacing {
/**
* Class constructor
*
* @param pin the pin number being read. Can be 'NOT_A_PIN' to disable.
* @param pin the pin number being read. Can be 'UnusedPin' to disable.
* @param invert whether the input is inverted, so 'LOW' is detected instead of 'HIGH'
* @param detectTime the amount of time, in ms, the input must be stable for
* before it's interpreted as 'detected'
*/
DeviceConnection(uint8_t pin, bool invert = false, unsigned long detectTime = 250);
DeviceConnection(PinNum pin, bool invert = false, unsigned long detectTime = 250);

/**
* Checks if the pin detects a connection. This polls the input and checks
Expand Down Expand Up @@ -108,13 +120,13 @@ namespace SimRacing {
*/
bool readPin() const;

const uint8_t Pin; ///< The pin number being read from. Can be 'NOT_A_PIN' to disable
const bool Inverted; ///< Whether the input is inverted, so 'LOW' is detected instead of 'HIGH'
PinNum pin; ///< The pin number being read from. Can be 'UnusedPin' to disable
bool inverted; ///< Whether the input is inverted, so 'LOW' is detected instead of 'HIGH'
unsigned long stablePeriod; ///< The amount of time the input must be stable for (ms)

ConnectionState state; ///< The current state of the connection
bool pinState; ///< Buffered state of the input pin, accounting for inversion
unsigned long lastChange; ///< Timestamp of the last pin change, in ms (using millis())
ConnectionState state; ///< The current state of the connection
bool pinState; ///< Buffered state of the input pin, accounting for inversion
unsigned long lastChange; ///< Timestamp of the last pin change, in ms (using millis())
};


Expand All @@ -129,9 +141,9 @@ namespace SimRacing {
/**
* Class constructor
*
* @param p the I/O pin for this input (Arduino numbering)
* @param pin the I/O pin for this input (Arduino numbering)
*/
AnalogInput(uint8_t p);
AnalogInput(PinNum pin);

/**
* Updates the current value of the axis by polling the ADC
Expand Down Expand Up @@ -225,9 +237,9 @@ namespace SimRacing {
void setCalibration(Calibration newCal);

private:
const uint8_t Pin = NOT_A_PIN; ///< the digital pin number for this input
int position; ///< the axis' position in its range, buffered
Calibration cal; ///< the calibration values for the axis
PinNum pin; ///< the digital pin number for this input
int position; ///< the axis' position in its range, buffered
Calibration cal; ///< the calibration values for the axis
};


Expand Down Expand Up @@ -285,7 +297,7 @@ namespace SimRacing {
* @param nPedals the number of pedals stored in said data pointer
* @param detectPin the digital pin for device detection (high is detected)
*/
Pedals(AnalogInput* dataPtr, uint8_t nPedals, uint8_t detectPin);
Pedals(AnalogInput* dataPtr, uint8_t nPedals, PinNum detectPin);

/** @copydoc Peripheral::begin() */
virtual void begin();
Expand Down Expand Up @@ -381,11 +393,11 @@ namespace SimRacing {
/**
* Class constructor
*
* @param gasPin the analog pin for the gas pedal potentiometer
* @param brakePin the analog pin for the brake pedal potentiometer
* @param detectPin the digital pin for device detection (high is detected)
* @param pinGas the analog pin for the gas pedal potentiometer
* @param pinBrake the analog pin for the brake pedal potentiometer
* @param pinDetect the digital pin for device detection (high is detected)
*/
TwoPedals(uint8_t gasPin, uint8_t brakePin, uint8_t detectPin = NOT_A_PIN);
TwoPedals(PinNum pinGas, PinNum pinBrake, PinNum pinDetect = UnusedPin);

/**
* Sets the calibration data (min/max) for the pedals
Expand All @@ -409,12 +421,12 @@ namespace SimRacing {
/**
* Class constructor
*
* @param gasPin the analog pin for the gas pedal potentiometer
* @param brakePin the analog pin for the brake pedal potentiometer
* @param clutchPin the analog pin for the clutch pedal potentiometer
* @param detectPin the digital pin for device detection (high is detected)
* @param pinGas the analog pin for the gas pedal potentiometer
* @param pinBrake the analog pin for the brake pedal potentiometer
* @param pinClutch the analog pin for the clutch pedal potentiometer
* @param pinDetect the digital pin for device detection (high is detected)
*/
ThreePedals(uint8_t gasPin, uint8_t brakePin, uint8_t clutchPin, uint8_t detectPin = NOT_A_PIN);
ThreePedals(PinNum pinGas, PinNum pinBrake, PinNum pinClutch, PinNum pinDetect = UnusedPin);

/**
* Sets the calibration data (min/max) for the pedals
Expand Down Expand Up @@ -540,9 +552,9 @@ namespace SimRacing {
* @param pinX the analog input pin for the X axis
* @param pinY the analog input pin for the Y axis
* @param pinRev the digital input pin for the 'reverse' button
* @param detectPin the digital pin for device detection (high is detected)
* @param pinDetect the digital pin for device detection (high is detected)
*/
AnalogShifter(uint8_t pinX, uint8_t pinY, uint8_t pinRev = NOT_A_PIN, uint8_t detectPin = NOT_A_PIN);
AnalogShifter(PinNum pinX, PinNum pinY, PinNum pinRev = UnusedPin, PinNum pinDetect = UnusedPin);

/**
* Initializes the hardware pins for reading the gear states.
Expand Down Expand Up @@ -663,7 +675,7 @@ namespace SimRacing {
} calibration;

AnalogInput analogAxis[2]; ///< Axis data for X and Y
const uint8_t PinReverse; ///< The pin for the reverse gear button
PinNum pinReverse; ///< The pin for the reverse gear button
DeviceConnection detector; ///< detector instance for checking if the shifter is connected
};

Expand All @@ -679,9 +691,9 @@ namespace SimRacing {
* Class constructor
*
* @param pinAx analog pin number for the handbrake axis
* @param detectPin the digital pin for device detection (high is detected)
* @param pinDetect the digital pin for device detection (high is detected)
*/
Handbrake(uint8_t pinAx, uint8_t detectPin = NOT_A_PIN);
Handbrake(PinNum pinAx, PinNum pinDetect = UnusedPin);

/**
* Initializes the pin for reading from the handbrake.
Expand Down Expand Up @@ -748,7 +760,7 @@ namespace SimRacing {
class LogitechPedals : public ThreePedals {
public:
/** @copydoc ThreePedals::ThreePedals */
LogitechPedals(uint8_t gasPin, uint8_t brakePin, uint8_t clutchPin, uint8_t detectPin = NOT_A_PIN);
LogitechPedals(PinNum pinGas, PinNum pinBrake, PinNum pinClutch, PinNum pinDetect = UnusedPin);
};

/**
Expand All @@ -763,7 +775,7 @@ namespace SimRacing {
class LogitechDrivingForceGT_Pedals : public TwoPedals {
public:
/** @copydoc TwoPedals::TwoPedals */
LogitechDrivingForceGT_Pedals(uint8_t gasPin, uint8_t brakePin, uint8_t detectPin = NOT_A_PIN);
LogitechDrivingForceGT_Pedals(PinNum pinGas, PinNum pinBrake, PinNum pinDetect = UnusedPin);
};

/**
Expand All @@ -775,7 +787,7 @@ namespace SimRacing {
class LogitechShifter : public AnalogShifter {
public:
/** @copydoc AnalogShifter::AnalogShifter */
LogitechShifter(uint8_t pinX, uint8_t pinY, uint8_t pinRev = NOT_A_PIN, uint8_t detectPin = NOT_A_PIN);
LogitechShifter(PinNum pinX, PinNum pinY, PinNum pinRev = UnusedPin, PinNum pinDetect = UnusedPin);
};


Expand Down
Loading