Introduction: Remote Car Starter Using Bluetooth

Welcome to a project of mine that has been in the works for a while. In this Instructable, I'll try to give you an idea of what I did to start my car using Bluetooth. First off, here is video to show what I have been able to accomplish.



Since I took this video, I have been able to put the Arduino chip onto its own board and fit everything into the little black box. I also bought a Bluetooth dongle for my computer so the range is significantly better.

In this Instructable, I'll do my best to step through the whole process I went through for this project.

Let's get started...

Step 1: Requirements

Because I started this project from scratch, I needed some guidelines before I started to design the system. Here are the requirements that I set for my system before getting started:

1) The remote start system shall appear transparent to (not affect the operation of) the the vehicle's starting/operating system.

2) Functionality of the key-start shall be available at all times.

3) After starting, the engine shall remain operational regardless of Bluetooth connectivity.

4) After turning off, the engine shall remain off regardless of Bluetooth connectivity.

5) The engine shall start only while the shifter is in the neutral position.

6) The engine shall only start if the engine is not currently running.

7) The starter shall be disengaged after the engine reaches idle RPM or after 4 seconds have passed, whichever comes first.

8) The engine shall turn off if the vehicle is moving and the key is not in the ignition. (Note: This is to prevent theft and also to eliminate the possibility of the engine shutting off due to a failure in this remote start system.)

9) After starting, the engine shall automatically shut down after 30 minutes of operation. (Note: This is to prevent the vehicle from running out of gas if the user forgets that it was started.)

Step 2: Data Collection (System Inputs)

Now that the basic requirements were set in place, it was time to collect the necessary data from my car's engine and ignition systems. Because of the requirements I had drawn up, I knew I needed to understand the inner workings of the signals that are sent to my car's engine control module (ECM).

The signals that would become inputs to my system included:
-Engine RPM sensor
-Shifter neutral position sensor
-Vehicle speed sensor

The first place I went to find information about these signals was the maintenance manual for my car. After finding the preliminary data on these signals (see the excerpts from the maintenance manual), I decided it would be smart to find these wires going into the ECM and make some measurements of my own. I figured doing this would help validate the information in the manual and also give me more specific information (more specific information being waveform frequency vs. RPM/speed ratios). Here is what I found:

Engine RPM Sensor Output
- Location: Connector B134, pin 30
- Wire Color: Green
- Voltage:
  Using oscilloscope: 5 Vp-p (including DC offset)
  Using multimeter: 6.7 Vdc
                                   7.4 Vac
-Frequency:
   RPM         Period            Frequency
750 (Idle)     44ms               22.7 Hz
  1000           30ms               33.3 Hz
  1500           20ms               50.0 Hz
  2000          15ms                66.7 Hz
  2500          12ms                83.3 Hz

Shifter Neutral Position Switch:
- Location: Connector B135, pin 26
- Wire Color: Green/Black
- Voltage:
           In neutral position: 4.87 Vdc
           In any gear:  0.00 Vdc

Vehicle Speed Sensor
- Location: Connector B135, pin 24
- Wire Color: Green/Black
- Frequency:
    Speed        Period       Frequency
   10 mph        80 ms         12.5 Hz
   15 mph        60 ms         16.7 Hz


The process of using an oscilloscope to measure the vehicle speed signal was a bit comical. You probably would have enjoyed watching me trying to read the waveform period on the oscilloscope as I drove around the parking lot, trying not to crash into anything :-)

With this data about the inputs to my system, I was then ready to figure out what my system needed to output in order to function per my requirements.



Step 3: Data Collection (System Outputs)

The required system outputs were a bit easier to determine than the required system inputs. By disconnecting the connector at the ignition, I was able to use an ammeter to determine the amount of current flowing through the different parts of the ignition switch. This gave me an idea of the type of devices I should use to control the current flowing through the ignition switch. See the schematic from my maintenance manual for the location at which I took these measurements. Here is what I found:

Current flowing through the Accessory (ACC) part of the switch: 170 mA
Current flowing through the Ignition (IGN) part of the switch: 1.9 A
Current flowing through the Start (ST): 7.5 A

These values are all approximations. Some of them also change based on the system load. (For example, if the A/C fan was on while measuring the Ignition current, the current value was larger.)

The last output required of my system was the ability to ground the clutch safety switch. In order to start my car, the clutch has to be depressed. When the clutch is depressed, the clutch safety switch grounds a pin on the Engine Control Module (ECM). This type of signal made this safety feature easy to bypass -- simply grounding the this pin on the ECM allows the engine to be started. When disconnecting the connector and measuring current flow through this clutch safety switch, I found that approximately 150 mA flows from the ECM to ground when the pin is grounded.

