Arduino launch control

Good day guys.

So this is a project i am planning and investing some time in it. i am a noob at programming arduino but i can learn !

so the project consist of a arduino reading rpm signal from my car ecu , and if set rpm is reached i want to activate a relay to cut power from the coil pack, so the wiring is quite simple, the programming is my problem.

there is 2 ideas i'm dealing with.....

this is what i was thinking , using a micro witch to ground a certain pin on the arduino eg. e-brake or clutch pedal , when the pin is ground, i want the program to start its loop , i want the cut off rpm to be adjustable by a pod. so other words, the arduino reads the rpm and switches the relay on and of correspondingly to the set rpm until the pedal or clutch is released an then it dose nothing.

the second plan is to use a momentary switch to activate the arduino program , then read the same data for rpm and use the pod to set the max rpm and relay to cut the ignition , but the main difference is that i take a reading from the speed sensor as well and when the program reads a small value (eg the car starts moving) Then the program stops and the car runs normal thru the rpm range.

i like the second idea more but i think it involves a bit more.

What do you guise think ? and can someone help me to develop such a program?
btw i'm using a arduino Duemilenove.

Am I getting it wrong, or are you seriously planning to just stop igniting the fuel-air-mix?

lg, couka

Oh if only David Visard had an Arduino when he wrote 'Tuning the A series'

I think he would recommend analogue pin #1 on throttle position
analogue pin #2 on the manifold vacuum sensor
Digital write #1 on the NO2 injector

You need to write your code as follows

If throttle == 1023 and pin#2 reads less than boost pressure, then digital write #3 HIGH
//injects Nox which will put turbo into boost in an insant
Read Analogue #2
IF Analogue #2 == 0 then digitalwrite Nox injector Low

Hope this helps :slight_smile:

Adriaankenny:
so the project consist of a arduino reading rpm signal from my car ecu , and if set rpm is reached i want to activate a relay to cut power from the coil pack, so the wiring is quite simple, the programming is my problem.

there is 2 ideas i'm dealing with.....

this is what i was thinking , using a micro witch to ground a certain pin on the arduino eg. e-brake or clutch pedal , when the pin is ground, i want the program to start its loop , i want the cut off rpm to be adjustable by a pod. so other words, the arduino reads the rpm and switches the relay on and of correspondingly to the set rpm until the pedal or clutch is released an then it dose nothing.

the second plan is to use a momentary switch to activate the arduino program , then read the same data for rpm and use the pod to set the max rpm and relay to cut the ignition , but the main difference is that i take a reading from the speed sensor as well and when the program reads a small value (eg the car starts moving) Then the program stops and the car runs normal thru the rpm range.

i like the second idea more but i think it involves a bit more.

What do you guise think ? and can someone help me to develop such a program?
btw i'm using a arduino Duemilenove.

Hi,
Does this car have a catalytic converter?
They do not like unburn't fuel and the danger of explosive ignition.
A car with ECU will probably not like having its ignition coils suddenly robbed of current.
You may end up with an ECU fault code that may need expert removal, and or cause your car to drop into "Limp Home MODE".
I think you need to explore the how the ECU works in your car.
Thanks.. Tom.. :slight_smile:

Thanks guys.

The car has a full cat back exhaust, and is already tuned with stage 2 software, unfortunately the software does not have the option for launch control, so this idea is based on the 'bee r rev limiters' that the Honda guys use (there is lots of videos on this type of launch control).

This is the code so far - i am having trouble with the following....

32: error: 'setPin' was not declared in this scope

setPin(clutchPin, INPUT);

^

exit status 1
'setPin' was not declared in this scope

Any advice?

couka:
Am I getting it wrong, or are you seriously planning to just stop igniting the fuel-air-mix?

I know at least some some motorcycle ECUs worked this way, but that may have changed since fuel injection and catalytic converters became more commonplace which happened much later than it did for automobiles. In theory one shouldn't be bouncing off the rev limiter all that often anyway.

In an electronic fuel injection system it might make more sense to turn off the injectors which I believe is more commonly done in modern automobiles.

This is the code so far

Where?
Did you mean pinMode?

Hi,

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html then look down to item #7 about how to post your code.

Tom... :slight_smile:

As soon as i saw this thread i knew i had to help you out on making this launch control system, i made a working setup with a fan as a complementary engine that i will soon test out on my pitbike. I am uploading a video on youtube of everything i have managed so far. Here is my code btw. Anyone feel free to use it for whatever you like!

It only uses 1 sensor per revolution so it should be pretty universal to use for anyone.
Right now i use a transistor to power the fan (ignition coil), but you could just use a high power relay if you want, it's has basically the same purpose. Just switch any "LOW" "High" "Rising" "Falling" etc to suite the needs for your own project.

