Introduction: "STOP!!" Said the Sensor to the Car.

Picture of "STOP!!" Said the Sensor to the Car.

Motivation:

There are many car sensor projects available online utilizing code that I don't thoroughly understand as of December 2017. Thus I took it upon myself to better learn arduino and apply logic operators to construct a code that could assist a person in parking a vehicle. If you don't own an autonomous self parking vehicle, here's a much cheaper way to park your car in your garage or driveway. Perhaps while you're waiting for your Tesla you could make this.

This project is also devoted to my parents who drive me to school and back every week. This should make their tiring routine a bit easier.

This is my attempt at constructing the simplest software and hardware package to help park a car.

Approach:

To accomplish my goal, I decided to divide the project into four steps.

  1. Sense car
  2. Detect accurate distance to car
  3. Orient lights in a way that a person can understand their proximity to the wall
  4. Put microprocessor into a low power mode

Only 2 LEDs, one 200ohm resistor, 4 jumper wires, an arduino, a breadboard and an ultrasonic sensor are needed for this project.

Step 1: Sense Car and Detect Accurate Distance to Car

Out of simplicity, I decided to use an ultrasonic sensor to detect the car and detect its distance to the car.

Ultrasonic Sensor: HC-SR04 Sensor

My code for this step is based on this arduino site: http://playground.arduino.cc/Code/NewPing

However, I found that the ultrasonic sensor gave occasionally erroneous readings. Therefore I had to average the distance values outputted from the sensor in order to "smoothen" the distance values.

My code for smoothing is based on this arduino site: https://www.arduino.cc/en/Tutorial/Smoothing

When the sensor detects that no car is present, it will check to see if there's an incoming vehicle once a second by sending 1 ping.

Below is the segmented code that I constructed when integrating both distance detection and smoothing.

The full code and the arduino sketch are provided at the end of the instructable.

