Controlling an Electronic Throttle Body using a PID controller

Hi all,

I’m part of the MSOE SAE Formula Hybrid Team and I need to interface and electronic throttle pedal to an electronic throttle body. I realize that most people don’t want to help with this because of liability reasons, but don’t worry because we have a fail-safe button that the driver can press to cut power if anything goes wrong.

So far I have the Bosch throttle body being controlled by the Arduino through an L298N H-Bridge. I can currently working on having it controlled by a PID controller in a similar fashion as what I imagine a servo works. The throttle body has two TPS’s that output an inverse voltage so that the difference can be taken to determine the position. I have almost everything working except the PID controller. The throttle body bounces all over the place for a good minute or so before settling in. Since this is a throttle body the reaction time needs to be very fast.

My code is below, any help is much appreciated as we need to have this vehicle moving under it’s own power in two days.

int pinI1=8;
int pinI2=11;
int speedPin=9;

#include <PID_v1.h>

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Define the aggressive and conservative Tuning Parameters
double aggKp=4, aggKi=0.2, aggKd=1;
double consKp=1, consKi=0.05, consKd=0.25;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT);

void setup()
{
  int TPS0 = analogRead(0);
  int TPS1 = analogRead(1);
  int TPS =  TPS1 - TPS0;
  TPS = map(TPS, -715, 910, 0, 255);
  //initialize the variables we're linked to
  Input = TPS;
  Setpoint = map(analogRead(2), 0, 1023, 0, 255);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  
  pinMode(pinI1,OUTPUT);
  pinMode(pinI2,OUTPUT);
  pinMode(speedPin,OUTPUT);
  digitalWrite(pinI2,LOW);
  digitalWrite(pinI1,HIGH);
  Serial.begin(9600);
}

void loop()
{
  int TPS0 = analogRead(0);
  int TPS1 = analogRead(1);
  int TPS =  TPS1 - TPS0;
  TPS = map(TPS, -715, 910, 0, 255);
  Input = TPS;
  Setpoint = map(analogRead(2), 0, 1023, 0, 255);
  double gap = abs(Setpoint-Input); //distance away from setpoint
  if(gap<100)
  {  //we're close to setpoint, use conservative tuning parameters
    myPID.SetTunings(consKp, consKi, consKd);
  }
  else
  {
     //we're far from setpoint, use aggressive tuning parameters
     myPID.SetTunings(aggKp, aggKi, aggKd);
  }
  
  myPID.Compute();
  analogWrite(speedPin,Output);
}
  Setpoint = map(analogRead(2), 0, 1023, 0, 255);

There is no clue, from reading this, exactly what is controlling the set point. Why not?

map() is a pretty expensive way to divide by 4.

  double gap = abs(Setpoint-Input); //distance away from setpoint
  if(gap<100)
  {  //we're close to setpoint, use conservative tuning parameters
    myPID.SetTunings(consKp, consKi, consKd);
  }
  else
  {
     //we're far from setpoint, use aggressive tuning parameters
     myPID.SetTunings(aggKp, aggKi, aggKd);
  }

You don’t diddle with the tuning parameters based on how far off you are. You experiment to find tuning parameters, and use them. Consistently.

  int TPS0 = analogRead(0);
  int TPS1 = analogRead(1);

Again, there is no clue what you are reading from. Why not?

  int TPS =  TPS1 - TPS0;
  TPS = map(TPS, -715, 910, 0, 255);
  Input = TPS;

What purpose is TPS serving? None that I can see.

JohnLincoln:
I’m not familiar with the item that you call a throttle body.
Does it have a motor and gearbox inside it to drive the two Throttle Position Sensors? Why are two needed?
Can you provide a link to the throttle body? or a photograph of it?

What exactly do you mean by :

The throttle body bounces all over the place for a good minute or so before settling in.

The throttle body is a mechanical device that controls the flow of air into an engine in order to control the RPM’s. It does have a motor and gearbox with two throttle position sensors inside. Two are needed for automotive safety reasons, so I take the difference of the two for the position. Here is a datasheet that is for a very similar throttle body: http://www.bosch-motorsport.de/media/catalog_resources/Electronic_Throttle_Body_Datasheet_51_en_10726070795pdf.pdf As for it bouncing around, when ever I try and set it to a specific position, whether it be WOT or closed it will jitter back and forth quite a lot trying to find the right point. I think it is going too fast and overcompensating.

