Being an inexperienced programmer, I had to look recursion up in the dictionary. Here is what it said:
recursion (noun).
see recursion.
All kidding aside, I really appreciate appreciate the help from you guys, especially since this is a Dash specific forum and not a general help the new guy with stupid questions forum.
So I changed my code to set a variable, named running, and if true in loop() it takes a cruise through vehicle_running(). This allows the device to check for 30 minute timeout, vehicle stalled or RPM high (theft protection - a pulse width equating to about 2000rpm) as well as continue to monitor for a KILL command via SMS. This works great Erik - thanks for the suggestion.
I wired the entire project to the car yesterday and it works well so far. However, since I have you experts looking and discussing recursion, please be so kind to take a look at my starter_engaged() function below. I am repeating using recursion there while the starter is cranking the car. There are two exit methods for that function for starter cutout - either 4 seconds max time or reaching idle(ish) rpm (as noted in pulse width range). Otherwise, the function calls itself to keep starter cranking. Even though this works great, my question is, regarding recursion, is this a bad idea and could it lead to another stack overflow and program crash? I have tested that loop likely 80 times between Uno prototype and Dash version with no trouble but want to make sure I’m not setting up for a fried starter/ car fire because I made another mistake.
The entire code is below. I really appreciate you guys taking a look. I’ll include a little more information about the project below the sketch if you are interested.
float pressLength = 0;
const int led = D30;
const int ign = D28;
const int immo = D26;
const int acc = D07;
const int starter = D17;
const int extra = D15;
const int tach = R04;
int button = D19;
bool armed = false;
bool running = false;
unsigned long start_time;
void cloud_sms(const String &sender, const rtc_datetime_t ×tamp, const String &message) {
if (message == “START”) {
start();
}
else if (message == “KILL”) {
vehicle_off();
}
}
void setup() {
pinMode(led, OUTPUT);
pinMode(ign, OUTPUT);
pinMode(immo, OUTPUT);
pinMode(acc, OUTPUT);
pinMode(starter, OUTPUT);
pinMode(extra, OUTPUT);
pinMode(button, INPUT);
pinMode(tach, INPUT);
digitalWrite(starter, LOW);
digitalWrite(ign, HIGH);
digitalWrite(acc, HIGH);
digitalWrite(immo, HIGH);
digitalWrite(extra, HIGH);
Serial.begin(9600);
HologramCloud.attachHandlerSMS(cloud_sms);
}
void loop() {
int status = HologramCloud.getConnectionStatus();
while (digitalRead(D19) == LOW) {
delay(100);
pressLength = pressLength + 100;
if ((pressLength >= 5000) && (status == 1)) {
pressLength = 0;
armed = true;
digitalWrite(led, HIGH);
Dash.snooze(3000);
digitalWrite(led, LOW);
}
}
HologramCloud.pollEvents();
if (running == true) {
vehicle_running();
}
}
void start() {
unsigned long rpm = pulseIn(tach, HIGH, 1000000);
if ((rpm == 0) && (armed == true)) {
//Serial.println(“Initiating Start”);
HologramCloud.sendMessage(“Initiating Start”, “remote”);
digitalWrite(immo, LOW);
delay (1000);
digitalWrite(ign, LOW);
delay(3000);
digitalWrite(starter, HIGH);
start_time = millis();
starter_engaged();
}
else if ((rpm == 0) && (armed == false)) {
HologramCloud.sendMessage(“Not Armed”, “remote”);
//Serial.println(“Not Armed”);
delay (1000);
vehicle_off();
}
else {
HologramCloud.sendMessage(“Already Running!”, “remote”);
//Serial.println(“Already Running”);
delay (1000);
vehicle_off();
}
}
void starter_engaged() {
unsigned long rpm = pulseIn(tach, HIGH, 1000000);
if (rpm > 9000 && rpm < 15000) {
digitalWrite(starter, LOW);
Dash.snooze(5000);
digitalWrite(acc, LOW);
Dash.snooze(1000);
digitalWrite(immo, HIGH);
start_verify();
}
else if ((start_time + 4000) < millis()) {
digitalWrite(starter, LOW);
Dash.snooze(2000);
HologramCloud.sendMessage(“Starter Timeout”, “remote”);
//Serial.println(“Starter Timeout”);
Dash.snooze(1000);
vehicle_off();
}
else {
starter_engaged();
}
}
void start_verify() {
unsigned long rpm = pulseIn(tach, HIGH, 1000000);
if (rpm > 4000 && rpm < 15000) {
HologramCloud.sendMessage(“Start Successful”, “remote”);
//Serial.println(“Start Succesful”);
running = true;
vehicle_running();
}
else {
HologramCloud.sendMessage(“Start FAILED”, “remote”);
//Serial.println(“Start FAILED”);
vehicle_off();
}
}
void vehicle_running() {
unsigned long rpm = pulseIn(tach, HIGH, 1000000);
if (rpm > 3000 && rpm < 4600) {
HologramCloud.sendMessage(“RPM HIGH”, “remote”);
//Serial.println(“RPM High”);
delay (1000);
vehicle_off();
}
else if (rpm == 0) {
HologramCloud.sendMessage(“Vehicle Stalled”, “remote”);
//Serial.println(“Vehicle Stalled”);
vehicle_off();
}
else if (start_time + 1800000 < millis()) {
HologramCloud.sendMessage(“30 Minute Timeout”, “remote”);
//Serial.println(“30 Minute Timeout”);
delay (1000);
vehicle_off();
}
else {
}
}
void vehicle_off() {
digitalWrite(starter, LOW);
digitalWrite(ign, HIGH);
digitalWrite(acc, HIGH);
digitalWrite(immo, HIGH);
Dash.snooze(2000);
HologramCloud.sendMessage(“Vehicle OFF”, “remote”);
//Serial.println(“Vehicle OFF”);
running = false;
}
A little more information:
Why buy a professionally designed product when you can roll your own? This is the most ambitious Arduino project I have yet developed. Here are some basics of the project.
Since the car is a manual transmission, the while loop exists to arm the system. I made the required button press 5 seconds to force myself to make sure the car is in neutral and parking brake set. My car has no neutral safety switch so this was a “human factors” solution to make sure I don’t try to start it when I left it in 1st gear. The LED then blinks, also confirming the DASH is connected and registered on the network.
Sensing the engine rpm was the most critical and difficult (for me) part of this project. After much trouble finding the correct wire (among about 70 wires from ECU to instrument cluster), I had to figure out the signal. Trying to decode it with my DMM was maddening. Not having an oscilloscope, I ordered Banggood’s $20 DSO Shell Oscilloscope kit, assembled it, learned to use it and found the signal was a perfect 13 volt square wave. Then I considered all manner of level shifters, mosfets and resistors to feed the signal to the Arduino. In the end, a simple voltage divider and use of pulseIn to find needed trigger points proved very effective.
The tach signal is used for several things:
- Make sure you don’t attempt a start when already running.
- Starter cutout during actual start (most important)!
- Theft protection for unattended running car. If revved above 2000, she dies.
- stall detection for running car.
Bypassing the immobilizer was another challenge. My first idea was pretty cheesy - I removed the RFID chip from a spare key and velcroed it to the sensing ring. Then I found that a simple piece of thin gauge stranded wire wrapped 6 times around the sensing ring and then 6 times around the RFID capsule could allow the capsule to be moved several feet away. Break that loop with a relay and wham - you have an on demand immobilizer bypass module !
A word on relays - I used a 4 relay board (10 amp each) from Banggood to control ignition, accessory and immo bypass. Since the starter solenoid can peak at 18 amps I used Sparkfun’s Beefcake Relay for that line. The Chinese module energizes with LOW signal whereas the Sparkfun relay with a HIGH signal. Not knowing this would lead to confusion going through my code.
Power supply - I opted for a tiny module (about the size of a dime) using the MP2315 chip as it’s supposed to be the most efficient available and handles the wide range of automotive voltage spikes. Again, just a couple of bucks on Banggood. It came with a female usb port installed but I removed that and soldered wires since it needs to power the Dash and two relay modules. Measured current draw with Dash at idle waiting for an SMS is 32ma. Not bad considering normal parasitic draw for the car is 45ma. I figure I can safely power the device for several days from my car battery and still be able to start the car.
A much easier way to do this project would be to repeat what others in this forum have done - install a commercial remote starter and then use the Dash to interface with an extra key fob for cellular capability. However, I wanted to learn and see if I could build it from the ground up. I learned a tremendous amount in the process. It’s really amazing to consider all the requirements in something so seemingly simple as starting a car! Now I just need to get the jungle of wires on my breadboard onto a nice perma-proto and into a project box.