<p>#include <NewPing.h>   //http://playground.arduino.cc/Code/NewPing<br>#include <LowPower.h> //added low power library from sparkfun <a href="https://learn.sparkfun.com/tutorials/reducing-arduino-power-consumption" rel="nofollow">  https://learn.sparkfun.com/tutorials/reducing-ard...>
#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 400 // Maximum  distance we want to ping for (in centimeters). was 400, max 500 was 500/2
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.
// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 10; //was 10
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average
int LED_STOP = 6; //Declare stop light
int LED_Yield =  7;// the number of the LED pin
int dist = 0; //Declare dist variable
int stopdist = 17; //declare stopping distance in cm (dist when red LED turns on)
int stopdist_plus_7 = stopdist+7; //declare distance when yellow LED stops blinking and becomes solid
int ultrasonic_power = 3;
// Variables will change :
int ledState = LOW;             // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned long previousMillis_2 = 0;
// constants won't change :
const long interval = average; //was 850/3          // interval at which to blink (milliseconds)
const long interval_2 = 1; // was 1000/2interval for turning off LEDs divide by 2 for 8mhz clock speed, else dont for 16, decreased interval_2 size to 100 to account for walking in front of sensor
unsigned long time;
void setup() 
{  
  Serial.begin(57600); // Open serial monitor at 115200 baud to see ping results.
  // set the digital pin as output:
  pinMode(ultrasonic_power, OUTPUT);
}
void loop() 
{
  const long interval = average/1;
  digitalWrite(ultrasonic_power, HIGH);
  delay(50);
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = dist;
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;   
  // if we're at the end of the array...
  if (readIndex >= numReadings) {
  // ...wrap around to the beginning:
  readIndex = 0;
   }   
  // calculate the average:
  average = (total / numReadings);
  // send it to the computer as ASCII digits
  //  Serial.println(average);     
  delay(50); // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
  unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  Serial.print("Ping: ");
  Serial.print(uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)
  dist = uS / US_ROUNDTRIP_CM; //did div by 2 in prescaler update
  Serial.println("cm");
  //  Serial.print(average);   
  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.</p>

Step 2: Orient Lights in a Way That a Person Can Understand Their Proximity to the Wall

Picture of Orient Lights in a Way That a Person Can Understand Their Proximity to the Wall

Here, is code that tells the LED lights what to do when the car is a certain distance away from the wall.

I used red and yellow LED lights such as these: LED lights

I found it neat that I could utilize code from the arduino website for blinking an LED to make the yellow LEDs in the project blink faster as the car gets closer to the wall. When the stop distance is reached, the red LEDs turn on. After 15 seconds, the red and yellow LEDs turn off to conserve power.

The site where this code came from is here: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

All other code is based off of if/else, and statements and for loops.

See Arduino if/else statements and for loop tutorials here:

https://www.arduino.cc/reference/en/language/structure/control-structure/else/

Notice that I turn off the LEDs and the ultrasonic sensor when the average distance is less than 10cm. This is to account for obstacles in the garage or irregular shapes that may "confuse" the sensor into thinking that an object is present. I found that this correction prevented the LEDs from turning on when no car was present.

The code is versatile. You can change the stop distance (in cm) by changing the variable stopdist. This will change the distance at which the red LEDs turn on. You can change the distance when the yellow LEDS become solid be changing the variable stopdist_plus_7. By changing the const long interval variable before setup, you can change the frequency in which the device blinks as seen here in the latest version that is running this code:

STOP!! SENSOR V7

<p>if (average > 10 && average <= stopdist) //added if/else structure was average > 10, removed average <=<br>    { 
      if (average < stopdist && (currentMillis - previousMillis_2 < 10000))//if less than 17cm (default stopdist) and close to wall for less than 10 s turn on light  
      {
      if (average > 10) // was 10
        {
        digitalWrite(LED_STOP, HIGH);
        delay(1000); //changed, was 2500
        }
        else
        {
        digitalWrite(LED_STOP, LOW);  
        }
      }  
      if ((currentMillis - previousMillis_2) > 15000) //was 7500
      {
        for (byte i = 0; i < 1; ++i) //wait to check every ~20 s
        {
        digitalWrite(ultrasonic_power, LOW);
        LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
        digitalWrite(LED_STOP, LOW); 
        digitalWrite(LED_Yield, LOW);   
        }
        Serial.println("just left low power jeelib");
      }
    }
    else
    {
    //Following code in if statement modified from <a href="https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay" rel="nofollow"> <a href="https://www.arduino.cc/en/Tutorial/BlinkWithoutDe...</a">  https://www.arduino.cc/en/Tutorial/BlinkWithoutDe...>>
    //if (average > 65)   //added nested if statement
    if (average > stopdist_plus_7) //
     {
      if (average < 390) //was 390 then 300 was 450/2
        {
        if (currentMillis - previousMillis >= interval) {
          // save the last time you blinked the LED
          previousMillis = currentMillis;
           
          // if the LED is off turn it on and vice-versa:
          if (ledState == LOW) {
            ledState = HIGH;
      
            } else {
            ledState = LOW;
            }
            // set the LED with the ledState of the variable:
            digitalWrite(LED_Yield, ledState);
          }
        } 
      }   
    //Serial.println("prevmill is");
    //Serial.println(previousMillis);
    //Serial.println("currentmill is");
    //Serial.println(currentMillis);
    //Serial.println(currentMillis-previousMillis);   
    //this if statement means we are within range of wall (< 390 cm) for interval_2 time,  "lights are blinking" 
    if ((currentMillis - previousMillis_2 >= interval_2) && average >= stopdist+3) { //changed average >= stopdist to > stopdist
      //if 17 cm (default stopdist or farther from wall for 1 second, prev becomes current, added +3 due to issue of light turning on when car parked
      previousMillis_2 = currentMillis; 
     }
    //Serial.println("prevmill_2 is");
    //Serial.println(previousMillis_2);
    //Serial.println("currentmill is");
    //Serial.println(currentMillis);     
    //Serial.println("prevmill_2 is");
    //Serial.println(previousMillis_2);
    //Serial.println("currentmill is");
    //Serial.println(currentMillis);
    //Serial.println(currentMillis - previousMillis_2); 
    if (average > stopdist) //
      {
      //  if (average < 65)
      if (average < stopdist_plus_45)
        {
        digitalWrite(LED_Yield, HIGH);
        }
      }
      if (average >= stopdist)
        {
        digitalWrite(LED_STOP, LOW); 
        }
    }
    if (average < 10) //was 10/2
      {
        digitalWrite(ultrasonic_power, LOW);
        digitalWrite(LED_Yield, LOW);
        digitalWrite(LED_STOP, LOW);
        delay(950); //works, added 11/8/17
        digitalWrite(ultrasonic_power, HIGH);
        delay(50);
      }
 }</p>