PaulS:

  Setpoint = map(analogRead(2), 0, 1023, 0, 255);

There is no clue, from reading this, exactly what is controlling the set point. Why not?

map() is a pretty expensive way to divide by 4.

  double gap = abs(Setpoint-Input); //distance away from setpoint

if(gap<100)
  {  //we’re close to setpoint, use conservative tuning parameters
    myPID.SetTunings(consKp, consKi, consKd);
  }
  else
  {
    //we’re far from setpoint, use aggressive tuning parameters
    myPID.SetTunings(aggKp, aggKi, aggKd);
  }



You don't diddle with the tuning parameters based on how far off you are. You experiment to find tuning parameters, and use them. Consistently.



int TPS0 = analogRead(0);
  int TPS1 = analogRead(1);



Again, there is no clue what you are reading from. Why not?



int TPS =  TPS1 - TPS0;
  TPS = map(TPS, -715, 910, 0, 255);
  Input = TPS;



What purpose is TPS serving? None that I can see.

The set-point is being controlled from a potentiometer, similarly as it will be by the electronic throttle pedal. Also, I’ll have to change it to divide by 4, I didn’t even realize that.

So what you’re saying is that I should just find tuning parameters that work and then keep them without having them changed based upon how far off?

The TPS0 and TPS1 read the two TPS’s inside the throttle body then calculates the difference and maps it to the same range as my control potentiometer. It then gets sent to the “input” of the PID while the “setpoint” of the PID is controlled by the potentiometer. That way it knows where I want it to be and where it currently is. The PID then outputs to the H-Bridge speed control pin.

Thanks!

arbartz:
Two are needed for automotive safety reasons, so I take the difference of the two for the position.

Using the difference doesn't seem strictly correct, to me. I assume you have them wired in reverse to that one goes from zero to max and the other goes from max to zero. If you just take the difference, the result will go from +max to -max. What you want to do is invert the inverted signal (inverted = max - signal) so that you have two values going from zero to max; now you can validate them against each other and (if valid) take the average.

The validation is why you have two sensors, so you can detect hardware failures. Just blindly taking the average (or difference) doesn't meet this safety objective. Having a separate manual cut-off doesn't remove the need to validate the sensors, IMO.

The idea of PID is that it encompasses the whole control algorithm and once correctly tuned will provide optimal closed loop control over the whole range of positions and errors. You challenge is to find the optimal set of tuning parameters - once chosen, they would be hard-coded and unchanged (unless the physical characteristics of the system changed).

There is a procedure you can go through to get an approximate value for these parameters by observing the system behaviour - Google PID tuning to learn about that. There is also a PID autotune library for the Arduino which you would use in a test sketch to optimise the PID tuning parameters on the live system.

Thank you for your suggestions! I have changed the code to have the correct safety features and have looked up PID tuning. I have tried the manual tuning method described on Wikipedia with some success. It no-longer jitters, but it takes a while to reach the set point sometimes. I have tried to use the PID Autotune program, but I can’t seem to get it to work. Here is my code so far:

/*
  PID_Basic_Throttle.ino

 Bosch electronic throttle body control
 for MSOE SAE Formula Hybrid 2014

 Author: Austin R. Bartz
 Last Revision Date: 4/3/2014

 Hardware Connections:
 -L298N H-Bridge Enable A: Pin 9
 -L298N H-Bridge Input 1:  Pin 8
 -L298N H-Bridge Input 2:  Pin 11
 -TPS 0:                   Pin A0
 -TPS 1:                   Pin A1
 -Throttle Input:          Pin A2

 */

//H-Brdige Pins
#define pinI1 8
#define pinI2 11
#define speedPin 9
//PID Library
#include <PID_v1.h>

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,1,0,0, DIRECT);

//TPS0 Idle= 863  WOT= 56
//TPS1 Idle= 164  WOT= 969

