Arduino Vehicle Traffic Counter

From MakerCave
Jump to navigation Jump to search
Arduino Vehicle Traffic Counter.jpg

I wanted a method of counting the number of vehicle that passed through the Sleep Country Amphitheater entrance gate during events.

Air Differential Pressure Sensor Enclosures

Air Differential Pressure Sensor Enclosure.jpg

I needed to position the air differential pressure sensors for the outer lanes at the edges of those lanes and then run wire back to the Arduino. Originally I was envisioning enclosing a sensor inside 2" PCV pipe fittings, with wiring coming out one end and the rubber tubing out the other. Then I realized I own a MakerBot Replicator Dual 3D Printer. Make an enclosure.

Single Lane

// include the library code:
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
RTC_DS1307 rtc; // define the Real Time Clock object

// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()

// these constants won't change:
const int ledPin = 13;      // led connected to digital pin 13
const int airSensorLane1 = A0; // the Lane 1 air pressure differential sensor is connected to analog pin 0
const int chipSelect = 10; // for the data logging shield, we use digital pin 10 for the SD cs line

File logfile; // the logging file

String timestamp;


// these variables will change:
int sensorReadingLane1 = 0;      // variable to store the value read from the sensor pin A0
int ledState = LOW;         // variable used to store the last LED status, to toggle the light
int initialLane1 = 0;
int countLane1 = 0;

void setup() {
  pinMode(ledPin, OUTPUT); // declare the ledPin as as OUTPUT
  
  Serial.begin(9600);       // use the serial port
  
  Serial.println("Starting setup...");
  delay(5000);
  
  initialLane1 = analogRead(airSensorLane1);
  
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  lcd.setBacklight(RED);
  
  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("CARD FAILURE!");
    // don't do anything more:
    return;
  }
  Serial.println("Card initialized.");  
  Serial.print("Logging to: ");
  Serial.println("TRAFFIC.CSV");
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Card initialized");
  lcd.setCursor(0,1);
  lcd.print("TRAFFIC.CSV");
  
  logfile = SD.open("TRAFFIC.CSV", FILE_WRITE);
  logfile.println("");
  logfile.println("NEW SESSION");
  //#if ECHO_TO_SERIAL
    //Serial.println("Creating header: milliseconds,time,lane,pressure,initialPressure");
  //#endif
  
  Wire.begin();  
  if (!rtc.begin()) {
    if (logfile) { 
      logfile.println("RTC failed");
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("RTC FAILURE!"); 
    }
    Serial.println("RTC failed");
  }
  logfile.close();
  Serial.println("Setup complete");
  Serial.print("Initial Lane 1 Pressure Reading:");
  Serial.println(initialLane1);
}

void loop() {  
  DateTime currentTimeDate = rtc.now(); // get current timestamp
  
  // read the sensor and store it in the variable sensorReading:
  sensorReadingLane1 = analogRead(airSensorLane1);
  
  uint8_t buttons = lcd.readButtons();
  
  if (buttons & BUTTON_UP) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("RESETTING COUNT");
    delay(5000);
    countLane1 = 0;
  }
  if (buttons & BUTTON_DOWN) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Lane1 Count:");
    lcd.print(countLane1);
  }
  
  // if the sensor reading is greater than the threshold:
  if (sensorReadingLane1 > initialLane1 + 2) {
    
    ledState = !ledState;   // toggle the status of the ledPin:
    digitalWrite(ledPin, ledState); // update the LED pin itself:  
    
    // log milliseconds since starting
    uint32_t m = millis();     
    
    // log time
    timestamp = String(currentTimeDate.year());
    timestamp += "/";
    timestamp += String(currentTimeDate.month());
    timestamp += "/";
    timestamp += String(currentTimeDate.day());
    timestamp += " ";
    timestamp += String(currentTimeDate.hour());
    timestamp += ":";
    timestamp += String(currentTimeDate.minute());
    timestamp += ":";
    timestamp += String(currentTimeDate.second());
    Serial.println(timestamp);
    
    ++countLane1; // increase the Lane 1 Count
    if (!SD.begin(chipSelect)) {logfile = SD.open("TRAFFIC.CSV", FILE_WRITE);} // open TRAFFIC.CSV file on SD Card
    if (logfile) {
      logfile.print(m);
      logfile.print(", ");
      logfile.print(timestamp);
      logfile.print(", 1, ");
      logfile.print(sensorReadingLane1);
      logfile.print(", ");
      logfile.println(initialLane1);
    }  
    //else {
      //lcd.clear();
      //lcd.setCursor(0,0);
      //lcd.print("UNABLE TO WRITE");
      //lcd.setCursor(0,1);
      //lcd.print("TO SD CARD!");
    //} // write data to TRAFFIC.CSV
    if (!SD.begin(chipSelect)) {logfile.close();}
    #if ECHO_TO_SERIAL
      Serial.print(m);
      Serial.print(", ");
      Serial.print(timestamp);
      Serial.print(", 1, ");
      Serial.print(sensorReadingLane1);
      Serial.print(", ");
      Serial.println(initialLane1);
      Serial.print("Lane 1 Air Differential Pressure Sensor Reading: ");
      Serial.print(sensorReadingLane1);
      Serial.print(" of ");
      Serial.println(initialLane1);
    #endif // echo to serial monitor
    
    // update LCD screen
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Lane 1:");
    lcd.print(countLane1);
    lcd.setCursor(0,1);
    lcd.print("Reading:");
    lcd.print(sensorReadingLane1);
    lcd.print(" [");
    lcd.print(initialLane1);
    lcd.print("]");      

  }
  delay(100);  // delay to avoid overloading the serial port buffer
}