Step 3: Enter Low Power Mode

I used a low power library found on the sparkfun website in order to put the arduino to sleep and conserve power.

https://learn.sparkfun.com/tutorials/reducing-arduino-power-consumption

No analog pins are used in this sketch, so I disabled the analog pins using code found from this site:

http://www.fiz-ix.com/2012/11/save-power-by-disabling-arduino-peripherals/

<p>if ((currentMillis - previousMillis_2) > 15000) //was 7500<br>      {
        for (byte i = 0; i < 1; ++i) //wait to check every ~20 s
        {
        digitalWrite(ultrasonic_power, LOW);
        LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
        digitalWrite(LED_STOP, LOW); 
        digitalWrite(LED_Yield, LOW);   
        }
        Serial.println("just left low power");
      }</p>

In further attempt to save power, I turn the ultrasonic pin which powers the ultrasonic sensor off, when not in use as seen above and below here:

<p>if (average < 10) //was 10/2<br>      {
        digitalWrite(ultrasonic_power, LOW);
        digitalWrite(LED_Yield, LOW);
        digitalWrite(LED_STOP, LOW);
        delay(950); //works, added 11/8/17
        digitalWrite(ultrasonic_power, HIGH);
        delay(50);
      }</p>

Step 4: Now That All the Steps Are Completed, We Need to Integrate the Code

Integrated Code:

I used an arduino nano to run this code. Watch out for some arduino clones as they may consume more power than genuine arduinos. Make sure the NewPing and LowPower libraries are installed and copy and paste this code into Arduino IDE!

If you're unsure how to get Arduino IDE, follow the tutorial here: https://learn.sparkfun.com/tutorials/installing-arduino-ide