void setup()
{
  TCCR1B = TCCR1B & 0b11111000 | 0x01;
  int TPS0 = -1*(analogRead(0)-862);
  int TPS1 = (analogRead(1)-163);
  int TPS =  (TPS1 + TPS0)/2;
  if(abs(TPS0 - TPS1) >= 5)
  {
    analogWrite(speedPin,0);
  }
  //initialize the variables we're linked to
  Input = TPS;
  Setpoint = map(analogRead(3), 85, 1023, 0, 800);
  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  pinMode(pinI1,OUTPUT);
  pinMode(pinI2,OUTPUT);
  pinMode(speedPin,OUTPUT);
  digitalWrite(pinI2,LOW);
  digitalWrite(pinI1,HIGH);
  Serial.begin(9600);
}

void loop()
{
  float Kp = mapfloat(analogRead(4), 0, 1023, 0, 5);
  Serial.print(Kp, DEC);
  Serial.print("  ");
  float Ki = mapfloat(analogRead(5), 0, 1023, 0, 5);
  Serial.print(Ki, DEC);
  Serial.print("  ");
  //myPID.SetTunings(Kp, Ki, 0.00);
  myPID.SetTunings(0.15, 2.00, 0.00);
  int TPS0 = constrain(-1*(analogRead(0)-867), 0, 810);
  int TPS1 = constrain((analogRead(1)-153), 0, 810);
  if(abs(TPS0 - TPS1) >= 10)
  {
    analogWrite(speedPin,0);
  }
  int TPS = (TPS1 + TPS0)/2;
  Input = TPS;
  int TP0 = map(analogRead(3), 85, 1023, 0, 800);
  int TP1 = map(analogRead(2), 555, 1023, 0, 800);
  int TP = ((TP0+TP1)/2);
  int TPdiff = abs(TP0-TP1);
  while(TPdiff >= 5)
  {
    analogWrite(speedPin,0);
    int TP0 = map(analogRead(3), 85, 1023, 0, 800);
    int TP1 = map(analogRead(2), 555, 1023, 0, 800);
    TPdiff = abs(TP0-TP1);
  }
  Setpoint = TP0;
  if(Setpoint <= 5)
  {
    analogWrite(speedPin,0);
  }
  else
  {
    myPID.Compute();
    analogWrite(speedPin,Output);
  }
  Serial.print("TPS0:");
  Serial.print(TPS0, DEC);
  Serial.print("  ");
  Serial.print("TPS1:");
  Serial.print(TPS1, DEC);
  Serial.print("  ");
  Serial.print("Input:");
  Serial.print(Input, 0);
  Serial.print("  ");
  Serial.print("Pot0:");
  Serial.print(TP0);
  Serial.print("  ");
  Serial.print("Pot1:");
  Serial.print(TP1);
  Serial.print("  ");
  Serial.print("Setpoint:");
  Serial.print(Setpoint, 0);
  Serial.print("  ");
  Serial.print("Output:");
  Serial.println(Output, 0);
}

float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Why in the heck would you want to use a PID for controlling a throttle body? I know a fair bit about cars, and bit about PID algos, but I fail to see how a PID helps you. PID are fairly slow algorithms by nature, they are designed for systems where there is lots of lag and overshoot, noise, or ripples. Furthermore, your servo will have a PID algo built into its controller for the purpose of maintaining a set position against any disturbances. Finally, what you want out of a throttle control is speed and a direct, linear relationship to the loud pedal. Don’t muck it up more than you absolutely have to.

The reason that carmakers these days use electronic throttle bodies is because it gives them the ability to “get in the way” of the driver for the purpose of fuel economy and safety (traction control). Since you’re in FSAE hybrid, the former is probably your concern. What I’d suggest is a “race” map for the Autocross portion - with a 1:1 mapping of throttle pot to throttle body - and an economy map for the efficiency portions of the test - using some damping factor.

You should have a well-trained driver in the cockpit, so don’t make the throttle body the brains of the operation.

Trust me, I was against the idea of an electronic throttle body from the start (and I’m an EE!), especially because we have a cable operated throttle already. The ME’s of the group insisted on using the electronic throttle body because they thought it would be easier than fabricating a throttle position sensor to a normal pedal that we can use to send to the motor controller. This way a commercial throttle pedal will control both the ICE and the motor.

At this point I don’t have much of a choice other than to get it to work. If I shouldn’t use a PID, then what should I use?

