Aetos999
Published © LGPL

Arduino Car Counter

A low-power Arduino car counter that works by logging each time a car drives over a rubber tube across the road.

IntermediateFull instructions provided8,968
Arduino Car Counter

Things used in this project

Hardware components

Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×1
Pimoroni RV 3028 RTC Breakout
×1
SparkFun Level Shifting microSD Breakout
SparkFun Level Shifting microSD Breakout
×1
NXP MPX5100DP Pressure Sensor
×1
DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
TaydaElectronics DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
×1
Flash Memory Card, MicroSD Card
Flash Memory Card, MicroSD Card
×1
SparkFun Barrel jack power switch
×1
4mm double headed bulkhead connector
×1
clear plastic tube, 4mm internal diamter
×1
4mm internal diameter rubber fuel hose
This is what the cars are going to drive over. Usually sold on ebay as something like “Cotton Braided Rubber Fuel Hose for Unleaded Petrol / Diesel, Oil Line Pipe”. Get as much as you need to go all the way across your road.
×1
4mm hose T-connector
this is to seal the end of the hose
×1
Pelicase 1120
Any waterproof case of this size works well
×1
Hook Up Wire Kit, 22 AWG
Hook Up Wire Kit, 22 AWG
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Plier, Needle Nose
Plier, Needle Nose
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Arduino Car Counter

This is a rough approximation of the circuit; the RTC and the pressure sensor aren't the models you use in my project but they have the same connections. (I'm rubbish at fritzing, half of the parts aren't in the library and nothing lines up with the holes. )

Car Counter Fritzing

Code

Car counter pressure test

Arduino
This code allows you to see that the pressure sensor is working. It's just the main code with some bits commented out.
/* ---------------------------------------------------------------
    I am trying to record a reading when the reading from an air
    pressure sensor jumps suddenly - ie when a car drives over a tube
    attached to the sensor. The Pressure Sensor is a MPX5500DP 
    I am using the movingAvg library to simplify the maths a bit.
   ---------------------------------------------------------------*/

#include <movingAvg.h> // https://github.com/JChristensen/movingAvg
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <RV-3028-C7.h>                 // this is a low-power clock

const uint8_t airSensor(A0);  // connect pressure sensor from A0 pin to ground
movingAvg airAvg(20);        // define the moving average object
//
//unsigned long new_time=0;     // set some variables for preventing reading multiple spikes
unsigned long old_time=0;     // 

RV3028 rtc;                  // get the clock going

String timestamp;           //

     
const int chipSelect = 8;      // SD card pin
int ledPin = 5;                // the pin the LED is connected to

File logFile; // the logging file



/* --------------------------------------------
/*  setup the average, pullup the air sensor, 
 *   begin the serial monitor and show an initial 
 *   reading so we knnow it is working
   --------------------------------------------*/
void setup()
{
  Wire.begin();
     Serial.begin(9600);                 // begin serial monitor
     if (rtc.begin() == false)
    {
     Serial.println("Something went wrong, check wiring");
     while (1);
    }
    else
      Serial.println("RTC online!");
    
  delay(1000);
   pinMode(airSensor, INPUT_PULLUP);   // air sensor

   airAvg.begin();                     //averages
 
   pinMode(chipSelect, OUTPUT); 
   digitalWrite(chipSelect, HIGH); //ALWAYS pullup the ChipSelect pin with the SD library
   delay(100);
   
  // 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(8, OUTPUT);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
   Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("Card initialized.");
  Serial.print("Logging to: ");
  Serial.println("TRAFFIC.CSV");
  logFile = SD.open("TRAFFIC.CSV", FILE_WRITE);
  logFile.println("");
  logFile.println("NEW SESSION");
  logFile.close();

  Serial.println("Setup complete");
  Serial.println("initial reading");
  int pc = analogRead(airSensor); // read the sensor
  Serial.println(pc);
}

/* --------------------------------------------
/*  Each loop should comapare the reading against 
 *   the moving average, and if it is greater than 
 *   the specific amount, print this to the monitor
   --------------------------------------------*/