Step 4: Flowchart

After writing out the requirements and collecting all the necessary data, the next step in the process was to draw up a flowchart based on my requirements. This gave me a starting point when writing the code. Prior to this project, I had only written code in Assembly language, so this flowchart helped give structure to the Arduino code and allowed me to write it fairly quickly.

The flowchart should be pretty straight forward. I don't think there is much explaining to do in this step.




Step 5: Arduino Code

Probably the most intimidating step for me in this whole process was writing the Arduino Code. As I mentioned earlier, this code writing stuff was all new to me. However, it went pretty quick with the flowchart in hand.

You'll notice in the first part of the code that two different signals can be used to initiate the car start command:

1) Sending an "s" to the Arduino via the bluetooth module (which is acting as a serial port) can be used to initiate the starting sequence. This is used when starting the vehicle from a terminal on a computer.

2) Setting a pin on the Arduino high will also initiate the starting sequence. This input pin on the Arduino is connected to the Bluetooth board. When using the Daisy On/Off app from an Android phone, the app will cause the pin on the Bluetooth board to be set high, which in turn will be seen by the Arduino and cause the starting sequence to begin.


Here is my code:

/* Program: Remote Car Start
* Author:  Chris Johnson
* Revised: December 5th, 2011
*
* Inputs:  BT Module "ON" signal
*          BT Module "OFF" signal
*          Vehicle RPM Sensor
*          Vehicle Neutral Position Sensor
*          Vehicle Speed Sensor
*          Serial port
* Outputs: Ignition Control Relay
*          Accessory Control Relay
*          Starter Control Relay
*          Clutch Safety Switch Relay
*          Serial Port
*/


//EQUATES
//Inputs
int BT_on = 2;                                               //Bluetooth "ON" signal inputed to pin 2
int RPM_sens = 3;                                       //Vehicle RPM sensor inputted to pin 3
int neutral_sens = 4;                                    //Vehicle Neutral Position Sensor inputted to pin 4 (12 volts +/- 0.5 volts)
int speed_sens = 5;                                     //Vehicle Speed Sesor inputted to pin 5
int BT_off = 7;                                                //Bluetooth "OFF" signal inputed on pin 7

//Outputs
int ign_ctrl = 8;                                               //Ignition Control Relay controlled by pin 8
int start_ctrl = 9;                                             //Start Control Relay controlled by pin 9
int clutch_sfty = 10;                                       //Clutch Safety Swithc Relay controlled by pin 10
int acc_ctrl = 11;                                            //Accessory Control Relay controlled by pin 11

unsigned long start_time;                            //Variable used to store the time at which the starter is engaged

char data = 0;                                                 //Variable used to store data received via the serial port

//DEFINITIONS
void setup()
{
  pinMode(BT_on, INPUT);                          //Define pin inputs and outputs
  pinMode(RPM_sens, INPUT);
  pinMode(neutral_sens, INPUT);
  pinMode(speed_sens, INPUT);
  pinMode(BT_off, INPUT);

  pinMode(ign_ctrl, OUTPUT);
  pinMode(start_ctrl, OUTPUT);
  pinMode(clutch_sfty, OUTPUT);
  pinMode(acc_ctrl, OUTPUT);

  Serial.begin(9600);                                     //Initialize the serial port with a baud rate of 9600
}

//PROGRAM

//Loop function: Wait for a START command to be received (command via serial port or
//voltage applied to BT_on pin)
void loop()
{
   if (Serial.available() > 0)                          //Check if any serial data is available
   {
     data = Serial.read();                                //Set "data" variable to currently available serial data
     check();                                                      //Go to "check" funtion if serial data is available
   }
   else if (digitalRead(BT_on) == HIGH)     //Check if BT Module has set pin high
   {
     begin();                                                       //Begin start sequence if pin is high
   }
   else
   {
     loop();                                                          //Repeat this function until a START command is received
   }
}

//Check function: Test for proper character set from BT
void check()                           
{
  if (data == 's')                                          //Check if an "s" has been sent
  {
    begin();                                                  //Begin start sequence if "s" has been sent
  }
  else
  {
    Serial.println("Invalid Command");  //If anything other than an "s" has been sent, send "Invalid Command" error message
    loop();                                                    //Return to beginning of program
  }
}

//Begin function: Turn on accessory and ignition
void begin()
{
  delay(500);                                     // 0.5 second delay
  digitalWrite(acc_ctrl, HIGH);        //Turn accessory ON
  digitalWrite(ign_ctrl, HIGH);         //Turn ignition ON
  delay(500);                                      // 0.5 second delay
  neutral();                                          //Go to "neutral" funciton
}