The throttle body doesn’t have any control circuitry in it, just direct connection to the motor and two potentiometers that work inversely from each other. The throttle pedal has two potentiometers in it as well, but one of them has an extra resistor in series with it and instead of working inversely the difference of the two signals is supposed to be taken. I can’t seem to find any documentation online about the system we have. All I have to go off of is this training manual we have. https://drive.google.com/file/d/0B7_wYqRQ2P5pT3l3RWhYRWpnaTA/edit?usp=sharing

In the manual there are no specific values listed, but a few plots of what the signals should look like and the pins we should use. Unfortunately though, it looks as if the ECU is supposed to do all the calculating. So I basically have to simulate that portion of the ECU with an Arduino.

With all the being said, do you have any suggestions as to how to control this in a better way?

Thank you!

jrubins:
PID are fairly slow algorithms by nature, they are designed for systems where there is lots of lag and overshoot, noise, or ripples.

I don't think that's at all accurate. PID is designed for controlling linear dynamic systems. A PID controller can be as fast or as slow as you want, and they are absolutely the right way to control something like a throttle where you want maximum responsiveness and yet avoid oscillations and overshoots.

arbartz:
Trust me, I was against the idea of an electronic throttle body from the start (and I’m an EE!), especially because we have a cable operated throttle already. The ME’s of the group insisted on using the electronic throttle body because they thought it would be easier than fabricating a throttle position sensor to a normal pedal that we can use to send to the motor controller. This way a commercial throttle pedal will control both the ICE and the motor.

At this point I don’t have much of a choice other than to get it to work. If I shouldn’t use a PID, then what should I use?

The throttle body doesn’t have any control circuitry in it, just direct connection to the motor and two potentiometers that work inversely from each other. The throttle pedal has two potentiometers in it as well, but one of them has an extra resistor in series with it and instead of working inversely the difference of the two signals is supposed to be taken. I can’t seem to find any documentation online about the system we have. All I have to go off of is this training manual we have. https://drive.google.com/file/d/0B7_wYqRQ2P5pT3l3RWhYRWpnaTA/edit?usp=sharing

In the manual there are no specific values listed, but a few plots of what the signals should look like and the pins we should use. Unfortunately though, it looks as if the ECU is supposed to do all the calculating. So I basically have to simulate that portion of the ECU with an Arduino.

With all the being said, do you have any suggestions as to how to control this in a better way?

Thank you!

I’m glad to hear you’re suspicious of drive-by-wire. That could be one of the worst pieces of technology foisted upon mankind in recent years. :slight_smile:

The throttle body doesn’t have any control circuitry in it, just direct connection to the motor and two potentiometers that work inversely from each other

So what you’re saying is that you don’t have a servo controlling the t-body. That makes a difference, so you want the the PID algo on the Arduino to turn your motor+TPS into a servo. I gotcha now :slight_smile:

PeterH:

jrubins:
PID are fairly slow algorithms by nature, they are designed for systems where there is lots of lag and overshoot, noise, or ripples.

I don’t think that’s at all accurate. PID is designed for controlling linear dynamic systems. A PID controller can be as fast or as slow as you want, and they are absolutely the right way to control something like a throttle where you want maximum responsiveness and yet avoid oscillations and overshoots.

It’s not that it’s not accurate per se, it’s that it’s a gross oversimplification. PID controls are good at controlling the rates of change. Servos use them to go fast to get near position X, then as they get close, they slow down. Once at the desired position they stop. If you manually turn the servo horn, the servo will apply more and more force from the motor to try to hold position.

Given that he isn’t using a servo to control the T-body, using PID might make some sense. I’m concerned that running it through the arduino may be too slow, and since there’s not really much disturbance or overshoot in a throttle system, may be unnecessary. The throttle just has to go to the position described and stay there, so a 1:1 mapping might be just as good.
Basically, what you are doing is like the Knob sketch except that you need to get the position yourself, reading one or both of your TPS.

I’d start with a simple read one throttle Pot, move the motor, read the TPS pot. Then get more complicated so you are using both throttle pots and both TPS pots. You could even do that with a separate pot, motor, pot to simulate your pedal, throttle, and tps.
Maybe something like this (pseudocode):
newThrottle = analogRead(pedal)
actualThrottle= analogRead(TPS)