Two Lanes

  • I had to account for a greater difference in air pressure in order to trigger the counter. I had to boost it from two increments higher than the initial reading to five increments.
  • Walking on the tubes is different than driving over the tubes. It was taking too long in between the sensor reads, so I eliminated any delay and move all triggered-based instructions inside trigger control structure (i.e. get the current timestamp or Arduino run time only if a trigger has been activated). Once I did that, the Arduino Vehicle Traffic Counter was able to count car driving over the tubes.
  • Binary sketch size: 21,818 bytes (of a 32,256 byte maximum)
 
/* Vehicle Traffic Counter
  
   This sketch reads a Freescale MPX5500DP Air Pressure Sensors to detect when a vehicle rolls over rubber tub. 
   It reads an analog pin and compares the result to a the initial readings of the sensors. 
   If the result is greater than the initial readings, it tallies the trigger, displays it on a LCD
   display and records the triggered event to a SD card.

 */
 
// include the library code:
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
RTC_DS1307 rtc; // define the Real Time Clock object

// These #defines make it easy to set the backlight color
#define RED 0x1
//#define YELLOW 0x3
//#define GREEN 0x2
//#define TEAL 0x6
//#define BLUE 0x4
//#define VIOLET 0x5
//#define WHITE 0x7

#define ECHO_TO_SERIAL   0 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()

// these constants won't change:
//const int ledPin = 13;      // led connected to digital pin 13
const int airSensorLane1 = A0; // the Lane 1 air pressure differential sensor is connected to analog pin 0
const int airSensorLane2 = A1; // the Lane 1 air pressure differential sensor is connected to analog pin 0
const int chipSelect = 10; // for the data logging shield, we use digital pin 10 for the SD cs line

File logfile; // the logging file


// these variables will change:
int sensorReadingLane1 = 0;      // variable to store the value read from the sensor pin A0
int sensorReadingLane2 = 0;      // variable to store the value read from the sensor pin A0
//int ledState = LOW;         // variable used to store the last LED status, to toggle the light
int initialLane1 = 0;
int initialLane2 = 0;
int countLane1 = 0;
int countLane2 = 0;
char temp_date_and_time[22];

void setup() {
//  pinMode(ledPin, OUTPUT); // declare the ledPin as as OUTPUT
  
  #if ECHO_TO_SERIAL
    Serial.begin(9600);       // use the serial port
    Serial.println("Starting setup...");
  #endif
    // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  lcd.setBacklight(RED);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Starting...");
  delay(5000);
  
  initialLane1 = analogRead(airSensorLane1);
  initialLane2 = analogRead(airSensorLane2);
  
  
  // initialize the SD card
  #if ECHO_TO_SERIAL
    Serial.print("Initializing SD card...");
  #endif
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("CARD FAILURE!");
    // don't do anything more:
    return;
  }
  #if ECHO_TO_SERIAL
    Serial.println("Card initialized.");  
    Serial.print("Logging to:TRAFFIC.CSV");
  #endif
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Card initialized");
  lcd.setCursor(0,1);
  lcd.print("TRAFFIC.CSV->GO!");
  
  date_and_time(temp_date_and_time); //get current timestamp
  
  logfile = SD.open("TRAFFIC.CSV", FILE_WRITE);
  logfile.println("");
  logfile.print("NEW SESSION:");
  logfile.println(temp_date_and_time);
  
  Wire.begin();  
  if (!rtc.begin()) {
    if (logfile) { 
      logfile.println("RTC failed");
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("RTC FAILURE!"); 
    }
    #if ECHO_TO_SERIAL
      Serial.println("RTC failed");
    #endif
  }
  logfile.close();
  #if ECHO_TO_SERIAL
    Serial.println("Setup complete");
    Serial.print("Initial Lane 1 Pressure Reading:");
    Serial.println(initialLane1);
    Serial.print("Initial Lane 2 Pressure Reading:");
    Serial.println(initialLane2);
  #endif
}