void loop()
{
   rtc.updateTime();                       // get the time
   int pc = analogRead(airSensor);        // read the sensor
   int avg = airAvg.reading(pc);          // calculate the moving average
//   int avgPlus2 = avg + 2;               // to simplify conditional below
//   unsigned long new_time = millis();    // this is to make sure peaks are spaced, in case a single count causes a double spike
//   
  delay(10);   // For some reason, the If statement that follows doesn't work without a delay here?????
//    
//   if ((pc > avgPlus2) && ((new_time - old_time) > 500))  // if the reading is greater than the average, print it
   {

    // write data to serial
    Serial.print(rtc.stringDate());
    Serial.print(" ");
    Serial.print(rtc.stringTime());
    Serial.print(", ");
    Serial.print(pc);
    Serial.print(", ");
    Serial.println(avg);
    
    logFile = SD.open("TRAFFIC.CSV", FILE_WRITE); // open TRAFFIC.CSV file on SD Card and write to it
    logFile.print(rtc.stringDate()); 
    logFile.print(" ");
    logFile.print(rtc.stringTime());
    logFile.print(", ");
    logFile.print(pc);
    logFile.print(", ");
    logFile.println(avg);
    logFile.close();


//    old_time = new_time;  // spacing spikes

   }
////   else
//   {
//      delay(1);    // this is needed for some reason to make the IF statement work
//    
//
//   }
}

 

Car Counter Full Code

Arduino
This code counts each time the pressure sensor is triggered
/* ---------------------------------------------------------------
    I am trying to record a reading when the reading from an air
    pressure sensor jumps suddenly - ie when a car drives over a tube
    attached to the sensor. The Pressure Sensor is a MPX5500DP 
    I am using the movingAvg library to simplify the maths a bit.
    Sarah Dalrymple - Aetos999
   ---------------------------------------------------------------*/

#include <movingAvg.h>  // https://github.com/JChristensen/movingAvg
#include <SD.h>
#include <SPI.h> 
#include <Wire.h>
#include <RV-3028-C7.h>        // https://github.com/constiko/RV-3028_C7-Arduino_Library

const uint8_t airSensor(A0);  // connect pressure sensor from A0 pin to ground

// This is the moving average - change the figure in brackets to what you want

movingAvg airAvg(20);        

unsigned long new_time=0;     // set some variables for preventing reading multiple spikes
unsigned long old_time=0;     // 

RV3028 rtc;                  // get the clock going

String timestamp;           //
     
const int chipSelect = 8;      // SD card pin
int ledPin = 5;                // the pin the LED is connected to

File logFile; // the logging file



/* --------------------------------------------
/*  setup the average, pullup the air sensor, 
 *   begin the serial monitor and show an initial 
 *   reading so we knnow it is working
   --------------------------------------------*/
void setup()
{
  Wire.begin();
     Serial.begin(9600);                 // begin serial monitor
     if (rtc.begin() == false)
    {
     Serial.println("Something went wrong, check wiring");
     while (1);
    }
    else
      Serial.println("RTC online!");
    
  delay(1000);
   pinMode(airSensor, INPUT_PULLUP);   // air sensor

   airAvg.begin();                     //averages
 
   pinMode(chipSelect, OUTPUT); 
   digitalWrite(chipSelect, HIGH); //ALWAYS pullup the ChipSelect pin with the SD library
   delay(100);
   
  // 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(8, OUTPUT);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
   Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("Card initialized.");
  Serial.print("Logging to: ");
  Serial.println("TRAFFIC.CSV");
  logFile = SD.open("TRAFFIC.CSV", FILE_WRITE);
  logFile.println("");
  logFile.println("NEW SESSION");
  logFile.close();

  Serial.println("Setup complete");
  Serial.println("initial reading");
  int pc = analogRead(airSensor); // read the sensor
  Serial.println(pc);
}

/* --------------------------------------------
/*  Each loop should comapare the reading against 
 *   the moving average, and if it is greater than 
 *   the specific amount, print this to the monitor
   --------------------------------------------*/