if actualThrottle < newThrottle {motor ++}
if actualThrottle > newThrottle {motor --}

Alternatively, you could hack the Throttle Body with a servo instead of a motor,

Well, thank you all for the help! I now have a fully working program that does what it’s supposed to! For anyone interested here’s the code:

/*
  FH 2014 Throttle and Shift.ino

 Bosch electronic throttle body control
 for MSOE SAE Formula Hybrid 2014

 Author: Austin R. Bartz
 Last Revision Date: 4/6/2014

 Hardware Connections:
 -TPS 0:                   Pin A0 (Blue Wire)
 -TPS 1:                   Pin A1 (Thin White Wire)
 -Throttle Input 0:        Pin A2 (White Wire)
 -Throttle Input 1:        Pin A3 (White Marked Wire)
 -Error LED                Pin A5 (Blue Wire)
 -UpShift Relay:           Pin 2  (Yellow Wire)
 -DownShift Relay:         Pin 3  (Pink Wire)
 -UpShift Button:          Pin 4  (Green Wire)
 -DownShift Button:        Pin 5  (Orange Wire)
 -Ignition Cut             Pin 6  (White with Green Stripe Wire)
 -Neutral Switch           Pin 7  (Brown Wire)
 -L298N H-Bridge Enable A: Pin 9  (N/A)
 -L298N H-Bridge Input 1:  Pin 8  (Other Speaker Wire)
 -L298N H-Bridge Input 2:  Pin 11 (Gray Line Wire)
 */

//Pins assignments
#define LEDPin 6
#define pinI1 8
#define pinI2 11
#define speedPin 9
const int upRelay = 2;
const int upSwitch = 4;
const int downRelay = 3;
const int downSwitch = 5;
const int neutralSwitch = 7;
const int ignCut = 6;

// Function Variables
int upTime = 150;
int downTime = 150;
int neutralTime = 42;
int debounceDelay = 5;
int gearCount = 0;
boolean downLast = false;
boolean upLast = false;
boolean neutralLast = false;
//PID Library
#include <PID_v1.h>

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,1,0,0, DIRECT);

void setup()
{
  //Set PWM frequency to 31.37255 kHz
  TCCR1B = TCCR1B & 0b11111000 | 0x01;
  
  Input = 0;
  Setpoint = 0;
  myPID.SetMode(AUTOMATIC);
  pinMode(pinI1,OUTPUT);
  pinMode(pinI2,OUTPUT);
  pinMode(speedPin,OUTPUT);
  pinMode(LEDPin,OUTPUT);
  digitalWrite(pinI2,LOW);
  digitalWrite(pinI1,HIGH);
  Serial.begin(9600);
  
  pinMode(downSwitch, INPUT_PULLUP);
  pinMode(upSwitch, INPUT_PULLUP);
  pinMode(neutralSwitch, INPUT_PULLUP);
  pinMode(downRelay, OUTPUT);
  pinMode(upRelay, OUTPUT);
  pinMode(ignCut, OUTPUT);
  Serial.println(gearCount);
}

