Go Down

Topic: Code to track car in 3 stages: parked, then moving, then away (Read 84 times) previous topic - next topic

s1godfrey

Ok, this is my first time posting for help on a code which should be simple. But, I simply can't get it.

What I want to do is while using ultra sonic sensor (HC-SR04), determine if car is parked, moving or away based on it's distance. When the car has gone from parked, to moving, then away, trigger relay to simulate pushing garage door opener button.

The code I used is from 'newping with median', which works well. Then I tried using a classic switch case example to create the 3 stages:

1) range 1/ case 0: car is parked (more than 0 but less than ~25).
2) range 2/ case 1: car is moving (more than 25 but less than ~50).
3) range 3/ case 2: car is away (more than 50 to end of range).

But what I can't do is use flags to determine that the car did indeed transition from parked->moving->away.

If I can get that phase, it will be good, then when car is away, something cannot simply block the sensor, like a insect or something random and trigger the garage door to open. It has to go through the stages (parked, moving, away).

I hope I have explained my desire well enough. Here is the code at hand:

Code: [Select]

// ---------------------------------------------------------------------------
// Calculate a ping median using the ping_timer() method.
// ---------------------------------------------------------------------------

#include <NewPing.h>

#define ITERATIONS    5 // Number of iterations.
#define TRIGGER_PIN   5 // Arduino pin tied to trigger pin on ping sensor.
#define ECHO_PIN      4 // Arduino pin tied to echo pin on ping sensor.
#define MAX_DISTANCE 150 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).
#define ledPin 10

unsigned long pingTimer[ITERATIONS]; // Holds the times when the next ping should happen for each iteration.
unsigned int cm[ITERATIONS];         // Where the ping distances are stored.
uint8_t currentIteration = 0;        // Keeps track of iteration step.
const int sensorMin = 0;      // sensor minimum, discovered through experiment
const int sensorMax = 50;    // sensor maximum, discovered through experiment
static bool parked = false;
int flag = 0;
int lastflag = 0;

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
  Serial.begin(115200);
  pingTimer[0] = millis() + 75;            // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < ITERATIONS; i++) // Set the starting time for each iteration.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
  digitalWrite (10, LOW);
}

void loop() {
  for (uint8_t i = 0; i < ITERATIONS; i++) { // Loop through all the iterations.
    if (millis() >= pingTimer[i]) {          // Is it this iteration's time to ping?
      pingTimer[i] += PING_INTERVAL * ITERATIONS; // Set next time this sensor will be pinged.
      if (i == 0 && currentIteration == ITERATIONS - 1) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      sonar.timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentIteration = i;        // Sensor being accessed.
      cm[currentIteration] = 0;    // Make distance zero in case there's no ping echo for this iteration.
      sonar.ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }
}
void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar.check_timer())
    cm[currentIteration] = sonar.ping_result / US_ROUNDTRIP_CM;
}

void oneSensorCycle() { // All iterations complete, calculate the median.
  unsigned int uS[ITERATIONS];
  uint8_t j, it = ITERATIONS;
  uS[0] = NO_ECHO;
  for (uint8_t i = 0; i < it; i++) { // Loop through iteration results.
    if (cm[i] != NO_ECHO) { // Ping in range, include as part of median.
      if (i > 0) {          // Don't start sort till second ping.
        for (j = i; j > 0 && uS[j - 1] < cm[i]; j--) // Insertion sort loop.
          uS[j] = uS[j - 1];                         // Shift ping array to correct position for sort insertion.
      } else j = 0;         // First ping is sort starting point.
      uS[j] = cm[i];        // Add last ping to array in sorted position.
    } else it--;            // Ping out of range, skip and don't include as part of median.
  }

  int sensorReading = uS[it >> 1];
  int range = map(sensorReading, sensorMin, sensorMax, 0, 2);

  Serial.println(sensorReading);
  lastflag=flag;

  switch (range) {
    case 0:    // car is parked
      Serial.print("parked");
      flag=0;
      Serial.print(flag);
      Serial.println(lastflag);
      break;
    case 1:    // car is moving out of garage
      Serial.println("moving");
      flag=1;
      Serial.print(flag);
      Serial.println(lastflag);
      break;
    case 2:    // car is past garage door, close door
      Serial.println("away");
      flag=2;
      Serial.print(flag);
      Serial.println(lastflag);
      break;
  }
  delay(1);        // delay in between reads for stability

// only allow relay to trigger if car goes from parked to moving and then to away.
// when car returns, only allow to go from away, then moving, then parked.
// this way something can't simply block and unblock the sensor triggering relay, since it didn't go through all the stages of moving car out of garage.


if (flag != lastflag) // something changed
{
  if ((flag=2) || (lastflag=1))  // changed from moving to away, trigger relay.
  {
  Trigger_Relay();
  }
}
}
void Trigger_Relay()
{
  Serial.println ("garage door toggle");  // will be replace with closing relay for 2 seconds and release, emulating garage door opener being pressed.
}