void loop()
{
   rtc.updateTime();                       // get the time
   int pc = analogRead(airSensor);        // read the sensor
   int avg = airAvg.reading(pc);          // calculate the moving average
   int avgPlus = avg + 5;               // to simplify conditional below
   unsigned long new_time = millis();    // this is to make sure peaks are spaced, in case a single count causes a double spike
   
   delay(1);   // For some reason, the If statement that follows doesn't work without a delay here?? I think this is a bug in my system

 // if the reading is greater than the average & however many ms has passed since last time, print it. 
 // This is the ms value between peaks - change it to help calibrate your counter
   if ((pc > avgPlus) && ((new_time - old_time) > 400))  
   {

    // write data to serial
    Serial.print(rtc.stringDate());
    Serial.print(" ");
    Serial.print(rtc.stringTime());
    Serial.print(", ");
    Serial.print(pc);
    Serial.print(", ");
    Serial.println(avg);
    
    logFile = SD.open("TRAFFIC.CSV", FILE_WRITE); // open TRAFFIC.CSV file on SD Card and write to it
    Serial.println("log");
    logFile.print(rtc.stringDate()); 
    logFile.print(" ");
    logFile.print(rtc.stringTime());
    logFile.print(", ");
    logFile.print(pc);
    logFile.print(", ");
    logFile.println(avg);
    logFile.close();
    Serial.println("done.");

    old_time = new_time;  // spacing spikes

   }
   else
   {
      delay(1);    // this is needed for some reason to make the IF statement work
    

   }
}

 

New! Overnight sleep

Arduino
I've added a few lines and rearranged the code so the arduino goes to sleep at night, as we are only open 10am - 5pm. This saves a bit of battery! you need to physically connect pin 2 on the arduino to the alm or int pin on the RTC.
/* ---------------------------------------------------------------
    Record a reading when the reading from an air
    pressure sensor jumps suddenly - ie when a car drives over a tube
    attached to the sensor. The Pressure Sensor is a MPX5500DP
    I am using the movingAvg library to simplify the maths a bit.
    It goes to sleep between 10pm and 8am as there is no traffic then.

   ---------------------------------------------------------------*/

#include <movingAvg.h>  // https://github.com/JChristensen/movingAvg
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <RV-3028-C7.h>        // https://github.com/constiko/RV-3028_C7-Arduino_Library
#include "LowPower.h"         // low power library

RV3028 rtc;                  // get the clock going

const uint8_t airSensor(A0);  // connect pressure sensor from A0 pin to ground

const int wakeUpPin = 2;      // Use pin2  to wake up. 

//The below variables control what the alarm will be set to. See the RV-3028 library for more details
int alm_minute = 28;           // this is ignored
int alm_hour = 8;            // wake up at 8am
int alm_date_or_weekday = 2; // this is ignored
bool alm_isweekday = false;  // this is ignored
uint8_t alm_mode = 5;         // this mode means we just check when the hours match = alarm

movingAvg airAvg(20);        // sets the moving average - change the figure in brackets to what you want

unsigned long new_time = 0;   // set some variables for preventing reading multiple spikes
unsigned long old_time = 0;   //

String timestamp;           //

const int chipSelect = 8;      // SD card pin
int ledPin = 5;                // the pin the LED is connected to

File logFile;               // the logging file


void wakeUp()
{
    // Just a handler for the pin interrupt.
}

/* --------------------------------------------
  /*  setup the average, pullup the air sensor,
     begin the serial monitor and show an initial
     reading so we knnow it is working
   --------------------------------------------*/