/*By Patrick B.
Contributors/Sources used in Code:
http://playground.arduino.cc/Code/NewPing
https://learn.sparkfun.com/tutorials/reducing-arduino-power-consumption
http://www.fiz-ix.com/2012/11/save-power-by-disabling-arduino-peripherals/
https://www.arduino.cc/en/Tutorial/Smoothing
https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
*/
#include <NewPing.h>   //http://playground.arduino.cc/Code/NewPing
#include <LowPower.h> //added low power library from sparkfun https://learn.sparkfun.com/tutorials/reducing-arduino-power-consumption
#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 400 // Maximum  distance we want to ping for (in centimeters). was 400, max 500 was 500/2
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.
// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 10; //was 10
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average
int LED_STOP = 6; //Declare stop light
int LED_Yield =  7;// the number of the LED pin
int dist = 0; //Declare dist variable
int stopdist = 17; //declare stopping distance in cm (dist when Red LED turns on) 
int stopdist_plus_7 = stopdist+7; //declare distance when yellow LED stops blinking and becomes solid
int ultrasonic_power = 3;
//add 1.5 in to desired distance in order to account for casing
// 1in --> 2.5 in --> 6 cm
// 2in --> 3.5 in --> 9 cm
// 3in --> 4.5 in --> 11 cm
// 4in --> 5.5 in --> 14 cm
// 5in --> 6.5 in --> 17 cm
// 6in --> 7.5 in --> 19 cm
// 7in --> 8.5 in --> 22 cm
// 8in --> 9.5 in --> 24 cm
// 9in --> 10.5 in -->27 cm
// 10in -->11.5 in -->29 cm
// 11in -->12.5 in -->32 cm
// 12in -->13.5 in -->34 cm
// 13in -->14.5 in -->37 cm
// 14in -->15.5 in -->39 cm
// 15in -->16.5 in -->42 cm
// 16in -->17.5 in -->44 cm
// Variables will change :
int ledState = LOW;             // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned long previousMillis_2 = 0;
// constants won't change :
const long interval = average; //was 850/3          // interval at which to blink (milliseconds)
const long interval_2 = 1; // was 1000/2interval for turning off LEDs divide by 2 for 8mhz clock speed, else dont for 16, decreased interval_2 size to 100 to account for walking in front of sensor
unsigned long time;
void setup() 
{  
  Serial.begin(57600); // Open serial monitor at 115200 baud to see ping results.
  // set the digital pin as output:
  pinMode(ultrasonic_power, OUTPUT);
  pinMode(LED_Yield, OUTPUT);
  pinMode(LED_STOP, OUTPUT);
  digitalWrite(LED_Yield, HIGH); //Yellow LED WORKS
  digitalWrite(LED_STOP, HIGH);  //Red LED WORKS
  delay(1000);
  digitalWrite(LED_Yield, LOW);
  digitalWrite(LED_STOP, LOW);
  delay(500);
  digitalWrite(LED_Yield, HIGH); 
  digitalWrite(LED_STOP, HIGH);
  delay(1000);
  //low power measures 11/8/17
  //from http://www.fiz-ix.com/2012/11/save-power-by-disabling-arduino-peripherals/
  // Disable the ADC by setting the ADEN bit (bit 7)  of the
  // ADCSRA register to zero.
  ADCSRA = ADCSRA & B01111111;
  // Disable the analog comparator by setting the ACD bit
  // (bit 7) of the ACSR register to one.
  ACSR = B10000000;
  // Disable digital input buffers on all analog input pins
  // by setting bits 0-5 of the DIDR0 register to one.
  // Of course, only do this if you are not using the analog 
  // inputs for your project.
  DIDR0 = DIDR0 | B00111111; 
}
void loop() 
{
  const long interval = average/1; //yellow LED blinking interval
  digitalWrite(ultrasonic_power, HIGH);
  delay(50);
  digitalWrite(ultrasonic_power, HIGH);
  //sensor smoothing code: https://www.arduino.cc/en/Tutorial/Smoothing
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = dist;
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;   
  // if we're at the end of the array...
  if (readIndex >= numReadings) {
  // ...wrap around to the beginning:
  readIndex = 0;
   }   
  // calculate the average:
  average = (total / numReadings);
  // send it to the computer as ASCII digits
  //  Serial.println(average);     
  delay(50); // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
  unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  Serial.print("Ping: ");
  Serial.print(uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)
  dist = uS / US_ROUNDTRIP_CM; //did div by 2 in prescaler update
  Serial.println("cm");
  //  Serial.print(average);   
  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.
  unsigned long currentMillis = millis(); 
if (average > 10 && average <= stopdist) //
    { 
      if (average < stopdist && (currentMillis - previousMillis_2 < 10000))//was 4500 //if less than 17cm (default stopdist) and close to wall for less than 10 s turn on light  
      {
      if (average > 10) // was 10
        {
        digitalWrite(LED_STOP, HIGH);
        delay(1000); //changed, was 2500
        }
        else
        {
        digitalWrite(LED_STOP, LOW);  
        }
      }  
      if ((currentMillis - previousMillis_2) > 15000) //was 7500
      {
        for (byte i = 0; i < 1; ++i) //wait to start detection again ~20s after car left parking spot
        {
        digitalWrite(ultrasonic_power, LOW);
        LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
        digitalWrite(LED_STOP, LOW); 
        digitalWrite(LED_Yield, LOW);   
        }
        Serial.println("just left low power");
      }
    }
    else
    {
    //Following code in if statement modified from https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
    //if (average > 65)   //added nested if statement
    if (average > stopdist_plus_7) //was 45 then 45/2
     {
      if (average < 390) //was 390 then 300 was 450/2
        {
        if (currentMillis - previousMillis >= interval) {
          // save the last time you blinked the LED
          previousMillis = currentMillis;
           
          // if the LED is off turn it on and vice-versa:
          if (ledState == LOW) {
            ledState = HIGH;
      
            } else {
            ledState = LOW;
            }
            // set the LED with the ledState of the variable:
            digitalWrite(LED_Yield, ledState);
          }
        } 
      }   
    //Serial.println("prevmill is");
    //Serial.println(previousMillis);
    //Serial.println("currentmill is");
    //Serial.println(currentMillis);
    //Serial.println(currentMillis-previousMillis);   
    //this if statement means we are within range of wall (< 390 cm) for interval_2 time,  lights are blinking 
    if ((currentMillis - previousMillis_2 >= interval_2) && average >= stopdist+3) { //changed average >= stopdist to > stopdist
      //if 17 cm (default stopdist) or farther from wall for 1 second, prev becomes current, added +3 due to issue of light turning on when car parked
      previousMillis_2 = currentMillis; 
     }
    //Serial.println("prevmill_2 is");
    //Serial.println(previousMillis_2);
    //Serial.println("currentmill is");
    //Serial.println(currentMillis);     
    //Serial.println("prevmill_2 is");
    //Serial.println(previousMillis_2);
    //Serial.println("currentmill is");
    //Serial.println(currentMillis);
    //Serial.println(currentMillis - previousMillis_2); 
    if (average > stopdist) //
      {
      //  if (average < 65)
      if (average < stopdist_plus_7)
        {
        digitalWrite(LED_Yield, HIGH);
        }
      }
      if (average >= stopdist)
        {
        digitalWrite(LED_STOP, LOW); 
        }
    }
    if (average < 10) //was 10/2
      {
        digitalWrite(ultrasonic_power, LOW);
        digitalWrite(LED_Yield, LOW);
        digitalWrite(LED_STOP, LOW);
        delay(950); //works, added 11/8/17
        digitalWrite(ultrasonic_power, HIGH);
        delay(50);
      }
 }