void loop()
{
  //Uncomment to tune the PID loop
  /*
  float Kp = mapfloat(analogRead(4), 0, 1023, 0, 5);
  Serial.print(Kp, DEC);
  Serial.print("  ");
  float Ki = mapfloat(analogRead(5), 0, 1023, 0, 5);
  Serial.print(Ki, DEC);
  Serial.print("  ");
  myPID.SetTunings(Kp, Ki, 0.00);
  */
  
  shiftUp();
  shiftDown();
  shiftNeutral();
  
  digitalWrite(LEDPin, LOW);
  
  //PID Loop tunings
  myPID.SetTunings(0.15, 2.00, 0.00);
  int TPS0 = constrain(analogRead(0), 663, 54);  //Range: 663 - 54
  int TPS1 = constrain(analogRead(1), 164, 970);  //Range: 164 - 970
  int diff0 = (TPS0-54)+map((TPS1-164), 0, 806, 0, 609);
  while(analogRead(0) < 40)
  {
    analogWrite(speedPin,0);
    digitalWrite(LEDPin, HIGH);
    analogRead(0);
    Serial.println("ERROR 1");
  }
  while(analogRead(1) > 1000)
  {
    analogWrite(speedPin,0);
    digitalWrite(LEDPin, HIGH);
    analogRead(1);
    Serial.println("ERROR 2");
  }
  
  //PID Input from TPS
  Input = map(constrain((TPS1 - 164), 0, 806), 0, 806, 0, 380);
  
  int Pedal0 = constrain(analogRead(2), 0, 180);  //Range: 0 - 180
  int Pedal1 = constrain(analogRead(3), 0, 380);  //Range: 0 - 380
  int diff1 = Pedal0 - map(Pedal1, 0, 380, 0, 180);
  while(analogRead(2) > 200)
  {
    analogWrite(speedPin,0);
    digitalWrite(LEDPin, HIGH);
    analogRead(2);
    Serial.println("ERROR 3");
  }
  while(analogRead(3) > 400)
  {
    analogWrite(speedPin,0);
    digitalWrite(LEDPin, HIGH);
    analogRead(3);
    Serial.println("ERROR 4");
  }
  while(diff1 > 50)
  {
  analogWrite(speedPin,0);
  digitalWrite(LEDPin, HIGH);
  int Pedal0 = analogRead(2);  //Range: 0 - 180
  int Pedal1 = analogRead(3);  //Range: 0 - 380
  int diff1 = Pedal0 - map(Pedal1, 0, 380, 0, 180);
  Serial.println("ERROR 5");
  }
  
  //PID Setpoint from Throttle Pedal
  Setpoint = Pedal1;
  //Set throttle to 0
  if(Setpoint <= 5)
  {
    analogWrite(speedPin,0);
  }
  else
  {
    myPID.Compute();
    analogWrite(speedPin,Output);
  }
  /*
  Serial.print(diff0, DEC);
  Serial.print("  ");
  Serial.print(diff1, DEC);
  Serial.print("  ");
  Serial.print(TPS0, DEC);
  Serial.print("  ");
  Serial.print(TPS1, DEC);
  Serial.print("  ");
  Serial.print(Pedal0, DEC);
  Serial.print("  ");
  Serial.print(Pedal1, DEC);
  Serial.print("  ");
  Serial.print(Input, DEC);
  Serial.print("  ");
  Serial.print(Setpoint, DEC);
  Serial.print("  ");
  Serial.println(Output, DEC);
  */
}

float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

// Downshift Sequence
void shiftDown()
{
  if(digitalRead(downSwitch) == LOW && downLast == false)
  {
    digitalWrite(downRelay, HIGH);
    delay(downTime);
    digitalWrite(downRelay, LOW);
    if(gearCount > 1)
    {
      gearCount --;
    }
    Serial.println(gearCount);
    downLast = true;
    
  }
  else
  {
    digitalWrite(downRelay, LOW);
  }
  
  if(digitalRead(downSwitch) == LOW)
  {
    delay(debounceDelay);
    downLast = false;
  }
}

// Upshift Sequence
void shiftUp()
{
  if(digitalRead(upSwitch) == LOW && upLast == false)
  {
    digitalWrite(ignCut, HIGH);
    digitalWrite(upRelay, HIGH);
    delay(upTime);
    digitalWrite(upRelay, LOW);
    digitalWrite(ignCut, LOW);
    if(gearCount < 5)
    {
    gearCount ++;
    }
    Serial.println(gearCount);
    upLast = true;
  }
  else
  {
    digitalWrite(upRelay, LOW);
  }
  
  if(digitalRead(upSwitch) == LOW)
  {
    delay(debounceDelay);
    upLast = false;
  }
}

// Neutral Shift Sequence
void shiftNeutral()
{
  if(digitalRead(neutralSwitch) == LOW && neutralLast == false)
  {
    digitalWrite(downRelay, HIGH);
    delay(neutralTime);
    digitalWrite(downRelay, LOW);
    gearCount = 0;
    Serial.println(gearCount);
    neutralLast = true;
    
  }
  else
  {
    digitalWrite(downRelay, LOW);
  }
  
  if(digitalRead(downSwitch) == LOW)
  {
    delay(debounceDelay);
    neutralLast = false;
  }
}
1 Like