jimLee

States..

//globals..
enum carStates { parked, moving, away }; // May not have gotten syntax right, but you get the idea.
carStates myCarIs;


in setup() you..

 myCarIs  = parked;


in loop() look at your ping value, look at your saved state, then decide what to do next, if anything.

example: if myCarIs==moving and the new state will be away? Close the door.

-jimlee

-jim lee
PNW Ardiuno & Maker club
1012 9Th Street, Anacortes, WA 98221 (Around the back of building)

gcjr

having a hard time understanding your code based on what you described you want to do

sounds like you want to periodically (~ 1/sec) and when the distance changes from < threshold to > threshold, you want to perform some action.  So you need to keep track of the previous distance.
greg - somerset, nj

s1godfrey

What I am trying to achieve is to verify that a parked car moved away from the sensor until it was far enough away to be considered "away" (outside the garage). Then to trigger a relay that is wired to the manual switch in parallel, which closes the garage door.

So, the sequence needs to follow this path:

parked -> moving -> away

This will ensure that the car was the trigger and not something random like someone going into the garage and in sensor range (say 20cm or so) causing it to false trigger and then open/close the garage door.

If I simply look at range limits (5-20cm) being parked and then did a simply if statement (if ping>20) for example, then whats to prevent false triggers.

By having to flow in a particular sequence its highly unlikely anything random coming across the sensor would be able to duplicate the exact sequence (parked, moving, away) or I could of used (close, near, far). The idea is the same.

What I have recently added is using an array to state the pattern then check if flag[] matches the pattern. I got this method by example of 'arduino safe'. Where they used push buttons that had to be in a predetermined order to open the safe (trigger a solenoid or like).

Code: [Select]

int awaySequence[3] = {1, 2, 3};
.
.
.
 ctr++;
  if (ctr > 3) {
    ctr = 0;
  }
  int sensorReading = uS[it >> 1];  // returned data from newping median result

  int range = map(sensorReading, sensorMin, sensorMax, 0, 2);

  Serial.println(sensorReading);  // debugging only

  switch (range) {
    case 0:
      Serial.println("parked  ");
      flag[ctr] = 1;
      break;
    case 1:
      Serial.println("moving  ");
      flag[ctr] = 2;
      break;
    case 2:
      Serial.println("away  ");
      flag[ctr] = 3;
      break;

for (int i = 0; i < 3; i++) {
    if (flag[i] == awaySequence[i])
    {
      Trigger_Relay();  //call function to momentarily close relay
      }




This seems to work, but then it gets stuck on away and keeps triggering the relay. I am not sure the method to clear/reset the array so the whole cycle can start over again.


Thank you for helping

 

gcjr

why do you care if it's parked or even not moving?

don't you just need to know when (!) it gets far enough?   or are you constantly going to close the door when it's far enough?





if the car is in the garage, won't it block detecting an other movement in the garage?

if the door is already closing/closed what else needs to be done?
greg - somerset, nj

s1godfrey

Think of it as a runner going over hurdles. If he clears on then knocks down the second and clears the third, he did not complete the course 100%.

I want to do the same, I want parked car (sensor distance ~ 10cm) to move out (sensor distance ~ 30cm) and then pull out of garage (sensor distance ~ 50cm ) then I will know that it was the only object that cleared all the hurdles.

If it was not that way, and say only set at some limit (sensor distance ~ 50 cm) then what if the sensor fails to read it or has a misread, then it wont close the garage door and I'd have to drive back and forth until I hit that sweet spot and finally close the door.

In this fashion you dont pass into 'away' unless you were first parked, and so on...

The code I have now seems to work, it doesnt skip stages, it clears all 3 hurdles, but then it simply stays that way continually calling the Trigger_Relay function over and over. I need to find a way to reset to a standby mode or something like that until the car is returned to 'parked' position and then start the routine over again.

A plus would be do be able to do the same in reverse (away, moving, parked) and if passing all 3 stages have it automatically close the garage door behind me, maybe with a time delay for insurance.

s1godfrey

Code: [Select]

int sensorReading = uS[it >> 1];
  int range = map(sensorReading, sensorMin, sensorMax, 0, 2);

  Serial.println(sensorReading);

  switch (range) {
    case 0:
      //   Serial.println("parked  ");
      flag[0] = 1;
      break;
    case 1:
      //   Serial.println("moving  ");
      flag[1] = 2;
      break;
    case 2:
      //   Serial.println("away  ");
      flag[2] = 3;
      break;
  }
  delay(1);        // delay in between reads for stability

  for (int i = 0; i < 3; i++) {
    Serial.print(flag[i]);
    Serial.print(" ");
    if (flag[i] == awaySequence[i])
    {
     Serial.println("correct");
    }
    else {
     Serial.println("wrong");
    }
    }

Go Up