void loop() {  

  // read the sensor and store it in the variable sensorReading:
  sensorReadingLane1 = analogRead(airSensorLane1);
  sensorReadingLane2 = analogRead(airSensorLane2);
  
  uint8_t buttons = lcd.readButtons(); 
  if (buttons & BUTTON_UP) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("RESETTING COUNT");
    delay(5000);
    countLane1 = 0;
    countLane2 = 0;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("COUNT RESET");
  }
  if (buttons & BUTTON_DOWN) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Lane1 Count:");
    lcd.print(countLane1);
    lcd.setCursor(0,1);
    lcd.print("Lane2 Count:");
    lcd.print(countLane2);
  }
  
  uint32_t m = millis(); // log milliseconds since starting

  // Lane1 Trigger:
  if (sensorReadingLane1 > initialLane1 + 2) {
    
//    ledState = !ledState;   // toggle the status of the ledPin:
//    digitalWrite(ledPin, ledState); // update the LED pin itself:      
    date_and_time(temp_date_and_time); // log time
    ++countLane1; // increase the Lane 1 Count
    if (!SD.begin(chipSelect)) {
      logfile = SD.open("TRAFFIC.CSV", FILE_WRITE);
    } // open TRAFFIC.CSV file on SD Card
    if (logfile) {
      logResults(m, temp_date_and_time, 1, sensorReadingLane1, initialLane1); // write data to TRAFFIC.CSV
    } else {
      cardError();
    } 
    if (!SD.begin(chipSelect)) {
      logfile.close();
    }
    #if ECHO_TO_SERIAL
      updateSerial(m, temp_date_and_time, 1, sensorReadingLane1, initialLane1); // echo to serial monitor
    #endif 
    
    updateLCD(1,countLane1,sensorReadingLane1,initialLane1); // update LCD screen

  }
  // Lane2 Trigger:
  if (sensorReadingLane2 > initialLane2 + 6) {
    
//    ledState = !ledState;   // toggle the status of the ledPin:
//    digitalWrite(ledPin, ledState); // update the LED pin itself:  
    date_and_time(temp_date_and_time); // log time
    ++countLane2; // increase the Lane 2 Count
    if (!SD.begin(chipSelect)) {
      logfile = SD.open("TRAFFIC.CSV", FILE_WRITE);
    } // open TRAFFIC.CSV file on SD Card
    if (logfile) {
      logResults(m, temp_date_and_time, 2, sensorReadingLane2, initialLane2); // write data to TRAFFIC.CSV
    } else {
      cardError();
    } 
    if (!SD.begin(chipSelect)) {
      logfile.close();
    }
    #if ECHO_TO_SERIAL
      updateSerial(m, temp_date_and_time, 2, sensorReadingLane2, initialLane2); // echo to serial monitor
    #endif 
    
    updateLCD(2,countLane2,sensorReadingLane2,initialLane2); // update LCD screen

  }
  #if ECHO_TO_SERIAL
    delay(100);  // delay to avoid overloading the serial port buffer; however, will cause issues with the device being able to recognize actual vehicle traffic.
  #endif
}

void date_and_time(char *buffer) {
  DateTime now = rtc.now();
  sprintf(buffer, "%02d/%02d/%d %02d:%02d:%02d", now.month(), now.day(), now.year(), now.hour(), now.minute(), now.second());
}

void updateLCD(int lane, int count, int reading, int initial) {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Lane ");
  lcd.print(lane);
  lcd.print(":");
  lcd.print(count);
  lcd.setCursor(0,1);
  lcd.print("Reading:");
  lcd.print(reading);
  lcd.print(" [");
  lcd.print(initial);
  lcd.print("]");
}

void updateSerial(uint32_t ms, char *cts, int lane, int reading, int initial) {
  Serial.print(ms);
  Serial.print(", ");
  Serial.print(cts);
  Serial.print(", ");
  Serial.print(lane);
  Serial.print(", ");
  Serial.print(reading);
  Serial.print(", ");
  Serial.println(initial);
  Serial.print("Lane ");
  Serial.print(lane);
  Serial.print(" Air Differential Pressure Sensor Reading: ");
  Serial.print(reading);
  Serial.print(" of ");
  Serial.println(initial);
}

void logResults(uint32_t ms, char *cts, int lane, int reading, int initial) {
  logfile.print(ms);
  logfile.print(", ");
  logfile.print(cts);
  logfile.print(", ");
  logfile.print(lane);
  logfile.print(", ");
  logfile.print(reading);
  logfile.print(", ");
  logfile.println(initial);
}

void cardError() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("UNABLE TO WRITE");
  lcd.setCursor(0,1);
  lcd.print("TO SD CARD!");
}