Also it should be noted that you may want to build a slightly different circuit in a vehicle application. You may want to apply the launch control by draining the ignition circuit of power, instead of integrating the relay in series with the ignition system.

HERE IS THE VIDEO Arduino Launch Control - YouTube

const int relayPin = 13;
const int pickupPin = 3;
int rev = 0;
const int buttonPin = 10;
const int potPin = 2;
                                  
volatile long revPrevious = 0;              //will store last time pickup signal was high
long currentMicros = micros();
volatile long revTime = 0;
volatile long RPM = 0;
volatile long pot = 512;
volatile long potRPM = 1000;
volatile long buttonState = LOW;

void setup() 
{
  // set the digital pin as output:
  pinMode(relayPin, OUTPUT);     
  pinMode(buttonPin, INPUT);
  pinMode(potPin, INPUT);
  attachInterrupt(1, newRev, FALLING);
  Serial.begin(9600);
}

void loop()
{
  
  currentMicros = micros();
  if (rev == 1) //CHECKS TOO SEE IF THERE IS A NEW INTERRUPT SIGNAL TO CALCULATE NEW RPM VALUE
  {
  revTime = currentMicros - revPrevious;
  RPM = 60*1000000/revTime;
  pot = analogRead(potPin);
  potRPM = pot*9;
  buttonState = digitalRead(buttonPin);
  Serial.print(RPM);
  Serial.print("\t");
  Serial.print(potRPM);
  Serial.print("\t");
  Serial.print(buttonState);
  Serial.print("\t");
  
  revPrevious = currentMicros;
  rev = 0;
  }
  if (buttonState == HIGH)
  {
    // check to see if it's time to activate relay based on rpm value
  if (RPM >= potRPM) //if rpm is too high:
  {
    digitalWrite(relayPin, LOW);  // turn off relay
  }
  if (RPM < potRPM)//if rpm is not too high
  {
    digitalWrite(relayPin, HIGH);    // turn on relay
  }
  }
  else
  {
    digitalWrite(relayPin, HIGH);    // turn on relay
    }
}
void newRev()
{
 static unsigned long last_interrupt_time = 0; //DEBOUNCE
 unsigned long interrupt_time = micros();       //DEBOUNCE
 // If interrupts come faster than 200ms, assume it's a bounce and ignore
 if (interrupt_time - last_interrupt_time > 500) 
 {
  rev= 1;
 }
 last_interrupt_time = interrupt_time; //DEBOUNCE
}

Launch_Control.ino (1.77 KB)

thanx for your help !!

what type of signal did you use ? when you say "It only uses 1 sensor per revolution"

this is the way i was planing on reading the rpm signal

