Distance Detector

Warning

This journal entry is the combination of analog IO, LEDs, ultrasonic sensors and shift registers

Parts Used

  • 7 Leds

  • 1 Buzzer

  • 1 shift register

  • 1 ultrasonic sensor

  • 1 2000Ω resistor

  • 20 jumper wires

  • 1 bread board

  • 1 arduino uno

Time Spent

Approximately 12 hours. Distrubuted highly unevenly over the range of 7 days.

Key Points

  • The program can run about 3 times faster if it does not write anything to Serial

  • Ultrasonic Sensor is not reliable all the time. To elimate inaccurate results, a smoothing algorithm is necessary.

  • There is a specialized library named newping for ultrasonic sensor

Smoothing algorithm

In this project, I wrote a smoothing algorithm to stay away from inaccuate ultrasonic sensor data while minimize the delay. At first, I tried to use moving average to smooth the data. However, it causes a perceivable amount of delay (approximately 0.5 seconds) in the output.

The final algorithm works as follow

  1. Read a new distance in cm from sensor

  2. If the distance is greater than 255, use 255 instead

  3. Let m be the median of last 5 inputs

  4. Decide the margin of error with this formula \(\frac{20}{(\log_{2}(m))^2}\times 100\%\)

  5. If the new distance beyond margin of error, output m, else output new distance.

  6. Goto 1

The Code

#include <math.h>

int potPin = 0;
int latchPin = 5;
int clockPin = 6;
int dataPin = 4;

int leds = 0;

const int trigPin = 9;
const int echoPin = 10;
// defines variables
long duration;
int distance;

#define dataSize 5

int data[dataSize];
int inputLoc = 0;

void setup()
{
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT); // Sets the echoPin as an Input
  for (int i = 0; i < dataSize; i++) data[i] = calculateDistance();


  Serial.begin(9600);
}

void loop()
{

  leds = 0;

  distance = correctedOutput(20);

  if (distance > 64) {
    leds = 0b10000000;
  } else {

    distance /= 8;
    bitSet(leds, distance);

  }
  updateShiftRegister();
  delay(50)

}


int correctedOutput(float threshold) {
  int r= calculateDistance();
  int newReading = r>256? 256:r ;
  int corrected;
  int m = median(data, dataSize);

  float scaling = (threshold / pow((log(m) / log(2)),2));

    data[inputLoc] = newReading;
    inputLoc++;
    if (inputLoc == dataSize) inputLoc = 0;

    if ( (newReading > m + scaling*m ) or (newReading < m - scaling*m) )
    {
      corrected = m;
    } else {
      corrected = newReading;

    }

  #define DEBUG

  #ifdef DEBUG

  Serial.print("| Input value: ");
  Serial.print(r);
  Serial.print(" | Corrected to: ");
  Serial.print(corrected);
  Serial.print(" | Margin: +/- ");
  Serial.print(scaling*100);

  Serial.print("% | Valid Range for this input: ");
  Serial.print (m - scaling*m);
  Serial.print(" to ");
  Serial.print( scaling*m + m);
  Serial.print(" | Input accepted: ");
  Serial.println(corrected==newReading? "Yes" : "No");

  #endif

  return corrected;
}


void updateShiftRegister()
{
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, leds);
  digitalWrite(latchPin, HIGH);
}

int calculateDistance() {
  // Clears the trigPin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);
  // Calculating the distance
  distance = duration * 0.034 / 2;
  // Prints the distance on the Serial Monitor
  return distance;
}

int median(const int * arr, int len) {

    int newArr[len];
    for (int j = 0; j < len; j++) { //Duplicate arr
        newArr[j] = arr[j];
    }

    //Begin BubbleSort

    bool sorted;
    int tmp;
    do {
        sorted = true;
        for (int i=0; i < len - 1; i++) {
            if (newArr[i] < newArr[i + 1]) { //swap two values
                tmp = newArr[i];
                newArr[i] = newArr[i + 1];
                newArr[i + 1] = tmp;
                sorted = false;
            }
        }
    } while (!sorted);
    //End BubbleSort newArr
    return newArr[len/2];
}

Major Issues Encontered

  1. The first time to wire the shift register was really troublesome. Initially, I forgot to ground the shift register; it was working, but became super-hot. After that, the code was not working as desired. I soon realized that I had the the entire right side connections reversed. Everything was straightforward after I learned how the shiftOut function works.

  2. Memory Overflow. Because arduino uno has only 2 KiB of RAM, it is possible to overflow the memory if not careful. In such case, the program stops working (gets stucked at a data value) after a working for a while. One temporary solution is to unplug the arduino for about 10 seconds, so the RAM is cleared. In my case, I did not use the array pointer correctly as I am a new programmer to C, so the obesolete data are not garbage collected but stay in the memory for as long as it still gets power. Everything works fine after I found the programming error and fixed it.

  3. Not always reliable data from ultrasonic sensor. Solution: smoothing algorithm.