void setup()
{
  pinMode(wakeUpPin, INPUT_PULLUP);        // Configure wake up pin as input.

  Wire.begin();
  Serial.begin(9600);                 // begin serial monitor
  if (rtc.begin() == false)
  {
    Serial.println("Something went wrong, check wiring");
    while (1);
  }
  else
    Serial.println("RTC online!");
  //Enable alarm interrupt
  rtc.enableAlarmInterrupt(alm_minute, alm_hour, alm_date_or_weekday, alm_isweekday, alm_mode);
  
  
  delay(1000);
  
  pinMode(airSensor, INPUT_PULLUP);   // air sensor

  airAvg.begin();                     //averages

  pinMode(chipSelect, OUTPUT);
  digitalWrite(chipSelect, HIGH); //ALWAYS pullup the ChipSelect pin with the SD library
  delay(100);

  // 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(8, OUTPUT);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("Card initialized.");
  Serial.print("Logging to: ");
  Serial.println("TRAFFIC.CSV");
  logFile = SD.open("TRAFFIC.CSV", FILE_WRITE);
  logFile.println("");
  logFile.println("NEW SESSION");
  logFile.close();

  Serial.println("Setup complete");
  Serial.println("initial reading");
  int pc = analogRead(airSensor); // read the sensor
  Serial.println(pc);
  delay(250);
}

/* --------------------------------------------
  /*  Each loop should comapare the reading against
     the moving average, and if it is greater than
     the specific amount, it records to SD and serial
   --------------------------------------------*/
void loop()
{
  rtc.updateTime();             // get the time
  int hour = rtc.getHours();    // get the hour
  if (hour == 22)               // if it's a match, go to sleep
   {
    Serial.println("sleep time");
    GoToSleep();  //go to the sleep void
   }
  CountCars(); // or just keep counting the cars
}

void GoToSleep()
{
    // Allow wake up pin to trigger interrupt on low.
    attachInterrupt(0, wakeUp, LOW); // go to the wakeUp void when alarm triggers
    // Enter power down state with ADC and BOD module disabled.
    // Wake up when wake up pin is low.
    logFile = SD.open("TRAFFIC.CSV", FILE_WRITE); // open TRAFFIC.CSV file on SD Card and write to it, plus a few lines to know it's all working. you can remove these once you are happy it is
    logFile.print("went to sleep");
    logFile.print(",");
    logFile.print(rtc.stringDate());
    logFile.print("-");
    logFile.println(rtc.stringTime());
    logFile.close();
    Serial.println("sleep time");
    delay(50);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    
    // Wakes up here!
    detachInterrupt(0); //disable the interupt pin

    Serial.println("alarm"); //let me know we woke up OK
    logFile = SD.open("TRAFFIC.CSV", FILE_WRITE); // open TRAFFIC.CSV file on SD Card and write to it
    logFile.print("woke up");
    logFile.print(",");
    logFile.print(rtc.stringDate());
    logFile.print("-");
    logFile.println(rtc.stringTime());
    logFile.close();
    delay(50);

 CountCars(); //go back to counting cars
}

void CountCars()
{
    rtc.updateTime();                      // get the time
    int pc = analogRead(airSensor);        // read the sensor
    int avg = airAvg.reading(pc);          // calculate the moving average
    int avgPlus = avg + 5;                 // to simplify conditional below
    unsigned long new_time = millis();     // this is to make sure spikes are spaced, in case a single count causes a double spike

    delay(1);   // For some reason, the If statement that follows doesn't work without a delay here?? I think this is a bug in my system

    // if the reading is greater than the average & however many ms has passed since last time, print it.
    // This is the ms value between spikes - change it to help calibrate your counter
    // eg don't count a spike if it occurs within 400ms of the last one
    if ((pc > avgPlus) && ((new_time - old_time) > 400))
    {
      // write data to serial
      Serial.print(rtc.stringDate());
      Serial.print(" ");
      Serial.print(rtc.stringTime());
      Serial.print(", ");
      Serial.print(pc);
      Serial.print(", ");
      Serial.println(avg);

      // write data to the csv file
      logFile = SD.open("TRAFFIC.CSV", FILE_WRITE); // open TRAFFIC.CSV file on SD Card and write to it
      Serial.println("log");
      logFile.print(rtc.stringDate());
      logFile.print(",");
      logFile.print(rtc.stringTime());
      logFile.print(",");
      logFile.print(pc);
      logFile.print(",");
      logFile.println(avg);
      logFile.close();
      Serial.println("done.");

      old_time = new_time;  // spacing spikes

    }
    else
    {
      delay(5);    

    }

}

Credits

Aetos999

Aetos999

0 projects • 0 followers

Comments