void loop()
{
  //check for current RPM
  pulseDuration = pulseIn(signalRPM, LOW);
  pulseDuration = pulseDuration + pulseIn(signalRPM, HIGH);
  currentRPM = ((60000000/pulseDuration)*0.5);

it is part of a other guys program , he uses the rpm to power a RGB led so the led changes color as the revs reach certain points dictated by the program.

so what i was thinking of adopting his program, because the signals on his car and mine are the same
(square signals, 12V and GND in 50/50 proportion). Then instead of only a led lighting up , switch a relay.
and adjust the "set" rpm value with a pod instead of hard writing it into the program.

here is HIS full program.

// HARDWARE PIN CONFIG
int ledR = 6;
int ledG = 10;
int ledB = 11;
int buzzer = 8;
int signalRPM = 7;

// SETTINGS
int alertRPM = 5600;
int buzzerFreq = 3000;

// HELPERS
int currentRPM = 1000;
int borderNoRPM = 300;
int borderLevel1 = alertRPM * 0.8;  //4400 by default
int borderLevel2 = alertRPM * 0.97; //5335 by default
int borderLevel3 = alertRPM * 1.03; //5665 by default
int borderLevel4 = alertRPM * 1.1;  //6050 by default
int pulseDuration;

// ALERTS
int alertNoRPM()
{
  analogWrite(ledR, 255);
  analogWrite(ledG, 255);
  analogWrite(ledB, 128);
  noTone(buzzer);
  delay(200);
  analogWrite(ledR, 0);
  analogWrite(ledG, 0);
  analogWrite(ledB, 0);
  delay(200);
}
int alertLevel0()
{
  analogWrite(ledR, 0);
  analogWrite(ledG, 0);
  analogWrite(ledB, 0);
  noTone(buzzer);
}
int alertLevel1()
{
  analogWrite(ledR, 0);
  analogWrite(ledG, 255);
  analogWrite(ledB, 0);
  noTone(buzzer);
}
int alertLevel2()
{
  analogWrite(ledR, 0);
  analogWrite(ledG, 0);
  analogWrite(ledB, 255);
  noTone(buzzer);  
}
int alertLevel3()
{
  analogWrite(ledR, 255);
  analogWrite(ledG, 0);
  analogWrite(ledB, 0);
  noTone(buzzer);
}
int alertLevel4()
{
  analogWrite(ledR, 0);
  analogWrite(ledG, 0);
  analogWrite(ledB, 0);
  tone(buzzer, buzzerFreq, 75);
  delay(50);
  analogWrite(ledR, 255);
  delay(50);
}
void setup()
{
  //setup procedure
  pinMode(ledR, OUTPUT);
  pinMode(ledG, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(signalRPM, INPUT);
  pinMode(buzzer, OUTPUT);
  
  
  //welcome procedure
  alertLevel1();
  delay(200);
  alertLevel2();
  delay(200);
  alertLevel3();
  delay(200);
  alertLevel4();
  delay(200);
  alertLevel0();
}

void loop()
{
  //check for current RPM
  pulseDuration = pulseIn(signalRPM, LOW);
  pulseDuration = pulseDuration + pulseIn(signalRPM, HIGH);
  currentRPM = ((60000000/pulseDuration)*0.5);
  
  //start alert for currentRPM
  if(currentRPM>=0 && currentRPM<=borderNoRPM)
  {
    alertNoRPM();
  }
  else
  {
    if(currentRPM>borderNoRPM && currentRPM<=borderLevel1)
    {
      alertLevel0();
    }
    if(currentRPM>borderLevel1 && currentRPM<=borderLevel2)
    {
      alertLevel1();
    }
    if(currentRPM>borderLevel2 && currentRPM<=borderLevel3)
    {
      alertLevel2();
    }
    if(currentRPM>borderLevel3 && currentRPM<=borderLevel4)
    {
      alertLevel3();
    }
    if(currentRPM>borderLevel4)
    {
      alertLevel4();
    }
  }
}

do you think im on the right track ??

I use only the beginning of one square wave every revolution (aka a falling or rising edge). The signal is from a hall effect sensor and one magnet stuck to a blade on the fan.

You say your signal consists of square signals of Low(GROUND) and High(12v) in 50/50 proportion all the way around 1 complete engine revolution?

I know that most car engines have something that is called missing tooth wheels. The missing tooth is missing to tell the ecu about the orientation of the engine.
I only tell you this so that you will be aware of this when you are programming. I dont think it will be a problem.

If you can count the duration of each square pulse, you will get a new RPM value after each pulse. That gives a very accurate RPM reading all around the clock. It will be a little less accurate at the missing tooth, because you will have twice the time between those pulses.
I just thought i should help you out with everything, even this small detail.

I think you are on the right track. If you are going to use his code, make sure to rename everything so it fits your needs. This way it will be easier to figure out what you will need for your project and what to toss from his code, and be less likely to mix things up.

Even get your Pen and Paper out and sketch some circuit and thought down on paper. This makes it easier to think and can help you solve many mysteries!

I see that you will not need all the lines that he wrote in his code, so you can erase the "Colours" that you dont need for your rev limiter and rename the things you do need.

Hope this helps, and makes it a bit clearer for you

My method of measuring rpm is quite a bit different from the one you are using. I measure the time it takes for the engine to make a complete revolution. Then i calculate the rpm based on the time.

Your way may be much more accurate if it is correctly implrmented, as you have much more frequent measurements of each and every tooth on the triggerwheel, which are maybe around 20 to 50 measurements per revolution compared to me having only 1.

I have one more question, do you fully understand how the code you posted works? I mean, when you read it, can you follow the code through a full loop and understand how each integer plays a role for what the output will be?

I am currently working with your code and progressing to convert it to suite your car and your needs.
But you will need to understand the code to test it and troubleshoot it if it does not work.

I am grateful for all your help and ideas.

I have a basic understanding of how the code works, I can read schematics and I can analyze the code as well as to determine what is going to happen and which pins do I connect to what. The problem comes in, when I have to start writing a code from scratch, I'm not so familiar with the functions, but once the code is in front of me I can normally decode it.

I have written some codes before and made some modifications on other programs, however I am still new to this specific programming language world.

I know exactly how you feel, depending on my mood i can get completely stuck without the ability to write any code because i dont know the exact command. The way i always try to overcome this is by starting to write normal text instead of real code, and mix in real code only where i can remember it. Then when i got the whole picture down in the code i come back and replace the text with codes in the correct format. Almost everything i write i need to google to remember how to correctly format it.

sooo i have tamperd with this code, the code is what i want to do with a few exceptions,

i am not sure if my measuring technique is accurate or going to work , i do not know how to use the count pulses like in the above program and incorporate it into this one(i know doing it like that will work)

and also the launch rpm is set to 4000 , i want to use a pod to adjust it in real time and do not know how to incorporate that into the program

// settings
int launch = 4000; // rpm to launch at
int step = 25 * 1000; // 25ms stepper
 
// inputs
int RPMPin = 1; // RPM signal
int clutchPin = 2; // clutch
 
// outputs
int ignitionCoilPin = 4; // ignition coil power (using Relay)
 
// variables
int rpm = 0;
unsigned long lastInterruptTime;
 
// setup
void setup()
{
  // attach interrupt to injectors
  attachInterrupt(RPMPin, rpmCalculation, RISING);

  // set clutch mode
  
 pinMode(clutchPin, INPUT);
 
 // set ign coil as output
 pinMode(ignitionCoilPin, OUTPUT);
 
 // enable by default
 digitalWrite(ignitionCoilPin, HIGH);
 
 // wait 5 seconds
 delay(5000);
}
 
// loop
void loop()
{
 // check if actual rpm is equal or greater than launch rpm, and if clutch petal is down
 if (rpm >= launch && digitalRead(clutchPin) == HIGH)
 {
   // cut power to ignition coil for some milliseconds.
   digitalWrite(ignitionCoilPin, LOW);
   delayMicroseconds(step);
   digitalWrite(ignitionCoilPin, HIGH);
   delayMicroseconds(step);
 }
}
 
// calculate rpm
void rpmCalculation()
{
 unsigned long now = micros();
 
 rpm = 60 / ((now - lastInterruptTime) / 1000000) / 2;
 
 lastInterruptTime = now;
}

I would do like this with the loop instead, this way you get a constant rpm instead.

// loop
void loop()
{
 // check if actual rpm is equal or greater than launch rpm, and if clutch petal is down
 if (rpm >= launch && digitalRead(clutchPin) == HIGH)
 {
   // cut power to ignition coil for some milliseconds.
   digitalWrite(ignitionCoilPin, LOW);
 }
 if (rpm < launch && digitalRead(clutchPin) == HIGH)
 {
   // cut power to ignition coil for some milliseconds.
   digitalWrite(ignitionCoilPin, HIGH);
 }
}

i see , it is a bit more refined,t il be using the loop code like that definitely thank you.

can you help me with how to use a pod to change the set launch rpm in the program ?

i am research it on my own but it takes quite a wile to go thru all the tutorials heheh

Yes of course i can help with that, you can do like i did in my code.

You analog read the value from the potentiometer every time you get a new rpm value from the engine.

The value from the potentiometer is going to be between 0 and 1024.

But as i wanted a range between 0 and 9000 rpm ish, i just took the value from the pot and and multiplied by 9. that gives me launch rpm from pot between 0 and 9216 rpm

Then in the loop i use the potRPM as an extra condition that has to be reached before launch control is engaged.

void loop()
{
  
  currentMicros = micros();
  if (rev == 1) //CHECKS TOO SEE IF THERE IS A NEW INTERRUPT SIGNAL TO CALCULATE NEW RPM VALUE
  {
  revTime = currentMicros - revPrevious;
  RPM = 60*1000000/revTime;
  pot = analogRead(potPin);
  potRPM = pot*9;
  buttonState = digitalRead(buttonPin);
  
  revPrevious = currentMicros;
  rev = 0;
  }
  if (buttonState == HIGH)
  {
    // check to see if it's time to activate relay based on rpm value
  if (RPM >= potRPM) //if rpm is too high:
  {
    digitalWrite(relayPin, HIGH);  // turn on relay
  }
  if (RPM < potRPM)//if rpm is not too high
  {
    digitalWrite(relayPin, LOW);    // turn off relay
  }
  }
  else
  {
    digitalWrite(relayPin, LOW);    // turn off relay
    }
}
void newRev()
{
 static unsigned long last_interrupt_time = 0; //DEBOUNCE
 unsigned long interrupt_time = micros();       //DEBOUNCE
 // If interrupts come faster than 200ms, assume it's a bounce and ignore
 if (interrupt_time - last_interrupt_time > 500) 
 {
  rev= 1;
 }
 last_interrupt_time = interrupt_time; //DEBOUNCE
}

You might want to see if the Speeduino (ECU - Arduino site) has anything good for your project.

GoForSmoke:
You might want to see if the Speeduino (ECU - Arduino site) has anything good for your project.

Speeduino is another whole level of complexity, it offers ignition control as well as electronic fuel injection control.

But in its current stage it does not support antilag, due to its lack of so called table switching.

For the simple task i dont think it is worthwhile to go into such complexity as speeduino accually is to a beginner.