//Neutral function: Check if the vehicle's transmission is in the neutral position.
// Continue if vehicle is in neutral. Exit start sequence if vehicle is in gear.
void neutral()
{
  if (digitalRead(neutral_sens) == HIGH )       //Continue only if vehicle is in neutral
  {
    start();                                                                //Start vehicle if in neutral
  }
  else
  {
    Serial.println("Vehicle Not in Neutral");       //If in gear, send "Vehicle Not in Neutral" error message
    vehicle_off();                                                     //Exit start sequence if in gear
  }
}

//Start function: Engage starter only if engine is not already running.
void start()
{
   int RPM = pulseIn(RPM_sens, HIGH, 1000000);    //Get RPM value
   if(RPM == 0)                                                                   //Continue start sequence only if vehicle is not running.
   {
     digitalWrite(clutch_sfty, HIGH);             //"Depress" clutch
     digitalWrite(acc_ctrl, LOW);                   //Turn accessory OFF
     delay(500);                                               //0.5 second delay
     digitalWrite(start_ctrl, HIGH);                //Engage starter
     start_time = millis();                                //Capture the time at which the starter was engaged
     starter_engaged();                                 //Go to Starter_engaged function
   }
   else                                         
   {
     Serial.println("Vehicle Already Running");  //Send "Vehicle Already Running" message if engine was previously started
     vehicle_off();                                                    //Exit start sequence if already running
   }
}

//Starter_engaged function: Disengage starter after vehicle is starter or turn off starter if
//vehicle has not started within 4 seconds.
void starter_engaged()
{
  int RPM = pulseIn(RPM_sens, HIGH);            //Get RPM value
  if (RPM > 1000)                                                   //Continue if engine has started (reached low idle)
  {
    Serial.println("Engine Running");                  //Send "Engine Running" message after engine has started
    disengage_starter();                                        //Go to disengage_starter after engine is running
  }
  else if ((start_time+4000) < millis())                //Test if 4 seconds has passed since the starter was engaged
  {

    disengage_starter_timeout();                 //Go to disengage_starter if engine has not started within 4 seconds of starter engagement
  }
  else
  {
    starter_engaged();                                    //Repeat this function if engine has not started or 4 seconds has not elapsed
  }
}

//Disengage_starter function: Disengage the starter.
void disengage_starter()
{
  digitalWrite(start_ctrl, LOW);                   //Disengage the starter
  digitalWrite(clutch_sfty, LOW);               //"Release" the clutch
  digitalWrite(acc_ctrl, HIGH);                   //Turn accessory ON
  vehicle_off_sense();                                //Go to vehicle_off_sense function
}

//Disengage_starter_timeout function: Disengage the starter (used after 4 seconds has elapsed without an engine start)
void disengage_starter_timeout()
{
  digitalWrite(start_ctrl, LOW);                                 //Disengage the starter
  digitalWrite(clutch_sfty, LOW);                              //"Release" the clutch
  Serial.println("Vehicle Start Unsuccessful");      //Send "Vehicle Start Unsuccessful"
  vehicle_off();                                                            //Exit start sequence
}

//Vehicle_off_sense function: Waits for an "off" signal to be sent while the engine is running.
//If no "off" signal is received, turns off the vehicle after 30 minutes has elapsed since the engine start.
void vehicle_off_sense()
{
  int moving = pulseIn(speed_sens, HIGH);                //Get vehicle speed
  if (moving > 1000)                                                         //Check if vehicle is moving
  {
    Serial.println("Vehicle OFF -- Movement");           //Send "Vehicle OFF -- Movement" message ifvehcile is moving
    vehicle_off();                                                               //Turn vehicle off if it is moving
  }
  else if (start_time+1800000 < millis())                    //Check if 30 minutes has elapsed since engine start
  {
    Serial.println("Vehicle OFF -- Automatic Timeout");  //Send "Vehicle OFF -- Automatic Timeout" message if engine has been                            
                                                                                                     //running for 30 minutes
    vehicle_off();                                                                    //Turn vehicle off if engine is running for 30 minutes
  }
  else if (Serial.available() > 0)                                         //Check if a signal has been sent via serial data
  {
    data = Serial.read();                                                      //Store serial sent serial data
    if (data == 'o')                                                                  //Check if signal sent is an OFF command ("o")
    {
      Serial.println("Vehicle OFF -- User Commanded");   //Send "Vehicle OFF -- User Commanded" message if serial data is the OFF
                                                                                                        //command ("o")
      vehicle_off();                                                                     //Turn vehicle off if OFF command is sent via serial data
    }
  }
  else if (digitalRead(BT_off) == HIGH)                              //Check if OFF command has been sent via BT module
  {
    Serial.println("Vehicle OFF -- User Commanded");     //Send "Vehicle OFF -- User Commanded" message if OFF
                                                                                                        //command was sent
    vehicle_off();                                                                       //Turn vehicle off if OFF command was sent via BT module
  }
  else
  {
    vehicle_off_sense();                                                   //Repeat this function if none of the above conditions have been met
  }
}