Nuances:

Delays are needed after the ultrasonic sensor is turned on and off.

Step 5: Hardware Checks/Casing and Circuitry

Picture of Hardware Checks/Casing and Circuitry

Make sure that the ultrasonic sensor is exactly parallel to the incoming surface of the car. I've also done a bit of prototyping to find a casing that looks nice and supports the sensor and arduino. For the casing, I used foam board. I've added 1.5 in (~3.81 cm) to the stop distance in order to account for the thickness of the casing surrounding the ultrasonic sensor.

Only 2 LEDs, one 200ohm resistor, 4 jumper wires, an arduino, a breadboard and an ultrasonic sensor are needed for this project.

Ultrasonic Sensor to Arduino Pin Assignments:

Trigger Pin ---> Pin 12

Echo Pin ------> Pin 11

Vcc--------------> Pin 3

GND ------------> GND

LED Pin Assignments: (Using a breadboard connect the GND Pin of the Red and Yellow LEDs to GND of the arduino to use only one ground pin in the arduino and reserve the other for the ultrasonic sensor)

Use a 220 ohm resistor for many 2 V LEDs as seen here: https://www.arduino.cc/en/Tutorial/Blink?from=Tutorial.BlinkingLED

Red LED-------> Pin 6

Yellow LED ---> Pin 7

I used a 200 ohm resistor for 2V, 20mA LEDs and it worked fine. Please see my photo for more detailed circuitry and calculations. Given that an LED is not ohmic, I wasn't 100% sure how to account for the dimming I noticed when both LEDs connected to the same ground are on at the same time. I hypothesize that it could be (I^2)R heating losses due to increased current coming from each LED since the current would pass through the same resistor. If anyone could provide insight on this, that would be appreciated. If this is the case, then the mimimum resistance needed is 3V/.04A = 75 ohms. This is less than the 150 ohm resistance that was needed for 1 LED to operate at its rated amperage.

Once this is done, make sure the NewPing and LowPower libraries are installed, upload the code and you're on your way to using a cool simple automated parking device! Perhaps you too could make it for your family or friends! I'd love to see others make this device too!

If you are interested in purchasing a kit or a fully assembled version of the device, message me on instructables or leave a comment showing interest. I'd just like to get an idea. I'm working on a better system that's not perfected yet but should be pretty neat once done. The funds gained from this will support my other projects which are in the area of aviation as seen here: https://www.youtube.com/user/Aeropib. If you know how to make efficient plastic or cardboard casing or custom designed circuits, also please let me know. I know that the arduino is a prototyping board so it would be neat to see what the next level of boards looks like.


Thank you Instructables for allowing the world wide sharing of ideas!

Comments

MillennialDIYer (author)2017-12-07

The title sounds like a great start to a joke. I wonder if the car answers back "roadkill"...

Pib2 (author)MillennialDIYer2017-12-08

haha, I know right but not if the sensor has anything to do with it:)

About This Instructable

1,304views

14favorites

License:

Bio: Imagine___Design___Build___Test___ Post //with enlightening purpose
More by Pib2:"STOP!!" Said the Sensor to the Car.3D Printed Servo Receptacle for Durability The Flying Gyro Stabilized VTOL Cardboard Box
Add instructable to: