Here as a Proof-of-Concept is a sketch that does what you want to do. It uses a potentiometer as a stand-in tacho, with its analog readings of 0-1023 mapped to 0-7000 rpm for testing purposes. The solenoids are 2x leds, and there's an led for each gear.
Arduino powers up into PRN mode, and leaves there for D when the gear lever is moved to D. (You didn't say what should happen if the lever is moved out of D at any stage, so I haven't coded that. Right now it stays in D and the paddles work, even if it's moved out of D.)
// https://forum.arduino.cc/index.php?topic=682363
// 6 may 2020
/*
The transmission uses 2 x 12v solenoids - the 4 combinations of solenoids states achieve 4 forward gears
1st Gear - S1 ON / S2 OFF
2nd Gear - S1 ON / S2 ON
3rd Gear - S1 OFF / S2 ON
Overdrive - S1 OFF / S2 OFF
Park, Reverse and Neutral are engaged mechanically by the selector lever but, once in Drive, all changes are electronic. Obviously this is controlled by a factory computer in the car, but I would like to have manual control over the system.
The trigger consist of 2 inputs - Input 1 triggers an upshift, Input 2 triggers a downshift. Using the solenoid states shown above, I would expect the logic to go something like this...
Sequence of triggers from Input 1 (upshift)
If state = 1st Gear, change to 2nd Gear
If state = 2nd Gear, change to 3rd Gear
If state = 3rd Gear, change to Overdrive
If state = Overdrive, do nothing
Sequence of triggers from Input 2 (downshift)
If state = 1st Gear, do nothing
If state = 2nd Gear, change to 1st Gear
If state = 3rd Gear, change to 2nd Gear
If state = Overdrive, change to 3rd Gear
Does the Arduino have the ability to use and RPM input signal to lockout downshifts - i.e if RPM > 4500, inhibit downshift?
*/
// so this one has up and down buttons and a pot as a pretend tacho
//states
enum {ST_PRN, ST_D1, ST_D2, ST_D3, ST_D_OD} currentGear = ST_PRN;
// ala https://www.gammon.com.au/statemachine
bool goingUp = false;
bool goingDown = false;
const byte goingUpPin = 8;
bool goingUpState;
bool lastgoingUpState;
const byte goingDownPin = 9;
bool goingDownState;
bool lastgoingDownState;
//leds to indicate which solenoid is on, and current gear
const byte sol1 = 2;
const byte sol2 = 3;
const byte firstGearLed = 4;
const byte secondGearLed = 5;
const byte thirdGearLed = 6;
const byte ODGearLed = 7;
// and the ersatz tacho
const byte tacho = A0;
int tachoVal;
//gear lever
// a switch from pin to ground, if it's LOW it's in D, if HIGH it's PRN
const byte gearLever = 15;
//solenoid logic
#define ON HIGH
#define OFF LOW
void setup()
{
// initialize serial communication:
Serial.begin(9600);
Serial.println("setup() ... ");
Serial.println("*** gear box ***");
Serial.print("Compiler: ");
Serial.print(__VERSION__);
Serial.print(", Arduino IDE: ");
Serial.println(ARDUINO);
Serial.print("Created: ");
Serial.print(__TIME__);
Serial.print(", ");
Serial.println(__DATE__);
Serial.println(__FILE__);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(sol1, OUTPUT);
pinMode(sol2, OUTPUT);
pinMode(firstGearLed, OUTPUT);
pinMode(secondGearLed, OUTPUT);
pinMode(thirdGearLed, OUTPUT);
pinMode(ODGearLed, OUTPUT);
pinMode(gearLever, INPUT_PULLUP);
pinMode(goingUpPin, INPUT_PULLUP);
pinMode(goingDownPin, INPUT_PULLUP);
//initialize button states
goingUpState = digitalRead(goingUpPin);
lastgoingUpState = goingUpState;
goingDownState = digitalRead(goingDownPin);
lastgoingDownState = goingDownState;
delay(1000);
Serial.println("setup() done");
Serial.println(" ");
Serial.println("Starting in PRN, move lever to D");
}
void loop()
{
tachoVal = map(analogRead(tacho), 0, 1023, 0, 7000);
goingUp = checkgoingUpButton();
goingDown = checkgoingDownButton();
changeGear();
} //loop
void changeGear()
{
switch (currentGear) //ST_PRN, ST_D1, ST_D2, ST_D3, ST_D_OD
{
case ST_PRN:
//Serial.println(" prn");
//all gear leds off
digitalWrite(firstGearLed, OFF);
digitalWrite(secondGearLed, OFF);
digitalWrite(thirdGearLed, OFF);
digitalWrite(ODGearLed, OFF);
//no idea what solenoids should be doing in this state
digitalWrite(sol1, OFF);
digitalWrite(sol2, OFF);
//here all we check is the lever and go to to ST_D1 if it's not in P, R or N
if (!digitalRead(gearLever))
{
currentGear = ST_D1;
Serial.println(" Going out of PRN, use the up and down buttons");
}
//the up and down buttons are ignored
break;
case ST_D1:
//solenoids on/off, first gear led
digitalWrite(sol1, ON);
digitalWrite(sol2, OFF);
digitalWrite(firstGearLed, ON);
digitalWrite(secondGearLed, OFF);
digitalWrite(thirdGearLed, OFF);
digitalWrite(ODGearLed, OFF);
if (goingUp) //this would only have been returned true by checkYesButton() if the goingUp button was pressed
{
currentGear = ST_D2;
goingUp = false;
}
//going down is ignored in first gear
break;
case ST_D2:
//solenoids on/on, second gear led
digitalWrite(sol1, ON);
digitalWrite(sol2, ON);
digitalWrite(firstGearLed, OFF);
digitalWrite(secondGearLed, ON);
digitalWrite(thirdGearLed, OFF);
digitalWrite(ODGearLed, OFF);
if (goingDown && tachoVal < 4500) //this would only have been returned true by checkgoingDownButton() if the goingDown button was pressed
{
currentGear = ST_D1;
goingDown = false;
}
if (goingUp) //this would only have been returned true by checkYesButton() if the goingUp button was pressed
{
currentGear = ST_D3;
goingUp = false;
}
break;
case ST_D3:
//solenoids off/on, third gear led
digitalWrite(sol1, OFF);
digitalWrite(sol2, ON);
digitalWrite(firstGearLed, OFF);
digitalWrite(secondGearLed, OFF);
digitalWrite(thirdGearLed, ON);
digitalWrite(ODGearLed, OFF);
if (goingDown && tachoVal < 4500) //this would only have been returned true by checkgoingDownButton() if the goingDown button was pressed
{
currentGear = ST_D2;
goingDown = false;
}
if (goingUp) //this would only have been returned true by checkYesButton() if the goingUp button was pressed
{
currentGear = ST_D_OD;
goingUp = false;
}
break;
case ST_D_OD:
//solenoids off/off, overdrive gear led
digitalWrite(sol1, OFF);
digitalWrite(sol2, OFF);
digitalWrite(firstGearLed, OFF);
digitalWrite(secondGearLed, OFF);
digitalWrite(thirdGearLed, OFF);
digitalWrite(ODGearLed, ON);
if (goingDown && tachoVal < 4500) //this would only have been returned true by checkgoingDownButton() if the goingDown button was pressed
{
currentGear = ST_D3;
goingDown = false;
}
//going up is ignored in over drive gear
break;
}//switch
}//changeGear
bool checkgoingUpButton()
{
bool temp = false;
// read the button:
goingUpState = digitalRead(goingUpPin);
if (goingUpState == LOW && lastgoingUpState == HIGH)
{
Serial.println("New goingUp ");
temp = true;
// Delay a little bit to avoid bouncing
delay(50);
}//change
lastgoingUpState = goingUpState;
return temp;
}//goingUp
bool checkgoingDownButton()
{
bool temp = false;
// read the button:
goingDownState = digitalRead(goingDownPin);
// compare the buttonState to its previous state
if (goingDownState == LOW && lastgoingDownState == HIGH)
{
Serial.println("New goingDown ");
temp = true;
// Delay a little bit to avoid bouncing
delay(50);
}//change
// save the current state as the last state, for next time through the loop
lastgoingDownState = goingDownState;
return temp;
}//goingDown