//Vehicle_off function: Turns the vechile off and starts the whole program over
void vehicle_off()
{
  digitalWrite(ign_ctrl, LOW);                           //Turn ignition OFF
  digitalWrite(acc_ctrl, LOW);                           //Turn accessory OFF
  loop();                                                //Repeat program (look for start command)
}


Step 6: Hardware Schematic

Once I developed the Arduino code and had a chance to test it on a bread board, I was ready to move onto the next step of designing the hardware.

The first step in designing the hardware aspect of this project was to figure out how to make the Arduino chip (ATMEGA328) a standalone unit. With this figured out, I could save a lot of money (the chip with the Arduino bootloader on it is only $5.50, compared to an Arduino Uno that is $30) and make my board much smaller. I used this tutorial from the Arduino website to design the standalone Arduino unit: http://www.arduino.cc/en/Main/Standalone

Because I had measured the current that flows through the ignition switch of my vehicle, I found that a simple bi-polar junction transistor (BJT) did not have a high enough current rating for this application. Therefore, I used BJTs to control relays that are rated for 40 amps. This is very much over-designed, but I had the parts on hand. If I were to start over, I would probably try to find a high current MOSFET that would replace both the BJT and the relay. (Using a MOSFET would be a great possibility because MOSFETs require very little to no current for biasing purposes, yet can typically handle quite a bit of source/drain current.) All that to say, yes: the system is over-designed as far as current requirements are concerned. But, I would rather the system be over-designed than under-designed in this regard.

The one location that I was able to get away with just a simple BJT was in the clutch safety switch circuit. Because the ECM only outputs 150 mA for this signal, the BJT (rated at 200 mA maximum continuous current) was sufficient without an additional relay.

Step 7: Vehicle Wiring

With the system design pretty much completed, and the software tested as much as possible before testing in the car was required, I was ready to install the wiring needed in the car. 

I decided to use a DB-9 connector for all signals and control circuits. This makes install and removal of my system very easy. A plus to easy installation and removal is that I can quick get my system out of the car and into the house to make adjustments to software or hardware, if needed. If you look at the schematic in the previous step, you can see how the DB-9 connector in the car is wired. You'll notice that I have power from the Arduino and bluetooth module going from inside my box, out to the switch via the DB-9, and then back to the Arduino/Bluetooth board. This probably seems a bit weird, but by designing the system this way, I was able to put a fuse inside the box. Another feature of this design is that I used a switch with an LED that turns on when the switch is in the ON position and power is applied to the switch. If the switch is turned ON but the LED does not turn on, it is obvious very quickly that the box is not connected properly (or at all).

Soldering the wires into the DB-9 connector under the dash was a bit of a challenge, but doable. I could have used the crimp-type DB-9 connector, but didn't have any on hand. Plus, a little soldering practice never hurts anyone :-)

The most intimidating part of adding wiring into the car was cutting into the ignition leads. If you decide to make any modifications to your ignition system, make sure to DISCONNECT THE BATTERY first. I wasn't totally sure how to go about splicing a wire into the ignition leads, mostly because of their size (about 12 gauge wires). I ended up cutting the ignition leads and putting ring terminals on all three wires (the two original ends and the new wire I added). I then used a bolt and self-locking nut to attach the three ring terminals together. A little bit of plastic tubing cut appropriately, and some electrical tape, did the trick to insulate the splice connections from each other and other things. I used two 2-pin Molex connectors for the four ignition leads that I spliced in (Power, Accessory, Ignition, and Start).

Step 8: Conclusion

Thanks for looking at my project! It was a great learning experience for me.

Since building this little contraption, I had to sell my Forester. I have not tried to install this system on my new car. The range of the bluetooth signal was not as good as I was expecting, even with a modified antenna. Before I install this system on my new car, I want to see if I can increase the range to make it more useful. Also, I never got a new Android phone. My Droid Incredible has problems with the bluetooth feature, so I was only ever able to use my laptop to control my project, even though it should be possible to control it from an working Android device. Perhaps I will revisit this project if I get a new phone and find a way to increase the bluetooth range.

Cheers!